mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 01:47:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			549 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const fs = require('fs');
 | |
| const path = require('path');
 | |
| 
 | |
| const { createTestBuilder } = require('api-tests/builder');
 | |
| const { createStrapiInstance } = require('api-tests/strapi');
 | |
| const { createAuthRequest } = require('api-tests/request');
 | |
| 
 | |
| let strapi;
 | |
| let rq;
 | |
| const data = {
 | |
|   folders: [],
 | |
|   files: [],
 | |
| };
 | |
| 
 | |
| const createFolder = async (name, parent = null) => {
 | |
|   const res = await rq({
 | |
|     method: 'POST',
 | |
|     url: '/upload/folders',
 | |
|     body: { name, parent },
 | |
|   });
 | |
|   return res.body.data;
 | |
| };
 | |
| 
 | |
| const createAFile = async (parent = null) => {
 | |
|   const res = await rq({
 | |
|     method: 'POST',
 | |
|     url: '/upload',
 | |
|     formData: {
 | |
|       files: fs.createReadStream(path.join(__dirname, '../utils/rec.jpg')),
 | |
|       fileInfo: JSON.stringify({ folder: parent }),
 | |
|     },
 | |
|   });
 | |
|   return res.body[0];
 | |
| };
 | |
| 
 | |
| describe('Bulk actions for folders & files', () => {
 | |
|   const builder = createTestBuilder();
 | |
| 
 | |
|   beforeAll(async () => {
 | |
|     strapi = await createStrapiInstance();
 | |
|     rq = await createAuthRequest({ strapi });
 | |
|   });
 | |
| 
 | |
|   afterAll(async () => {
 | |
|     await rq({
 | |
|       method: 'POST',
 | |
|       url: '/upload/actions/bulk-delete',
 | |
|       body: {
 | |
|         folderIds: data.folders.map((f) => f.id),
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     await strapi.destroy();
 | |
|     await builder.cleanup();
 | |
|   });
 | |
| 
 | |
|   describe('delete', () => {
 | |
|     test('Can delete folders and files', async () => {
 | |
|       const folder1 = await createFolder('folder-a-1', null);
 | |
|       const folder1a = await createFolder('folder-a-1a', folder1.id);
 | |
|       const folder1b = await createFolder('folder-a-1b', folder1.id);
 | |
|       const folder1a1 = await createFolder('folder-a-1a1', folder1a.id);
 | |
|       const file1 = await createAFile(null);
 | |
|       const file1b = await createAFile(folder1b.id);
 | |
|       const file1a = await createAFile(folder1a.id);
 | |
|       const file1a1 = await createAFile(folder1a1.id);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-delete',
 | |
|         body: {
 | |
|           fileIds: [file1.id],
 | |
|           folderIds: [folder1a.id],
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(res.body.data).toMatchObject({
 | |
|         files: [
 | |
|           {
 | |
|             alternativeText: null,
 | |
|             caption: null,
 | |
|             createdAt: expect.anything(),
 | |
|             ext: '.jpg',
 | |
|             folderPath: '/',
 | |
|             formats: null,
 | |
|             hash: expect.anything(),
 | |
|             height: 20,
 | |
|             id: file1.id,
 | |
|             mime: 'image/jpeg',
 | |
|             name: 'rec.jpg',
 | |
|             previewUrl: null,
 | |
|             provider: 'local',
 | |
|             provider_metadata: null,
 | |
|             size: 0.27,
 | |
|             updatedAt: expect.anything(),
 | |
|             url: expect.anything(),
 | |
|             width: 20,
 | |
|           },
 | |
|         ],
 | |
|         folders: [
 | |
|           {
 | |
|             id: folder1a.id,
 | |
|             name: 'folder-a-1a',
 | |
|             path: expect.anything(),
 | |
|             pathId: expect.any(Number),
 | |
|             createdAt: expect.anything(),
 | |
|             updatedAt: expect.anything(),
 | |
|           },
 | |
|         ],
 | |
|       });
 | |
| 
 | |
|       const resFolder = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/folders',
 | |
|       });
 | |
| 
 | |
|       const existingfoldersIds = resFolder.body.data.map((f) => f.id);
 | |
|       expect(existingfoldersIds).toEqual(expect.not.arrayContaining([folder1a.id, folder1a1.id]));
 | |
|       expect(existingfoldersIds).toEqual(expect.arrayContaining([folder1.id, folder1b.id]));
 | |
| 
 | |
|       const resFiles = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/files',
 | |
|         qs: {
 | |
|           pageSize: 100,
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       const existingfilesIds = resFiles.body.results.map((f) => f.id);
 | |
|       expect(existingfilesIds).toEqual(
 | |
|         expect.not.arrayContaining([file1.id, file1a.id, file1a1.id])
 | |
|       );
 | |
|       expect(existingfilesIds).toEqual(expect.arrayContaining([file1b.id]));
 | |
| 
 | |
|       data.folders.push(folder1, folder1b);
 | |
|       data.files.push(file1b);
 | |
|     });
 | |
| 
 | |
|     test('Can delete only files', async () => {
 | |
|       const file = await createAFile(null);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-delete',
 | |
|         body: {
 | |
|           fileIds: [file.id],
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(res.body.data).toMatchObject({
 | |
|         files: [
 | |
|           {
 | |
|             alternativeText: null,
 | |
|             caption: null,
 | |
|             createdAt: expect.anything(),
 | |
|             ext: '.jpg',
 | |
|             folderPath: '/',
 | |
|             formats: null,
 | |
|             hash: expect.anything(),
 | |
|             height: 20,
 | |
|             id: file.id,
 | |
|             mime: 'image/jpeg',
 | |
|             name: 'rec.jpg',
 | |
|             previewUrl: null,
 | |
|             provider: 'local',
 | |
|             provider_metadata: null,
 | |
|             size: 0.27,
 | |
|             updatedAt: expect.anything(),
 | |
|             url: expect.anything(),
 | |
|             width: 20,
 | |
|           },
 | |
|         ],
 | |
|         folders: [],
 | |
|       });
 | |
| 
 | |
|       const resFiles = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/files',
 | |
|         qs: {
 | |
|           pageSize: 100,
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       const existingfilesIds = resFiles.body.results.map((f) => f.id);
 | |
|       expect(existingfilesIds).toEqual(expect.not.arrayContaining([file.id]));
 | |
|     });
 | |
| 
 | |
|     test('Can delete only folders', async () => {
 | |
|       const folder = await createFolder('a random folder', null);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-delete',
 | |
|         body: {
 | |
|           folderIds: [folder.id],
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(res.body.data).toMatchObject({
 | |
|         files: [],
 | |
|         folders: [
 | |
|           {
 | |
|             id: folder.id,
 | |
|             name: 'a random folder',
 | |
|             path: expect.anything(),
 | |
|             pathId: expect.any(Number),
 | |
|             createdAt: expect.anything(),
 | |
|             updatedAt: expect.anything(),
 | |
|           },
 | |
|         ],
 | |
|       });
 | |
| 
 | |
|       const resFolder = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/folders',
 | |
|       });
 | |
| 
 | |
|       const existingfoldersIds = resFolder.body.data.map((f) => f.id);
 | |
|       expect(existingfoldersIds).toEqual(expect.not.arrayContaining([folder.id]));
 | |
|     });
 | |
| 
 | |
|     test('Can delete folders without deleting others with similar path', async () => {
 | |
|       // Ensure the folder algo does not only delete by folders that start with the same path (startswith /1 matches /10 too)
 | |
| 
 | |
|       // Delete all previous folders, so first file path is /1
 | |
|       await rq
 | |
|         .get('/upload/folders')
 | |
|         .then((res) => res.body.data.map((f) => f.id))
 | |
|         .then((folderIds) => rq.post('/upload/actions/bulk-delete', { body: { folderIds } }));
 | |
| 
 | |
|       // Create folders
 | |
|       const folder1 = await createFolder('folderToDelete', null);
 | |
|       for (let i = 0; i < 20; i++) {
 | |
|         await createFolder(`folderToKeep-${i}`, null);
 | |
|       }
 | |
|       // Delete folder1
 | |
|       await rq.post('/upload/actions/bulk-delete', {
 | |
|         body: { folderIds: [folder1.id] },
 | |
|       });
 | |
| 
 | |
|       const folderIds = await rq
 | |
|         .get('/upload/folders')
 | |
|         .then((res) => res.body.data.map((f) => f.id));
 | |
| 
 | |
|       // Should include all folders except the one we deleted
 | |
|       expect(folderIds.length).toBe(20);
 | |
|       expect(folderIds).toEqual(expect.not.arrayContaining([folder1.id]));
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('move', () => {
 | |
|     test('Can move folders and files into another folder', async () => {
 | |
|       const folder1 = await createFolder('folder-b-1', null);
 | |
|       const folder1a = await createFolder('folder-b-1a', folder1.id);
 | |
|       const folder1b = await createFolder('folder-b-1b', folder1.id);
 | |
|       const folder1a1 = await createFolder('folder-b-1a1', folder1a.id);
 | |
|       const file1 = await createAFile(null);
 | |
|       const file1a = await createAFile(folder1a.id);
 | |
|       const file1b = await createAFile(folder1b.id);
 | |
|       const file1a1 = await createAFile(folder1a1.id);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: folder1b.id,
 | |
|           fileIds: [file1a.id],
 | |
|           folderIds: [folder1a.id],
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(res.body.data).toMatchObject({
 | |
|         files: [
 | |
|           {
 | |
|             alternativeText: null,
 | |
|             caption: null,
 | |
|             createdAt: expect.anything(),
 | |
|             ext: '.jpg',
 | |
|             folderPath: folder1b.path,
 | |
|             formats: null,
 | |
|             hash: expect.anything(),
 | |
|             height: 20,
 | |
|             id: file1a.id,
 | |
|             mime: 'image/jpeg',
 | |
|             name: 'rec.jpg',
 | |
|             previewUrl: null,
 | |
|             provider: 'local',
 | |
|             provider_metadata: null,
 | |
|             size: 0.27,
 | |
|             updatedAt: expect.anything(),
 | |
|             url: expect.anything(),
 | |
|             width: 20,
 | |
|           },
 | |
|         ],
 | |
|         folders: [
 | |
|           {
 | |
|             id: folder1a.id,
 | |
|             name: 'folder-b-1a',
 | |
|             path: `${folder1b.path}/${folder1a.pathId}`,
 | |
|             pathId: expect.any(Number),
 | |
|             createdAt: expect.anything(),
 | |
|             updatedAt: expect.anything(),
 | |
|           },
 | |
|         ],
 | |
|       });
 | |
| 
 | |
|       const {
 | |
|         body: { data: folderResults },
 | |
|       } = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/folders',
 | |
|         qs: {
 | |
|           populate: 'parent',
 | |
|           sort: 'id:asc',
 | |
|           filters: { id: { $in: [folder1.id, folder1a.id, folder1b.id, folder1a1.id] } },
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(folderResults[0]).toMatchObject({ ...folder1, parent: null });
 | |
|       expect(folderResults[1]).toMatchObject({
 | |
|         ...folder1a,
 | |
|         path: `${folder1b.path}/${folder1a.pathId}`,
 | |
|         parent: { id: folder1b.id },
 | |
|         updatedAt: expect.anything(),
 | |
|       });
 | |
|       expect(folderResults[2]).toMatchObject({ ...folder1b, parent: { id: folder1.id } });
 | |
|       expect(folderResults[3]).toMatchObject({
 | |
|         ...folder1a1,
 | |
|         path: `${folder1b.path}/${folder1a.pathId}/${folder1a1.pathId}`,
 | |
|         parent: { id: folder1a.id },
 | |
|       });
 | |
| 
 | |
|       const {
 | |
|         body: { results: fileResults },
 | |
|       } = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/files',
 | |
|         qs: {
 | |
|           pageSize: 100,
 | |
|           populate: 'folder',
 | |
|           sort: 'id:asc',
 | |
|           filters: { id: { $in: [file1.id, file1a.id, file1b.id, file1a1.id] } },
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(fileResults[0]).toMatchObject({ ...file1, folder: null });
 | |
|       expect(fileResults[1]).toMatchObject({
 | |
|         ...file1a,
 | |
|         folderPath: folder1b.path,
 | |
|         folder: { id: folder1b.id },
 | |
|         updatedAt: expect.anything(),
 | |
|       });
 | |
|       expect(fileResults[2]).toMatchObject({ ...file1b, folder: { id: folder1b.id } });
 | |
|       expect(fileResults[3]).toMatchObject({
 | |
|         ...file1a1,
 | |
|         folderPath: `${folder1b.path}/${folder1a.pathId}/${folder1a1.pathId}`,
 | |
|         folder: { id: folder1a1.id },
 | |
|       });
 | |
| 
 | |
|       data.folders.push(...folderResults);
 | |
|       data.files.push(...fileResults);
 | |
|     });
 | |
|     test('Can move folders and files to the root level', async () => {
 | |
|       const folder1 = await createFolder('folder-c-1', null);
 | |
|       const folder1a = await createFolder('folder-c-1a', folder1.id);
 | |
|       const folder1a1 = await createFolder('folder-c-1a1', folder1a.id);
 | |
|       const file1 = await createAFile(null);
 | |
|       const file1a = await createAFile(folder1a.id);
 | |
|       const file1a1 = await createAFile(folder1a1.id);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: null,
 | |
|           fileIds: [file1a.id],
 | |
|           folderIds: [folder1a.id],
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(res.body.data).toMatchObject({
 | |
|         files: [
 | |
|           {
 | |
|             alternativeText: null,
 | |
|             caption: null,
 | |
|             createdAt: expect.anything(),
 | |
|             ext: '.jpg',
 | |
|             folderPath: '/',
 | |
|             formats: null,
 | |
|             hash: expect.anything(),
 | |
|             height: 20,
 | |
|             id: file1a.id,
 | |
|             mime: 'image/jpeg',
 | |
|             name: 'rec.jpg',
 | |
|             previewUrl: null,
 | |
|             provider: 'local',
 | |
|             provider_metadata: null,
 | |
|             size: 0.27,
 | |
|             updatedAt: expect.anything(),
 | |
|             url: expect.anything(),
 | |
|             width: 20,
 | |
|           },
 | |
|         ],
 | |
|         folders: [
 | |
|           {
 | |
|             id: folder1a.id,
 | |
|             name: 'folder-c-1a',
 | |
|             path: `/${folder1a.pathId}`,
 | |
|             pathId: expect.any(Number),
 | |
|             createdAt: expect.anything(),
 | |
|             updatedAt: expect.anything(),
 | |
|           },
 | |
|         ],
 | |
|       });
 | |
| 
 | |
|       const {
 | |
|         body: { data: folderResults },
 | |
|       } = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/folders?populate=parent',
 | |
|         qs: {
 | |
|           populate: 'parent',
 | |
|           sort: 'id:asc',
 | |
|           filters: { id: { $in: [folder1.id, folder1a.id, folder1a1.id] } },
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(folderResults[0]).toMatchObject({ ...folder1, parent: null });
 | |
|       expect(folderResults[1]).toMatchObject({
 | |
|         ...folder1a,
 | |
|         path: `/${folder1a.pathId}`,
 | |
|         parent: null,
 | |
|         updatedAt: expect.anything(),
 | |
|       });
 | |
|       expect(folderResults[2]).toMatchObject({
 | |
|         ...folder1a1,
 | |
|         path: `/${folder1a.pathId}/${folder1a1.pathId}`,
 | |
|         parent: { id: folder1a.id },
 | |
|       });
 | |
| 
 | |
|       const {
 | |
|         body: { results: fileResults },
 | |
|       } = await rq({
 | |
|         method: 'GET',
 | |
|         url: '/upload/files',
 | |
|         qs: {
 | |
|           pageSize: 100,
 | |
|           populate: 'folder',
 | |
|           sort: 'id:asc',
 | |
|           filters: { id: { $in: [file1.id, file1a.id, file1a1.id] } },
 | |
|         },
 | |
|       });
 | |
| 
 | |
|       expect(fileResults[0]).toMatchObject({ ...file1, folder: null });
 | |
|       expect(fileResults[1]).toMatchObject({
 | |
|         ...file1a,
 | |
|         folderPath: '/',
 | |
|         folder: null,
 | |
|         updatedAt: expect.anything(),
 | |
|       });
 | |
|       expect(fileResults[2]).toMatchObject({
 | |
|         ...file1a1,
 | |
|         folderPath: `/${folder1a.pathId}/${folder1a1.pathId}`,
 | |
|         folder: { id: folder1a1.id },
 | |
|       });
 | |
| 
 | |
|       data.folders.push(...folderResults);
 | |
|       data.files.push(...fileResults);
 | |
|     });
 | |
| 
 | |
|     test('Cannot move a folder inside itself (0 level)', async () => {
 | |
|       const folder1 = await createFolder('folder-d-1', null);
 | |
|       data.folders.push(folder1);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: folder1.id,
 | |
|           folderIds: [folder1.id],
 | |
|         },
 | |
|       });
 | |
|       expect(res.status).toBe(400);
 | |
|       expect(res.body.error.message).toBe(
 | |
|         'folders cannot be moved inside themselves or one of its children: folder-d-1'
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     test('Cannot move a folder inside itself (1 level)', async () => {
 | |
|       const folder1 = await createFolder('folder-e-1', null);
 | |
|       const folder1a = await createFolder('folder-e-1a', folder1.id);
 | |
|       data.folders.push(folder1, folder1a);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: folder1a.id,
 | |
|           folderIds: [folder1.id],
 | |
|         },
 | |
|       });
 | |
|       expect(res.status).toBe(400);
 | |
|       expect(res.body.error.message).toBe(
 | |
|         'folders cannot be moved inside themselves or one of its children: folder-e-1'
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     test('Cannot move a folder inside itself (2 levels)', async () => {
 | |
|       const folder1 = await createFolder('folder-f-1', null);
 | |
|       const folder1a = await createFolder('folder-f-1a', folder1.id);
 | |
|       const folder1a1 = await createFolder('folder-f-1a1', folder1a.id);
 | |
|       data.folders.push(folder1, folder1a, folder1a1);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: folder1a1.id,
 | |
|           folderIds: [folder1.id],
 | |
|         },
 | |
|       });
 | |
|       expect(res.status).toBe(400);
 | |
|       expect(res.body.error.message).toBe(
 | |
|         'folders cannot be moved inside themselves or one of its children: folder-f-1'
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     test('Cannot move a folder if it creates a duplicate', async () => {
 | |
|       const folder1 = await createFolder('folder-g-1', null);
 | |
|       const folder1a = await createFolder('folder-g-1a', folder1.id);
 | |
|       const folder2 = await createFolder('folder-g-1a', null);
 | |
|       data.folders.push(folder1, folder1a, folder2);
 | |
| 
 | |
|       const res = await rq({
 | |
|         method: 'POST',
 | |
|         url: '/upload/actions/bulk-move',
 | |
|         body: {
 | |
|           destinationFolderId: folder1.id,
 | |
|           folderIds: [folder2.id],
 | |
|         },
 | |
|       });
 | |
|       expect(res.status).toBe(400);
 | |
|       expect(res.body.error.message).toBe('some folders already exists: folder-g-1a');
 | |
|     });
 | |
|   });
 | |
| });
 | 
