facet.e2e-spec.ts 28 KB

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