channel.e2e-spec.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  2. import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
  3. import {
  4. createErrorResultGuard,
  5. createTestEnvironment,
  6. E2E_DEFAULT_CHANNEL_TOKEN,
  7. ErrorResultGuard,
  8. } from '@vendure/testing';
  9. import gql from 'graphql-tag';
  10. import path from 'path';
  11. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  12. import { initialData } from '../../../e2e-common/e2e-initial-data';
  13. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  14. import * as Codegen from './graphql/generated-e2e-admin-types';
  15. import {
  16. CurrencyCode,
  17. DeletionResult,
  18. ErrorCode,
  19. LanguageCode,
  20. Permission,
  21. } from './graphql/generated-e2e-admin-types';
  22. import {
  23. ASSIGN_PRODUCT_TO_CHANNEL,
  24. CREATE_ADMINISTRATOR,
  25. CREATE_CHANNEL,
  26. CREATE_ROLE,
  27. GET_CHANNELS,
  28. GET_CUSTOMER_LIST,
  29. GET_PRODUCT_WITH_VARIANTS,
  30. ME,
  31. UPDATE_CHANNEL,
  32. } from './graphql/shared-definitions';
  33. import { GET_ACTIVE_ORDER } from './graphql/shop-definitions';
  34. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  35. describe('Channels', () => {
  36. const { server, adminClient, shopClient } = createTestEnvironment(testConfig());
  37. const SECOND_CHANNEL_TOKEN = 'second_channel_token';
  38. let secondChannelAdminRole: Codegen.CreateRoleMutation['createRole'];
  39. let customerUser: Codegen.GetCustomerListQuery['customers']['items'][number];
  40. const channelGuard: ErrorResultGuard<Codegen.ChannelFragment> =
  41. createErrorResultGuard<Codegen.ChannelFragment>(input => !!input.defaultLanguageCode);
  42. beforeAll(async () => {
  43. await server.init({
  44. initialData,
  45. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  46. customerCount: 1,
  47. });
  48. await adminClient.asSuperAdmin();
  49. const { customers } = await adminClient.query<
  50. Codegen.GetCustomerListQuery,
  51. Codegen.GetCustomerListQueryVariables
  52. >(GET_CUSTOMER_LIST, {
  53. options: { take: 1 },
  54. });
  55. customerUser = customers.items[0];
  56. }, TEST_SETUP_TIMEOUT_MS);
  57. afterAll(async () => {
  58. await server.destroy();
  59. });
  60. it('createChannel returns error result defaultLanguageCode not available', async () => {
  61. const { createChannel } = await adminClient.query<
  62. Codegen.CreateChannelMutation,
  63. Codegen.CreateChannelMutationVariables
  64. >(CREATE_CHANNEL, {
  65. input: {
  66. code: 'second-channel',
  67. token: SECOND_CHANNEL_TOKEN,
  68. defaultLanguageCode: LanguageCode.zh,
  69. currencyCode: CurrencyCode.GBP,
  70. pricesIncludeTax: true,
  71. defaultShippingZoneId: 'T_1',
  72. defaultTaxZoneId: 'T_1',
  73. },
  74. });
  75. channelGuard.assertErrorResult(createChannel);
  76. expect(createChannel.message).toBe(
  77. 'Language "zh" is not available. First enable it via GlobalSettings and try again',
  78. );
  79. expect(createChannel.errorCode).toBe(ErrorCode.LANGUAGE_NOT_AVAILABLE_ERROR);
  80. expect(createChannel.languageCode).toBe('zh');
  81. });
  82. it('create a new Channel', async () => {
  83. const { createChannel } = await adminClient.query<
  84. Codegen.CreateChannelMutation,
  85. Codegen.CreateChannelMutationVariables
  86. >(CREATE_CHANNEL, {
  87. input: {
  88. code: 'second-channel',
  89. token: SECOND_CHANNEL_TOKEN,
  90. defaultLanguageCode: LanguageCode.en,
  91. currencyCode: CurrencyCode.GBP,
  92. pricesIncludeTax: true,
  93. defaultShippingZoneId: 'T_1',
  94. defaultTaxZoneId: 'T_1',
  95. },
  96. });
  97. channelGuard.assertSuccess(createChannel);
  98. expect(createChannel).toEqual({
  99. id: 'T_2',
  100. code: 'second-channel',
  101. token: SECOND_CHANNEL_TOKEN,
  102. currencyCode: 'GBP',
  103. defaultLanguageCode: 'en',
  104. defaultShippingZone: {
  105. id: 'T_1',
  106. },
  107. defaultTaxZone: {
  108. id: 'T_1',
  109. },
  110. pricesIncludeTax: true,
  111. });
  112. });
  113. it('update currencyCode', async () => {
  114. const { updateChannel } = await adminClient.query<
  115. Codegen.UpdateChannelMutation,
  116. Codegen.UpdateChannelMutationVariables
  117. >(UPDATE_CHANNEL, {
  118. input: {
  119. id: 'T_1',
  120. currencyCode: CurrencyCode.MYR,
  121. },
  122. });
  123. channelGuard.assertSuccess(updateChannel);
  124. expect(updateChannel.currencyCode).toBe('MYR');
  125. });
  126. it('superadmin has all permissions on new channel', async () => {
  127. const { me } = await adminClient.query<Codegen.MeQuery>(ME);
  128. expect(me!.channels.length).toBe(2);
  129. const secondChannelData = me!.channels.find(c => c.token === SECOND_CHANNEL_TOKEN);
  130. const nonOwnerPermissions = Object.values(Permission).filter(
  131. p => p !== Permission.Owner && p !== Permission.Public,
  132. );
  133. expect(secondChannelData!.permissions.sort()).toEqual(nonOwnerPermissions);
  134. });
  135. it('customer has Authenticated permission on new channel', async () => {
  136. await shopClient.asUserWithCredentials(customerUser.emailAddress, 'test');
  137. const { me } = await shopClient.query<Codegen.MeQuery>(ME);
  138. expect(me!.channels.length).toBe(2);
  139. const secondChannelData = me!.channels.find(c => c.token === SECOND_CHANNEL_TOKEN);
  140. expect(me!.channels).toEqual([
  141. {
  142. code: DEFAULT_CHANNEL_CODE,
  143. permissions: ['Authenticated'],
  144. token: E2E_DEFAULT_CHANNEL_TOKEN,
  145. },
  146. {
  147. code: 'second-channel',
  148. permissions: ['Authenticated'],
  149. token: SECOND_CHANNEL_TOKEN,
  150. },
  151. ]);
  152. });
  153. it('createRole on second Channel', async () => {
  154. const { createRole } = await adminClient.query<
  155. Codegen.CreateRoleMutation,
  156. Codegen.CreateRoleMutationVariables
  157. >(CREATE_ROLE, {
  158. input: {
  159. description: 'second channel admin',
  160. code: 'second-channel-admin',
  161. channelIds: ['T_2'],
  162. permissions: [
  163. Permission.ReadCatalog,
  164. Permission.ReadSettings,
  165. Permission.ReadAdministrator,
  166. Permission.CreateAdministrator,
  167. Permission.UpdateAdministrator,
  168. ],
  169. },
  170. });
  171. expect(createRole.channels).toEqual([
  172. {
  173. id: 'T_2',
  174. code: 'second-channel',
  175. token: SECOND_CHANNEL_TOKEN,
  176. },
  177. ]);
  178. secondChannelAdminRole = createRole;
  179. });
  180. it('createAdministrator with second-channel-admin role', async () => {
  181. const { createAdministrator } = await adminClient.query<
  182. Codegen.CreateAdministratorMutation,
  183. Codegen.CreateAdministratorMutationVariables
  184. >(CREATE_ADMINISTRATOR, {
  185. input: {
  186. firstName: 'Admin',
  187. lastName: 'Two',
  188. emailAddress: 'admin2@test.com',
  189. password: 'test',
  190. roleIds: [secondChannelAdminRole.id],
  191. },
  192. });
  193. expect(createAdministrator.user.roles.map(r => r.description)).toEqual(['second channel admin']);
  194. });
  195. it(
  196. 'cannot create role on channel for which admin does not have CreateAdministrator permission',
  197. assertThrowsWithMessage(async () => {
  198. await adminClient.asUserWithCredentials('admin2@test.com', 'test');
  199. await adminClient.query<Codegen.CreateRoleMutation, Codegen.CreateRoleMutationVariables>(
  200. CREATE_ROLE,
  201. {
  202. input: {
  203. description: 'read default channel catalog',
  204. code: 'read default channel catalog',
  205. channelIds: ['T_1'],
  206. permissions: [Permission.ReadCatalog],
  207. },
  208. },
  209. );
  210. }, 'You are not currently authorized to perform this action'),
  211. );
  212. it('can create role on channel for which admin has CreateAdministrator permission', async () => {
  213. const { createRole } = await adminClient.query<
  214. Codegen.CreateRoleMutation,
  215. Codegen.CreateRoleMutationVariables
  216. >(CREATE_ROLE, {
  217. input: {
  218. description: 'read second channel catalog',
  219. code: 'read-second-channel-catalog',
  220. channelIds: ['T_2'],
  221. permissions: [Permission.ReadCatalog],
  222. },
  223. });
  224. expect(createRole.channels).toEqual([
  225. {
  226. id: 'T_2',
  227. code: 'second-channel',
  228. token: SECOND_CHANNEL_TOKEN,
  229. },
  230. ]);
  231. });
  232. it('createRole with no channelId implicitly uses active channel', async () => {
  233. await adminClient.asSuperAdmin();
  234. const { createRole } = await adminClient.query<
  235. Codegen.CreateRoleMutation,
  236. Codegen.CreateRoleMutationVariables
  237. >(CREATE_ROLE, {
  238. input: {
  239. description: 'update second channel catalog',
  240. code: 'update-second-channel-catalog',
  241. permissions: [Permission.UpdateCatalog],
  242. },
  243. });
  244. expect(createRole.channels).toEqual([
  245. {
  246. id: 'T_2',
  247. code: 'second-channel',
  248. token: SECOND_CHANNEL_TOKEN,
  249. },
  250. ]);
  251. });
  252. describe('setting defaultLanguage', () => {
  253. it('returns error result if languageCode not in availableLanguages', async () => {
  254. await adminClient.asSuperAdmin();
  255. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  256. const { updateChannel } = await adminClient.query<
  257. Codegen.UpdateChannelMutation,
  258. Codegen.UpdateChannelMutationVariables
  259. >(UPDATE_CHANNEL, {
  260. input: {
  261. id: 'T_1',
  262. defaultLanguageCode: LanguageCode.zh,
  263. },
  264. });
  265. channelGuard.assertErrorResult(updateChannel);
  266. expect(updateChannel.message).toBe(
  267. 'Language "zh" is not available. First enable it via GlobalSettings and try again',
  268. );
  269. expect(updateChannel.errorCode).toBe(ErrorCode.LANGUAGE_NOT_AVAILABLE_ERROR);
  270. expect(updateChannel.languageCode).toBe('zh');
  271. });
  272. it('allows setting to an available language', async () => {
  273. await adminClient.asSuperAdmin();
  274. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  275. await adminClient.query<
  276. Codegen.UpdateGlobalLanguagesMutation,
  277. Codegen.UpdateGlobalLanguagesMutationVariables
  278. >(UPDATE_GLOBAL_LANGUAGES, {
  279. input: {
  280. availableLanguages: [LanguageCode.en, LanguageCode.zh],
  281. },
  282. });
  283. const { updateChannel } = await adminClient.query<
  284. Codegen.UpdateChannelMutation,
  285. Codegen.UpdateChannelMutationVariables
  286. >(UPDATE_CHANNEL, {
  287. input: {
  288. id: 'T_1',
  289. defaultLanguageCode: LanguageCode.zh,
  290. },
  291. });
  292. expect(updateChannel.defaultLanguageCode).toBe(LanguageCode.zh);
  293. });
  294. });
  295. it('deleteChannel', async () => {
  296. const PROD_ID = 'T_1';
  297. await adminClient.asSuperAdmin();
  298. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  299. const { assignProductsToChannel } = await adminClient.query<
  300. Codegen.AssignProductsToChannelMutation,
  301. Codegen.AssignProductsToChannelMutationVariables
  302. >(ASSIGN_PRODUCT_TO_CHANNEL, {
  303. input: {
  304. channelId: 'T_2',
  305. productIds: [PROD_ID],
  306. },
  307. });
  308. expect(assignProductsToChannel[0].channels.map(c => c.id).sort()).toEqual(['T_1', 'T_2']);
  309. // create a Session on the Channel to be deleted to ensure it gets cleaned up
  310. shopClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  311. await shopClient.query(GET_ACTIVE_ORDER);
  312. const { deleteChannel } = await adminClient.query<
  313. Codegen.DeleteChannelMutation,
  314. Codegen.DeleteChannelMutationVariables
  315. >(DELETE_CHANNEL, {
  316. id: 'T_2',
  317. });
  318. expect(deleteChannel.result).toBe(DeletionResult.DELETED);
  319. const { channels } = await adminClient.query<Codegen.GetChannelsQuery>(GET_CHANNELS);
  320. expect(channels.map(c => c.id).sort()).toEqual(['T_1']);
  321. const { product } = await adminClient.query<
  322. Codegen.GetProductWithVariantsQuery,
  323. Codegen.GetProductWithVariantsQueryVariables
  324. >(GET_PRODUCT_WITH_VARIANTS, {
  325. id: PROD_ID,
  326. });
  327. expect(product!.channels.map(c => c.id)).toEqual(['T_1']);
  328. });
  329. });
  330. const DELETE_CHANNEL = gql`
  331. mutation DeleteChannel($id: ID!) {
  332. deleteChannel(id: $id) {
  333. message
  334. result
  335. }
  336. }
  337. `;
  338. const UPDATE_GLOBAL_LANGUAGES = gql`
  339. mutation UpdateGlobalLanguages($input: UpdateGlobalSettingsInput!) {
  340. updateGlobalSettings(input: $input) {
  341. ... on GlobalSettings {
  342. id
  343. availableLanguages
  344. }
  345. }
  346. }
  347. `;