facet.e2e-spec.ts 14 KB

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