Skip to content

Commit

Permalink
test: add utils/formatLibraryImageResquest
Browse files Browse the repository at this point in the history
  • Loading branch information
dcoa committed Nov 5, 2024
1 parent cfde72f commit d92d00a
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 66 deletions.
6 changes: 3 additions & 3 deletions src/editors/data/redux/thunkActions/requests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StrictDict, parseLibrariesImageData, getLibrariesImageAssets } from '../../../utils';
import { StrictDict, parseLibraryImageData, getLibraryImageAssets } from '../../../utils';

import { RequestKeys } from '../../constants/requests';
import api, { loadImages } from '../../services/cms/api';
Expand Down Expand Up @@ -134,7 +134,7 @@ export const uploadAsset = ({ asset, ...rest }) => (dispatch, getState) => {
if (isLibraryKey(learningContextId)) {
return ({
...resp,
data: { asset: parseLibrariesImageData(resp.data) },
data: { asset: parseLibraryImageData(resp.data) },
});
}
return resp;
Expand All @@ -156,7 +156,7 @@ export const fetchImages = ({ pageNumber, ...rest }) => (dispatch, getState) =>
})
.then(({ data }) => {
if (isLibraryKey(learningContextId)) {
const images = getLibrariesImageAssets(data.files);
const images = getLibraryImageAssets(data.files);
return { images, imageCount: Object.keys(images).length };
}
return { images: loadImages(data.assets), imageCount: data.totalCount };
Expand Down
10 changes: 5 additions & 5 deletions src/editors/data/redux/thunkActions/requests.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keyStore, parseLibrariesImageData, getLibrariesImageAssets } from '../../../utils';
import { keyStore, parseLibraryImageData, getLibraryImageAssets } from '../../../utils';
import { RequestKeys } from '../../constants/requests';
import api from '../../services/cms/api';
import * as requests from './requests';
Expand Down Expand Up @@ -42,8 +42,8 @@ jest.mock('../../services/cms/api', () => ({

jest.mock('../../../utils', () => ({
...jest.requireActual('../../../utils'),
parseLibrariesImageData: jest.fn(),
getLibrariesImageAssets: jest.fn(() => ({})),
parseLibraryImageData: jest.fn(),
getLibraryImageAssets: jest.fn(() => ({})),
}));

const apiKeys = keyStore(api);
Expand Down Expand Up @@ -298,7 +298,7 @@ describe('requests thunkActions module', () => {
});
test('api.fetchImages promise called with studioEndpointUrl and blockId', () => {
expect(fetchImages).toHaveBeenCalledWith(expectedArgs);
expect(getLibrariesImageAssets).toHaveBeenCalled();
expect(getLibraryImageAssets).toHaveBeenCalled();
});
});
});
Expand Down Expand Up @@ -401,7 +401,7 @@ describe('requests thunkActions module', () => {
});
test('api.uploadAsset promise called with studioEndpointUrl and blockId', () => {
expect(uploadAsset).toHaveBeenCalledWith(expectedArgs);
expect(parseLibrariesImageData).toHaveBeenCalled();
expect(parseLibraryImageData).toHaveBeenCalled();
});
});
});
Expand Down
57 changes: 0 additions & 57 deletions src/editors/utils/formatLibrariesImgRequest.ts

This file was deleted.

134 changes: 134 additions & 0 deletions src/editors/utils/formatLibraryImgRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import StrictDict from './StrictDict';

/**
* A dictionary that maps file extensions to their corresponding MIME types for images.
*
* @example
* acceptedImgMimeTypes.gif // "image/gif"
*/

const acceptedImgMimeTypes = StrictDict({
gif: 'image/gif',
jpg: 'image/jpg',
jpeg: 'image/jpeg',
png: 'image/png',
tif: 'image/tiff',
tiff: 'image/tiff',
ico: 'image/x-icon',
});

type TinyMCEImageData = {
displayName: string,
contentType: string,
url: string,
externalUrl: string,
portableUrl: string,
thumbnail: string,
id: string,
locked: boolean,
};

export type LibraryAssetResponse = {
path: string,
size: number,
url: string,
};

