@pranaysahith/decap-cms-core 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +9 -0
  2. package/dist/@pranaysahith/decap-cms-core.js +52 -0
  3. package/dist/@pranaysahith/decap-cms-core.js.LICENSE.txt +141 -0
  4. package/dist/@pranaysahith/decap-cms-core.js.map +1 -0
  5. package/dist/decap-cms-core.js +47 -0
  6. package/dist/decap-cms-core.js.LICENSE.txt +116 -0
  7. package/dist/decap-cms-core.js.map +1 -0
  8. package/dist/esm/actions/auth.js +97 -0
  9. package/dist/esm/actions/collections.js +15 -0
  10. package/dist/esm/actions/config.js +493 -0
  11. package/dist/esm/actions/deploys.js +79 -0
  12. package/dist/esm/actions/editorialWorkflow.js +480 -0
  13. package/dist/esm/actions/entries.js +865 -0
  14. package/dist/esm/actions/media.js +147 -0
  15. package/dist/esm/actions/mediaLibrary.js +552 -0
  16. package/dist/esm/actions/notifications.js +21 -0
  17. package/dist/esm/actions/search.js +149 -0
  18. package/dist/esm/actions/status.js +74 -0
  19. package/dist/esm/actions/waitUntil.js +32 -0
  20. package/dist/esm/backend.js +1082 -0
  21. package/dist/esm/bootstrap.js +101 -0
  22. package/dist/esm/components/App/App.js +289 -0
  23. package/dist/esm/components/App/Header.js +172 -0
  24. package/dist/esm/components/App/NotFoundPage.js +19 -0
  25. package/dist/esm/components/Collection/Collection.js +198 -0
  26. package/dist/esm/components/Collection/CollectionControls.js +46 -0
  27. package/dist/esm/components/Collection/CollectionSearch.js +222 -0
  28. package/dist/esm/components/Collection/CollectionTop.js +68 -0
  29. package/dist/esm/components/Collection/ControlButton.js +17 -0
  30. package/dist/esm/components/Collection/Entries/Entries.js +73 -0
  31. package/dist/esm/components/Collection/Entries/EntriesCollection.js +241 -0
  32. package/dist/esm/components/Collection/Entries/EntriesSearch.js +113 -0
  33. package/dist/esm/components/Collection/Entries/EntryCard.js +177 -0
  34. package/dist/esm/components/Collection/Entries/EntryListing.js +143 -0
  35. package/dist/esm/components/Collection/FilterControl.js +33 -0
  36. package/dist/esm/components/Collection/FolderRenameControl.js +403 -0
  37. package/dist/esm/components/Collection/GroupControl.js +33 -0
  38. package/dist/esm/components/Collection/NestedCollection.js +308 -0
  39. package/dist/esm/components/Collection/Sidebar.js +91 -0
  40. package/dist/esm/components/Collection/SortControl.js +59 -0
  41. package/dist/esm/components/Collection/ViewStyleControl.js +38 -0
  42. package/dist/esm/components/Editor/Editor.js +466 -0
  43. package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +395 -0
  44. package/dist/esm/components/Editor/EditorControlPane/EditorControlPane.js +254 -0
  45. package/dist/esm/components/Editor/EditorControlPane/Widget.js +374 -0
  46. package/dist/esm/components/Editor/EditorInterface.js +386 -0
  47. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreview.js +47 -0
  48. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewContent.js +66 -0
  49. package/dist/esm/components/Editor/EditorPreviewPane/EditorPreviewPane.js +288 -0
  50. package/dist/esm/components/Editor/EditorPreviewPane/PreviewHOC.js +27 -0
  51. package/dist/esm/components/Editor/EditorToolbar.js +536 -0
  52. package/dist/esm/components/Editor/EntryPathEditor.js +272 -0
  53. package/dist/esm/components/Editor/withWorkflow.js +56 -0
  54. package/dist/esm/components/EditorWidgets/Unknown/UnknownControl.js +18 -0
  55. package/dist/esm/components/EditorWidgets/Unknown/UnknownPreview.js +20 -0
  56. package/dist/esm/components/EditorWidgets/index.js +4 -0
  57. package/dist/esm/components/MediaLibrary/EmptyMessage.js +22 -0
  58. package/dist/esm/components/MediaLibrary/MediaLibrary.js +446 -0
  59. package/dist/esm/components/MediaLibrary/MediaLibraryButtons.js +93 -0
  60. package/dist/esm/components/MediaLibrary/MediaLibraryCard.js +99 -0
  61. package/dist/esm/components/MediaLibrary/MediaLibraryCardGrid.js +198 -0
  62. package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +32 -0
  63. package/dist/esm/components/MediaLibrary/MediaLibraryModal.js +156 -0
  64. package/dist/esm/components/MediaLibrary/MediaLibrarySearch.js +51 -0
  65. package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +123 -0
  66. package/dist/esm/components/UI/DragDrop.js +67 -0
  67. package/dist/esm/components/UI/ErrorBoundary.js +173 -0
  68. package/dist/esm/components/UI/FileUploadButton.js +27 -0
  69. package/dist/esm/components/UI/Modal.js +104 -0
  70. package/dist/esm/components/UI/Notifications.js +62 -0
  71. package/dist/esm/components/UI/SettingsDropdown.js +107 -0
  72. package/dist/esm/components/UI/index.js +6 -0
  73. package/dist/esm/components/Workflow/Workflow.js +133 -0
  74. package/dist/esm/components/Workflow/WorkflowCard.js +128 -0
  75. package/dist/esm/components/Workflow/WorkflowList.js +204 -0
  76. package/dist/esm/constants/collectionTypes.js +2 -0
  77. package/dist/esm/constants/collectionViews.js +2 -0
  78. package/dist/esm/constants/commitProps.js +2 -0
  79. package/dist/esm/constants/configSchema.js +644 -0
  80. package/dist/esm/constants/fieldInference.js +57 -0
  81. package/dist/esm/constants/publishModes.js +18 -0
  82. package/dist/esm/constants/validationErrorTypes.js +6 -0
  83. package/dist/esm/formats/formats.js +83 -0
  84. package/dist/esm/formats/frontmatter.js +146 -0
  85. package/dist/esm/formats/helpers.js +12 -0
  86. package/dist/esm/formats/json.js +8 -0
  87. package/dist/esm/formats/toml.js +32 -0
  88. package/dist/esm/formats/yaml.js +51 -0
  89. package/dist/esm/index.js +7 -0
  90. package/dist/esm/integrations/index.js +28 -0
  91. package/dist/esm/integrations/providers/algolia/implementation.js +174 -0
  92. package/dist/esm/integrations/providers/assetStore/implementation.js +165 -0
  93. package/dist/esm/lib/consoleError.js +3 -0
  94. package/dist/esm/lib/formatters.js +191 -0
  95. package/dist/esm/lib/i18n.js +367 -0
  96. package/dist/esm/lib/phrases.js +6 -0
  97. package/dist/esm/lib/polyfill.js +8 -0
  98. package/dist/esm/lib/registry.js +329 -0
  99. package/dist/esm/lib/serializeEntryValues.js +67 -0
  100. package/dist/esm/lib/stega.js +142 -0
  101. package/dist/esm/lib/textHelper.js +9 -0
  102. package/dist/esm/lib/urlHelper.js +111 -0
  103. package/dist/esm/mediaLibrary.js +37 -0
  104. package/dist/esm/reducers/auth.js +27 -0
  105. package/dist/esm/reducers/collections.js +428 -0
  106. package/dist/esm/reducers/combinedReducer.js +8 -0
  107. package/dist/esm/reducers/config.js +29 -0
  108. package/dist/esm/reducers/cursors.js +31 -0
  109. package/dist/esm/reducers/deploys.js +45 -0
  110. package/dist/esm/reducers/editorialWorkflow.js +83 -0
  111. package/dist/esm/reducers/entries.js +568 -0
  112. package/dist/esm/reducers/entryDraft.js +212 -0
  113. package/dist/esm/reducers/globalUI.js +25 -0
  114. package/dist/esm/reducers/index.js +66 -0
  115. package/dist/esm/reducers/integrations.js +53 -0
  116. package/dist/esm/reducers/mediaLibrary.js +252 -0
  117. package/dist/esm/reducers/medias.js +68 -0
  118. package/dist/esm/reducers/notifications.js +23 -0
  119. package/dist/esm/reducers/search.js +92 -0
  120. package/dist/esm/reducers/status.js +30 -0
  121. package/dist/esm/redux/index.js +7 -0
  122. package/dist/esm/redux/middleware/waitUntilAction.js +48 -0
  123. package/dist/esm/routing/history.js +12 -0
  124. package/dist/esm/types/diacritics.d.js +0 -0
  125. package/dist/esm/types/global.d.js +1 -0
  126. package/dist/esm/types/immutable.js +7 -0
  127. package/dist/esm/types/redux.js +14 -0
  128. package/dist/esm/types/tomlify-j0.4.d.js +0 -0
  129. package/dist/esm/valueObjects/AssetProxy.js +44 -0
  130. package/dist/esm/valueObjects/EditorComponent.js +34 -0
  131. package/dist/esm/valueObjects/Entry.js +20 -0
  132. package/index.d.ts +618 -0
  133. package/package.json +106 -0
  134. package/src/__tests__/backend.spec.js +1161 -0
  135. package/src/actions/__tests__/config.spec.js +1009 -0
  136. package/src/actions/__tests__/editorialWorkflow.spec.js +216 -0
  137. package/src/actions/__tests__/entries.spec.js +596 -0
  138. package/src/actions/__tests__/media.spec.ts +171 -0
  139. package/src/actions/__tests__/mediaLibrary.spec.js +327 -0
  140. package/src/actions/__tests__/search.spec.js +209 -0
  141. package/src/actions/auth.ts +127 -0
  142. package/src/actions/collections.ts +18 -0
  143. package/src/actions/config.ts +565 -0
  144. package/src/actions/deploys.ts +104 -0
  145. package/src/actions/editorialWorkflow.ts +567 -0
  146. package/src/actions/entries.ts +1055 -0
  147. package/src/actions/media.ts +139 -0
  148. package/src/actions/mediaLibrary.ts +574 -0
  149. package/src/actions/notifications.ts +36 -0
  150. package/src/actions/search.ts +221 -0
  151. package/src/actions/status.ts +99 -0
  152. package/src/actions/waitUntil.ts +49 -0
  153. package/src/backend.ts +1400 -0
  154. package/src/bootstrap.js +104 -0
  155. package/src/components/App/App.js +286 -0
  156. package/src/components/App/Header.js +266 -0
  157. package/src/components/App/NotFoundPage.js +23 -0
  158. package/src/components/Collection/Collection.js +210 -0
  159. package/src/components/Collection/CollectionControls.js +58 -0
  160. package/src/components/Collection/CollectionSearch.js +243 -0
  161. package/src/components/Collection/CollectionTop.js +81 -0
  162. package/src/components/Collection/ControlButton.js +27 -0
  163. package/src/components/Collection/Entries/Entries.js +82 -0
  164. package/src/components/Collection/Entries/EntriesCollection.js +277 -0
  165. package/src/components/Collection/Entries/EntriesSearch.js +102 -0
  166. package/src/components/Collection/Entries/EntryCard.js +246 -0
  167. package/src/components/Collection/Entries/EntryListing.js +151 -0
  168. package/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +163 -0
  169. package/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +46 -0
  170. package/src/components/Collection/FilterControl.js +39 -0
  171. package/src/components/Collection/GroupControl.js +39 -0
  172. package/src/components/Collection/NestedCollection.js +330 -0
  173. package/src/components/Collection/Sidebar.js +136 -0
  174. package/src/components/Collection/SortControl.js +68 -0
  175. package/src/components/Collection/ViewStyleControl.js +50 -0
  176. package/src/components/Collection/__tests__/Collection.spec.js +75 -0
  177. package/src/components/Collection/__tests__/NestedCollection.spec.js +445 -0
  178. package/src/components/Collection/__tests__/Sidebar.spec.js +87 -0
  179. package/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap +144 -0
  180. package/src/components/Collection/__tests__/__snapshots__/NestedCollection.spec.js.snap +550 -0
  181. package/src/components/Collection/__tests__/__snapshots__/Sidebar.spec.js.snap +312 -0
  182. package/src/components/Editor/Editor.js +497 -0
  183. package/src/components/Editor/EditorControlPane/EditorControl.js +452 -0
  184. package/src/components/Editor/EditorControlPane/EditorControlPane.js +269 -0
  185. package/src/components/Editor/EditorControlPane/Widget.js +384 -0
  186. package/src/components/Editor/EditorInterface.js +444 -0
  187. package/src/components/Editor/EditorPreviewPane/EditorPreview.js +40 -0
  188. package/src/components/Editor/EditorPreviewPane/EditorPreviewContent.js +74 -0
  189. package/src/components/Editor/EditorPreviewPane/EditorPreviewPane.js +333 -0
  190. package/src/components/Editor/EditorPreviewPane/PreviewHOC.js +33 -0
  191. package/src/components/Editor/EditorToolbar.js +691 -0
  192. package/src/components/Editor/__tests__/Editor.spec.js +221 -0
  193. package/src/components/Editor/__tests__/EditorToolbar.spec.js +120 -0
  194. package/src/components/Editor/__tests__/__snapshots__/Editor.spec.js.snap +45 -0
  195. package/src/components/Editor/__tests__/__snapshots__/EditorToolbar.spec.js.snap +4233 -0
  196. package/src/components/Editor/withWorkflow.js +61 -0
  197. package/src/components/EditorWidgets/Unknown/UnknownControl.js +17 -0
  198. package/src/components/EditorWidgets/Unknown/UnknownPreview.js +19 -0
  199. package/src/components/EditorWidgets/index.js +5 -0
  200. package/src/components/MediaLibrary/EmptyMessage.js +28 -0
  201. package/src/components/MediaLibrary/MediaLibrary.js +411 -0
  202. package/src/components/MediaLibrary/MediaLibraryButtons.js +136 -0
  203. package/src/components/MediaLibrary/MediaLibraryCard.js +128 -0
  204. package/src/components/MediaLibrary/MediaLibraryCardGrid.js +199 -0
  205. package/src/components/MediaLibrary/MediaLibraryHeader.js +48 -0
  206. package/src/components/MediaLibrary/MediaLibraryModal.js +200 -0
  207. package/src/components/MediaLibrary/MediaLibrarySearch.js +61 -0
  208. package/src/components/MediaLibrary/MediaLibraryTop.js +143 -0
  209. package/src/components/MediaLibrary/__tests__/MediaLibraryButtons.spec.js +45 -0
  210. package/src/components/MediaLibrary/__tests__/MediaLibraryCard.spec.js +49 -0
  211. package/src/components/MediaLibrary/__tests__/__snapshots__/MediaLibraryCard.spec.js.snap +264 -0
  212. package/src/components/UI/DragDrop.js +66 -0
  213. package/src/components/UI/ErrorBoundary.js +214 -0
  214. package/src/components/UI/FileUploadButton.js +24 -0
  215. package/src/components/UI/Modal.js +112 -0
  216. package/src/components/UI/Notifications.tsx +83 -0
  217. package/src/components/UI/SettingsDropdown.js +103 -0
  218. package/src/components/UI/__tests__/ErrorBoundary.spec.js +57 -0
  219. package/src/components/UI/index.js +6 -0
  220. package/src/components/Workflow/Workflow.js +169 -0
  221. package/src/components/Workflow/WorkflowCard.js +177 -0
  222. package/src/components/Workflow/WorkflowList.js +272 -0
  223. package/src/constants/__tests__/configSchema.spec.js +611 -0
  224. package/src/constants/collectionTypes.ts +2 -0
  225. package/src/constants/collectionViews.js +2 -0
  226. package/src/constants/commitProps.ts +2 -0
  227. package/src/constants/configSchema.js +441 -0
  228. package/src/constants/fieldInference.tsx +78 -0
  229. package/src/constants/publishModes.ts +22 -0
  230. package/src/constants/validationErrorTypes.js +6 -0
  231. package/src/formats/__tests__/formats.spec.js +87 -0
  232. package/src/formats/__tests__/frontmatter.spec.js +429 -0
  233. package/src/formats/__tests__/toml.spec.js +9 -0
  234. package/src/formats/__tests__/yaml.spec.js +162 -0
  235. package/src/formats/formats.ts +97 -0
  236. package/src/formats/frontmatter.ts +150 -0
  237. package/src/formats/helpers.ts +14 -0
  238. package/src/formats/json.ts +9 -0
  239. package/src/formats/toml.ts +33 -0
  240. package/src/formats/yaml.ts +58 -0
  241. package/src/index.js +8 -0
  242. package/src/integrations/index.js +35 -0
  243. package/src/integrations/providers/algolia/implementation.js +176 -0
  244. package/src/integrations/providers/assetStore/implementation.js +148 -0
  245. package/src/lib/__tests__/formatters.spec.js +751 -0
  246. package/src/lib/__tests__/i18n.spec.js +792 -0
  247. package/src/lib/__tests__/phrases.spec.js +119 -0
  248. package/src/lib/__tests__/registry.spec.js +261 -0
  249. package/src/lib/__tests__/serializeEntryValues.spec.js +22 -0
  250. package/src/lib/__tests__/urlHelper.spec.js +138 -0
  251. package/src/lib/consoleError.js +7 -0
  252. package/src/lib/formatters.ts +286 -0
  253. package/src/lib/i18n.ts +454 -0
  254. package/src/lib/phrases.js +8 -0
  255. package/src/lib/polyfill.js +9 -0
  256. package/src/lib/registry.js +312 -0
  257. package/src/lib/serializeEntryValues.js +75 -0
  258. package/src/lib/stega.ts +145 -0
  259. package/src/lib/textHelper.js +11 -0
  260. package/src/lib/urlHelper.ts +128 -0
  261. package/src/mediaLibrary.ts +51 -0
  262. package/src/reducers/__tests__/auth.spec.ts +38 -0
  263. package/src/reducers/__tests__/collections.spec.js +610 -0
  264. package/src/reducers/__tests__/config.spec.js +38 -0
  265. package/src/reducers/__tests__/entries.spec.js +694 -0
  266. package/src/reducers/__tests__/entryDraft.spec.js +297 -0
  267. package/src/reducers/__tests__/globalUI.js +43 -0
  268. package/src/reducers/__tests__/integrations.spec.ts +76 -0
  269. package/src/reducers/__tests__/mediaLibrary.spec.js +154 -0
  270. package/src/reducers/__tests__/medias.spec.ts +49 -0
  271. package/src/reducers/auth.ts +46 -0
  272. package/src/reducers/collections.ts +535 -0
  273. package/src/reducers/combinedReducer.ts +11 -0
  274. package/src/reducers/config.ts +38 -0
  275. package/src/reducers/cursors.js +36 -0
  276. package/src/reducers/deploys.ts +52 -0
  277. package/src/reducers/editorialWorkflow.ts +163 -0
  278. package/src/reducers/entries.ts +819 -0
  279. package/src/reducers/entryDraft.js +260 -0
  280. package/src/reducers/globalUI.ts +45 -0
  281. package/src/reducers/index.ts +82 -0
  282. package/src/reducers/integrations.ts +59 -0
  283. package/src/reducers/mediaLibrary.ts +296 -0
  284. package/src/reducers/medias.ts +66 -0
  285. package/src/reducers/notifications.ts +52 -0
  286. package/src/reducers/search.ts +111 -0
  287. package/src/reducers/status.ts +40 -0
  288. package/src/redux/index.ts +18 -0
  289. package/src/redux/middleware/waitUntilAction.ts +64 -0
  290. package/src/routing/__tests__/history.spec.ts +49 -0
  291. package/src/routing/history.ts +17 -0
  292. package/src/types/diacritics.d.ts +1 -0
  293. package/src/types/global.d.ts +8 -0
  294. package/src/types/immutable.ts +49 -0
  295. package/src/types/redux.ts +827 -0
  296. package/src/types/tomlify-j0.4.d.ts +13 -0
  297. package/src/valueObjects/AssetProxy.ts +48 -0
  298. package/src/valueObjects/EditorComponent.js +38 -0
  299. package/src/valueObjects/Entry.ts +63 -0
