UNPKG

netlify-cms-core

Version:

Netlify CMS core application, see netlify-cms package for the main distribution.

695 lines (631 loc) 21.3 kB
import { OrderedMap, fromJS } from 'immutable'; import * as actions from '../../actions/entries'; import reducer, { selectMediaFolder, selectMediaFilePath, selectMediaFilePublicPath, selectEntries, } from '../entries'; const initialState = OrderedMap({ posts: fromJS({ name: 'posts' }), }); describe('entries', () => { describe('reducer', () => { it('should mark entries as fetching', () => { expect(reducer(initialState, actions.entriesLoading(fromJS({ name: 'posts' })))).toEqual( OrderedMap( fromJS({ posts: { name: 'posts' }, pages: { posts: { isFetching: true }, }, }), ), ); }); it('should handle loaded entries', () => { const entries = [ { slug: 'a', path: '' }, { slug: 'b', title: 'B' }, ]; expect( reducer(initialState, actions.entriesLoaded(fromJS({ name: 'posts' }), entries, 0)), ).toEqual( OrderedMap( fromJS({ posts: { name: 'posts' }, entities: { 'posts.a': { slug: 'a', path: '', isFetching: false }, 'posts.b': { slug: 'b', title: 'B', isFetching: false }, }, pages: { posts: { page: 0, ids: ['a', 'b'], }, }, }), ), ); }); it('should handle loaded entry', () => { const entry = { slug: 'a', path: '' }; expect(reducer(initialState, actions.entryLoaded(fromJS({ name: 'posts' }), entry))).toEqual( OrderedMap( fromJS({ posts: { name: 'posts' }, entities: { 'posts.a': { slug: 'a', path: '' }, }, pages: { posts: { ids: ['a'], }, }, }), ), ); }); }); describe('selectMediaFolder', () => { it("should return global media folder when collection doesn't specify media_folder", () => { expect( selectMediaFolder( { media_folder: 'static/media' }, fromJS({ name: 'posts' }), undefined, undefined, ), ).toEqual('static/media'); }); it('should return draft media folder when collection specifies media_folder and entry is undefined', () => { expect( selectMediaFolder( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts', media_folder: '' }), undefined, undefined, ), ).toEqual('posts/DRAFT_MEDIA_FILES'); }); it('should return relative media folder when collection specifies media_folder and entry path is not null', () => { expect( selectMediaFolder( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts', media_folder: '' }), fromJS({ path: 'posts/title/index.md' }), undefined, ), ).toEqual('posts/title'); }); it('should resolve collection relative media folder', () => { expect( selectMediaFolder( fromJS({ media_folder: 'static/media' }), fromJS({ name: 'posts', folder: 'posts', media_folder: '../' }), fromJS({ path: 'posts/title/index.md' }), undefined, ), ).toEqual('posts'); }); it('should resolve field relative media folder', () => { const field = fromJS({ media_folder: '' }); expect( selectMediaFolder( { media_folder: '/static/img' }, fromJS({ name: 'other', folder: 'other', fields: [field], media_folder: '../', }), fromJS({ path: 'src/other/other.md', data: {} }), field, ), ).toEqual('src/other'); }); it('should return collection absolute media folder without leading slash', () => { expect( selectMediaFolder( { media_folder: '/static/Images' }, fromJS({ name: 'getting-started', folder: 'src/docs/getting-started', media_folder: '/static/images/docs/getting-started', }), fromJS({}), undefined, ), ).toEqual('static/images/docs/getting-started'); }); it('should compile relative media folder template', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const collection = fromJS({ name: 'posts', folder: 'content', media_folder: '../../../{{media_folder}}/{{category}}/{{slug}}', fields: [{ name: 'title', widget: 'string' }], }); expect( selectMediaFolder( { media_folder: 'static/media', slug: slugConfig }, collection, entry, undefined, ), ).toEqual('static/media/hosting-and-deployment/deployment-with-nanobox'); }); it('should compile absolute media folder template', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ data: { title: 'Overview' }, }); const collection = fromJS({ name: 'extending', folder: 'src/docs/extending', media_folder: '{{media_folder}}/docs/extending', fields: [{ name: 'title', widget: 'string' }], }); expect( selectMediaFolder( { media_folder: '/static/images', slug: slugConfig }, collection, entry, undefined, ), ).toEqual('static/images/docs/extending'); }); it('should compile field media folder template', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const collection = fromJS({ name: 'posts', folder: 'content', fields: [ { name: 'title', widget: 'string', media_folder: '../../../{{media_folder}}/{{category}}/{{slug}}', }, ], }); expect( selectMediaFolder( { media_folder: 'static/media', slug: slugConfig }, collection, entry, collection.get('fields').get(0), ), ).toEqual('static/media/hosting-and-deployment/deployment-with-nanobox'); }); it('should handle double slashes', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const collection = fromJS({ name: 'posts', folder: 'content', media_folder: '{{media_folder}}/blog', fields: [{ name: 'title', widget: 'string' }], }); expect( selectMediaFolder( { media_folder: '/static/img/', slug: slugConfig }, collection, entry, undefined, ), ).toEqual('static/img/blog'); expect( selectMediaFolder( { media_folder: 'static/img/', slug: slugConfig }, collection, entry, undefined, ), ).toEqual('content/en/hosting-and-deployment/static/img/blog'); }); it('should handle file media_folder', () => { expect( selectMediaFolder( { media_folder: 'static/media' }, fromJS({ name: 'posts', files: [{ name: 'index', media_folder: '/static/images/' }] }), fromJS({ path: 'posts/title/index.md', slug: 'index' }), undefined, ), ).toBe('static/images'); }); it('should cascade media_folders', () => { const mainImageField = fromJS({ name: 'main_image' }); const logoField = fromJS({ name: 'logo', media_folder: '{{media_folder}}/logos/' }); const nestedField3 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested3/' }); const nestedField2 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested2/', types: [nestedField3], }); const nestedField1 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested1/', fields: [nestedField2], }); const args = [ { media_folder: '/static/img' }, fromJS({ name: 'general', media_folder: '{{media_folder}}/general/', files: [ { name: 'customers', media_folder: '{{media_folder}}/customers/', fields: [ mainImageField, logoField, { media_folder: '{{media_folder}}/nested', field: nestedField1 }, ], }, ], }), fromJS({ path: 'src/customers/customers.md', slug: 'customers', data: { title: 'title' } }), ]; expect(selectMediaFolder(...args, mainImageField)).toBe('static/img/general/customers'); expect(selectMediaFolder(...args, logoField)).toBe('static/img/general/customers/logos'); expect(selectMediaFolder(...args, nestedField1)).toBe( 'static/img/general/customers/nested/nested1', ); expect(selectMediaFolder(...args, nestedField2)).toBe( 'static/img/general/customers/nested/nested1/nested2', ); expect(selectMediaFolder(...args, nestedField3)).toBe( 'static/img/general/customers/nested/nested1/nested2/nested3', ); }); }); describe('selectMediaFilePath', () => { it('should return absolute URL as is', () => { expect(selectMediaFilePath(null, null, null, 'https://www.netlify.com/image.png')).toBe( 'https://www.netlify.com/image.png', ); }); it('should resolve path from global media folder for collection with no media folder', () => { expect( selectMediaFilePath( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts' }), undefined, 'image.png', undefined, ), ).toBe('static/media/image.png'); }); it('should resolve path from collection media folder for collection with media folder', () => { expect( selectMediaFilePath( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts', media_folder: '' }), undefined, 'image.png', undefined, ), ).toBe('posts/DRAFT_MEDIA_FILES/image.png'); }); it('should handle relative media_folder', () => { expect( selectMediaFilePath( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts', media_folder: '../../static/media/' }), fromJS({ path: 'posts/title/index.md' }), 'image.png', undefined, ), ).toBe('static/media/image.png'); }); it('should handle field media_folder', () => { const field = fromJS({ media_folder: '../../static/media/' }); expect( selectMediaFilePath( { media_folder: 'static/media' }, fromJS({ name: 'posts', folder: 'posts', fields: [field] }), fromJS({ path: 'posts/title/index.md' }), 'image.png', field, ), ).toBe('static/media/image.png'); }); }); describe('selectMediaFilePublicPath', () => { it('should return absolute URL as is', () => { expect(selectMediaFilePublicPath(null, null, 'https://www.netlify.com/image.png')).toBe( 'https://www.netlify.com/image.png', ); }); it('should resolve path from public folder for collection with no media folder', () => { expect( selectMediaFilePublicPath( { public_folder: '/media' }, null, '/media/image.png', undefined, undefined, ), ).toBe('/media/image.png'); }); it('should resolve path from collection public folder for collection with public folder', () => { expect( selectMediaFilePublicPath( { public_folder: '/media' }, fromJS({ name: 'posts', folder: 'posts', public_folder: '' }), 'image.png', undefined, undefined, ), ).toBe('image.png'); }); it('should handle relative public_folder', () => { expect( selectMediaFilePublicPath( { public_folder: '/media' }, fromJS({ name: 'posts', folder: 'posts', public_folder: '../../static/media/' }), 'image.png', undefined, undefined, ), ).toBe('../../static/media/image.png'); }); it('should handle absolute public_folder', () => { expect( selectMediaFilePublicPath( { public_folder: 'https://www.netlify.com/media' }, fromJS({ name: 'posts', folder: 'posts', public_folder: 'https://www.netlify.com/media', }), 'image.png', undefined, undefined, ), ).toBe('https://www.netlify.com/media/image.png'); }); it('should compile collection public folder template', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const collection = fromJS({ name: 'posts', folder: 'content', public_folder: '/{{public_folder}}/{{category}}/{{slug}}', fields: [{ name: 'title', widget: 'string' }], }); expect( selectMediaFilePublicPath( { public_folder: 'static/media', slug: slugConfig }, collection, 'image.png', entry, undefined, ), ).toEqual('/static/media/hosting-and-deployment/deployment-with-nanobox/image.png'); }); it('should compile field public folder template', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const field = fromJS({ name: 'title', widget: 'string', public_folder: '/{{public_folder}}/{{category}}/{{slug}}', }); const collection = fromJS({ name: 'posts', folder: 'content', fields: [field], }); expect( selectMediaFilePublicPath( { public_folder: 'static/media', slug: slugConfig }, collection, 'image.png', entry, field, ), ).toEqual('/static/media/hosting-and-deployment/deployment-with-nanobox/image.png'); }); it('should handle double slashes', () => { const slugConfig = { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-', }; const entry = fromJS({ path: 'content/en/hosting-and-deployment/deployment-with-nanobox.md', data: { title: 'Deployment With NanoBox', category: 'Hosting And Deployment' }, }); const field = fromJS({ name: 'title', widget: 'string', public_folder: '/{{public_folder}}/{{category}}/{{slug}}', }); const collection = fromJS({ name: 'posts', folder: 'content', fields: [field], }); expect( selectMediaFilePublicPath( { public_folder: 'static/media/', slug: slugConfig }, collection, 'image.png', entry, field, ), ).toEqual('/static/media/hosting-and-deployment/deployment-with-nanobox/image.png'); }); it('should handle file public_folder', () => { const entry = fromJS({ path: 'src/posts/index.md', slug: 'index', }); const collection = fromJS({ name: 'posts', files: [ { name: 'index', public_folder: '/images', fields: [{ name: 'title', widget: 'string' }], }, ], }); expect( selectMediaFilePublicPath( { public_folder: 'static/media/' }, collection, 'image.png', entry, undefined, ), ).toBe('/images/image.png'); }); }); describe('selectEntries', () => { it('should return all entries', () => { const state = fromJS({ entities: { 'posts.1': { slug: '1' }, 'posts.2': { slug: '2' }, 'posts.3': { slug: '3' }, 'posts.4': { slug: '4' }, }, pages: { posts: { ids: ['1', '2', '3', '4'] } }, }); const collection = fromJS({ name: 'posts', }); expect(selectEntries(state, collection)).toEqual( fromJS([{ slug: '1' }, { slug: '2' }, { slug: '3' }, { slug: '4' }]), ); }); }); it('should return sorted entries entries by field', () => { const state = fromJS({ entities: { 'posts.1': { slug: '1', data: { title: '1' } }, 'posts.2': { slug: '2', data: { title: '2' } }, 'posts.3': { slug: '3', data: { title: '3' } }, 'posts.4': { slug: '4', data: { title: '4' } }, }, pages: { posts: { ids: ['1', '2', '3', '4'] } }, sort: { posts: { title: { key: 'title', direction: 'Descending' } } }, }); const collection = fromJS({ name: 'posts', }); expect(selectEntries(state, collection)).toEqual( fromJS([ { slug: '4', data: { title: '4' } }, { slug: '3', data: { title: '3' } }, { slug: '2', data: { title: '2' } }, { slug: '1', data: { title: '1' } }, ]), ); }); it('should return sorted entries entries by nested field', () => { const state = fromJS({ entities: { 'posts.1': { slug: '1', data: { title: '1', nested: { date: 4 } } }, 'posts.2': { slug: '2', data: { title: '2', nested: { date: 3 } } }, 'posts.3': { slug: '3', data: { title: '3', nested: { date: 2 } } }, 'posts.4': { slug: '4', data: { title: '4', nested: { date: 1 } } }, }, pages: { posts: { ids: ['1', '2', '3', '4'] } }, sort: { posts: { title: { key: 'nested.date', direction: 'Ascending' } } }, }); const collection = fromJS({ name: 'posts', }); expect(selectEntries(state, collection)).toEqual( fromJS([ { slug: '4', data: { title: '4', nested: { date: 1 } } }, { slug: '3', data: { title: '3', nested: { date: 2 } } }, { slug: '2', data: { title: '2', nested: { date: 3 } } }, { slug: '1', data: { title: '1', nested: { date: 4 } } }, ]), ); }); it('should return filtered entries entries by field', () => { const state = fromJS({ entities: { 'posts.1': { slug: '1', data: { title: '1' } }, 'posts.2': { slug: '2', data: { title: '2' } }, 'posts.3': { slug: '3', data: { title: '3' } }, 'posts.4': { slug: '4', data: { title: '4' } }, }, pages: { posts: { ids: ['1', '2', '3', '4'] } }, filter: { posts: { title__1: { field: 'title', pattern: '4', active: true } } }, }); const collection = fromJS({ name: 'posts', }); expect(selectEntries(state, collection)).toEqual(fromJS([{ slug: '4', data: { title: '4' } }])); }); it('should return filtered entries entries by nested field', () => { const state = fromJS({ entities: { 'posts.1': { slug: '1', data: { title: '1', nested: { draft: true } } }, 'posts.2': { slug: '2', data: { title: '2', nested: { draft: true } } }, 'posts.3': { slug: '3', data: { title: '3', nested: { draft: false } } }, 'posts.4': { slug: '4', data: { title: '4', nested: { draft: false } } }, }, pages: { posts: { ids: ['1', '2', '3', '4'] } }, filter: { posts: { 'nested.draft__false': { field: 'nested.draft', pattern: false, active: true } }, }, }); const collection = fromJS({ name: 'posts', }); expect(selectEntries(state, collection)).toEqual( fromJS([ { slug: '3', data: { title: '3', nested: { draft: false } } }, { slug: '4', data: { title: '4', nested: { draft: false } } }, ]), ); }); });