/**
* Extracts the file name from a file path.
* This function strips the directory structure and returns the base file name.
*
* @param data - The asset data containing the file path.
* @returns The file name extracted from the path.
*
* @example
* const data = { path: '/static/example.jpg', size: 12345, url: 'http://example.com/static/example.jpg' };
* const fileName = getFileName(data); // "example.jpg"
*/

export const getFileName = (data: LibraryAssetResponse): string => data.path.replace(/^.*[\\/]/, '');

/**
* Determines the MIME type of a file based on its extension.
*
* @param data - The asset data containing the file path.
* @returns The MIME type of the file, or 'unknown' if the MIME type is not recognized.
*
* @example
* const data = { path: '/static/example.jpg', size: 12345, url: 'http://example.com/static/example.jpg' };
* const mimeType = getFileMimeType(data); // "image/jpg"
*/

export const getFileMimeType = (data: LibraryAssetResponse): string => {
const ext = data.path.split('.').pop()?.toLowerCase(); // Extract and lowercase the file extension
return ext && acceptedImgMimeTypes[ext] ? acceptedImgMimeTypes[ext] : 'unknown';
};
/**
* Parses a `LibraryAssetResponse` into a `TinyMCEImageData` object.
* This includes extracting the file name, MIME type, and constructing other image-related metadata.
*
* @param data - The asset data to parse.
* @returns The parsed image data with properties like `displayName`, `contentType`, etc.
*
* @example
* const data = { path: '/static/example.jpg', size: 12345, url: 'http://example.com/static/example.jpg' };
* const imageData = parseLibraryImageData(data);
* // {
* // displayName: 'example.jpg',
* // contentType: 'image/jpg',
* // url: 'http://example.com/static/example.jpg',
* // externalUrl: 'http://example.com/static/example.jpg',
* // portableUrl: '/static/example.jpg',
* // thumbnail: 'http://example.com/static/example.jpg',
* // id: '/static/example.jpg',
* // locked: false
* // }
*/

export const parseLibraryImageData = (data: LibraryAssetResponse): TinyMCEImageData => ({
displayName: getFileName(data),
contentType: getFileMimeType(data),
url: data.url,
externalUrl: data.url,
portableUrl: data.path,
thumbnail: data.url,
id: data.path,
locked: false,
});

/**
* Filters and transforms an array of `LibrariesAssetResponse` objects into a dictionary of `TinyMCEImageData`.
* Only assets with recognized MIME types (i.e., valid image files) are included in the result.
*
* @param librariesAssets - The array of asset data to process.
* @returns A dictionary where each key is the file name and the value is the corresponding `TinyMCEImageData`.
*
* @example
* const assets = [
* { path: '/static/example.jpg', size: 12345, url: 'http://example.com/static/example.jpg' },
* { path: '/assets/files/unsupported.xyz', size: 67890, url: 'http://example.com/assets/files/unsupported.xyz' }
* ];
* const imageAssets = getLibraryImageAssets(assets);
* // {
* // 'example.jpg': {
* // displayName: 'example.jpg',
* // contentType: 'image/jpg',
* // url: 'http://example.com/static/example.jpg',
* // externalUrl: 'http://example.com/static/example.jpg',
* // portableUrl: '/static/example.jpg',
* // thumbnail: 'http://example.com/static/example.jpg',
* // id: '/static/example.jpg',
* // locked: false
* // }
* // }
*/

export const getLibraryImageAssets = (
librariesAssets: Array<LibraryAssetResponse>,
): Record<string, TinyMCEImageData> => librariesAssets.reduce((obj, file) => {
if (getFileMimeType(file) !== 'unknown') {
const imageData = parseLibraryImageData(file);
return { ...obj, [imageData.displayName]: imageData };
}
return obj;
}, {} as Record<string, TinyMCEImageData>);
121 changes: 121 additions & 0 deletions src/editors/utils/formatLibreryImgRequest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
parseLibraryImageData, getLibraryImageAssets, getFileMimeType, getFileName, LibraryAssetResponse

Check failure on line 2 in src/editors/utils/formatLibreryImgRequest.test.ts

