From 4a1dc929a681514eb18bd9d51acedc2bbb012dcd Mon Sep 17 00:00:00 2001 From: barthc Date: Wed, 4 Mar 2020 00:22:51 +0100 Subject: [PATCH] fix: add unit tests --- .../netlify-cms-backend-github/src/API.ts | 5 +- .../src/__tests__/API.spec.js | 2 +- .../src/__tests__/implementation.spec.js | 27 ++-- .../src/__tests__/implementation.spec.js | 8 +- .../src/__tests__/backend.spec.js | 121 ++++++++++++++++++ .../src/actions/__tests__/config.spec.js | 35 ++++- .../netlify-cms-core/src/actions/config.js | 22 ++-- .../netlify-cms-core/src/actions/entries.ts | 4 +- packages/netlify-cms-core/src/backend.ts | 10 +- 9 files changed, 188 insertions(+), 46 deletions(-) diff --git a/packages/netlify-cms-backend-github/src/API.ts b/packages/netlify-cms-backend-github/src/API.ts index 70f35222f867..8d40deb1820d 100644 --- a/packages/netlify-cms-backend-github/src/API.ts +++ b/packages/netlify-cms-backend-github/src/API.ts @@ -531,10 +531,7 @@ export default class API { let entries = diffs.filter(d => d.patch && !d.filename.endsWith('.svg')); let mediaFiles = difference(diffs, entries); entries = entries.map(e => ({ path: e.filename, newFile: e.status === 'added' })); - mediaFiles = mediaFiles.map(({ filename: path, sha: id }) => ({ - path, - id, - })); + mediaFiles = mediaFiles.map(e => ({ path: e.filename, id: e.sha })); const label = pullRequest.labels.find(l => isCMSLabel(l.name)) as { name: string }; const status = labelToStatus(label.name); return { branch, collection, slug, status, entries, mediaFiles, pullRequest }; diff --git a/packages/netlify-cms-backend-github/src/__tests__/API.spec.js b/packages/netlify-cms-backend-github/src/__tests__/API.spec.js index af10f92aeaf6..973bcf6903f9 100644 --- a/packages/netlify-cms-backend-github/src/__tests__/API.spec.js +++ b/packages/netlify-cms-backend-github/src/__tests__/API.spec.js @@ -277,7 +277,7 @@ describe('github API', () => { }, ]; - await api.persistFiles(entry, mediaFiles, { useWorkflow: true }); + await api.persistFiles([entry], mediaFiles, { useWorkflow: true }); expect(api.uploadBlob).toHaveBeenCalledTimes(3); expect(api.uploadBlob).toHaveBeenCalledWith(entry); diff --git a/packages/netlify-cms-backend-github/src/__tests__/implementation.spec.js b/packages/netlify-cms-backend-github/src/__tests__/implementation.spec.js index b51f945e871b..c5b8c502eee6 100644 --- a/packages/netlify-cms-backend-github/src/__tests__/implementation.spec.js +++ b/packages/netlify-cms-backend-github/src/__tests__/implementation.spec.js @@ -165,6 +165,7 @@ describe('github backend implementation', () => { describe('unpublishedEntry', () => { const generateContentKey = jest.fn(); const readUnpublishedBranchFile = jest.fn(); + const loadEntryMediaFiles = jest.fn(); const mockAPI = { generateContentKey, @@ -174,14 +175,13 @@ describe('github backend implementation', () => { it('should return unpublished entry', async () => { const gitHubImplementation = new GitHubImplementation(config); gitHubImplementation.api = mockAPI; - gitHubImplementation.loadEntryMediaFiles = jest - .fn() - .mockResolvedValue([{ path: 'image.png', id: 'sha' }]); generateContentKey.mockReturnValue('contentKey'); const data = { - fileData: 'fileData', + slug: 'slug', + file: { path: 'entry-path', id: null }, + data: 'fileData', isModification: true, metaData: { branch: 'branch', @@ -189,29 +189,20 @@ describe('github backend implementation', () => { entry: { path: 'entry-path', mediaFiles: [{ path: 'image.png', id: 'sha' }] }, }, }, + mediaFiles: [{ path: 'image.png', id: 'sha' }], }; readUnpublishedBranchFile.mockResolvedValue(data); const collection = 'posts'; - await expect(gitHubImplementation.unpublishedEntry(collection, 'slug')).resolves.toEqual({ - slug: 'slug', - file: { path: 'entry-path', id: null }, - data: 'fileData', - metaData: data.metaData, - mediaFiles: [{ path: 'image.png', id: 'sha' }], - isModification: true, - }); + await expect( + gitHubImplementation.unpublishedEntry(collection, 'slug', { loadEntryMediaFiles }), + ); expect(generateContentKey).toHaveBeenCalledTimes(1); expect(generateContentKey).toHaveBeenCalledWith('posts', 'slug'); expect(readUnpublishedBranchFile).toHaveBeenCalledTimes(1); - expect(readUnpublishedBranchFile).toHaveBeenCalledWith('contentKey'); - - expect(gitHubImplementation.loadEntryMediaFiles).toHaveBeenCalledTimes(1); - expect(gitHubImplementation.loadEntryMediaFiles).toHaveBeenCalledWith('branch', [ - { path: 'image.png', id: 'sha' }, - ]); + expect(readUnpublishedBranchFile).toHaveBeenCalledWith('contentKey', loadEntryMediaFiles); }); }); }); diff --git a/packages/netlify-cms-backend-test/src/__tests__/implementation.spec.js b/packages/netlify-cms-backend-test/src/__tests__/implementation.spec.js index bdf99a2864cb..9d25ecb1d84d 100644 --- a/packages/netlify-cms-backend-test/src/__tests__/implementation.spec.js +++ b/packages/netlify-cms-backend-test/src/__tests__/implementation.spec.js @@ -52,7 +52,7 @@ describe('test backend implementation', () => { const backend = new TestBackend(); const entry = { path: 'posts/some-post.md', raw: 'content', slug: 'some-post.md' }; - await backend.persistEntry(entry, [], { newEntry: true }); + await backend.persistEntry([entry], [], { newEntry: true }); expect(window.repoFiles).toEqual({ posts: { @@ -80,7 +80,7 @@ describe('test backend implementation', () => { const backend = new TestBackend(); const entry = { path: 'posts/new-post.md', raw: 'content', slug: 'new-post.md' }; - await backend.persistEntry(entry, [], { newEntry: true }); + await backend.persistEntry([entry], [], { newEntry: true }); expect(window.repoFiles).toEqual({ pages: { @@ -107,7 +107,7 @@ describe('test backend implementation', () => { const slug = 'dir1/dir2/some-post.md'; const path = `posts/${slug}`; const entry = { path, raw: 'content', slug }; - await backend.persistEntry(entry, [], { newEntry: true }); + await backend.persistEntry([entry], [], { newEntry: true }); expect(window.repoFiles).toEqual({ posts: { @@ -141,7 +141,7 @@ describe('test backend implementation', () => { const slug = 'dir1/dir2/some-post.md'; const path = `posts/${slug}`; const entry = { path, raw: 'new content', slug }; - await backend.persistEntry(entry, [], { newEntry: false }); + await backend.persistEntry([entry], [], { newEntry: false }); expect(window.repoFiles).toEqual({ posts: { diff --git a/packages/netlify-cms-core/src/__tests__/backend.spec.js b/packages/netlify-cms-core/src/__tests__/backend.spec.js index 994e76a1a761..246a82fa1aae 100644 --- a/packages/netlify-cms-core/src/__tests__/backend.spec.js +++ b/packages/netlify-cms-core/src/__tests__/backend.spec.js @@ -454,4 +454,125 @@ describe('Backend', () => { ); }); }); + + describe('combineMultiContentEntries', () => { + const implementation = { + init: jest.fn(() => implementation), + }; + const config = Map({}); + const backend = new Backend(implementation, { config, backendName: 'github' }); + + it('should combine mutiple content same folder entries', () => { + const entries = [ + { path: 'posts/post.en.md', data: { title: 'Title en', content: 'Content en' } }, + { path: 'posts/post.fr.md', data: { title: 'Title fr', content: 'Content fr' } }, + ]; + const collection = fromJS({ multi_content: 'same_folder' }); + + expect(backend.combineMultiContentEntries(entries, collection)).toEqual([ + { + path: 'posts/post.md', + raw: '', + data: { + en: { title: 'Title en', content: 'Content en' }, + fr: { title: 'Title fr', content: 'Content fr' }, + }, + }, + ]); + }); + + it('should combine mutiple content different folder entries', () => { + const entries = [ + { path: 'posts/en/post.md', data: { title: 'Title en', content: 'Content en' } }, + { path: 'posts/fr/post.md', data: { title: 'Title fr', content: 'Content fr' } }, + ]; + const collection = fromJS({ multi_content: 'diff_folder' }); + + expect(backend.combineMultiContentEntries(entries, collection)).toEqual([ + { + path: 'posts/post.md', + raw: '', + data: { + en: { title: 'Title en', content: 'Content en' }, + fr: { title: 'Title fr', content: 'Content fr' }, + }, + }, + ]); + }); + }); + + describe('listAllMultipleEntires', () => { + const implementation = { + init: jest.fn(() => implementation), + }; + const config = Map({}); + const backend = new Backend(implementation, { config, backendName: 'github' }); + const locales = fromJS(['en', 'fr']); + + it('should combine mutiple content same folder entries', async () => { + const entries = [ + { + slug: 'post.en', + path: 'posts/post.en.md', + data: { title: 'Title en', content: 'Content en' }, + }, + { + slug: 'post.fr', + path: 'posts/post.fr.md', + data: { title: 'Title fr', content: 'Content fr' }, + }, + ]; + const collection = fromJS({ multi_content: 'same_folder' }); + + backend.listAllEntries = jest.fn().mockResolvedValue(entries); + + await expect(backend.listAllMultipleEntires(collection, '', locales)).resolves.toEqual({ + entries: [ + { + slug: 'post', + path: 'posts/post.md', + raw: '', + data: { + en: { title: 'Title en', content: 'Content en' }, + fr: { title: 'Title fr', content: 'Content fr' }, + }, + multiContentKey: 'posts/post', + }, + ], + }); + }); + + it('should combine mutiple content different folder entries', async () => { + const entries = [ + { + slug: 'en/post', + path: 'posts/en/post.md', + data: { title: 'Title en', content: 'Content en' }, + }, + { + slug: 'fr/post', + path: 'posts/fr/post.md', + data: { title: 'Title fr', content: 'Content fr' }, + }, + ]; + const collection = fromJS({ multi_content: 'diff_folder' }); + + backend.listAllEntries = jest.fn().mockResolvedValue(entries); + + await expect(backend.listAllMultipleEntires(collection, '', locales)).resolves.toEqual({ + entries: [ + { + slug: 'post', + path: 'posts/post.md', + raw: '', + data: { + en: { title: 'Title en', content: 'Content en' }, + fr: { title: 'Title fr', content: 'Content fr' }, + }, + multiContentKey: 'posts/post.md', + }, + ], + }); + }); + }); }); diff --git a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js index 688726db72e1..ac8c32c18687 100644 --- a/packages/netlify-cms-core/src/actions/__tests__/config.spec.js +++ b/packages/netlify-cms-core/src/actions/__tests__/config.spec.js @@ -1,5 +1,5 @@ import { fromJS } from 'immutable'; -import { applyDefaults, detectProxyServer, handleLocalBackend } from '../config'; +import { applyDefaults, detectProxyServer, handleLocalBackend, addLocaleFields } from '../config'; jest.spyOn(console, 'log').mockImplementation(() => {}); @@ -311,4 +311,37 @@ describe('config', () => { ); }); }); + + describe('addLocaleFields', () => { + it('should add locale fields', () => { + const fields = [ + { name: 'title', widget: 'string' }, + { name: 'content', widget: 'markdown' }, + ]; + const actual = addLocaleFields(fields, ['en', 'fr']); + + expect(actual).toEqual([ + { + label: 'en', + name: 'en', + widget: 'object', + multiContentId: Symbol.for('multiContentId'), + fields: [ + { name: 'title', widget: 'string' }, + { name: 'content', widget: 'markdown' }, + ], + }, + { + label: 'fr', + name: 'fr', + widget: 'object', + multiContentId: Symbol.for('multiContentId'), + fields: [ + { name: 'title', widget: 'string' }, + { name: 'content', widget: 'markdown' }, + ], + }, + ]); + }); + }); }); diff --git a/packages/netlify-cms-core/src/actions/config.js b/packages/netlify-cms-core/src/actions/config.js index 2c5816504715..565e77d33693 100644 --- a/packages/netlify-cms-core/src/actions/config.js +++ b/packages/netlify-cms-core/src/actions/config.js @@ -56,24 +56,20 @@ export function applyDefaults(config) { map.setIn(['slug', 'sanitize_replacement'], '-'); } - const langs = map.get('locales'); + const locales = map.get('locales'); // Strip leading slash from collection folders and files map.set( 'collections', map.get('collections').map(collection => { const folder = collection.get('folder'); if (folder) { - if (collection.has('media_folder') && !collection.has('public_folder')) { - collection = collection.set('public_folder', collection.get('media_folder')); - } - const fields = collection.get('fields'); const identifier_field = selectIdentifier(collection); - if (langs && fields && collection.get('multi_content')) { - // add languague fields + if (locales && fields && collection.get('multi_content')) { + // add locale fields collection = collection.set( 'fields', - fromJS(addLanguageFields(fields.toJS(), langs.toJS())), + fromJS(addLocaleFields(fields.toJS(), locales.toJS())), ); // remove path for same or different folder config @@ -83,7 +79,7 @@ export function applyDefaults(config) { // add identifier config collection = collection.set( 'identifier_field', - `${langs.first()}.${identifier_field}`, + `${locales.first()}.${identifier_field}`, ); } @@ -92,6 +88,10 @@ export function applyDefaults(config) { collection = collection.set('media_folder', ''); } + if (collection.has('media_folder') && !collection.has('public_folder')) { + collection = collection.set('public_folder', collection.get('media_folder')); + } + return collection.set('folder', trimStart(folder, '/')); } @@ -112,8 +112,8 @@ export function applyDefaults(config) { }); } -export function addLanguageFields(fields, langs) { - return langs.reduce((acc, item) => { +export function addLocaleFields(fields, locales) { + return locales.reduce((acc, item) => { return [ ...acc, { diff --git a/packages/netlify-cms-core/src/actions/entries.ts b/packages/netlify-cms-core/src/actions/entries.ts index c50b15bf83f9..5450c8337c8d 100644 --- a/packages/netlify-cms-core/src/actions/entries.ts +++ b/packages/netlify-cms-core/src/actions/entries.ts @@ -712,12 +712,10 @@ export function deleteEntry(collection: Collection, slug: string) { return (dispatch: ThunkDispatch, getState: () => State) => { const state = getState(); const backend = currentBackend(state.config); - const locales = state.config.get('locales'); - const multiContent = DIFF_FILE_TYPES.includes(collection.get('multi_content')); dispatch(entryDeleting(collection, slug)); return backend - .deleteEntry(state, collection, slug, locales, multiContent) + .deleteEntry(state, collection, slug) .then(() => { return dispatch(entryDeleted(collection, slug)); }) diff --git a/packages/netlify-cms-core/src/backend.ts b/packages/netlify-cms-core/src/backend.ts index c3002d0a1367..2221adf07aed 100644 --- a/packages/netlify-cms-core/src/backend.ts +++ b/packages/netlify-cms-core/src/backend.ts @@ -963,15 +963,17 @@ export class Backend { return this.implementation.persistMedia(file, options); } - async deleteEntry(state: State, collection: Collection, slug: string, locales, multiContent) { + async deleteEntry(state: State, collection: Collection, slug: string) { + const config = state.config; const path = selectEntryPath(collection, slug) as string; const extension = selectFolderEntryExtension(collection) as string; + const multiContent = DIFF_FILE_TYPES.includes(collection.get('multi_content')); + const locales = config.get('locales'); if (!selectAllowDeletion(collection)) { throw new Error('Not allowed to delete entries in this collection'); } - const config = state.config; const user = (await this.currentUser()) as User; const commitMessage = commitMessageFormatter( 'delete', @@ -989,7 +991,7 @@ export class Backend { let result; const entry = selectEntry(state.entries, collection.get('name'), slug); await this.invokePreUnpublishEvent(entry); - if (locales && multiContent === 'same_folder') { + if (locales && multiContent === SAME_FOLDER) { result = await Promise.all( locales.map(l => this.implementation @@ -997,7 +999,7 @@ export class Backend { .catch(() => undefined), ), ); - } else if (locales && multiContent === 'diff_folder') { + } else if (locales && multiContent === DIFF_FOLDER) { result = await Promise.all( locales.map(l => this.implementation