asset.e2e-spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* tslint:disable:no-non-null-assertion */
  2. import { omit } from '@vendure/common/lib/omit';
  3. import { mergeConfig } from '@vendure/core';
  4. import { createTestEnvironment } from '@vendure/testing';
  5. import gql from 'graphql-tag';
  6. import path from 'path';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  9. import { ASSET_FRAGMENT } from './graphql/fragments';
  10. import {
  11. CreateAssets,
  12. DeleteAsset,
  13. DeletionResult,
  14. GetAsset,
  15. GetAssetList,
  16. GetProductWithVariants,
  17. SortOrder,
  18. UpdateAsset,
  19. } from './graphql/generated-e2e-admin-types';
  20. import {
  21. DELETE_ASSET,
  22. GET_ASSET_LIST,
  23. GET_PRODUCT_WITH_VARIANTS,
  24. UPDATE_ASSET,
  25. } from './graphql/shared-definitions';
  26. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  27. describe('Asset resolver', () => {
  28. const { server, adminClient } = createTestEnvironment(
  29. mergeConfig(testConfig, {
  30. assetOptions: {
  31. permittedFileTypes: ['image/*', '.pdf'],
  32. },
  33. }),
  34. );
  35. let firstAssetId: string;
  36. let createdAssetId: string;
  37. beforeAll(async () => {
  38. await server.init({
  39. initialData,
  40. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  41. customerCount: 1,
  42. });
  43. await adminClient.asSuperAdmin();
  44. }, TEST_SETUP_TIMEOUT_MS);
  45. afterAll(async () => {
  46. await server.destroy();
  47. });
  48. it('assets', async () => {
  49. const { assets } = await adminClient.query<GetAssetList.Query, GetAssetList.Variables>(
  50. GET_ASSET_LIST,
  51. {
  52. options: {
  53. sort: {
  54. name: SortOrder.ASC,
  55. },
  56. },
  57. },
  58. );
  59. expect(assets.totalItems).toBe(4);
  60. expect(assets.items.map(a => omit(a, ['id']))).toEqual([
  61. {
  62. fileSize: 1680,
  63. mimeType: 'image/jpeg',
  64. name: 'alexandru-acea-686569-unsplash.jpg',
  65. preview: 'test-url/test-assets/alexandru-acea-686569-unsplash__preview.jpg',
  66. source: 'test-url/test-assets/alexandru-acea-686569-unsplash.jpg',
  67. type: 'IMAGE',
  68. },
  69. {
  70. fileSize: 1680,
  71. mimeType: 'image/jpeg',
  72. name: 'derick-david-409858-unsplash.jpg',
  73. preview: 'test-url/test-assets/derick-david-409858-unsplash__preview.jpg',
  74. source: 'test-url/test-assets/derick-david-409858-unsplash.jpg',
  75. type: 'IMAGE',
  76. },
  77. {
  78. fileSize: 1680,
  79. mimeType: 'image/jpeg',
  80. name: 'florian-olivo-1166419-unsplash.jpg',
  81. preview: 'test-url/test-assets/florian-olivo-1166419-unsplash__preview.jpg',
  82. source: 'test-url/test-assets/florian-olivo-1166419-unsplash.jpg',
  83. type: 'IMAGE',
  84. },
  85. {
  86. fileSize: 1680,
  87. mimeType: 'image/jpeg',
  88. name: 'vincent-botta-736919-unsplash.jpg',
  89. preview: 'test-url/test-assets/vincent-botta-736919-unsplash__preview.jpg',
  90. source: 'test-url/test-assets/vincent-botta-736919-unsplash.jpg',
  91. type: 'IMAGE',
  92. },
  93. ]);
  94. firstAssetId = assets.items[0].id;
  95. });
  96. it('asset', async () => {
  97. const { asset } = await adminClient.query<GetAsset.Query, GetAsset.Variables>(GET_ASSET, {
  98. id: firstAssetId,
  99. });
  100. expect(asset).toEqual({
  101. fileSize: 1680,
  102. height: 48,
  103. id: firstAssetId,
  104. mimeType: 'image/jpeg',
  105. name: 'alexandru-acea-686569-unsplash.jpg',
  106. preview: 'test-url/test-assets/alexandru-acea-686569-unsplash__preview.jpg',
  107. source: 'test-url/test-assets/alexandru-acea-686569-unsplash.jpg',
  108. type: 'IMAGE',
  109. width: 48,
  110. });
  111. });
  112. describe('createAssets', () => {
  113. it('permitted types by mime type', async () => {
  114. const filesToUpload = [
  115. path.join(__dirname, 'fixtures/assets/pps1.jpg'),
  116. path.join(__dirname, 'fixtures/assets/pps2.jpg'),
  117. ];
  118. const { createAssets }: CreateAssets.Mutation = await adminClient.fileUploadMutation({
  119. mutation: CREATE_ASSETS,
  120. filePaths: filesToUpload,
  121. mapVariables: filePaths => ({
  122. input: filePaths.map(p => ({ file: null })),
  123. }),
  124. });
  125. expect(createAssets.map(a => omit(a, ['id'])).sort((a, b) => (a.name < b.name ? -1 : 1))).toEqual(
  126. [
  127. {
  128. fileSize: 1680,
  129. focalPoint: null,
  130. mimeType: 'image/jpeg',
  131. name: 'pps1.jpg',
  132. preview: 'test-url/test-assets/pps1__preview.jpg',
  133. source: 'test-url/test-assets/pps1.jpg',
  134. type: 'IMAGE',
  135. },
  136. {
  137. fileSize: 1680,
  138. focalPoint: null,
  139. mimeType: 'image/jpeg',
  140. name: 'pps2.jpg',
  141. preview: 'test-url/test-assets/pps2__preview.jpg',
  142. source: 'test-url/test-assets/pps2.jpg',
  143. type: 'IMAGE',
  144. },
  145. ],
  146. );
  147. createdAssetId = createAssets[0].id;
  148. });
  149. it('permitted type by file extension', async () => {
  150. const filesToUpload = [path.join(__dirname, 'fixtures/assets/dummy.pdf')];
  151. const { createAssets }: CreateAssets.Mutation = await adminClient.fileUploadMutation({
  152. mutation: CREATE_ASSETS,
  153. filePaths: filesToUpload,
  154. mapVariables: filePaths => ({
  155. input: filePaths.map(p => ({ file: null })),
  156. }),
  157. });
  158. expect(createAssets.map(a => omit(a, ['id']))).toEqual([
  159. {
  160. fileSize: 1680,
  161. focalPoint: null,
  162. mimeType: 'application/pdf',
  163. name: 'dummy.pdf',
  164. preview: 'test-url/test-assets/dummy__preview.pdf.png',
  165. source: 'test-url/test-assets/dummy.pdf',
  166. type: 'BINARY',
  167. },
  168. ]);
  169. });
  170. it(
  171. 'not permitted type',
  172. assertThrowsWithMessage(async () => {
  173. const filesToUpload = [path.join(__dirname, 'fixtures/assets/dummy.txt')];
  174. const { createAssets }: CreateAssets.Mutation = await adminClient.fileUploadMutation({
  175. mutation: CREATE_ASSETS,
  176. filePaths: filesToUpload,
  177. mapVariables: filePaths => ({
  178. input: filePaths.map(p => ({ file: null })),
  179. }),
  180. });
  181. }, `The MIME type 'text/plain' is not permitted.`),
  182. );
  183. });
  184. describe('updateAsset', () => {
  185. it('update name', async () => {
  186. const { updateAsset } = await adminClient.query<UpdateAsset.Mutation, UpdateAsset.Variables>(
  187. UPDATE_ASSET,
  188. {
  189. input: {
  190. id: firstAssetId,
  191. name: 'new name',
  192. },
  193. },
  194. );
  195. expect(updateAsset.name).toEqual('new name');
  196. });
  197. it('update focalPoint', async () => {
  198. const { updateAsset } = await adminClient.query<UpdateAsset.Mutation, UpdateAsset.Variables>(
  199. UPDATE_ASSET,
  200. {
  201. input: {
  202. id: firstAssetId,
  203. focalPoint: {
  204. x: 0.3,
  205. y: 0.9,
  206. },
  207. },
  208. },
  209. );
  210. expect(updateAsset.focalPoint).toEqual({
  211. x: 0.3,
  212. y: 0.9,
  213. });
  214. });
  215. it('unset focalPoint', async () => {
  216. const { updateAsset } = await adminClient.query<UpdateAsset.Mutation, UpdateAsset.Variables>(
  217. UPDATE_ASSET,
  218. {
  219. input: {
  220. id: firstAssetId,
  221. focalPoint: null,
  222. },
  223. },
  224. );
  225. expect(updateAsset.focalPoint).toEqual(null);
  226. });
  227. });
  228. describe('deleteAsset', () => {
  229. let firstProduct: GetProductWithVariants.Product;
  230. beforeAll(async () => {
  231. const { product } = await adminClient.query<
  232. GetProductWithVariants.Query,
  233. GetProductWithVariants.Variables
  234. >(GET_PRODUCT_WITH_VARIANTS, {
  235. id: 'T_1',
  236. });
  237. firstProduct = product!;
  238. });
  239. it('non-featured asset', async () => {
  240. const { deleteAsset } = await adminClient.query<DeleteAsset.Mutation, DeleteAsset.Variables>(
  241. DELETE_ASSET,
  242. {
  243. id: createdAssetId,
  244. },
  245. );
  246. expect(deleteAsset.result).toBe(DeletionResult.DELETED);
  247. const { asset } = await adminClient.query<GetAsset.Query, GetAsset.Variables>(GET_ASSET, {
  248. id: createdAssetId,
  249. });
  250. expect(asset).toBeNull();
  251. });
  252. it('featured asset not deleted', async () => {
  253. const { deleteAsset } = await adminClient.query<DeleteAsset.Mutation, DeleteAsset.Variables>(
  254. DELETE_ASSET,
  255. {
  256. id: firstProduct.featuredAsset!.id,
  257. },
  258. );
  259. expect(deleteAsset.result).toBe(DeletionResult.NOT_DELETED);
  260. expect(deleteAsset.message).toContain(`The selected Asset is featured by 1 Product`);
  261. const { asset } = await adminClient.query<GetAsset.Query, GetAsset.Variables>(GET_ASSET, {
  262. id: firstAssetId,
  263. });
  264. expect(asset).not.toBeNull();
  265. });
  266. it('featured asset force deleted', async () => {
  267. const { product: p1 } = await adminClient.query<
  268. GetProductWithVariants.Query,
  269. GetProductWithVariants.Variables
  270. >(GET_PRODUCT_WITH_VARIANTS, {
  271. id: firstProduct.id,
  272. });
  273. expect(p1!.assets.length).toEqual(1);
  274. const { deleteAsset } = await adminClient.query<DeleteAsset.Mutation, DeleteAsset.Variables>(
  275. DELETE_ASSET,
  276. {
  277. id: firstProduct.featuredAsset!.id,
  278. force: true,
  279. },
  280. );
  281. expect(deleteAsset.result).toBe(DeletionResult.DELETED);
  282. const { asset } = await adminClient.query<GetAsset.Query, GetAsset.Variables>(GET_ASSET, {
  283. id: firstAssetId,
  284. });
  285. expect(asset).not.toBeNull();
  286. const { product } = await adminClient.query<
  287. GetProductWithVariants.Query,
  288. GetProductWithVariants.Variables
  289. >(GET_PRODUCT_WITH_VARIANTS, {
  290. id: firstProduct.id,
  291. });
  292. expect(product!.featuredAsset).toBeNull();
  293. expect(product!.assets.length).toEqual(0);
  294. });
  295. });
  296. });
  297. export const GET_ASSET = gql`
  298. query GetAsset($id: ID!) {
  299. asset(id: $id) {
  300. ...Asset
  301. width
  302. height
  303. }
  304. }
  305. ${ASSET_FRAGMENT}
  306. `;
  307. export const CREATE_ASSETS = gql`
  308. mutation CreateAssets($input: [CreateAssetInput!]!) {
  309. createAssets(input: $input) {
  310. ...Asset
  311. focalPoint {
  312. x
  313. y
  314. }
  315. }
  316. }
  317. ${ASSET_FRAGMENT}
  318. `;