@fgv/ts-res-ui-components 5.0.0-10

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 (231) hide show
  1. package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1111 -0
  7. package/README.md +18 -0
  8. package/REFACTORING_PLAN.md +171 -0
  9. package/config/jest.config.json +16 -0
  10. package/config/jest.setup.js +64 -0
  11. package/config/rig.json +16 -0
  12. package/lib/components/common/QualifierContextControl.d.ts +14 -0
  13. package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
  14. package/lib/components/common/QualifierContextControl.js +78 -0
  15. package/lib/components/common/QualifierContextControl.js.map +1 -0
  16. package/lib/components/common/ResourceListView.d.ts +11 -0
  17. package/lib/components/common/ResourceListView.d.ts.map +1 -0
  18. package/lib/components/common/ResourceListView.js +20 -0
  19. package/lib/components/common/ResourceListView.js.map +1 -0
  20. package/lib/components/common/ResourceTreeView.d.ts +12 -0
  21. package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
  22. package/lib/components/common/ResourceTreeView.js +162 -0
  23. package/lib/components/common/ResourceTreeView.js.map +1 -0
  24. package/lib/components/forms/HierarchyEditor.d.ts +10 -0
  25. package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
  26. package/lib/components/forms/HierarchyEditor.js +106 -0
  27. package/lib/components/forms/HierarchyEditor.js.map +1 -0
  28. package/lib/components/forms/QualifierEditForm.d.ts +11 -0
  29. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
  30. package/lib/components/forms/QualifierEditForm.js +181 -0
  31. package/lib/components/forms/QualifierEditForm.js.map +1 -0
  32. package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
  33. package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
  34. package/lib/components/forms/QualifierTypeEditForm.js +172 -0
  35. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
  36. package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
  37. package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
  38. package/lib/components/forms/ResourceTypeEditForm.js +188 -0
  39. package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
  40. package/lib/components/forms/index.d.ts +9 -0
  41. package/lib/components/forms/index.d.ts.map +1 -0
  42. package/lib/components/forms/index.js +5 -0
  43. package/lib/components/forms/index.js.map +1 -0
  44. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
  45. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
  46. package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
  47. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
  48. package/lib/components/views/CompiledView/index.d.ts +5 -0
  49. package/lib/components/views/CompiledView/index.d.ts.map +1 -0
  50. package/lib/components/views/CompiledView/index.js +595 -0
  51. package/lib/components/views/CompiledView/index.js.map +1 -0
  52. package/lib/components/views/ConfigurationView/index.d.ts +5 -0
  53. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
  54. package/lib/components/views/ConfigurationView/index.js +363 -0
  55. package/lib/components/views/ConfigurationView/index.js.map +1 -0
  56. package/lib/components/views/FilterView/index.d.ts +5 -0
  57. package/lib/components/views/FilterView/index.d.ts.map +1 -0
  58. package/lib/components/views/FilterView/index.js +463 -0
  59. package/lib/components/views/FilterView/index.js.map +1 -0
  60. package/lib/components/views/ImportView/index.d.ts +5 -0
  61. package/lib/components/views/ImportView/index.d.ts.map +1 -0
  62. package/lib/components/views/ImportView/index.js +514 -0
  63. package/lib/components/views/ImportView/index.js.map +1 -0
  64. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
  65. package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
  66. package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
  67. package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
  68. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
  69. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
  70. package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
  71. package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
  72. package/lib/components/views/ResolutionView/index.d.ts +5 -0
  73. package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
  74. package/lib/components/views/ResolutionView/index.js +255 -0
  75. package/lib/components/views/ResolutionView/index.js.map +1 -0
  76. package/lib/components/views/SourceView/index.d.ts +5 -0
  77. package/lib/components/views/SourceView/index.d.ts.map +1 -0
  78. package/lib/components/views/SourceView/index.js +316 -0
  79. package/lib/components/views/SourceView/index.js.map +1 -0
  80. package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
  81. package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
  82. package/lib/components/views/ZipLoaderView/index.js +313 -0
  83. package/lib/components/views/ZipLoaderView/index.js.map +1 -0
  84. package/lib/hooks/useConfigurationState.d.ts +46 -0
  85. package/lib/hooks/useConfigurationState.d.ts.map +1 -0
  86. package/lib/hooks/useConfigurationState.js +239 -0
  87. package/lib/hooks/useConfigurationState.js.map +1 -0
  88. package/lib/hooks/useFilterState.d.ts +7 -0
  89. package/lib/hooks/useFilterState.d.ts.map +1 -0
  90. package/lib/hooks/useFilterState.js +80 -0
  91. package/lib/hooks/useFilterState.js.map +1 -0
  92. package/lib/hooks/useResolutionState.d.ts +8 -0
  93. package/lib/hooks/useResolutionState.d.ts.map +1 -0
  94. package/lib/hooks/useResolutionState.js +253 -0
  95. package/lib/hooks/useResolutionState.js.map +1 -0
  96. package/lib/hooks/useResourceData.d.ts +19 -0
  97. package/lib/hooks/useResourceData.d.ts.map +1 -0
  98. package/lib/hooks/useResourceData.js +368 -0
  99. package/lib/hooks/useResourceData.js.map +1 -0
  100. package/lib/hooks/useViewState.d.ts +10 -0
  101. package/lib/hooks/useViewState.d.ts.map +1 -0
  102. package/lib/hooks/useViewState.js +29 -0
  103. package/lib/hooks/useViewState.js.map +1 -0
  104. package/lib/index.d.ts +27 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +34 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/test/helpers/testDataLoader.d.ts +37 -0
  109. package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
  110. package/lib/test/helpers/testDataLoader.js +171 -0
  111. package/lib/test/helpers/testDataLoader.js.map +1 -0
  112. package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
  113. package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
  114. package/lib/test/unit/utils/configurationUtils.test.js +497 -0
  115. package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
  116. package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
  117. package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
  118. package/lib/test/unit/utils/fileProcessing.test.js +321 -0
  119. package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
  120. package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
  121. package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
  122. package/lib/test/unit/utils/filterResources.test.js +403 -0
  123. package/lib/test/unit/utils/filterResources.test.js.map +1 -0
  124. package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
  125. package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
  126. package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
  127. package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
  128. package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
  129. package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
  130. package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
  131. package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
  132. package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
  133. package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
  134. package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
  135. package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
  136. package/lib/types/index.d.ts +251 -0
  137. package/lib/types/index.d.ts.map +1 -0
  138. package/lib/types/index.js +2 -0
  139. package/lib/types/index.js.map +1 -0
  140. package/lib/utils/configurationUtils.d.ts +74 -0
  141. package/lib/utils/configurationUtils.d.ts.map +1 -0
  142. package/lib/utils/configurationUtils.js +359 -0
  143. package/lib/utils/configurationUtils.js.map +1 -0
  144. package/lib/utils/fileProcessing.d.ts +18 -0
  145. package/lib/utils/fileProcessing.d.ts.map +1 -0
  146. package/lib/utils/fileProcessing.js +142 -0
  147. package/lib/utils/fileProcessing.js.map +1 -0
  148. package/lib/utils/filterResources.d.ts +38 -0
  149. package/lib/utils/filterResources.d.ts.map +1 -0
  150. package/lib/utils/filterResources.js +153 -0
  151. package/lib/utils/filterResources.js.map +1 -0
  152. package/lib/utils/resolutionEditing.d.ts +58 -0
  153. package/lib/utils/resolutionEditing.d.ts.map +1 -0
  154. package/lib/utils/resolutionEditing.js +246 -0
  155. package/lib/utils/resolutionEditing.js.map +1 -0
  156. package/lib/utils/resolutionUtils.d.ts +28 -0
  157. package/lib/utils/resolutionUtils.d.ts.map +1 -0
  158. package/lib/utils/resolutionUtils.js +216 -0
  159. package/lib/utils/resolutionUtils.js.map +1 -0
  160. package/lib/utils/tsResIntegration.d.ts +71 -0
  161. package/lib/utils/tsResIntegration.d.ts.map +1 -0
  162. package/lib/utils/tsResIntegration.js +294 -0
  163. package/lib/utils/tsResIntegration.js.map +1 -0
  164. package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
  165. package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
  166. package/lib/utils/zipLoader/browserZipLoader.js +247 -0
  167. package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
  168. package/lib/utils/zipLoader/index.d.ts +8 -0
  169. package/lib/utils/zipLoader/index.d.ts.map +1 -0
  170. package/lib/utils/zipLoader/index.js +13 -0
  171. package/lib/utils/zipLoader/index.js.map +1 -0
  172. package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
  173. package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
  174. package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
  175. package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
  176. package/lib/utils/zipLoader/types.d.ts +139 -0
  177. package/lib/utils/zipLoader/types.d.ts.map +1 -0
  178. package/lib/utils/zipLoader/types.js +2 -0
  179. package/lib/utils/zipLoader/types.js.map +1 -0
  180. package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
  181. package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
  182. package/lib/utils/zipLoader/zipUtils.js +229 -0
  183. package/lib/utils/zipLoader/zipUtils.js.map +1 -0
  184. package/package.json +69 -0
  185. package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
  186. package/rush-logs/ts-res-ui-components.build.log +9 -0
  187. package/src/components/common/QualifierContextControl.tsx +151 -0
  188. package/src/components/common/ResourceListView.tsx +63 -0
  189. package/src/components/common/ResourceTreeView.tsx +271 -0
  190. package/src/components/forms/HierarchyEditor.tsx +204 -0
  191. package/src/components/forms/QualifierEditForm.tsx +355 -0
  192. package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
  193. package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
  194. package/src/components/forms/index.ts +11 -0
  195. package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
  196. package/src/components/views/CompiledView/index.tsx +922 -0
  197. package/src/components/views/ConfigurationView/index.tsx +800 -0
  198. package/src/components/views/FilterView/index.tsx +825 -0
  199. package/src/components/views/ImportView/index.tsx +717 -0
  200. package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
  201. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
  202. package/src/components/views/ResolutionView/index.tsx +591 -0
  203. package/src/components/views/SourceView/index.tsx +536 -0
  204. package/src/components/views/ZipLoaderView/index.tsx +485 -0
  205. package/src/hooks/useConfigurationState.ts +374 -0
  206. package/src/hooks/useFilterState.ts +97 -0
  207. package/src/hooks/useResolutionState.ts +355 -0
  208. package/src/hooks/useResourceData.ts +467 -0
  209. package/src/hooks/useViewState.ts +44 -0
  210. package/src/index.ts +45 -0
  211. package/src/test/helpers/testDataLoader.ts +195 -0
  212. package/src/test/unit/utils/configurationUtils.test.ts +630 -0
  213. package/src/test/unit/utils/fileProcessing.test.ts +391 -0
  214. package/src/test/unit/utils/filterResources.test.ts +574 -0
  215. package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
  216. package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
  217. package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
  218. package/src/types/index.ts +322 -0
  219. package/src/utils/configurationUtils.ts +424 -0
  220. package/src/utils/fileProcessing.ts +160 -0
  221. package/src/utils/filterResources.ts +206 -0
  222. package/src/utils/resolutionEditing.ts +319 -0
  223. package/src/utils/resolutionUtils.ts +289 -0
  224. package/src/utils/tsResIntegration.ts +440 -0
  225. package/src/utils/zipLoader/browserZipLoader.ts +319 -0
  226. package/src/utils/zipLoader/index.ts +26 -0
  227. package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
  228. package/src/utils/zipLoader/types.ts +175 -0
  229. package/src/utils/zipLoader/zipUtils.ts +266 -0
  230. package/temp/build/typescript/ts_gZid87Hu.json +1 -0
  231. package/tsconfig.json +15 -0
