facet.e2e-spec.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import { createTestEnvironment } from '@vendure/testing';
  2. import gql from 'graphql-tag';
  3. import path from 'path';
  4. import { dataDir, TEST_SETUP_TIMEOUT_MS, testConfig } from './config/test-config';
  5. import { initialData } from './fixtures/e2e-initial-data';
  6. import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/fragments';
  7. import {
  8. CreateFacet,
  9. CreateFacetValues,
  10. DeleteFacet,
  11. DeleteFacetValues,
  12. DeletionResult,
  13. FacetWithValues,
  14. GetFacetList,
  15. GetFacetWithValues,
  16. GetProductListWithVariants,
  17. GetProductWithVariants,
  18. LanguageCode,
  19. UpdateFacet,
  20. UpdateFacetValues,
  21. UpdateProduct,
  22. UpdateProductVariants,
  23. } from './graphql/generated-e2e-admin-types';
  24. import {
  25. CREATE_FACET,
  26. GET_FACET_LIST,
  27. GET_PRODUCT_WITH_VARIANTS,
  28. UPDATE_FACET,
  29. UPDATE_PRODUCT,
  30. UPDATE_PRODUCT_VARIANTS,
  31. } from './graphql/shared-definitions';
  32. // tslint:disable:no-non-null-assertion
  33. describe('Facet resolver', () => {
  34. const { server, adminClient } = createTestEnvironment(testConfig);
  35. let brandFacet: FacetWithValues.Fragment;
  36. let speakerTypeFacet: FacetWithValues.Fragment;
  37. beforeAll(async () => {
  38. await server.init({
  39. dataDir,
  40. initialData,
  41. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  42. customerCount: 1,
  43. });
  44. await adminClient.init();
  45. await adminClient.asSuperAdmin();
  46. }, TEST_SETUP_TIMEOUT_MS);
  47. afterAll(async () => {
  48. await server.destroy();
  49. });
  50. it('createFacet', async () => {
  51. const result = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(CREATE_FACET, {
  52. input: {
  53. isPrivate: false,
  54. code: 'speaker-type',
  55. translations: [{ languageCode: LanguageCode.en, name: 'Speaker Type' }],
  56. values: [
  57. {
  58. code: 'portable',
  59. translations: [{ languageCode: LanguageCode.en, name: 'Portable' }],
  60. },
  61. ],
  62. },
  63. });
  64. speakerTypeFacet = result.createFacet;
  65. expect(speakerTypeFacet).toMatchSnapshot();
  66. });
  67. it('updateFacet', async () => {
  68. const result = await adminClient.query<UpdateFacet.Mutation, UpdateFacet.Variables>(UPDATE_FACET, {
  69. input: {
  70. id: speakerTypeFacet.id,
  71. translations: [{ languageCode: LanguageCode.en, name: 'Speaker Category' }],
  72. },
  73. });
  74. expect(result.updateFacet.name).toBe('Speaker Category');
  75. });
  76. it('createFacetValues', async () => {
  77. const result = await adminClient.query<CreateFacetValues.Mutation, CreateFacetValues.Variables>(
  78. CREATE_FACET_VALUES,
  79. {
  80. input: [
  81. {
  82. facetId: speakerTypeFacet.id,
  83. code: 'pc',
  84. translations: [{ languageCode: LanguageCode.en, name: 'PC Speakers' }],
  85. },
  86. {
  87. facetId: speakerTypeFacet.id,
  88. code: 'hi-fi',
  89. translations: [{ languageCode: LanguageCode.en, name: 'Hi Fi Speakers' }],
  90. },
  91. ],
  92. },
  93. );
  94. expect(result.createFacetValues).toMatchSnapshot();
  95. });
  96. it('updateFacetValues', async () => {
  97. const result = await adminClient.query<UpdateFacetValues.Mutation, UpdateFacetValues.Variables>(
  98. UPDATE_FACET_VALUES,
  99. {
  100. input: [
  101. {
  102. id: speakerTypeFacet.values[0].id,
  103. code: 'compact',
  104. },
  105. ],
  106. },
  107. );
  108. expect(result.updateFacetValues[0].code).toBe('compact');
  109. });
  110. it('facets', async () => {
  111. const result = await adminClient.query<GetFacetList.Query>(GET_FACET_LIST);
  112. const { items } = result.facets;
  113. expect(items.length).toBe(2);
  114. expect(items[0].name).toBe('category');
  115. expect(items[1].name).toBe('Speaker Category');
  116. brandFacet = items[0];
  117. speakerTypeFacet = items[1];
  118. });
  119. it('facet', async () => {
  120. const result = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  121. GET_FACET_WITH_VALUES,
  122. {
  123. id: speakerTypeFacet.id,
  124. },
  125. );
  126. expect(result.facet!.name).toBe('Speaker Category');
  127. });
  128. describe('deletion', () => {
  129. let products: GetProductListWithVariants.Items[];
  130. beforeAll(async () => {
  131. // add the FacetValues to products and variants
  132. const result1 = await adminClient.query<GetProductListWithVariants.Query>(
  133. GET_PRODUCTS_LIST_WITH_VARIANTS,
  134. );
  135. products = result1.products.items;
  136. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  137. input: {
  138. id: products[0].id,
  139. facetValueIds: [speakerTypeFacet.values[0].id],
  140. },
  141. });
  142. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  143. UPDATE_PRODUCT_VARIANTS,
  144. {
  145. input: [
  146. {
  147. id: products[0].variants[0].id,
  148. facetValueIds: [speakerTypeFacet.values[0].id],
  149. },
  150. ],
  151. },
  152. );
  153. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  154. input: {
  155. id: products[1].id,
  156. facetValueIds: [speakerTypeFacet.values[1].id],
  157. },
  158. });
  159. });
  160. it('deleteFacetValues deletes unused facetValue', async () => {
  161. const facetValueToDelete = speakerTypeFacet.values[2];
  162. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  163. DELETE_FACET_VALUES,
  164. {
  165. ids: [facetValueToDelete.id],
  166. force: false,
  167. },
  168. );
  169. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  170. GET_FACET_WITH_VALUES,
  171. {
  172. id: speakerTypeFacet.id,
  173. },
  174. );
  175. expect(result1.deleteFacetValues).toEqual([
  176. {
  177. result: DeletionResult.DELETED,
  178. message: ``,
  179. },
  180. ]);
  181. expect(result2.facet!.values[0]).not.toEqual(facetValueToDelete);
  182. });
  183. it('deleteFacetValues for FacetValue in use returns NOT_DELETED', async () => {
  184. const facetValueToDelete = speakerTypeFacet.values[0];
  185. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  186. DELETE_FACET_VALUES,
  187. {
  188. ids: [facetValueToDelete.id],
  189. force: false,
  190. },
  191. );
  192. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  193. GET_FACET_WITH_VALUES,
  194. {
  195. id: speakerTypeFacet.id,
  196. },
  197. );
  198. expect(result1.deleteFacetValues).toEqual([
  199. {
  200. result: DeletionResult.NOT_DELETED,
  201. message: `The selected FacetValue is assigned to 1 Product, 1 ProductVariant`,
  202. },
  203. ]);
  204. expect(result2.facet!.values[0]).toEqual(facetValueToDelete);
  205. });
  206. it('deleteFacetValues for FacetValue in use can be force deleted', async () => {
  207. const facetValueToDelete = speakerTypeFacet.values[0];
  208. const result1 = await adminClient.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
  209. DELETE_FACET_VALUES,
  210. {
  211. ids: [facetValueToDelete.id],
  212. force: true,
  213. },
  214. );
  215. expect(result1.deleteFacetValues).toEqual([
  216. {
  217. result: DeletionResult.DELETED,
  218. message: `The selected FacetValue was removed from 1 Product, 1 ProductVariant and deleted`,
  219. },
  220. ]);
  221. // FacetValue no longer in the Facet.values array
  222. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  223. GET_FACET_WITH_VALUES,
  224. {
  225. id: speakerTypeFacet.id,
  226. },
  227. );
  228. expect(result2.facet!.values[0]).not.toEqual(facetValueToDelete);
  229. // FacetValue no longer in the Product.facetValues array
  230. const result3 = await adminClient.query<
  231. GetProductWithVariants.Query,
  232. GetProductWithVariants.Variables
  233. >(GET_PRODUCT_WITH_VARIANTS, {
  234. id: products[0].id,
  235. });
  236. expect(result3.product!.facetValues).toEqual([]);
  237. });
  238. it('deleteFacet that is in use returns NOT_DELETED', async () => {
  239. const result1 = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  240. DELETE_FACET,
  241. {
  242. id: speakerTypeFacet.id,
  243. force: false,
  244. },
  245. );
  246. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  247. GET_FACET_WITH_VALUES,
  248. {
  249. id: speakerTypeFacet.id,
  250. },
  251. );
  252. expect(result1.deleteFacet).toEqual({
  253. result: DeletionResult.NOT_DELETED,
  254. message: `The selected Facet includes FacetValues which are assigned to 1 Product`,
  255. });
  256. expect(result2.facet).not.toBe(null);
  257. });
  258. it('deleteFacet that is in use can be force deleted', async () => {
  259. const result1 = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  260. DELETE_FACET,
  261. {
  262. id: speakerTypeFacet.id,
  263. force: true,
  264. },
  265. );
  266. expect(result1.deleteFacet).toEqual({
  267. result: DeletionResult.DELETED,
  268. message: `The Facet was deleted and its FacetValues were removed from 1 Product`,
  269. });
  270. // FacetValue no longer in the Facet.values array
  271. const result2 = await adminClient.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
  272. GET_FACET_WITH_VALUES,
  273. {
  274. id: speakerTypeFacet.id,
  275. },
  276. );
  277. expect(result2.facet).toBe(null);
  278. // FacetValue no longer in the Product.facetValues array
  279. const result3 = await adminClient.query<
  280. GetProductWithVariants.Query,
  281. GetProductWithVariants.Variables
  282. >(GET_PRODUCT_WITH_VARIANTS, {
  283. id: products[1].id,
  284. });
  285. expect(result3.product!.facetValues).toEqual([]);
  286. });
  287. it('deleteFacet with no FacetValues works', async () => {
  288. const { createFacet } = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(
  289. CREATE_FACET,
  290. {
  291. input: {
  292. code: 'test',
  293. isPrivate: false,
  294. translations: [{ languageCode: LanguageCode.en, name: 'Test' }],
  295. },
  296. },
  297. );
  298. const result = await adminClient.query<DeleteFacet.Mutation, DeleteFacet.Variables>(
  299. DELETE_FACET,
  300. {
  301. id: createFacet.id,
  302. force: false,
  303. },
  304. );
  305. expect(result.deleteFacet.result).toBe(DeletionResult.DELETED);
  306. });
  307. });
  308. });
  309. export const GET_FACET_WITH_VALUES = gql`
  310. query GetFacetWithValues($id: ID!) {
  311. facet(id: $id) {
  312. ...FacetWithValues
  313. }
  314. }
  315. ${FACET_WITH_VALUES_FRAGMENT}
  316. `;
  317. const DELETE_FACET_VALUES = gql`
  318. mutation DeleteFacetValues($ids: [ID!]!, $force: Boolean) {
  319. deleteFacetValues(ids: $ids, force: $force) {
  320. result
  321. message
  322. }
  323. }
  324. `;
  325. const DELETE_FACET = gql`
  326. mutation DeleteFacet($id: ID!, $force: Boolean) {
  327. deleteFacet(id: $id, force: $force) {
  328. result
  329. message
  330. }
  331. }
  332. `;
  333. const GET_PRODUCTS_LIST_WITH_VARIANTS = gql`
  334. query GetProductListWithVariants {
  335. products {
  336. items {
  337. id
  338. name
  339. variants {
  340. id
  341. name
  342. }
  343. }
  344. totalItems
  345. }
  346. }
  347. `;
  348. export const CREATE_FACET_VALUES = gql`
  349. mutation CreateFacetValues($input: [CreateFacetValueInput!]!) {
  350. createFacetValues(input: $input) {
  351. ...FacetValue
  352. }
  353. }
  354. ${FACET_VALUE_FRAGMENT}
  355. `;
  356. export const UPDATE_FACET_VALUES = gql`
  357. mutation UpdateFacetValues($input: [UpdateFacetValueInput!]!) {
  358. updateFacetValues(input: $input) {
  359. ...FacetValue
  360. }
  361. }
  362. ${FACET_VALUE_FRAGMENT}
  363. `;