View workflow job for this annotation

GitHub Actions / tests

Missing trailing comma
} from './formatLibraryImgRequest';

// Mock the StrictDict function to avoid unnecessary complexity in the test
jest.mock('./StrictDict', () => ({
__esModule: true,
default: jest.fn().mockReturnValue({
gif: 'image/gif',
jpg: 'image/jpg',
jpeg: 'image/jpeg',
png: 'image/png',
tif: 'image/tiff',
tiff: 'image/tiff',
ico: 'image/x-icon',
}),
}));

describe('parseLibraryImageData', () => {
describe('getFileName', () => {
it('should return the file name from the path', () => {
const data: LibraryAssetResponse = {
path: 'static/example.jpg',
size: 12345,
url: 'http://example.com/static/example.jpg',
};

const result = getFileName(data);
expect(result).toBe('example.jpg');
});
});

describe('getFileMimeType', () => {
it('should return the correct MIME type for supported file extensions', () => {
const data: LibraryAssetResponse = {
path: 'static/example.jpg',
size: 12345,
url: 'http://example.com/static/example.jpg',
};

const result = getFileMimeType(data);
expect(result).toBe('image/jpg');
});

it('should return "unknown" for unsupported file extensions', () => {
const data: LibraryAssetResponse = {
path: '/assets/files/unknown.xyz',
size: 12345,
url: 'http://example.com/assets/files/unknown.xyz',
};

const result = getFileMimeType(data);
expect(result).toBe('unknown');
});
});

describe('parseLibraryImageData', () => {
it('should correctly parse a valid LibraryAssetResponse into TinyMCEImageData', () => {
const data: LibraryAssetResponse = {
path: 'static/example.jpg',
size: 12345,
url: 'http://example.com/static/example.jpg',
};

const result = parseLibraryImageData(data);
expect(result).toEqual({
displayName: 'example.jpg',
contentType: 'image/jpg',
url: 'http://example.com/static/example.jpg',
externalUrl: 'http://example.com/static/example.jpg',
portableUrl: 'static/example.jpg',
thumbnail: 'http://example.com/static/example.jpg',
id: 'static/example.jpg',
locked: false,
});
});

it('should handle unknown MIME types by setting a fallback MIME type', () => {
const data: LibraryAssetResponse = {
path: '/assets/files/unknown.xyz',
size: 12345,
url: 'http://example.com/assets/files/unknown.xyz',
};

const result = parseLibraryImageData(data);
expect(result.contentType).toBe('unknown');
});
});

describe('getLibraryImageAssets', () => {
it('should filter out assets with unsupported MIME types and return a dictionary of valid images', () => {
const assets: LibraryAssetResponse[] = [
{ path: 'static/example.jpg', size: 12345, url: 'http://example.com/static/example.jpg' },
{ path: '/assets/files/unsupported.xyz', size: 67890, url: 'http://example.com/assets/files/unsupported.xyz' },
];

const result = getLibraryImageAssets(assets);
expect(result).toEqual({
'example.jpg': {
displayName: 'example.jpg',
contentType: 'image/jpg',
url: 'http://example.com/static/example.jpg',
externalUrl: 'http://example.com/static/example.jpg',
portableUrl: 'static/example.jpg',
thumbnail: 'http://example.com/static/example.jpg',
id: 'static/example.jpg',
locked: false,
},
});
});

it('should return an empty object if no valid images are found', () => {
const assets: LibraryAssetResponse[] = [
{ path: '/assets/files/unsupported.xyz', size: 67890, url: 'http://example.com/assets/files/unsupported.xyz' },
];

const result = getLibraryImageAssets(assets);
expect(result).toEqual({});
});
});
});
2 changes: 1 addition & 1 deletion src/editors/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export { default as camelizeKeys } from './camelizeKeys';
export { default as removeItemOnce } from './removeOnce';
export { default as formatDuration } from './formatDuration';
export { default as snakeCaseKeys } from './snakeCaseKeys';
export * from './formatLibrariesImgRequest';
export * from './formatLibraryImgRequest';

0 comments on commit d92d00a

Please sign in to comment.