zone.e2e-spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import { DeletionResult } from '@vendure/common/lib/generated-types';
  2. import { Facet, LanguageCode, mergeConfig } from '@vendure/core';
  3. import { createTestEnvironment } 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 { graphql, ResultOf, VariablesOf } from './graphql/graphql-admin';
  9. import {
  10. addMembersToZoneDocument,
  11. createFacetDocument,
  12. createZoneDocument,
  13. deleteZoneDocument,
  14. getActiveChannelWithZoneMembersDocument,
  15. getCountryListDocument,
  16. getZoneDocument,
  17. getZonesDocument,
  18. removeMembersFromZoneDocument,
  19. updateChannelDocument,
  20. updateZoneDocument,
  21. } from './graphql/shared-definitions';
  22. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  23. describe('Zone resolver', () => {
  24. const { server, adminClient } = createTestEnvironment(
  25. mergeConfig(testConfig(), {
  26. customFields: {
  27. Zone: [
  28. {
  29. name: 'relatedFacet',
  30. type: 'relation',
  31. entity: Facet,
  32. },
  33. ],
  34. },
  35. }),
  36. );
  37. let countries: ResultOf<typeof getCountryListDocument>['countries']['items'];
  38. let zones: Array<{ id: string; name: string }>;
  39. let oceania: { id: string; name: string };
  40. let pangaea: ResultOf<typeof createZoneDocument>['createZone'];
  41. beforeAll(async () => {
  42. await server.init({
  43. initialData,
  44. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  45. customerCount: 1,
  46. });
  47. await adminClient.asSuperAdmin();
  48. const result = await adminClient.query(getCountryListDocument, {});
  49. countries = result.countries.items;
  50. }, TEST_SETUP_TIMEOUT_MS);
  51. afterAll(async () => {
  52. await server.destroy();
  53. });
  54. it('zones', async () => {
  55. const result = await adminClient.query(getZonesDocument);
  56. expect(result.zones.items.length).toBe(5);
  57. zones = result.zones.items;
  58. oceania = zones[0];
  59. });
  60. it('zone', async () => {
  61. const result = await adminClient.query(getZoneDocument, {
  62. id: oceania.id,
  63. });
  64. expect(result.zone!.name).toBe('Oceania');
  65. });
  66. it('zone.members field resolver', async () => {
  67. const { activeChannel } = await adminClient.query(getActiveChannelWithZoneMembersDocument);
  68. expect(activeChannel.defaultShippingZone?.members.length).toBe(2);
  69. });
  70. it('updateZone', async () => {
  71. const result = await adminClient.query(updateZoneDocument, {
  72. input: {
  73. id: oceania.id,
  74. name: 'oceania2',
  75. },
  76. });
  77. expect(result.updateZone.name).toBe('oceania2');
  78. });
  79. it('createZone', async () => {
  80. const result = await adminClient.query(createZoneDocument, {
  81. input: {
  82. name: 'Pangaea',
  83. memberIds: [countries[0].id, countries[1].id],
  84. },
  85. });
  86. pangaea = result.createZone;
  87. expect(pangaea.name).toBe('Pangaea');
  88. expect(pangaea.members.map(m => m.name)).toEqual([countries[0].name, countries[1].name]);
  89. });
  90. it('addMembersToZone', async () => {
  91. const result = await adminClient.query(addMembersToZoneDocument, {
  92. zoneId: oceania.id,
  93. memberIds: [countries[2].id, countries[3].id],
  94. });
  95. expect(!!result.addMembersToZone.members.find(m => m.name === countries[2].name)).toBe(true);
  96. expect(!!result.addMembersToZone.members.find(m => m.name === countries[3].name)).toBe(true);
  97. });
  98. it('removeMembersFromZone', async () => {
  99. const result = await adminClient.query(removeMembersFromZoneDocument, {
  100. zoneId: oceania.id,
  101. memberIds: [countries[0].id, countries[2].id],
  102. });
  103. expect(!!result.removeMembersFromZone.members.find(m => m.name === countries[0].name)).toBe(false);
  104. expect(!!result.removeMembersFromZone.members.find(m => m.name === countries[2].name)).toBe(false);
  105. expect(!!result.removeMembersFromZone.members.find(m => m.name === countries[3].name)).toBe(true);
  106. });
  107. describe('deletion', () => {
  108. it('deletes Zone not used in any TaxRate', async () => {
  109. const result1 = await adminClient.query(deleteZoneDocument, {
  110. id: pangaea.id,
  111. });
  112. expect(result1.deleteZone).toEqual({
  113. result: DeletionResult.DELETED,
  114. message: '',
  115. });
  116. const result2 = await adminClient.query(getZonesDocument);
  117. expect(result2.zones.items.find(c => c.id === pangaea.id)).toBeUndefined();
  118. });
  119. it('does not delete Zone that is used in one or more TaxRates', async () => {
  120. const result1 = await adminClient.query(deleteZoneDocument, {
  121. id: oceania.id,
  122. });
  123. expect(result1.deleteZone).toEqual({
  124. result: DeletionResult.NOT_DELETED,
  125. message:
  126. 'The selected Zone cannot be deleted as it is used in the following ' +
  127. 'TaxRates: Standard Tax Oceania, Reduced Tax Oceania, Zero Tax Oceania',
  128. });
  129. const result2 = await adminClient.query(getZonesDocument);
  130. expect(result2.zones.items.find(c => c.id === oceania.id)).not.toBeUndefined();
  131. });
  132. it('does not delete Zone that is used as a Channel defaultTaxZone', async () => {
  133. await adminClient.query(updateChannelDocument, {
  134. input: {
  135. id: 'T_1',
  136. defaultTaxZoneId: oceania.id,
  137. },
  138. });
  139. const result1 = await adminClient.query(deleteZoneDocument, {
  140. id: oceania.id,
  141. });
  142. expect(result1.deleteZone).toEqual({
  143. result: DeletionResult.NOT_DELETED,
  144. message:
  145. 'The selected Zone cannot be deleted as it used as a default in the following Channels: ' +
  146. '__default_channel__',
  147. });
  148. const result2 = await adminClient.query(getZonesDocument);
  149. expect(result2.zones.items.find(c => c.id === oceania.id)).not.toBeUndefined();
  150. });
  151. it('does not delete Zone that is used as a Channel defaultShippingZone', async () => {
  152. await adminClient.query(updateChannelDocument, {
  153. input: {
  154. id: 'T_1',
  155. defaultTaxZoneId: 'T_1',
  156. defaultShippingZoneId: oceania.id,
  157. },
  158. });
  159. const result1 = await adminClient.query(deleteZoneDocument, {
  160. id: oceania.id,
  161. });
  162. expect(result1.deleteZone).toEqual({
  163. result: DeletionResult.NOT_DELETED,
  164. message:
  165. 'The selected Zone cannot be deleted as it used as a default in the following Channels: ' +
  166. '__default_channel__',
  167. });
  168. const result2 = await adminClient.query(getZonesDocument);
  169. expect(result2.zones.items.find(c => c.id === oceania.id)).not.toBeUndefined();
  170. });
  171. });
  172. describe('Zone custom fields', () => {
  173. let testFacet: ResultOf<typeof createFacetDocument>['createFacet'];
  174. // Create a target entity (Facet) to link the Zone to
  175. it('create a target Facet', async () => {
  176. const result = await adminClient.query(createFacetDocument, {
  177. input: {
  178. code: 'test-relation-facet',
  179. isPrivate: false,
  180. translations: [{ languageCode: LanguageCode.en, name: 'Test Relation Facet' }],
  181. },
  182. });
  183. testFacet = result.createFacet;
  184. expect(testFacet.name).toBe('Test Relation Facet');
  185. });
  186. // Test createZone with a custom relation field
  187. it('createZone persists custom relation field', async () => {
  188. const input: VariablesOf<typeof createZoneDocument>['input'] = {
  189. name: 'Zone with Custom Relation',
  190. memberIds: [],
  191. customFields: {
  192. relatedFacetId: testFacet.id,
  193. },
  194. };
  195. const result = await adminClient.query(createZoneWithCustomFieldsDocument, { input });
  196. // Verify the return value
  197. expect(result.createZone.customFields.relatedFacet.id).toBe(testFacet.id);
  198. // Verify by querying it again from the database
  199. const result2 = await adminClient.query(getZoneWithCustomFieldsDocument, {
  200. id: result.createZone.id,
  201. });
  202. expect(result2.zone?.customFields.relatedFacet.id).toBe(testFacet.id);
  203. });
  204. // Test updateZone with a custom relation field
  205. it('updateZone persists custom relation field', async () => {
  206. const result = await adminClient.query(updateZoneWithCustomFieldsDocument, {
  207. input: {
  208. id: zones[1].id,
  209. customFields: {
  210. relatedFacetId: testFacet.id,
  211. },
  212. },
  213. });
  214. // Verify the return value
  215. expect(result.updateZone.customFields.relatedFacet.id).toBe(testFacet.id);
  216. // Verify by querying it again from the database
  217. const result2 = await adminClient.query(getZoneWithCustomFieldsDocument, { id: zones[1].id });
  218. expect(result2.zone?.customFields.relatedFacet.id).toBe(testFacet.id);
  219. });
  220. });
  221. });
  222. const zoneWithCustomFieldsFragment = graphql(`
  223. fragment ZoneCustomFields on Zone {
  224. customFields {
  225. relatedFacet {
  226. id
  227. }
  228. }
  229. }
  230. `);
  231. const createZoneWithCustomFieldsDocument = graphql(
  232. `
  233. mutation CreateZoneWithCF($input: CreateZoneInput!) {
  234. createZone(input: $input) {
  235. id
  236. name
  237. ...ZoneCustomFields
  238. }
  239. }
  240. `,
  241. [zoneWithCustomFieldsFragment],
  242. );
  243. const updateZoneWithCustomFieldsDocument = graphql(
  244. `
  245. mutation UpdateZoneWithCF($input: UpdateZoneInput!) {
  246. updateZone(input: $input) {
  247. id
  248. name
  249. ...ZoneCustomFields
  250. }
  251. }
  252. `,
  253. [zoneWithCustomFieldsFragment],
  254. );
  255. const getZoneWithCustomFieldsDocument = graphql(
  256. `
  257. query GetZoneWithCustomFields($id: ID!) {
  258. zone(id: $id) {
  259. id
  260. name
  261. ...ZoneCustomFields
  262. }
  263. }
  264. `,
  265. [zoneWithCustomFieldsFragment],
  266. );