@@ -0,0 +1,439 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+ import '@fgv/ts-utils-jest';
23
+ import { succeed, fail } from '@fgv/ts-utils';
24
+ import { validateEditedResource, computeResourceDelta, createCandidateDeclarations, extractResolutionContext, createEditCollisionKey, checkEditConflicts } from '../../../utils/resolutionEditing';
25
+ describe('resolutionEditing utilities', () => {
26
+ describe('validateEditedResource', () => {
27
+ test('validates valid JSON objects', () => {
28
+ const validValue = { message: 'Hello', count: 42 };
29
+ const result = validateEditedResource(validValue);
30
+ expect(result.isValid).toBe(true);
31
+ expect(result.errors).toEqual([]);
32
+ expect(result.warnings).toEqual([]);
33
+ });
34
+ test('validates simple values', () => {
35
+ expect(validateEditedResource('string')).toEqual({
36
+ isValid: true,
37
+ errors: [],
38
+ warnings: []
39
+ });
40
+ expect(validateEditedResource(123)).toEqual({
41
+ isValid: true,
42
+ errors: [],
43
+ warnings: []
44
+ });
45
+ expect(validateEditedResource(true)).toEqual({
46
+ isValid: true,
47
+ errors: [],
48
+ warnings: []
49
+ });
50
+ });
51
+ test('rejects null values', () => {
52
+ const result = validateEditedResource(null);
53
+ expect(result.isValid).toBe(false);
54
+ expect(result.errors).toContain('Resource value cannot be null or undefined');
55
+ });
56
+ test('rejects undefined values', () => {
57
+ const result = validateEditedResource(undefined);
58
+ expect(result.isValid).toBe(false);
59
+ expect(result.errors).toContain('Resource value cannot be null or undefined');
60
+ });
61
+ test('detects circular references', () => {
62
+ const circularObj = { name: 'test' };
63
+ circularObj.self = circularObj;
64
+ const result = validateEditedResource(circularObj);
65
+ expect(result.isValid).toBe(false);
66
+ expect(result.errors).toContain('Resource contains circular references');
67
+ });
68
+ test('handles complex nested objects', () => {
69
+ const complexObj = {
70
+ level1: {
71
+ level2: {
72
+ level3: {
73
+ value: 'deep value',
74
+ array: [1, 2, { nested: true }]
75
+ }
76
+ }
77
+ },
78
+ other: 'value'
79
+ };
80
+ const result = validateEditedResource(complexObj);
81
+ expect(result.isValid).toBe(true);
82
+ expect(result.errors).toEqual([]);
83
+ });
84
+ test('handles arrays with objects', () => {
85
+ const arrayValue = [{ id: 1, name: 'first' }, { id: 2, name: 'second' }, 'simple string', 123];
86
+ const result = validateEditedResource(arrayValue);
87
+ expect(result.isValid).toBe(true);
88
+ expect(result.errors).toEqual([]);
89
+ });
90
+ test('detects JSON serialization errors', () => {
91
+ // Create an object with a property that can't be serialized
92
+ const badObj = {
93
+ name: 'test'
94
+ };
95
+ // Add a circular reference to cause JSON serialization to fail
96
+ badObj.self = badObj;
97
+ const result = validateEditedResource(badObj);
98
+ expect(result.isValid).toBe(false);
99
+ expect(result.errors.length).toBeGreaterThan(0);
100
+ expect(result.errors[0]).toContain('Invalid JSON');
101
+ });
102
+ });
103
+ describe('computeResourceDelta', () => {
104
+ test('returns null for identical values', () => {
105
+ const baseValue = { message: 'Hello' };
106
+ const resolvedValue = { message: 'Hello' };
107
+ const editedValue = { message: 'Hello' };
108
+ const result = computeResourceDelta(baseValue, resolvedValue, editedValue);
109
+ expect(result).toSucceed();
110
+ expect(result.orThrow()).toBe(null);
111
+ });
112
+ test('computes delta for simple changes', () => {
113
+ const baseValue = { message: 'Hello' };
114
+ const resolvedValue = { message: 'Hello' };
115
+ const editedValue = { message: 'Hello World' };
116
+ const result = computeResourceDelta(baseValue, resolvedValue, editedValue);
117
+ expect(result).toSucceed();
118
+ // The exact delta format depends on ts-json implementation
119
+ expect(result.orThrow()).toBeDefined();
120
+ });
121
+ test('computes delta for additions', () => {
122
+ const baseValue = { message: 'Hello' };
123
+ const resolvedValue = { message: 'Hello' };
124
+ const editedValue = { message: 'Hello', newField: 'added' };
125
+ const result = computeResourceDelta(baseValue, resolvedValue, editedValue);
126
+ expect(result).toSucceed();
127
+ expect(result.orThrow()).toBeDefined();
128
+ });
129
+ test('handles deletion by computing appropriate delta', () => {
130
+ const baseValue = { message: 'Hello', extra: 'field' };
131
+ const resolvedValue = { message: 'Hello', extra: 'field' };
132
+ const editedValue = { message: 'Hello' };
133
+ const result = computeResourceDelta(baseValue, resolvedValue, editedValue);
134
+ expect(result).toSucceed();
135
+ expect(result.orThrow()).toBeDefined();
136
+ });
137
+ test('falls back to full replacement on diff failure', () => {
138
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
139
+ // The function should return the edited value as fallback when diff fails
140
+ // Since we can't easily mock the diff function in this context, we'll test the actual fallback behavior
141
+ const editedValue = { message: 'Edited' };
142
+ const resolvedValue = { original: 'value' };
143
+ const result = computeResourceDelta(undefined, resolvedValue, editedValue);
144
+ expect(result).toSucceed();
145
+ // The result should contain the actual diff result, not exactly the edited value
146
+ expect(result.orThrow()).toBeDefined();
147
+ consoleSpy.mockRestore();
148
+ });
149
+ test('handles complex nested objects', () => {
150
+ const resolvedValue = {
151
+ user: { name: 'John', age: 30 },
152
+ settings: { theme: 'dark', language: 'en' }
153
+ };
154
+ const editedValue = {
155
+ user: { name: 'Jane', age: 30 },
156
+ settings: { theme: 'light', language: 'en' }
157
+ };
158
+ const result = computeResourceDelta(undefined, resolvedValue, editedValue);
159
+ expect(result).toSucceed();
160
+ expect(result.orThrow()).toBeDefined();
161
+ });
162
+ });
163
+ describe('createCandidateDeclarations', () => {
164
+ test('creates declarations for edited resources', () => {
165
+ const editedResources = new Map([
166
+ [
167
+ 'resource1',
168
+ {
169
+ originalValue: { message: 'Hello' },
170
+ editedValue: { message: 'Hello World' },
171
+ delta: { message: 'Hello World' }
172
+ }
173
+ ]
174
+ ]);
175
+ const currentContext = { language: 'en', territory: 'US' };
176
+ const result = createCandidateDeclarations(editedResources, currentContext);
177
+ expect(result).toHaveLength(1);
178
+ expect(result[0]).toEqual({
179
+ id: 'resource1',
180
+ conditions: [
181
+ { qualifierName: 'language', operator: 'matches', value: 'en', priority: 900 },
182
+ { qualifierName: 'territory', operator: 'matches', value: 'US', priority: 900 }
183
+ ],
184
+ json: { message: 'Hello World' },
185
+ isPartial: true,
186
+ mergeMethod: 'augment'
187
+ });
188
+ });
189
+ test('skips resources with no delta', () => {
190
+ const editedResources = new Map([
191
+ [
192
+ 'resource1',
193
+ {
194
+ originalValue: { message: 'Hello' },
195
+ editedValue: { message: 'Hello' },
196
+ delta: null // No changes
197
+ }
198
+ ],
199
+ [
200
+ 'resource2',
201
+ {
202
+ originalValue: { message: 'Hi' },
203
+ editedValue: { message: 'Hi there' },
204
+ delta: { message: 'Hi there' }
205
+ }
206
+ ]
207
+ ]);
208
+ const currentContext = { language: 'en' };
209
+ const result = createCandidateDeclarations(editedResources, currentContext);
210
+ expect(result).toHaveLength(1);
211
+ expect(result[0].id).toBe('resource2');
212
+ });
213
+ test('handles empty context', () => {
214
+ const editedResources = new Map([
215
+ [
216
+ 'resource1',
217
+ {
218
+ originalValue: { message: 'Hello' },
219
+ editedValue: { message: 'Hello World' },
220
+ delta: { message: 'Hello World' }
221
+ }
222
+ ]
223
+ ]);
224
+ const currentContext = {};
225
+ const result = createCandidateDeclarations(editedResources, currentContext);
226
+ expect(result).toHaveLength(1);
227
+ expect(result[0].conditions).toBeUndefined();
228
+ });
229
+ test('filters out empty context values', () => {
230
+ const editedResources = new Map([
231
+ [
232
+ 'resource1',
233
+ {
234
+ originalValue: { message: 'Hello' },
235
+ editedValue: { message: 'Hello World' },
236
+ delta: { message: 'Hello World' }
237
+ }
238
+ ]
239
+ ]);
240
+ const currentContext = {
241
+ language: 'en',
242
+ territory: '', // Empty string
243
+ platform: ' ', // Whitespace only
244
+ theme: 'dark'
245
+ };
246
+ const result = createCandidateDeclarations(editedResources, currentContext);
247
+ expect(result).toHaveLength(1);
248
+ expect(result[0].conditions).toHaveLength(2); // Only language and theme
249
+ expect(result[0].conditions).toEqual([
250
+ { qualifierName: 'language', operator: 'matches', value: 'en', priority: 900 },
251
+ { qualifierName: 'theme', operator: 'matches', value: 'dark', priority: 900 }
252
+ ]);
253
+ });
254
+ test('handles multiple edited resources', () => {
255
+ const editedResources = new Map([
256
+ [
257
+ 'resource1',
258
+ {
259
+ originalValue: { msg: 'A' },
260
+ editedValue: { msg: 'A1' },
261
+ delta: { msg: 'A1' }
262
+ }
263
+ ],
264
+ [
265
+ 'resource2',
266
+ {
267
+ originalValue: { msg: 'B' },
268
+ editedValue: { msg: 'B1' },
269
+ delta: { msg: 'B1' }
270
+ }
271
+ ]
272
+ ]);
273
+ const currentContext = { language: 'en' };
274
+ const result = createCandidateDeclarations(editedResources, currentContext);
275
+ expect(result).toHaveLength(2);
276
+ expect(result.map((r) => r.id)).toEqual(['resource1', 'resource2']);
277
+ });
278
+ });
279
+ describe('extractResolutionContext', () => {
280
+ test('filters out empty and undefined values', () => {
281
+ const mockResolver = {};
282
+ const contextValues = {
283
+ language: 'en',
284
+ territory: '',
285
+ platform: undefined,
286
+ theme: 'dark',
287
+ empty: ' '
288
+ };
289
+ const result = extractResolutionContext(mockResolver, contextValues);
290
+ expect(result).toEqual({
291
+ language: 'en',
292
+ theme: 'dark'
293
+ });
294
+ });
295
+ test('trims whitespace from values', () => {
296
+ const mockResolver = {};
297
+ const contextValues = {
298
+ language: ' en ',
299
+ territory: ' US '
300
+ };
301
+ const result = extractResolutionContext(mockResolver, contextValues);
302
+ expect(result).toEqual({
303
+ language: 'en',
304
+ territory: 'US'
305
+ });
306
+ });
307
+ test('handles empty context', () => {
308
+ const mockResolver = {};
309
+ const contextValues = {};
310
+ const result = extractResolutionContext(mockResolver, contextValues);
311
+ expect(result).toEqual({});
312
+ });
313
+ test('handles all empty values', () => {
314
+ const mockResolver = {};
315
+ const contextValues = {
316
+ language: '',
317
+ territory: ' ',
318
+ platform: undefined
319
+ };
320
+ const result = extractResolutionContext(mockResolver, contextValues);
321
+ expect(result).toEqual({});
322
+ });
323
+ });
324
+ describe('createEditCollisionKey', () => {
325
+ test('creates consistent key for same inputs', () => {
326
+ const resourceId = 'resource1';
327
+ const context = { language: 'en', territory: 'US' };
328
+ const key1 = createEditCollisionKey(resourceId, context);
329
+ const key2 = createEditCollisionKey(resourceId, context);
330
+ expect(key1).toBe(key2);
331
+ expect(key1).toMatch(/^resource1\?/);
332
+ });
333
+ test('sorts context entries for consistency', () => {
334
+ const resourceId = 'resource1';
335
+ const context1 = { language: 'en', territory: 'US' };
336
+ const context2 = { territory: 'US', language: 'en' };
337
+ const key1 = createEditCollisionKey(resourceId, context1);
338
+ const key2 = createEditCollisionKey(resourceId, context2);
339
+ expect(key1).toBe(key2);
340
+ });
341
+ test('creates different keys for different resources', () => {
342
+ const context = { language: 'en' };
343
+ const key1 = createEditCollisionKey('resource1', context);
344
+ const key2 = createEditCollisionKey('resource2', context);
345
+ expect(key1).not.toBe(key2);
346
+ });
347
+ test('creates different keys for different contexts', () => {
348
+ const resourceId = 'resource1';
349
+ const key1 = createEditCollisionKey(resourceId, { language: 'en' });
350
+ const key2 = createEditCollisionKey(resourceId, { language: 'fr' });
351
+ expect(key1).not.toBe(key2);
352
+ });
353
+ test('handles empty context', () => {
354
+ const key = createEditCollisionKey('resource1', {});
355
+ expect(key).toBe('resource1?');
356
+ });
357
+ test('properly encodes context values', () => {
358
+ const resourceId = 'resource1';
359
+ const context = { language: 'en', territory: 'US' };
360
+ const key = createEditCollisionKey(resourceId, context);
361
+ expect(key).toContain('language=en');
362
+ expect(key).toContain('territory=US');
363
+ expect(key).toContain('&');
364
+ });
365
+ });
366
+ describe('checkEditConflicts', () => {
367
+ let mockResourceManager;
368
+ beforeEach(() => {
369
+ mockResourceManager = {
370
+ getBuiltResource: jest.fn()
371
+ };
372
+ });
373
+ test('returns no conflicts for single candidate resources', () => {
374
+ const mockResource = {
375
+ candidates: [{ value: 'single candidate' }]
376
+ };
377
+ mockResourceManager.getBuiltResource.mockReturnValue(succeed(mockResource));
378
+ const editedResources = new Map([['resource1', { some: 'edit' }]]);
379
+ const currentContext = { language: 'en' };
380
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
381
+ expect(result.conflicts).toEqual([]);
382
+ expect(result.warnings).toEqual([]);
383
+ });
384
+ test('warns about multiple candidates', () => {
385
+ const mockResource = {
386
+ candidates: [{ value: 'candidate 1' }, { value: 'candidate 2' }, { value: 'candidate 3' }]
387
+ };
388
+ mockResourceManager.getBuiltResource.mockReturnValue(succeed(mockResource));
389
+ const editedResources = new Map([['resource1', { some: 'edit' }]]);
390
+ const currentContext = { language: 'en' };
391
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
392
+ expect(result.conflicts).toEqual([]);
393
+ expect(result.warnings).toHaveLength(1);
394
+ expect(result.warnings[0]).toContain('Resource resource1 has 3 candidates');
395
+ });
396
+ test('handles resource manager errors gracefully', () => {
397
+ mockResourceManager.getBuiltResource.mockReturnValue(fail('Resource not found'));
398
+ const editedResources = new Map([['resource1', { some: 'edit' }]]);
399
+ const currentContext = { language: 'en' };
400
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
401
+ expect(result.conflicts).toEqual([]);
402
+ expect(result.warnings).toEqual([]);
403
+ });
404
+ test('handles multiple edited resources', () => {
405
+ const singleCandidate = { candidates: [{ value: 'single' }] };
406
+ const multipleCandidate = { candidates: [{ value: 'one' }, { value: 'two' }] };
407
+ mockResourceManager.getBuiltResource
408
+ .mockReturnValueOnce(succeed(singleCandidate))
409
+ .mockReturnValueOnce(succeed(multipleCandidate));
410
+ const editedResources = new Map([
411
+ ['resource1', { some: 'edit' }],
412
+ ['resource2', { other: 'edit' }]
413
+ ]);
414
+ const currentContext = { language: 'en' };
415
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
416
+ expect(result.conflicts).toEqual([]);
417
+ expect(result.warnings).toHaveLength(1);
418
+ expect(result.warnings[0]).toContain('Resource resource2 has 2 candidates');
419
+ });
420
+ test('handles empty edited resources', () => {
421
+ const editedResources = new Map();
422
+ const currentContext = { language: 'en' };
423
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
424
+ expect(result.conflicts).toEqual([]);
425
+ expect(result.warnings).toEqual([]);
426
+ });
427
+ test('handles exceptions in resource manager gracefully', () => {
428
+ mockResourceManager.getBuiltResource.mockImplementation(() => {
429
+ throw new Error('Unexpected error');
430
+ });
431
+ const editedResources = new Map([['resource1', { some: 'edit' }]]);
432
+ const currentContext = { language: 'en' };
433
+ const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);
434
+ expect(result.conflicts).toEqual([]);
435
+ expect(result.warnings).toEqual([]);
436
+ });
437
+ });
438
+ });
439
+ //# sourceMappingURL=resolutionEditing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolutionEditing.test.js","sourceRoot":"","sources":["../../../../src/test/unit/utils/resolutionEditing.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2025 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport '@fgv/ts-utils-jest';\nimport { succeed, fail } from '@fgv/ts-utils';\nimport { JsonValue } from '@fgv/ts-json-base';\nimport {\n validateEditedResource,\n computeResourceDelta,\n createCandidateDeclarations,\n extractResolutionContext,\n createEditCollisionKey,\n checkEditConflicts,\n type EditedResourceInfo,\n type EditValidationResult\n} from '../../../utils/resolutionEditing';\n\ndescribe('resolutionEditing utilities', () => {\n describe('validateEditedResource', () => {\n test('validates valid JSON objects', () => {\n const validValue = { message: 'Hello', count: 42 };\n const result = validateEditedResource(validValue);\n\n expect(result.isValid).toBe(true);\n expect(result.errors).toEqual([]);\n expect(result.warnings).toEqual([]);\n });\n\n test('validates simple values', () => {\n expect(validateEditedResource('string')).toEqual({\n isValid: true,\n errors: [],\n warnings: []\n });\n\n expect(validateEditedResource(123)).toEqual({\n isValid: true,\n errors: [],\n warnings: []\n });\n\n expect(validateEditedResource(true)).toEqual({\n isValid: true,\n errors: [],\n warnings: []\n });\n });\n\n test('rejects null values', () => {\n const result = validateEditedResource(null);\n\n expect(result.isValid).toBe(false);\n expect(result.errors).toContain('Resource value cannot be null or undefined');\n });\n\n test('rejects undefined values', () => {\n const result = validateEditedResource(undefined as unknown as JsonValue);\n\n expect(result.isValid).toBe(false);\n expect(result.errors).toContain('Resource value cannot be null or undefined');\n });\n\n test('detects circular references', () => {\n const circularObj = { name: 'test' } as any;\n circularObj.self = circularObj;\n\n const result = validateEditedResource(circularObj);\n\n expect(result.isValid).toBe(false);\n expect(result.errors).toContain('Resource contains circular references');\n });\n\n test('handles complex nested objects', () => {\n const complexObj = {\n level1: {\n level2: {\n level3: {\n value: 'deep value',\n array: [1, 2, { nested: true }]\n }\n }\n },\n other: 'value'\n };\n\n const result = validateEditedResource(complexObj);\n\n expect(result.isValid).toBe(true);\n expect(result.errors).toEqual([]);\n });\n\n test('handles arrays with objects', () => {\n const arrayValue = [{ id: 1, name: 'first' }, { id: 2, name: 'second' }, 'simple string', 123];\n\n const result = validateEditedResource(arrayValue);\n\n expect(result.isValid).toBe(true);\n expect(result.errors).toEqual([]);\n });\n\n test('detects JSON serialization errors', () => {\n // Create an object with a property that can't be serialized\n const badObj = {\n name: 'test'\n };\n\n // Add a circular reference to cause JSON serialization to fail\n (badObj as any).self = badObj;\n\n const result = validateEditedResource(badObj);\n\n expect(result.isValid).toBe(false);\n expect(result.errors.length).toBeGreaterThan(0);\n expect(result.errors[0]).toContain('Invalid JSON');\n });\n });\n\n describe('computeResourceDelta', () => {\n test('returns null for identical values', () => {\n const baseValue = { message: 'Hello' };\n const resolvedValue = { message: 'Hello' };\n const editedValue = { message: 'Hello' };\n\n const result = computeResourceDelta(baseValue, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n expect(result.orThrow()).toBe(null);\n });\n\n test('computes delta for simple changes', () => {\n const baseValue = { message: 'Hello' };\n const resolvedValue = { message: 'Hello' };\n const editedValue = { message: 'Hello World' };\n\n const result = computeResourceDelta(baseValue, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n // The exact delta format depends on ts-json implementation\n expect(result.orThrow()).toBeDefined();\n });\n\n test('computes delta for additions', () => {\n const baseValue = { message: 'Hello' };\n const resolvedValue = { message: 'Hello' };\n const editedValue = { message: 'Hello', newField: 'added' };\n\n const result = computeResourceDelta(baseValue, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n expect(result.orThrow()).toBeDefined();\n });\n\n test('handles deletion by computing appropriate delta', () => {\n const baseValue = { message: 'Hello', extra: 'field' };\n const resolvedValue = { message: 'Hello', extra: 'field' };\n const editedValue = { message: 'Hello' };\n\n const result = computeResourceDelta(baseValue, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n expect(result.orThrow()).toBeDefined();\n });\n\n test('falls back to full replacement on diff failure', () => {\n const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});\n\n // The function should return the edited value as fallback when diff fails\n // Since we can't easily mock the diff function in this context, we'll test the actual fallback behavior\n const editedValue = { message: 'Edited' };\n const resolvedValue = { original: 'value' };\n const result = computeResourceDelta(undefined, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n // The result should contain the actual diff result, not exactly the edited value\n expect(result.orThrow()).toBeDefined();\n\n consoleSpy.mockRestore();\n });\n\n test('handles complex nested objects', () => {\n const resolvedValue = {\n user: { name: 'John', age: 30 },\n settings: { theme: 'dark', language: 'en' }\n };\n const editedValue = {\n user: { name: 'Jane', age: 30 },\n settings: { theme: 'light', language: 'en' }\n };\n\n const result = computeResourceDelta(undefined, resolvedValue, editedValue);\n\n expect(result).toSucceed();\n expect(result.orThrow()).toBeDefined();\n });\n });\n\n describe('createCandidateDeclarations', () => {\n test('creates declarations for edited resources', () => {\n const editedResources = new Map([\n [\n 'resource1',\n {\n originalValue: { message: 'Hello' },\n editedValue: { message: 'Hello World' },\n delta: { message: 'Hello World' }\n }\n ]\n ]);\n const currentContext = { language: 'en', territory: 'US' };\n\n const result = createCandidateDeclarations(editedResources, currentContext);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({\n id: 'resource1',\n conditions: [\n { qualifierName: 'language', operator: 'matches', value: 'en', priority: 900 },\n { qualifierName: 'territory', operator: 'matches', value: 'US', priority: 900 }\n ],\n json: { message: 'Hello World' },\n isPartial: true,\n mergeMethod: 'augment'\n });\n });\n\n test('skips resources with no delta', () => {\n const editedResources = new Map([\n [\n 'resource1',\n {\n originalValue: { message: 'Hello' },\n editedValue: { message: 'Hello' },\n delta: null // No changes\n }\n ],\n [\n 'resource2',\n {\n originalValue: { message: 'Hi' },\n editedValue: { message: 'Hi there' },\n delta: { message: 'Hi there' }\n }\n ]\n ]);\n const currentContext = { language: 'en' };\n\n const result = createCandidateDeclarations(editedResources, currentContext);\n\n expect(result).toHaveLength(1);\n expect(result[0].id).toBe('resource2');\n });\n\n test('handles empty context', () => {\n const editedResources = new Map([\n [\n 'resource1',\n {\n originalValue: { message: 'Hello' },\n editedValue: { message: 'Hello World' },\n delta: { message: 'Hello World' }\n }\n ]\n ]);\n const currentContext = {};\n\n const result = createCandidateDeclarations(editedResources, currentContext);\n\n expect(result).toHaveLength(1);\n expect(result[0].conditions).toBeUndefined();\n });\n\n test('filters out empty context values', () => {\n const editedResources = new Map([\n [\n 'resource1',\n {\n originalValue: { message: 'Hello' },\n editedValue: { message: 'Hello World' },\n delta: { message: 'Hello World' }\n }\n ]\n ]);\n const currentContext = {\n language: 'en',\n territory: '', // Empty string\n platform: ' ', // Whitespace only\n theme: 'dark'\n };\n\n const result = createCandidateDeclarations(editedResources, currentContext);\n\n expect(result).toHaveLength(1);\n expect(result[0].conditions).toHaveLength(2); // Only language and theme\n expect(result[0].conditions).toEqual([\n { qualifierName: 'language', operator: 'matches', value: 'en', priority: 900 },\n { qualifierName: 'theme', operator: 'matches', value: 'dark', priority: 900 }\n ]);\n });\n\n test('handles multiple edited resources', () => {\n const editedResources = new Map([\n [\n 'resource1',\n {\n originalValue: { msg: 'A' },\n editedValue: { msg: 'A1' },\n delta: { msg: 'A1' }\n }\n ],\n [\n 'resource2',\n {\n originalValue: { msg: 'B' },\n editedValue: { msg: 'B1' },\n delta: { msg: 'B1' }\n }\n ]\n ]);\n const currentContext = { language: 'en' };\n\n const result = createCandidateDeclarations(editedResources, currentContext);\n\n expect(result).toHaveLength(2);\n expect(result.map((r) => r.id)).toEqual(['resource1', 'resource2']);\n });\n });\n\n describe('extractResolutionContext', () => {\n test('filters out empty and undefined values', () => {\n const mockResolver = {} as any;\n const contextValues = {\n language: 'en',\n territory: '',\n platform: undefined as any,\n theme: 'dark',\n empty: ' '\n };\n\n const result = extractResolutionContext(mockResolver, contextValues);\n\n expect(result).toEqual({\n language: 'en',\n theme: 'dark'\n });\n });\n\n test('trims whitespace from values', () => {\n const mockResolver = {} as any;\n const contextValues = {\n language: ' en ',\n territory: ' US '\n };\n\n const result = extractResolutionContext(mockResolver, contextValues);\n\n expect(result).toEqual({\n language: 'en',\n territory: 'US'\n });\n });\n\n test('handles empty context', () => {\n const mockResolver = {} as any;\n const contextValues = {};\n\n const result = extractResolutionContext(mockResolver, contextValues);\n\n expect(result).toEqual({});\n });\n\n test('handles all empty values', () => {\n const mockResolver = {} as any;\n const contextValues = {\n language: '',\n territory: ' ',\n platform: undefined as any\n };\n\n const result = extractResolutionContext(mockResolver, contextValues);\n\n expect(result).toEqual({});\n });\n });\n\n describe('createEditCollisionKey', () => {\n test('creates consistent key for same inputs', () => {\n const resourceId = 'resource1';\n const context = { language: 'en', territory: 'US' };\n\n const key1 = createEditCollisionKey(resourceId, context);\n const key2 = createEditCollisionKey(resourceId, context);\n\n expect(key1).toBe(key2);\n expect(key1).toMatch(/^resource1\\?/);\n });\n\n test('sorts context entries for consistency', () => {\n const resourceId = 'resource1';\n const context1 = { language: 'en', territory: 'US' };\n const context2 = { territory: 'US', language: 'en' };\n\n const key1 = createEditCollisionKey(resourceId, context1);\n const key2 = createEditCollisionKey(resourceId, context2);\n\n expect(key1).toBe(key2);\n });\n\n test('creates different keys for different resources', () => {\n const context = { language: 'en' };\n const key1 = createEditCollisionKey('resource1', context);\n const key2 = createEditCollisionKey('resource2', context);\n\n expect(key1).not.toBe(key2);\n });\n\n test('creates different keys for different contexts', () => {\n const resourceId = 'resource1';\n const key1 = createEditCollisionKey(resourceId, { language: 'en' });\n const key2 = createEditCollisionKey(resourceId, { language: 'fr' });\n\n expect(key1).not.toBe(key2);\n });\n\n test('handles empty context', () => {\n const key = createEditCollisionKey('resource1', {});\n expect(key).toBe('resource1?');\n });\n\n test('properly encodes context values', () => {\n const resourceId = 'resource1';\n const context = { language: 'en', territory: 'US' };\n const key = createEditCollisionKey(resourceId, context);\n\n expect(key).toContain('language=en');\n expect(key).toContain('territory=US');\n expect(key).toContain('&');\n });\n });\n\n describe('checkEditConflicts', () => {\n let mockResourceManager: any;\n\n beforeEach(() => {\n mockResourceManager = {\n getBuiltResource: jest.fn()\n };\n });\n\n test('returns no conflicts for single candidate resources', () => {\n const mockResource = {\n candidates: [{ value: 'single candidate' }]\n };\n mockResourceManager.getBuiltResource.mockReturnValue(succeed(mockResource));\n\n const editedResources = new Map([['resource1', { some: 'edit' }]]);\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toEqual([]);\n });\n\n test('warns about multiple candidates', () => {\n const mockResource = {\n candidates: [{ value: 'candidate 1' }, { value: 'candidate 2' }, { value: 'candidate 3' }]\n };\n mockResourceManager.getBuiltResource.mockReturnValue(succeed(mockResource));\n\n const editedResources = new Map([['resource1', { some: 'edit' }]]);\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toHaveLength(1);\n expect(result.warnings[0]).toContain('Resource resource1 has 3 candidates');\n });\n\n test('handles resource manager errors gracefully', () => {\n mockResourceManager.getBuiltResource.mockReturnValue(fail('Resource not found'));\n\n const editedResources = new Map([['resource1', { some: 'edit' }]]);\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toEqual([]);\n });\n\n test('handles multiple edited resources', () => {\n const singleCandidate = { candidates: [{ value: 'single' }] };\n const multipleCandidate = { candidates: [{ value: 'one' }, { value: 'two' }] };\n\n mockResourceManager.getBuiltResource\n .mockReturnValueOnce(succeed(singleCandidate))\n .mockReturnValueOnce(succeed(multipleCandidate));\n\n const editedResources = new Map<string, JsonValue>([\n ['resource1', { some: 'edit' }],\n ['resource2', { other: 'edit' }]\n ]);\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toHaveLength(1);\n expect(result.warnings[0]).toContain('Resource resource2 has 2 candidates');\n });\n\n test('handles empty edited resources', () => {\n const editedResources = new Map();\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toEqual([]);\n });\n\n test('handles exceptions in resource manager gracefully', () => {\n mockResourceManager.getBuiltResource.mockImplementation(() => {\n throw new Error('Unexpected error');\n });\n\n const editedResources = new Map([['resource1', { some: 'edit' }]]);\n const currentContext = { language: 'en' };\n\n const result = checkEditConflicts(mockResourceManager, editedResources, currentContext);\n\n expect(result.conflicts).toEqual([]);\n expect(result.warnings).toEqual([]);\n });\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ import '@fgv/ts-utils-jest';
2
+ //# sourceMappingURL=resolutionUtils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolutionUtils.test.d.ts","sourceRoot":"","sources":["../../../../src/test/unit/utils/resolutionUtils.test.ts"],"names":[],"mappings":"AAsBA,OAAO,oBAAoB,CAAC"}