facet.e2e-spec.ts 29 KB

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