@@ -0,0 +1,1009 @@
1
+ import { stripIndent } from 'common-tags';
2
+ import { dump } from 'js-yaml';
3
+
4
+ import {
5
+ loadConfig,
6
+ parseConfig,
7
+ normalizeConfig,
8
+ applyDefaults,
9
+ detectProxyServer,
10
+ handleLocalBackend,
11
+ } from '../config';
12
+
13
+ jest.spyOn(console, 'log').mockImplementation(() => {});
14
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
15
+ jest.mock('../../backend', () => {
16
+ return {
17
+ resolveBackend: jest.fn(() => ({ isGitBackend: jest.fn(() => true) })),
18
+ };
19
+ });
20
+ jest.mock('../../constants/configSchema');
21
+
22
+ describe('config', () => {
23
+ describe('parseConfig', () => {
24
+ it('can parse simple yaml config', () => {
25
+ const file = stripIndent`
26
+ backend:
27
+ name: test-repo
28
+ media_folder: static/images
29
+ `;
30
+
31
+ expect(parseConfig(file)).toEqual({
32
+ backend: { name: 'test-repo' },
33
+ media_folder: 'static/images',
34
+ });
35
+ });
36
+
37
+ it('should merge yaml aliases', () => {
38
+ const file = stripIndent`
39
+ backend:
40
+ name: github
41
+ repo: decaporg/decap-cms
42
+ squash_merges: true
43
+ open_authoring: true
44
+ local_backend: true
45
+ site_url: https://www.decapcms.org
46
+ publish_mode: editorial_workflow
47
+ media_folder: website/static/img
48
+ public_folder: img
49
+ docs_collection: &docs_collection
50
+ folder: website/content/docs
51
+ create: true
52
+ preview_path: 'docs/{{slug}}'
53
+ fields:
54
+ - { label: Title, name: title }
55
+ - { label: Body, name: body, widget: markdown }
56
+ collections:
57
+ - <<: *docs_collection
58
+ name: docs_start
59
+ label: 'Docs: Quick Start'
60
+ `;
61
+
62
+ expect(parseConfig(file)).toEqual({
63
+ backend: {
64
+ name: 'github',
65
+ repo: 'decaporg/decap-cms',
66
+ squash_merges: true,
67
+ open_authoring: true,
68
+ },
69
+ local_backend: true,
70
+ site_url: 'https://www.decapcms.org',
71
+ publish_mode: 'editorial_workflow',
72
+ media_folder: 'website/static/img',
73
+ public_folder: 'img',
74
+ docs_collection: {
75
+ folder: 'website/content/docs',
76
+ create: true,
77
+ preview_path: 'docs/{{slug}}',
78
+ fields: [
79
+ { label: 'Title', name: 'title' },
80
+ { label: 'Body', name: 'body', widget: 'markdown' },
81
+ ],
82
+ },
83
+ collections: [
84
+ {
85
+ folder: 'website/content/docs',
86
+ create: true,
87
+ preview_path: 'docs/{{slug}}',
88
+ fields: [
89
+ { label: 'Title', name: 'title' },
90
+ { label: 'Body', name: 'body', widget: 'markdown' },
91
+ ],
92
+ name: 'docs_start',
93
+ label: 'Docs: Quick Start',
94
+ },
95
+ ],
96
+ });
97
+ });
98
+ });
99
+ describe('applyDefaults', () => {
100
+ describe('publish_mode', () => {
101
+ it('should set publish_mode if not set', () => {
102
+ const config = {
103
+ foo: 'bar',
104
+ media_folder: 'path/to/media',
105
+ public_folder: '/path/to/media',
106
+ collections: [],
107
+ };
108
+ expect(applyDefaults(config).publish_mode).toEqual('simple');
109
+ });
110
+
111
+ it('should set publish_mode from config', () => {
112
+ const config = {
113
+ foo: 'bar',
114
+ publish_mode: 'complex',
115
+ media_folder: 'path/to/media',
116
+ public_folder: '/path/to/media',
117
+ collections: [],
118
+ };
119
+ expect(applyDefaults(config).publish_mode).toEqual('complex');
120
+ });
121
+ });
122
+
123
+ describe('public_folder', () => {
124
+ it('should set public_folder based on media_folder if not set', () => {
125
+ expect(
126
+ applyDefaults({
127
+ foo: 'bar',
128
+ media_folder: 'path/to/media',
129
+ collections: [],
130
+ }).public_folder,
131
+ ).toEqual('/path/to/media');
132
+ });
133
+
134
+ it('should not overwrite public_folder if set', () => {
135
+ expect(
136
+ applyDefaults({
137
+ foo: 'bar',
138
+ media_folder: 'path/to/media',
139
+ public_folder: '/publib/path',
140
+ collections: [],
141
+ }).public_folder,
142
+ ).toEqual('/publib/path');
143
+ expect(
144
+ applyDefaults({
145
+ foo: 'bar',
146
+ media_folder: 'path/to/media',
147
+ public_folder: '',
148
+ collections: [],
149
+ }).public_folder,
150
+ ).toEqual('');
151
+ });
152
+ });
153
+
154
+ describe('slug', () => {
155
+ it('should set default slug config if not set', () => {
156
+ expect(applyDefaults({ collections: [] }).slug).toEqual({
157
+ encoding: 'unicode',
158
+ clean_accents: false,
159
+ sanitize_replacement: '-',
160
+ });
161
+ });
162
+
163
+ it('should not overwrite slug encoding if set', () => {
164
+ expect(
165
+ applyDefaults({ collections: [], slug: { encoding: 'ascii' } }).slug.encoding,
166
+ ).toEqual('ascii');
167
+ });
168
+
169
+ it('should not overwrite slug clean_accents if set', () => {
170
+ expect(
171
+ applyDefaults({ collections: [], slug: { clean_accents: true } }).slug.clean_accents,
172
+ ).toEqual(true);
173
+ });
174
+
175
+ it('should not overwrite slug sanitize_replacement if set', () => {
176
+ expect(
177
+ applyDefaults({ collections: [], slug: { sanitize_replacement: '_' } }).slug
178
+ .sanitize_replacement,
179
+ ).toEqual('_');
180
+ });
181
+ });
182
+
183
+ describe('collections', () => {
184
+ it('should strip leading slashes from collection folder', () => {
185
+ expect(
186
+ applyDefaults({
187
+ collections: [{ folder: '/foo', fields: [{ name: 'title', widget: 'string' }] }],
188
+ }).collections[0].folder,
189
+ ).toEqual('foo');
190
+ });
191
+
192
+ it('should strip leading slashes from collection files', () => {
193
+ expect(
194
+ applyDefaults({
195
+ collections: [
196
+ { files: [{ file: '/foo', fields: [{ name: 'title', widget: 'string' }] }] },
197
+ ],
198
+ }).collections[0].files[0].file,
199
+ ).toEqual('foo');
200
+ });
201
+
202
+ describe('public_folder and media_folder', () => {
203
+ it('should set collection public_folder based on media_folder if not set', () => {
204
+ expect(
205
+ applyDefaults({
206
+ collections: [
207
+ {
208
+ folder: 'foo',
209
+ media_folder: 'static/images/docs',
210
+ fields: [{ name: 'title', widget: 'string' }],
211
+ },
212
+ ],
213
+ }).collections[0].public_folder,
214
+ ).toEqual('static/images/docs');
215
+ });
216
+
217
+ it('should not overwrite collection public_folder if set to non empty string', () => {
218
+ expect(
219
+ applyDefaults({
220
+ collections: [
221
+ {
222
+ folder: 'foo',
223
+ media_folder: 'static/images/docs',
224
+ public_folder: 'images/docs',
225
+ fields: [{ name: 'title', widget: 'string' }],
226
+ },
227
+ ],
228
+ }).collections[0].public_folder,
229
+ ).toEqual('images/docs');
230
+ });
231
+
232
+ it('should not overwrite collection public_folder if set to empty string', () => {
233
+ expect(
234
+ applyDefaults({
235
+ collections: [
236
+ {
237
+ folder: 'foo',
238
+ media_folder: 'static/images/docs',
239
+ public_folder: '',
240
+ fields: [{ name: 'title', widget: 'string' }],
241
+ },
242
+ ],
243
+ }).collections[0].public_folder,
244
+ ).toEqual('');
245
+ });
246
+
247
+ it("should set collection media_folder and public_folder to an empty string when collection path exists, but collection media_folder doesn't", () => {
248
+ const result = applyDefaults({
249
+ collections: [
250
+ {
251
+ folder: 'foo',
252
+ path: '{{slug}}/index',
253
+ fields: [{ name: 'title', widget: 'string' }],
254
+ },
255
+ ],
256
+ });
257
+ expect(result.collections[0].media_folder).toEqual('');
258
+ expect(result.collections[0].public_folder).toEqual('');
259
+ });
260
+
261
+ it('should set file public_folder based on media_folder if not set', () => {
262
+ expect(
263
+ applyDefaults({
264
+ collections: [
265
+ {
266
+ files: [
267
+ {
268
+ file: 'foo',
269
+ media_folder: 'static/images/docs',
270
+ fields: [{ name: 'title', widget: 'string' }],
271
+ },
272
+ ],
273
+ },
274
+ ],
275
+ }).collections[0].files[0].public_folder,
276
+ ).toEqual('static/images/docs');
277
+ });
278
+
279
+ it('should not overwrite file public_folder if set', () => {
280
+ expect(
281
+ applyDefaults({
282
+ collections: [
283
+ {
284
+ files: [
285
+ {
286
+ file: 'foo',
287
+ media_folder: 'static/images/docs',
288
+ public_folder: 'images/docs',
289
+ fields: [{ name: 'title', widget: 'string' }],
290
+ },
291
+ ],
292
+ },
293
+ ],
294
+ }).collections[0].files[0].public_folder,
295
+ ).toEqual('images/docs');
296
+ });
297
+
298
+ it('should set nested field public_folder based on media_folder if not set', () => {
299
+ const config = applyDefaults({
300
+ collections: [
301
+ {
302
+ folder: 'foo',
303
+ path: '{{slug}}/index',
304
+ fields: [
305
+ {
306
+ name: 'title',
307
+ widget: 'string',
308
+ media_folder: 'collection/static/images/docs',
309
+ },
310
+ ],
311
+ },
312
+ {
313
+ files: [
314
+ {
315
+ file: 'foo',
316
+ fields: [
317
+ {
318
+ name: 'title',
319
+ widget: 'string',
320
+ media_folder: 'file/static/images/docs',
321
+ },
322
+ ],
323
+ },
324
+ ],
325
+ },
326
+ ],
327
+ });
328
+ expect(config.collections[0].fields[0].public_folder).toEqual(
329
+ 'collection/static/images/docs',
330
+ );
331
+ expect(config.collections[1].files[0].fields[0].public_folder).toEqual(
332
+ 'file/static/images/docs',
333
+ );
334
+ });
335
+
336
+ it('should not overwrite nested field public_folder if set', () => {
337
+ const config = applyDefaults({
338
+ collections: [
339
+ {
340
+ folder: 'foo',
341
+ path: '{{slug}}/index',
342
+ fields: [
343
+ {
344
+ name: 'title',
345
+ widget: 'string',
346
+ media_folder: 'collection/static/images/docs',
347
+ public_folder: 'collection/public_folder',
348
+ },
349
+ ],
350
+ },
351
+ {
352
+ files: [
353
+ {
354
+ file: 'foo',
355
+ fields: [
356
+ {
357
+ name: 'title',
358
+ widget: 'string',
359
+ public_folder: 'file/public_folder',
360
+ },
361
+ ],
362
+ },
363
+ ],
364
+ },
365
+ ],
366
+ });
367
+ expect(config.collections[0].fields[0].public_folder).toEqual('collection/public_folder');
368
+ expect(config.collections[1].files[0].fields[0].public_folder).toEqual(
369
+ 'file/public_folder',
370
+ );
371
+ });
372
+ });
373
+
374
+ describe('publish', () => {
375
+ it('should set publish to true if not set', () => {
376
+ expect(
377
+ applyDefaults({
378
+ collections: [
379
+ {
380
+ folder: 'foo',
381
+ media_folder: 'static/images/docs',
382
+ fields: [{ name: 'title', widget: 'string' }],
383
+ },
384
+ ],
385
+ }).collections[0].publish,
386
+ ).toEqual(true);
387
+ });
388
+
389
+ it('should not override existing publish config', () => {
390
+ expect(
391
+ applyDefaults({
392
+ collections: [
393
+ {
394
+ folder: 'foo',
395
+ media_folder: 'static/images/docs',
396
+ publish: false,
397
+ fields: [{ name: 'title', widget: 'string' }],
398
+ },
399
+ ],
400
+ }).collections[0].publish,
401
+ ).toEqual(false);
402
+ });
403
+ });
404
+
405
+ describe('editor preview', () => {
406
+ it('should set editor preview honoring global config before and specific config after', () => {
407
+ const config = applyDefaults({
408
+ editor: {
409
+ preview: false,
410
+ },
411
+ collections: [
412
+ {
413
+ fields: [{ name: 'title' }],
414
+ folder: 'foo',
415
+ },
416
+ {
417
+ editor: {
418
+ preview: true,
419
+ },
420
+ fields: [{ name: 'title' }],
421
+ folder: 'bar',
422
+ },
423
+ ],
424
+ });
425
+
426
+ expect(config.collections[0].editor.preview).toEqual(false);
427
+ expect(config.collections[1].editor.preview).toEqual(true);
428
+ });
429
+ });
430
+ });
431
+
432
+ test('should convert camel case to snake case', () => {
433
+ expect(
434
+ applyDefaults(
435
+ normalizeConfig({
436
+ collections: [
437
+ {
438
+ sortableFields: ['title'],
439
+ folder: 'src',
440
+ identifier_field: 'datetime',
441
+ fields: [
442
+ {
443
+ name: 'datetime',
444
+ widget: 'datetime',
445
+ dateFormat: 'YYYY/MM/DD',
446
+ timeFormat: 'HH:mm',
447
+ pickerUtc: true,
448
+ },
449
+ {
450
+ widget: 'number',
451
+ valueType: 'float',
452
+ },
453
+ ],
454
+ },
455
+ {
456
+ sortableFields: [],
457
+ files: [
458
+ {
459
+ name: 'file',
460
+ file: 'src/file.json',
461
+ fields: [
462
+ {
463
+ widget: 'markdown',
464
+ editorComponents: ['code'],
465
+ },
466
+ {
467
+ widget: 'relation',
468
+ valueField: 'title',
469
+ searchFields: ['title'],
470
+ displayFields: ['title'],
471
+ optionsLength: 5,
472
+ },
473
+ ],
474
+ },
475
+ ],
476
+ },
477
+ ],
478
+ }),
479
+ ),
480
+ ).toEqual({
481
+ collections: [
482
+ {
483
+ sortable_fields: [{ field: 'title', default_sort: undefined }],
484
+ folder: 'src',
485
+ type: 'folder_based_collection',
486
+ view_filters: [],
487
+ view_groups: [],
488
+ identifier_field: 'datetime',
489
+ fields: [
490
+ {
491
+ name: 'datetime',
492
+ widget: 'datetime',
493
+ date_format: 'YYYY/MM/DD',
494
+ dateFormat: 'YYYY/MM/DD',
495
+ time_format: 'HH:mm',
496
+ timeFormat: 'HH:mm',
497
+ picker_utc: true,
498
+ pickerUtc: true,
499
+ },
500
+ {
501
+ widget: 'number',
502
+ value_type: 'float',
503
+ valueType: 'float',
504
+ },
505
+ ],
506
+ publish: true,
507
+ },
508
+ {
509
+ sortable_fields: [],
510
+ files: [
511
+ {
512
+ name: 'file',
513
+ file: 'src/file.json',
514
+ fields: [
515
+ {
516
+ widget: 'markdown',
517
+ editor_components: ['code'],
518
+ editorComponents: ['code'],
519
+ },
520
+ {
521
+ widget: 'relation',
522
+ value_field: 'title',
523
+ valueField: 'title',
524
+ search_fields: ['title'],
525
+ searchFields: ['title'],
526
+ display_fields: ['title'],
527
+ displayFields: ['title'],
528
+ options_length: 5,
529
+ optionsLength: 5,
530
+ },
531
+ ],
532
+ },
533
+ ],
534
+ type: 'file_based_collection',
535
+ view_filters: [],
536
+ view_groups: [],
537
+ publish: true,
538
+ },
539
+ ],
540
+ public_folder: '/',
541
+ publish_mode: 'simple',
542
+ slug: { clean_accents: false, encoding: 'unicode', sanitize_replacement: '-' },
543
+ });
544
+ });
545
+
546
+ describe('i18n', () => {
547
+ it('should set root i18n on collection when collection i18n is set to true', () => {
548
+ expect(
549
+ applyDefaults({
550
+ i18n: {
551
+ structure: 'multiple_folders',
552
+ locales: ['en', 'de'],
553
+ },
554
+ collections: [
555
+ { folder: 'foo', i18n: true, fields: [{ name: 'title', widget: 'string' }] },
556
+ ],
557
+ }).collections[0].i18n,
558
+ ).toEqual({ structure: 'multiple_folders', locales: ['en', 'de'], default_locale: 'en' });
559
+ });
560
+
561
+ it('should not set root i18n on collection when collection i18n is not set', () => {
562
+ expect(
563
+ applyDefaults({
564
+ i18n: {
565
+ structure: 'multiple_folders',
566
+ locales: ['en', 'de'],
567
+ },
568
+ collections: [{ folder: 'foo', fields: [{ name: 'title', widget: 'string' }] }],
569
+ }).collections[0].i18n,
570
+ ).toBeUndefined();
571
+ });
572
+
573
+ it('should not set root i18n on collection when collection i18n is set to false', () => {
574
+ expect(
575
+ applyDefaults({
576
+ i18n: {
577
+ structure: 'multiple_folders',
578
+ locales: ['en', 'de'],
579
+ },
580
+ collections: [
581
+ { folder: 'foo', i18n: false, fields: [{ name: 'title', widget: 'string' }] },
582
+ ],
583
+ }).collections[0].i18n,
584
+ ).toBeUndefined();
585
+ });
586
+
587
+ it('should merge root i18n on collection when collection i18n is set to an object', () => {
588
+ expect(
589
+ applyDefaults({
590
+ i18n: {
591
+ structure: 'multiple_folders',
592
+ locales: ['en', 'de'],
593
+ default_locale: 'en',
594
+ },
595
+ collections: [
596
+ {
597
+ folder: 'foo',
598
+ i18n: { locales: ['en', 'fr'], default_locale: 'fr' },
599
+ fields: [{ name: 'title', widget: 'string' }],
600
+ },
601
+ ],
602
+ }).collections[0].i18n,
603
+ ).toEqual({ structure: 'multiple_folders', locales: ['en', 'fr'], default_locale: 'fr' });
604
+ });
605
+
606
+ it('should throw when i18n structure is not single_file on files collection', () => {
607
+ expect(() =>
608
+ applyDefaults({
609
+ i18n: {
610
+ structure: 'multiple_folders',
611
+ locales: ['en', 'de'],
612
+ },
613
+ collections: [
614
+ {
615
+ files: [
616
+ {
617
+ name: 'file',
618
+ file: 'file',
619
+ i18n: true,
620
+ fields: [{ name: 'title', widget: 'string', i18n: true }],
621
+ },
622
+ ],
623
+ i18n: true,
624
+ },
625
+ ],
626
+ }),
627
+ ).toThrow('i18n configuration for files collections is limited to single_file structure');
628
+ });
629
+
630
+ it('should throw when i18n structure is set to multiple_folders and contains a single file collection', () => {
631
+ expect(() =>
632
+ applyDefaults({
633
+ i18n: {
634
+ structure: 'multiple_folders',
635
+ locales: ['en', 'de'],
636
+ },
637
+ collections: [
638
+ {
639
+ files: [
640
+ { name: 'file', file: 'file', fields: [{ name: 'title', widget: 'string' }] },
641
+ ],
642
+ i18n: true,
643
+ },
644
+ ],
645
+ }),
646
+ ).toThrow('i18n configuration for files collections is limited to single_file structure');
647
+ });
648
+
649
+ it('should throw when i18n structure is set to multiple_files and contains a single file collection', () => {
650
+ expect(() =>
651
+ applyDefaults({
652
+ i18n: {
653
+ structure: 'multiple_files',
654
+ locales: ['en', 'de'],
655
+ },
656
+ collections: [
657
+ {
658
+ files: [
659
+ { name: 'file', file: 'file', fields: [{ name: 'title', widget: 'string' }] },
660
+ ],
661
+ i18n: true,
662
+ },
663
+ ],
664
+ }),
665
+ ).toThrow('i18n configuration for files collections is limited to single_file structure');
666
+ });
667
+
668
+ it('should set i18n value to translate on field when i18n=true for field in files collection', () => {
669
+ expect(
670
+ applyDefaults({
671
+ i18n: {
672
+ structure: 'multiple_folders',
673
+ locales: ['en', 'de'],
674
+ },
675
+ collections: [
676
+ {
677
+ files: [
678
+ {
679
+ name: 'file',
680
+ file: 'file',
681
+ i18n: true,
682
+ fields: [{ name: 'title', widget: 'string', i18n: true }],
683
+ },
684
+ ],
685
+ i18n: {
686
+ structure: 'single_file',
687
+ },
688
+ },
689
+ ],
690
+ }).collections[0].files[0].fields[0].i18n,
691
+ ).toEqual('translate');
692
+ });
693
+
694
+ it('should set i18n value to translate on field when i18n=true for field', () => {
695
+ expect(
696
+ applyDefaults({
697
+ i18n: {
698
+ structure: 'multiple_folders',
699
+ locales: ['en', 'de'],
700
+ },
701
+ collections: [
702
+ {
703
+ folder: 'foo',
704
+ i18n: true,
705
+ fields: [{ name: 'title', widget: 'string', i18n: true }],
706
+ },
707
+ ],
708
+ }).collections[0].fields[0].i18n,
709
+ ).toEqual('translate');
710
+ });
711
+
712
+ it('should set i18n value to none on field when i18n=false for field', () => {
713
+ expect(
714
+ applyDefaults({
715
+ i18n: {
716
+ structure: 'multiple_folders',
717
+ locales: ['en', 'de'],
718
+ },
719
+ collections: [
720
+ {
721
+ folder: 'foo',
722
+ i18n: true,
723
+ fields: [{ name: 'title', widget: 'string', i18n: false }],
724
+ },
725
+ ],
726
+ }).collections[0].fields[0].i18n,
727
+ ).toEqual('none');
728
+ });
729
+
730
+ it('should throw is default locale is missing from root i18n config', () => {
731
+ expect(() =>
732
+ applyDefaults({
733
+ i18n: {
734
+ structure: 'multiple_folders',
735
+ locales: ['en', 'de'],
736
+ default_locale: 'fr',
737
+ },
738
+ collections: [
739
+ {
740
+ folder: 'foo',
741
+ fields: [{ name: 'title', widget: 'string' }],
742
+ },
743
+ ],
744
+ }),
745
+ ).toThrow("i18n locales 'en, de' are missing the default locale fr");
746
+ });
747
+
748
+ it('should throw is default locale is missing from collection i18n config', () => {
749
+ expect(() =>
750
+ applyDefaults({
751
+ i18n: {
752
+ structure: 'multiple_folders',
753
+ locales: ['en', 'de'],
754
+ },
755
+ collections: [
756
+ {
757
+ folder: 'foo',
758
+ i18n: {
759
+ default_locale: 'fr',
760
+ },
761
+ fields: [{ name: 'title', widget: 'string' }],
762
+ },
763
+ ],
764
+ }),
765
+ ).toThrow("i18n locales 'en, de' are missing the default locale fr");
766
+ });
767
+ });
768
+ });
769
+
770
+ describe('detectProxyServer', () => {
771
+ function assetFetchCalled(url = 'http://localhost:8081/api/v1') {
772
+ expect(global.fetch).toHaveBeenCalledTimes(1);
773
+ expect(global.fetch).toHaveBeenCalledWith(url, {
774
+ method: 'POST',
775
+ headers: { 'Content-Type': 'application/json' },
776
+ body: JSON.stringify({ action: 'info' }),
777
+ });
778
+ }
779
+
780
+ beforeEach(() => {
781
+ delete window.location;
782
+ });
783
+
784
+ it('should return empty object when not on localhost', async () => {
785
+ window.location = { hostname: 'www.netlify.com' };
786
+ global.fetch = jest.fn();
787
+ await expect(detectProxyServer()).resolves.toEqual({});
788
+
789
+ expect(global.fetch).toHaveBeenCalledTimes(0);
790
+ });
791
+
792
+ it('should return empty object when fetch returns an error', async () => {
793
+ window.location = { hostname: 'localhost' };
794
+ global.fetch = jest.fn().mockRejectedValue(new Error());
795
+ await expect(detectProxyServer(true)).resolves.toEqual({});
796
+
797
+ assetFetchCalled();
798
+ });
799
+
800
+ it('should return empty object when fetch returns an invalid response', async () => {
801
+ window.location = { hostname: 'localhost' };
802
+ global.fetch = jest
803
+ .fn()
804
+ .mockResolvedValue({ json: jest.fn().mockResolvedValue({ repo: [] }) });
805
+ await expect(detectProxyServer(true)).resolves.toEqual({});
806
+
807
+ assetFetchCalled();
808
+ });
809
+
810
+ it('should return result object when fetch returns a valid response', async () => {
811
+ window.location = { hostname: 'localhost' };
812
+ global.fetch = jest.fn().mockResolvedValue({
813
+ json: jest.fn().mockResolvedValue({
814
+ repo: 'test-repo',
815
+ publish_modes: ['simple', 'editorial_workflow'],
816
+ type: 'local_git',
817
+ }),
818
+ });
819
+ await expect(detectProxyServer(true)).resolves.toEqual({
820
+ proxyUrl: 'http://localhost:8081/api/v1',
821
+ publish_modes: ['simple', 'editorial_workflow'],
822
+ type: 'local_git',
823
+ });
824
+
825
+ assetFetchCalled();
826
+ });
827
+
828
+ it('should use local_backend url', async () => {
829
+ const url = 'http://localhost:8082/api/v1';
830
+ window.location = { hostname: 'localhost' };
831
+ global.fetch = jest.fn().mockResolvedValue({
832
+ json: jest.fn().mockResolvedValue({
833
+ repo: 'test-repo',
834
+ publish_modes: ['simple', 'editorial_workflow'],
835
+ type: 'local_git',
836
+ }),
837
+ });
838
+ await expect(detectProxyServer({ url })).resolves.toEqual({
839
+ proxyUrl: url,
840
+ publish_modes: ['simple', 'editorial_workflow'],
841
+ type: 'local_git',
842
+ });
843
+
844
+ assetFetchCalled(url);
845
+ });
846
+
847
+ it('should use local_backend allowed_hosts', async () => {
848
+ const allowed_hosts = ['192.168.0.1'];
849
+ window.location = { hostname: '192.168.0.1' };
850
+ global.fetch = jest.fn().mockResolvedValue({
851
+ json: jest.fn().mockResolvedValue({
852
+ repo: 'test-repo',
853
+ publish_modes: ['simple', 'editorial_workflow'],
854
+ type: 'local_git',
855
+ }),
856
+ });
857
+ await expect(detectProxyServer({ allowed_hosts })).resolves.toEqual({
858
+ proxyUrl: 'http://192.168.0.1:8081/api/v1',
859
+ publish_modes: ['simple', 'editorial_workflow'],
860
+ type: 'local_git',
861
+ });
862
+
863
+ assetFetchCalled('http://192.168.0.1:8081/api/v1');
864
+ });
865
+ });
866
+
867
+ describe('handleLocalBackend', () => {
868
+ beforeEach(() => {
869
+ delete window.location;
870
+ });
871
+
872
+ it('should not replace backend config when proxy is not detected', async () => {
873
+ window.location = { hostname: 'localhost' };
874
+ global.fetch = jest.fn().mockRejectedValue(new Error());
875
+
876
+ const config = { local_backend: true, backend: { name: 'github' } };
877
+ const actual = await handleLocalBackend(config);
878
+
879
+ expect(actual).toEqual(config);
880
+ });
881
+
882
+ it('should replace backend config when proxy is detected', async () => {
883
+ window.location = { hostname: 'localhost' };
884
+ global.fetch = jest.fn().mockResolvedValue({
885
+ json: jest.fn().mockResolvedValue({
886
+ repo: 'test-repo',
887
+ publish_modes: ['simple', 'editorial_workflow'],
888
+ type: 'local_git',
889
+ }),
890
+ });
891
+
892
+ const config = { local_backend: true, backend: { name: 'github' } };
893
+ const actual = await handleLocalBackend(config);
894
+
895
+ expect(actual).toEqual({
896
+ local_backend: true,
897
+ backend: { name: 'proxy', proxy_url: 'http://localhost:8081/api/v1' },
898
+ });
899
+ });
900
+
901
+ it('should replace publish mode when not supported by proxy', async () => {
902
+ window.location = { hostname: 'localhost' };
903
+ global.fetch = jest.fn().mockResolvedValue({
904
+ json: jest.fn().mockResolvedValue({
905
+ repo: 'test-repo',
906
+ publish_modes: ['simple'],
907
+ type: 'local_fs',
908
+ }),
909
+ });
910
+
911
+ const config = {
912
+ local_backend: true,
913
+ publish_mode: 'editorial_workflow',
914
+ backend: { name: 'github' },
915
+ };
916
+ const actual = await handleLocalBackend(config);
917
+
918
+ expect(actual).toEqual({
919
+ local_backend: true,
920
+ publish_mode: 'simple',
921
+ backend: { name: 'proxy', proxy_url: 'http://localhost:8081/api/v1' },
922
+ });
923
+ });
924
+ });
925
+
926
+ describe('loadConfig', () => {
927
+ beforeEach(() => {
928
+ document.querySelector = jest.fn();
929
+ global.fetch = jest.fn();
930
+ });
931
+
932
+ test(`should fetch default 'config.yml'`, async () => {
933
+ const dispatch = jest.fn();
934
+
935
+ global.fetch.mockResolvedValue({
936
+ status: 200,
937
+ text: () => Promise.resolve(dump({ backend: { repo: 'test-repo' } })),
938
+ headers: new Headers(),
939
+ });
940
+ await loadConfig()(dispatch);
941
+
942
+ expect(global.fetch).toHaveBeenCalledTimes(1);
943
+ expect(global.fetch).toHaveBeenCalledWith('config.yml', { credentials: 'same-origin' });
944
+
945
+ expect(dispatch).toHaveBeenCalledTimes(2);
946
+ expect(dispatch).toHaveBeenCalledWith({ type: 'CONFIG_REQUEST' });
947
+ expect(dispatch).toHaveBeenCalledWith({
948
+ type: 'CONFIG_SUCCESS',
949
+ payload: {
950
+ backend: { repo: 'test-repo' },
951
+ collections: [],
952
+ publish_mode: 'simple',
953
+ slug: { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-' },
954
+ public_folder: '/',
955
+ },
956
+ });
957
+ });
958
+
959
+ test(`should fetch from custom 'config.yml'`, async () => {
960
+ const dispatch = jest.fn();
961
+
962
+ document.querySelector.mockReturnValue({ type: 'text/yaml', href: 'custom-config.yml' });
963
+ global.fetch.mockResolvedValue({
964
+ status: 200,
965
+ text: () => Promise.resolve(dump({ backend: { repo: 'github' } })),
966
+ headers: new Headers(),
967
+ });
968
+ await loadConfig()(dispatch);
969
+
970
+ expect(document.querySelector).toHaveBeenCalledTimes(1);
971
+ expect(document.querySelector).toHaveBeenCalledWith('link[rel="cms-config-url"]');
972
+
973
+ expect(global.fetch).toHaveBeenCalledTimes(1);
974
+ expect(global.fetch).toHaveBeenCalledWith('custom-config.yml', {
975
+ credentials: 'same-origin',
976
+ });
977
+
978
+ expect(dispatch).toHaveBeenCalledTimes(2);
979
+ expect(dispatch).toHaveBeenCalledWith({ type: 'CONFIG_REQUEST' });
980
+ expect(dispatch).toHaveBeenCalledWith({
981
+ type: 'CONFIG_SUCCESS',
982
+ payload: {
983
+ backend: { repo: 'github' },
984
+ collections: [],
985
+ publish_mode: 'simple',
986
+ slug: { encoding: 'unicode', clean_accents: false, sanitize_replacement: '-' },
987
+ public_folder: '/',
988
+ },
989
+ });
990
+ });
991
+
992
+ test(`should throw on failure to fetch 'config.yml'`, async () => {
993
+ const dispatch = jest.fn();
994
+
995
+ global.fetch.mockRejectedValue(new Error('Failed to fetch'));
996
+ await expect(() => loadConfig()(dispatch)).rejects.toEqual(
997
+ new Error('Failed to load config.yml (Failed to fetch)'),
998
+ );
999
+
1000
+ expect(dispatch).toHaveBeenCalledTimes(2);
1001
+ expect(dispatch).toHaveBeenCalledWith({ type: 'CONFIG_REQUEST' });
1002
+ expect(dispatch).toHaveBeenCalledWith({
1003
+ type: 'CONFIG_FAILURE',
1004
+ error: 'Error loading config',
1005
+ payload: new Error('Failed to load config.yml (Failed to fetch)'),
1006
+ });
1007
+ });
1008
+ });
1009
+ });