facet.e2e-spec.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. import { pick } from '@vendure/common/lib/pick';
  2. import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
  3. import gql from 'graphql-tag';
  4. import path from 'path';
  5. import { initialData } from '../../../e2e-common/e2e-initial-data';
  6. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  7. import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/fragments';
  8. import {
  9. AssignProductsToChannel,
  10. ChannelFragment,
  11. CreateChannel,
  12. CreateFacet,
  13. CreateFacetValues,
  14. CurrencyCode,
  15. DeleteFacet,
  16. DeleteFacetValues,
  17. DeletionResult,
  18. FacetWithValues,
  19. GetFacetList,
  20. GetFacetWithValues,
  21. GetProductListWithVariants,
  22. GetProductWithFacetValues,
  23. GetProductWithVariants,
  24. LanguageCode,
  25. UpdateFacet,
  26. UpdateFacetValues,
  27. UpdateProduct,
  28. UpdateProductVariants,
  29. } from './graphql/generated-e2e-admin-types';
  30. import {
  31. ASSIGN_PRODUCT_TO_CHANNEL,
  32. CREATE_CHANNEL,
  33. CREATE_FACET,
  34. GET_FACET_LIST,
  35. GET_FACET_LIST_SIMPLE,
  36. GET_PRODUCT_WITH_VARIANTS,
  37. UPDATE_FACET,
  38. UPDATE_PRODUCT,
  39. UPDATE_PRODUCT_VARIANTS,
  40. } from './graphql/shared-definitions';
  41. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  42. // tslint:disable:no-non-null-assertion
  43. describe('Facet resolver', () => {
  44. const { server, adminClient, shopClient } = createTestEnvironment(testConfig());
  45. let brandFacet: FacetWithValues.Fragment;
  46. let speakerTypeFacet: FacetWithValues.Fragment;
  47. beforeAll(async () => {
  48. await server.init({
  49. initialData,
  50. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  51. customerCount: 1,
  52. });
  53. await adminClient.asSuperAdmin();
  54. }, TEST_SETUP_TIMEOUT_MS);
  55. afterAll(async () => {
  56. await server.destroy();
  57. });
  58. it('createFacet', async () => {
  59. const result = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(CREATE_FACET, {
  60. input: {
  61. isPrivate: false,
  62. code: 'speaker-type',
  63. translations: [{ languageCode: LanguageCode.en, name: 'Speaker Type' }],
  64. values: [
  65. {
  66. code: 'portable',
  67. translations: [{ languageCode: LanguageCode.en, name: 'Portable' }],
  68. },
  69. ],
  70. },
  71. });
  72. speakerTypeFacet = result.createFacet;
  73. expect(speakerTypeFacet).toMatchSnapshot();
  74. });
  75. it('updateFacet', async () => {
  76. const result = await adminClient.query<UpdateFacet.Mutation, UpdateFacet.Variables>(UPDATE_FACET, {
  77. input: {
  78. id: speakerTypeFacet.id,
  79. translations: [{ languageCode: LanguageCode.en, name: 'Speaker Category' }],
  80. isPrivate: true,
  81. },
  82. });
  83. expect(result.updateFacet.name).toBe('Speaker Category');
  84. });
  85. it('createFacetValues', async () => {
  86. const { createFacetValues } = await adminClient.query<
  87. CreateFacetValues.Mutation,
  88. CreateFacetValues.Variables
  89. >(CREATE_FACET_VALUES, {
  90. input: [
  91. {
  92. facetId: speakerTypeFacet.id,
  93. code: 'pc',
  94. translations: [{ languageCode: LanguageCode.en, name: 'PC Speakers' }],
  95. },
  96. {
  97. facetId: speakerTypeFacet.id,
  98. code: 'hi-fi',
  99. translations: [{ languageCode: LanguageCode.en, name: 'Hi Fi Speakers' }],
  100. },
  101. ],
  102. });
  103. expect(createFacetValues.length).toBe(2);
  104. expect(pick(createFacetValues.find(fv => fv.code === 'pc')!, ['code', 'facet', 'name'])).toEqual({
  105. code: 'pc',
  106. facet: {
  107. id: 'T_2',
  108. name: 'Speaker Category',
  109. },
  110. name: 'PC Speakers',
  111. });
  112. expect(pick(createFacetValues.find(fv => fv.code === 'hi-fi')!, ['code', 'facet', 'name'])).toEqual({
  113. code: 'hi-fi',
  114. facet: {
  115. id: 'T_2',
  116. name: 'Speaker Category',
  117. },
  118. name: 'Hi Fi Speakers',
  119. });
  120. });
  121. it('updateFacetValues', async () => {
  122. const result = await adminClient.query<UpdateFacetValues.Mutation, UpdateFacetValues.Variables>(
  123. UPDATE_FACET_VALUES,
  124. {
  125. input: [
  126. {
  127. id: speakerTypeFacet.values[0].id,
  128. code: 'compact',
  129. },
  130. ],
  131. },
  132. );
  133. expect(result.updateFacetValues[0].code).toBe('compact');
  134. });
  135. it('facets', async () => {
  136. const result = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  137. const { items } = result.facets;
  138. expect(items.length).toBe(2);
  139. expect(items[0].name).toBe('category');
  140. expect(items[1].name).toBe('Speaker Category');
  141. brandFacet = items[0];
  142. speakerTypeFacet = items[1];
  143. });
  144. it('facets by shop-api', async () => {
  145. const result = await shopClient.query<GetFacetList.Query>(GET_FACET_LIST_SIMPLE);
  146. const { items } = result.facets;
  147. expect(items.length).toBe(1);
  148. expect(items[0].name).toBe('category');
  149. });
  150. it('facet', async () => {
  151. const result = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  152. GET_FACET_WITH_VALUES,
  153. {
  154. id: speakerTypeFacet.id,
  155. },
  156. );
  157. expect(result.facet!.name).toBe('Speaker Category');
  158. });
  159. it('product.facetValues resolver omits private facets in shop-api', async () => {
  160. const publicFacetValue = brandFacet.values[0];
  161. const privateFacetValue = speakerTypeFacet.values[0];
  162. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  163. input: {
  164. id: 'T_1',
  165. facetValueIds: [publicFacetValue.id, privateFacetValue.id],
  166. },
  167. });
  168. const { product } = await shopClient.query<
  169. GetProductWithFacetValues.Query,
  170. GetProductWithFacetValues.Variables
  171. >(GET_PRODUCT_WITH_FACET_VALUES, {
  172. id: 'T_1',
  173. });
  174. expect(product?.facetValues.map(v => v.id).includes(publicFacetValue.id)).toBe(true);
  175. expect(product?.facetValues.map(v => v.id).includes(privateFacetValue.id)).toBe(false);
  176. });
  177. it('productVariant.facetValues resolver omits private facets in shop-api', async () => {
  178. const publicFacetValue = brandFacet.values[0];
  179. const privateFacetValue = speakerTypeFacet.values[0];
  180. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  181. UPDATE_PRODUCT_VARIANTS,
  182. {
  183. input: [
  184. {
  185. id: 'T_1',
  186. facetValueIds: [publicFacetValue.id, privateFacetValue.id],
  187. },
  188. ],
  189. },
  190. );
  191. const { product } = await shopClient.query<
  192. GetProductWithFacetValues.Query,
  193. GetProductWithFacetValues.Variables
  194. >(GET_PRODUCT_WITH_FACET_VALUES, {
  195. id: 'T_1',
  196. });
  197. const productVariant1 = product?.variants.find(v => v.id === 'T_1');
  198. expect(productVariant1?.facetValues.map(v => v.id).includes(publicFacetValue.id)).toBe(true);
  199. expect(productVariant1?.facetValues.map(v => v.id).includes(privateFacetValue.id)).toBe(false);
  200. });
  201. describe('deletion', () => {
  202. let products: GetProductListWithVariants.Items[];
  203. beforeAll(async () => {
  204. // add the FacetValues to products and variants
  205. const result1 = await adminClient.query<GetProductListWithVariants.Query>(
  206. GET_PRODUCTS_LIST_WITH_VARIANTS,
  207. );
  208. products = result1.products.items;
  209. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  210. input: {
  211. id: products[0].id,
  212. facetValueIds: [speakerTypeFacet.values[0].id],
  213. },
  214. });
  215. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  216. UPDATE_PRODUCT_VARIANTS,
  217. {
  218. input: [
  219. {
  220. id: products[0].variants[0].id,
  221. facetValueIds: [speakerTypeFacet.values[0].id],
  222. },
  223. ],
  224. },
  225. );
  226. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  227. input: {
  228. id: products[1].id,
  229. facetValueIds: [speakerTypeFacet.values[1].id],
  230. },
  231. });
  232. });
  233. it('deleteFacetValues deletes unused facetValue', async () => {
  234. const facetValueToDelete = speakerTypeFacet.values[2];
  235. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  236. DELETE_FACET_VALUES,
  237. {
  238. ids: [facetValueToDelete.id],
  239. force: false,
  240. },
  241. );
  242. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  243. GET_FACET_WITH_VALUES,
  244. {
  245. id: speakerTypeFacet.id,
  246. },
  247. );
  248. expect(result1.deleteFacetValues).toEqual([
  249. {
  250. result: DeletionResult.DELETED,
  251. message: ``,
  252. },
  253. ]);
  254. expect(result2.facet!.values[0]).not.toEqual(facetValueToDelete);
  255. });
  256. it('deleteFacetValues for FacetValue in use returns NOT_DELETED', async () => {
  257. const facetValueToDelete = speakerTypeFacet.values[0];
  258. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  259. DELETE_FACET_VALUES,
  260. {
  261. ids: [facetValueToDelete.id],
  262. force: false,
  263. },
  264. );
  265. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  266. GET_FACET_WITH_VALUES,
  267. {
  268. id: speakerTypeFacet.id,
  269. },
  270. );
  271. expect(result1.deleteFacetValues).toEqual([
  272. {
  273. result: DeletionResult.NOT_DELETED,
  274. message: `The selected FacetValue is assigned to 1 Product, 1 ProductVariant`,
  275. },
  276. ]);
  277. expect(result2.facet!.values.find(v => v.id === facetValueToDelete.id)).toBeDefined();
  278. });
  279. it('deleteFacetValues for FacetValue in use can be force deleted', async () => {
  280. const facetValueToDelete = speakerTypeFacet.values[0];
  281. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  282. DELETE_FACET_VALUES,
  283. {
  284. ids: [facetValueToDelete.id],
  285. force: true,
  286. },
  287. );
  288. expect(result1.deleteFacetValues).toEqual([
  289. {
  290. result: DeletionResult.DELETED,
  291. message: `The selected FacetValue was removed from 1 Product, 1 ProductVariant and deleted`,
  292. },
  293. ]);
  294. // FacetValue no longer in the Facet.values array
  295. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  296. GET_FACET_WITH_VALUES,
  297. {
  298. id: speakerTypeFacet.id,
  299. },
  300. );
  301. expect(result2.facet!.values[0]).not.toEqual(facetValueToDelete);
  302. // FacetValue no longer in the Product.facetValues array
  303. const result3 = await adminClient.query<
  304. GetProductWithVariants.Query,
  305. GetProductWithVariants.Variables
  306. >(GET_PRODUCT_WITH_VARIANTS, {
  307. id: products[0].id,
  308. });
  309. expect(result3.product!.facetValues).toEqual([]);
  310. });
  311. it('deleteFacet that is in use returns NOT_DELETED', async () => {
  312. const result1 = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  313. DELETE_FACET,
  314. {
  315. id: speakerTypeFacet.id,
  316. force: false,
  317. },
  318. );
  319. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  320. GET_FACET_WITH_VALUES,
  321. {
  322. id: speakerTypeFacet.id,
  323. },
  324. );
  325. expect(result1.deleteFacet).toEqual({
  326. result: DeletionResult.NOT_DELETED,
  327. message: `The selected Facet includes FacetValues which are assigned to 1 Product`,
  328. });
  329. expect(result2.facet).not.toBe(null);
  330. });
  331. it('deleteFacet that is in use can be force deleted', async () => {
  332. const result1 = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  333. DELETE_FACET,
  334. {
  335. id: speakerTypeFacet.id,
  336. force: true,
  337. },
  338. );
  339. expect(result1.deleteFacet).toEqual({
  340. result: DeletionResult.DELETED,
  341. message: `The Facet was deleted and its FacetValues were removed from 1 Product`,
  342. });
  343. // FacetValue no longer in the Facet.values array
  344. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  345. GET_FACET_WITH_VALUES,
  346. {
  347. id: speakerTypeFacet.id,
  348. },
  349. );
  350. expect(result2.facet).toBe(null);
  351. // FacetValue no longer in the Product.facetValues array
  352. const result3 = await adminClient.query<
  353. GetProductWithVariants.Query,
  354. GetProductWithVariants.Variables
  355. >(GET_PRODUCT_WITH_VARIANTS, {
  356. id: products[1].id,
  357. });
  358. expect(result3.product!.facetValues).toEqual([]);
  359. });
  360. it('deleteFacet with no FacetValues works', async () => {
  361. const { createFacet } = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(
  362. CREATE_FACET,
  363. {
  364. input: {
  365. code: 'test',
  366. isPrivate: false,
  367. translations: [{ languageCode: LanguageCode.en, name: 'Test' }],
  368. },
  369. },
  370. );
  371. const result = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  372. DELETE_FACET,
  373. {
  374. id: createFacet.id,
  375. force: false,
  376. },
  377. );
  378. expect(result.deleteFacet.result).toBe(DeletionResult.DELETED);
  379. });
  380. });
  381. describe('channels', () => {
  382. const SECOND_CHANNEL_TOKEN = 'second_channel_token';
  383. let createdFacet: CreateFacet.CreateFacet;
  384. beforeAll(async () => {
  385. const { createChannel } = await adminClient.query<
  386. CreateChannel.Mutation,
  387. CreateChannel.Variables
  388. >(CREATE_CHANNEL, {
  389. input: {
  390. code: 'second-channel',
  391. token: SECOND_CHANNEL_TOKEN,
  392. defaultLanguageCode: LanguageCode.en,
  393. currencyCode: CurrencyCode.USD,
  394. pricesIncludeTax: true,
  395. defaultShippingZoneId: 'T_1',
  396. defaultTaxZoneId: 'T_1',
  397. },
  398. });
  399. const { assignProductsToChannel } = await adminClient.query<
  400. AssignProductsToChannel.Mutation,
  401. AssignProductsToChannel.Variables
  402. >(ASSIGN_PRODUCT_TO_CHANNEL, {
  403. input: {
  404. channelId: (createChannel as ChannelFragment).id,
  405. productIds: ['T_1'],
  406. priceFactor: 0.5,
  407. },
  408. });
  409. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  410. });
  411. it('create Facet in channel', async () => {
  412. const { createFacet } = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(
  413. CREATE_FACET,
  414. {
  415. input: {
  416. isPrivate: false,
  417. code: 'channel-facet',
  418. translations: [{ languageCode: LanguageCode.en, name: 'Channel Facet' }],
  419. values: [
  420. {
  421. code: 'channel-value-1',
  422. translations: [{ languageCode: LanguageCode.en, name: 'Channel Value 1' }],
  423. },
  424. {
  425. code: 'channel-value-2',
  426. translations: [{ languageCode: LanguageCode.en, name: 'Channel Value 2' }],
  427. },
  428. ],
  429. },
  430. },
  431. );
  432. expect(createFacet.code).toBe('channel-facet');
  433. createdFacet = createFacet;
  434. });
  435. it('facets list in channel', async () => {
  436. const result = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  437. const { items } = result.facets;
  438. expect(items.length).toBe(1);
  439. expect(items.map(i => i.code)).toEqual(['channel-facet']);
  440. });
  441. it('Product.facetValues in channel', async () => {
  442. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  443. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  444. input: {
  445. id: 'T_1',
  446. facetValueIds: [brandFacet.values[0].id, ...createdFacet.values.map(v => v.id)],
  447. },
  448. });
  449. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  450. UPDATE_PRODUCT_VARIANTS,
  451. {
  452. input: [
  453. {
  454. id: 'T_1',
  455. facetValueIds: [brandFacet.values[0].id, ...createdFacet.values.map(v => v.id)],
  456. },
  457. ],
  458. },
  459. );
  460. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  461. const { product } = await adminClient.query<
  462. GetProductWithVariants.Query,
  463. GetProductWithVariants.Variables
  464. >(GET_PRODUCT_WITH_VARIANTS, {
  465. id: 'T_1',
  466. });
  467. expect(product?.facetValues.map(fv => fv.code).sort()).toEqual([
  468. 'channel-value-1',
  469. 'channel-value-2',
  470. ]);
  471. });
  472. it('ProductVariant.facetValues in channel', async () => {
  473. const { product } = await adminClient.query<
  474. GetProductWithVariants.Query,
  475. GetProductWithVariants.Variables
  476. >(GET_PRODUCT_WITH_VARIANTS, {
  477. id: 'T_1',
  478. });
  479. expect(product?.variants[0].facetValues.map(fv => fv.code).sort()).toEqual([
  480. 'channel-value-1',
  481. 'channel-value-2',
  482. ]);
  483. });
  484. it('updating Product facetValuesIds in channel only affects that channel', async () => {
  485. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  486. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  487. input: {
  488. id: 'T_1',
  489. facetValueIds: [createdFacet.values[0].id],
  490. },
  491. });
  492. const { product: productC2 } = await adminClient.query<
  493. GetProductWithVariants.Query,
  494. GetProductWithVariants.Variables
  495. >(GET_PRODUCT_WITH_VARIANTS, {
  496. id: 'T_1',
  497. });
  498. expect(productC2?.facetValues.map(fv => fv.code)).toEqual([createdFacet.values[0].code]);
  499. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  500. const { product: productCD } = await adminClient.query<
  501. GetProductWithVariants.Query,
  502. GetProductWithVariants.Variables
  503. >(GET_PRODUCT_WITH_VARIANTS, {
  504. id: 'T_1',
  505. });
  506. expect(productCD?.facetValues.map(fv => fv.code)).toEqual([
  507. brandFacet.values[0].code,
  508. createdFacet.values[0].code,
  509. ]);
  510. });
  511. it('updating ProductVariant facetValuesIds in channel only affects that channel', async () => {
  512. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  513. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  514. UPDATE_PRODUCT_VARIANTS,
  515. {
  516. input: [
  517. {
  518. id: 'T_1',
  519. facetValueIds: [createdFacet.values[0].id],
  520. },
  521. ],
  522. },
  523. );
  524. const { product: productC2 } = await adminClient.query<
  525. GetProductWithVariants.Query,
  526. GetProductWithVariants.Variables
  527. >(GET_PRODUCT_WITH_VARIANTS, {
  528. id: 'T_1',
  529. });
  530. expect(productC2?.variants.find(v => v.id === 'T_1')?.facetValues.map(fv => fv.code)).toEqual([
  531. createdFacet.values[0].code,
  532. ]);
  533. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  534. const { product: productCD } = await adminClient.query<
  535. GetProductWithVariants.Query,
  536. GetProductWithVariants.Variables
  537. >(GET_PRODUCT_WITH_VARIANTS, {
  538. id: 'T_1',
  539. });
  540. expect(productCD?.variants.find(v => v.id === 'T_1')?.facetValues.map(fv => fv.code)).toEqual([
  541. brandFacet.values[0].code,
  542. createdFacet.values[0].code,
  543. ]);
  544. });
  545. it(
  546. 'attempting to create FacetValue in Facet from another Channel throws',
  547. assertThrowsWithMessage(async () => {
  548. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  549. await adminClient.query<CreateFacetValues.Mutation, CreateFacetValues.Variables>(
  550. CREATE_FACET_VALUES,
  551. {
  552. input: [
  553. {
  554. facetId: brandFacet.id,
  555. code: 'channel-brand',
  556. translations: [{ languageCode: LanguageCode.en, name: 'Channel Brand' }],
  557. },
  558. ],
  559. },
  560. );
  561. }, `No Facet with the id '1' could be found`),
  562. );
  563. });
  564. // https://github.com/vendure-ecommerce/vendure/issues/715
  565. describe('code conflicts', () => {
  566. function createFacetWithCode(code: string) {
  567. return adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(CREATE_FACET, {
  568. input: {
  569. isPrivate: false,
  570. code,
  571. translations: [{ languageCode: LanguageCode.en, name: `Test Facet (${code})` }],
  572. values: [],
  573. },
  574. });
  575. }
  576. // https://github.com/vendure-ecommerce/vendure/issues/831
  577. it('updateFacet with unchanged code', async () => {
  578. const { createFacet } = await createFacetWithCode('some-new-facet');
  579. const result = await adminClient.query<UpdateFacet.Mutation, UpdateFacet.Variables>(
  580. UPDATE_FACET,
  581. {
  582. input: {
  583. id: createFacet.id,
  584. code: createFacet.code,
  585. },
  586. },
  587. );
  588. expect(result.updateFacet.code).toBe(createFacet.code);
  589. });
  590. it('createFacet with conflicting slug gets renamed', async () => {
  591. const { createFacet: result1 } = await createFacetWithCode('test');
  592. expect(result1.code).toBe('test');
  593. const { createFacet: result2 } = await createFacetWithCode('test');
  594. expect(result2.code).toBe('test-2');
  595. });
  596. it('updateFacet with conflicting slug gets renamed', async () => {
  597. const { createFacet } = await createFacetWithCode('foo');
  598. expect(createFacet.code).toBe('foo');
  599. const { updateFacet } = await adminClient.query<UpdateFacet.Mutation, UpdateFacet.Variables>(
  600. UPDATE_FACET,
  601. {
  602. input: {
  603. id: createFacet.id,
  604. code: 'test-2',
  605. },
  606. },
  607. );
  608. expect(updateFacet.code).toBe('test-3');
  609. });
  610. });
  611. });
  612. export const GET_FACET_WITH_VALUES = gql`
  613. query GetFacetWithValues($id: ID!) {
  614. facet(id: $id) {
  615. ...FacetWithValues
  616. }
  617. }
  618. ${FACET_WITH_VALUES_FRAGMENT}
  619. `;
  620. const DELETE_FACET_VALUES = gql`
  621. mutation DeleteFacetValues($ids: [ID!]!, $force: Boolean) {
  622. deleteFacetValues(ids: $ids, force: $force) {
  623. result
  624. message
  625. }
  626. }
  627. `;
  628. const DELETE_FACET = gql`
  629. mutation DeleteFacet($id: ID!, $force: Boolean) {
  630. deleteFacet(id: $id, force: $force) {
  631. result
  632. message
  633. }
  634. }
  635. `;
  636. const GET_PRODUCT_WITH_FACET_VALUES = gql`
  637. query GetProductWithFacetValues($id: ID!) {
  638. product(id: $id) {
  639. id
  640. facetValues {
  641. id
  642. name
  643. code
  644. }
  645. variants {
  646. id
  647. facetValues {
  648. id
  649. name
  650. code
  651. }
  652. }
  653. }
  654. }
  655. `;
  656. const GET_PRODUCTS_LIST_WITH_VARIANTS = gql`
  657. query GetProductListWithVariants {
  658. products {
  659. items {
  660. id
  661. name
  662. variants {
  663. id
  664. name
  665. }
  666. }
  667. totalItems
  668. }
  669. }
  670. `;
  671. export const CREATE_FACET_VALUES = gql`
  672. mutation CreateFacetValues($input: [CreateFacetValueInput!]!) {
  673. createFacetValues(input: $input) {
  674. ...FacetValue
  675. }
  676. }
  677. ${FACET_VALUE_FRAGMENT}
  678. `;
  679. export const UPDATE_FACET_VALUES = gql`
  680. mutation UpdateFacetValues($input: [UpdateFacetValueInput!]!) {
  681. updateFacetValues(input: $input) {
  682. ...FacetValue
  683. }
  684. }
  685. ${FACET_VALUE_FRAGMENT}
  686. `;