@raketa-cloud/admin 1.8.10 → 1.8.11
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.
- package/dist/application.css +109 -0
- package/package.json +4 -2
- package/src/HonoAdapter.js +22 -13
- package/src/Logger.js +7 -8
- package/src/RaketaContext.js +1 -0
- package/src/cipher.js +2 -2
- package/src/cloud/ContentTasks.js +31 -0
- package/src/cloud/RaketaClient.js +1 -1
- package/src/cloud/Redirects.js +2 -2
- package/src/cloud/ResourceChanges.js +1 -1
- package/src/cloud/ResourceRevisions.js +2 -2
- package/src/cloud/Resources.js +28 -12
- package/src/cloud/Tags.js +4 -4
- package/src/cloud/documents/common.js +16 -0
- package/src/cloud/documents/contentTasksDocuments.js +59 -0
- package/src/cloud/documents/resourcesDocuments.js +12 -0
- package/src/filters.js +3 -3
- package/src/handlers/TagsHandler.js +2 -2
- package/src/handlers/_shared/getResourceUpdateDestination.js +1 -1
- package/src/handlers/categories/index.js +3 -3
- package/src/handlers/content_tasks/ContentTasksHandler.js +32 -0
- package/src/handlers/resource_revisions/LocksHandler.js +4 -1
- package/src/handlers/resources/ClonesHandler.js +1 -3
- package/src/handlers/resources/ExportsHandler.js +1 -5
- package/src/handlers/resources/ResourcesHandler.js +1 -1
- package/src/handlers/resources/TranslationsHandler.js +28 -19
- package/src/handlers/resources/URLsHandler.js +3 -1
- package/src/helpers/AuthHelper.js +1 -1
- package/src/helpers/FormBuilder.js +14 -14
- package/src/helpers/Pagination.js +5 -5
- package/src/helpers/historyUtils.js +3 -3
- package/src/helpers/utils.js +6 -1
- package/src/index.js +1 -0
- package/src/routeMember.js +9 -4
- package/src/routeNestedCollection.js +14 -29
- package/src/routeNestedMember.js +17 -6
- package/src/routeStandard.js +5 -1
- package/src/setupAuthentication.js +1 -1
- package/src/setupCloud.js +5 -0
- package/src/tests/Adapter.test.js +2 -2
- package/src/tests/HonoAdapter.coverage.test.js +44 -22
- package/src/tests/HonoAdapter.filter.test.js +28 -18
- package/src/tests/HonoAdapter.test.js +3 -3
- package/src/tests/RaketaClient.test.js +427 -413
- package/src/tests/RaketaContext.test.js +1 -1
- package/src/tests/buildResourcePath.test.js +1 -1
- package/src/tests/cipher.test.js +16 -16
- package/src/tests/filters.test.js +24 -24
- package/src/tests/handlers/RedirectsHandler.test/destroy.test.js +1 -1
- package/src/tests/handlers/TagsHandler.test/create.test.js +2 -2
- package/src/tests/handlers/TagsHandler.test/destroy.test.js +1 -1
- package/src/tests/handlers/TagsHandler.test/update.test.js +2 -2
- package/src/tests/handlers/categories/create.test.js +2 -2
- package/src/tests/handlers/categories/destroy.test.js +1 -1
- package/src/tests/handlers/content_tasks/ContentTasksHandler.test/index.test.js +161 -0
- package/src/tests/handlers/resource_revisions/CancelSchedulesHandler.test/create.test.js +2 -2
- package/src/tests/handlers/resource_revisions/CodeHandler.test/update.test.js +1 -1
- package/src/tests/handlers/resource_revisions/GeneralHandler.test/update.test.js +1 -1
- package/src/tests/handlers/resource_revisions/LocksHandler.test/create.test.js +5 -5
- package/src/tests/handlers/resource_revisions/LocksHandler.test/create_form.test.js +6 -2
- package/src/tests/handlers/resource_revisions/MetaHandler.test/update.test.js +1 -1
- package/src/tests/handlers/resource_revisions/PublishesHandler.test/create.test.js +5 -5
- package/src/tests/handlers/resource_revisions/ResourceRevisionsHandler.test/create.test.js +2 -2
- package/src/tests/handlers/resource_revisions/ResourceRevisionsHandler.test/destroy.test.js +1 -1
- package/src/tests/handlers/resource_revisions/RevertsHandler.test/create.test.js +18 -3
- package/src/tests/handlers/resource_revisions/TagHandler.test/update.test.js +1 -1
- package/src/tests/handlers/resource_revisions/UnlocksHandler.test/create.test.js +2 -2
- package/src/tests/handlers/resource_revisions/VisualHandler.test/update.test.js +1 -1
- package/src/tests/handlers/resource_revisions/WidgetHandler.test/update.test.js +2 -2
- package/src/tests/handlers/resources/ChangeHandler.test/show.test.js +7 -2
- package/src/tests/handlers/resources/ClonesHandler.test/create.test.js +1 -3
- package/src/tests/handlers/resources/ExportsHandler.test/index.test.js +8 -8
- package/src/tests/handlers/resources/HistoryHandler.test/index.test.js +1 -1
- package/src/tests/handlers/resources/ResourcesAPIHandler.test/index.test.js +1 -5
- package/src/tests/handlers/resources/ResourcesHandler.test/create.test.js +2 -2
- package/src/tests/handlers/resources/ResourcesHandler.test/show.test.js +1 -1
- package/src/tests/handlers/resources/TranslationsHandler.test/create.test.js +24 -14
- package/src/tests/handlers/resources/TranslationsHandler.test/create_form.test.js +1 -1
- package/src/tests/handlers/resources/TranslationsHandler.test/index.test.js +3 -3
- package/src/tests/handlers/resources/URLsHandler.test/create.test.js +4 -6
- package/src/tests/helpers/AuthHelper.test.js +11 -12
- package/src/tests/helpers/CSVExportHelper.test.js +3 -3
- package/src/tests/helpers/FormBuilder.test.js +21 -21
- package/src/tests/helpers/HTMLBuilder.test.js +4 -4
- package/src/tests/helpers/Inflector.test.js +1 -1
- package/src/tests/helpers/Pagination.test.js +1 -1
- package/src/tests/helpers/utils.test.js +46 -0
- package/src/tests/routeCategoriesModule.test.js +10 -5
- package/src/tests/routeCollection.test.js +1 -1
- package/src/tests/routeMember.test.js +2 -2
- package/src/tests/routeNestedCollection.test.js +3 -3
- package/src/tests/routeNestedMember.test.js +3 -3
- package/src/tests/routeResourceModule.test.js +15 -16
- package/src/tests/routeSettings.test.js +3 -3
- package/src/tests/routeStandard.test.js +27 -9
- package/src/tests/setupAuthentication.test.js +9 -6
- package/src/tests/support/factory.js +33 -0
- package/src/tests/utils.test.js +19 -19
- package/src/views/content_tasks/index.ejs +191 -0
- package/src/views/layouts/application.ejs +2 -1
- package/src/views/resources/_shared/_toolbar.ejs +33 -2
- package/src/views/resources/_shared/_translations_strip.ejs +71 -0
- package/src/views/resources/index.ejs +5 -0
- package/src/views/resources/show.ejs +1 -29
- package/src/views/resources/translations/create_form.ejs +13 -0
|
@@ -1,636 +1,650 @@
|
|
|
1
|
-
import RaketaClient from
|
|
2
|
-
import { request } from
|
|
1
|
+
import RaketaClient from "../cloud/RaketaClient";
|
|
2
|
+
import { request } from "graphql-request";
|
|
3
3
|
|
|
4
|
-
jest.mock(
|
|
4
|
+
jest.mock("graphql-request", () => ({
|
|
5
5
|
request: jest.fn(),
|
|
6
|
-
}))
|
|
6
|
+
}));
|
|
7
7
|
|
|
8
|
-
describe(
|
|
8
|
+
describe("RaketaClient", () => {
|
|
9
9
|
function buildMockConfig(overrides = {}) {
|
|
10
10
|
return {
|
|
11
11
|
cloud: {
|
|
12
|
-
gateway_url:
|
|
13
|
-
gateway_project:
|
|
14
|
-
dxp_service_private:
|
|
12
|
+
gateway_url: "https://api.example.com",
|
|
13
|
+
gateway_project: "test-project",
|
|
14
|
+
dxp_service_private: "test-service",
|
|
15
15
|
},
|
|
16
16
|
...overrides,
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function buildClient(config = buildMockConfig(), token =
|
|
21
|
-
const getConfig = () => config
|
|
22
|
-
return new RaketaClient(getConfig, token)
|
|
20
|
+
function buildClient(config = buildMockConfig(), token = "test-token") {
|
|
21
|
+
const getConfig = () => config;
|
|
22
|
+
return new RaketaClient(getConfig, token);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
beforeEach(() => {
|
|
26
|
-
jest.clearAllMocks()
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe(
|
|
30
|
-
test(
|
|
31
|
-
const getConfig = jest.fn(() => buildMockConfig())
|
|
32
|
-
const client = new RaketaClient(getConfig,
|
|
33
|
-
|
|
34
|
-
expect(client.getConfig).toBe(getConfig)
|
|
35
|
-
expect(client.token).toBe(
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
describe(
|
|
40
|
-
test(
|
|
41
|
-
const client = buildClient()
|
|
42
|
-
const query =
|
|
43
|
-
const variables = { id:
|
|
44
|
-
const headers = {
|
|
45
|
-
|
|
46
|
-
request.mockResolvedValue({ data:
|
|
47
|
-
|
|
48
|
-
await client.gqlRequest(
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("constructor", () => {
|
|
30
|
+
test("initializes with getConfig and token", () => {
|
|
31
|
+
const getConfig = jest.fn(() => buildMockConfig());
|
|
32
|
+
const client = new RaketaClient(getConfig, "my-token");
|
|
33
|
+
|
|
34
|
+
expect(client.getConfig).toBe(getConfig);
|
|
35
|
+
expect(client.token).toBe("my-token");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("gqlRequest", () => {
|
|
40
|
+
test("makes GraphQL request with correct parameters", async () => {
|
|
41
|
+
const client = buildClient();
|
|
42
|
+
const query = "query { test }";
|
|
43
|
+
const variables = { id: "123" };
|
|
44
|
+
const headers = { "x-custom": "value" };
|
|
45
|
+
|
|
46
|
+
request.mockResolvedValue({ data: "response" });
|
|
47
|
+
|
|
48
|
+
await client.gqlRequest(
|
|
49
|
+
"https://api.example.com/graphql",
|
|
50
|
+
query,
|
|
51
|
+
variables,
|
|
52
|
+
headers,
|
|
53
|
+
);
|
|
49
54
|
|
|
50
55
|
expect(request).toHaveBeenCalledWith({
|
|
51
|
-
url:
|
|
56
|
+
url: "https://api.example.com/graphql",
|
|
52
57
|
document: query,
|
|
53
58
|
variables: variables,
|
|
54
59
|
requestHeaders: headers,
|
|
55
|
-
})
|
|
56
|
-
})
|
|
60
|
+
});
|
|
61
|
+
});
|
|
57
62
|
|
|
58
|
-
test(
|
|
59
|
-
const client = buildClient()
|
|
60
|
-
const query =
|
|
63
|
+
test("makes request with default empty variables and headers", async () => {
|
|
64
|
+
const client = buildClient();
|
|
65
|
+
const query = "query { test }";
|
|
61
66
|
|
|
62
|
-
request.mockResolvedValue({ data:
|
|
67
|
+
request.mockResolvedValue({ data: "response" });
|
|
63
68
|
|
|
64
|
-
await client.gqlRequest(
|
|
69
|
+
await client.gqlRequest("https://api.example.com/graphql", query);
|
|
65
70
|
|
|
66
71
|
expect(request).toHaveBeenCalledWith({
|
|
67
|
-
url:
|
|
72
|
+
url: "https://api.example.com/graphql",
|
|
68
73
|
document: query,
|
|
69
74
|
variables: {},
|
|
70
75
|
requestHeaders: {},
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
})
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
74
79
|
|
|
75
|
-
describe(
|
|
76
|
-
test(
|
|
77
|
-
const client = buildClient()
|
|
78
|
-
const query =
|
|
79
|
-
const variables = { limit: 10 }
|
|
80
|
+
describe("query", () => {
|
|
81
|
+
test("makes request to gateway with proper headers", async () => {
|
|
82
|
+
const client = buildClient();
|
|
83
|
+
const query = "query { users }";
|
|
84
|
+
const variables = { limit: 10 };
|
|
80
85
|
|
|
81
|
-
request.mockResolvedValue({ data: { users: [] } })
|
|
86
|
+
request.mockResolvedValue({ data: { users: [] } });
|
|
82
87
|
|
|
83
|
-
await client.query(query, variables)
|
|
88
|
+
await client.query(query, variables);
|
|
84
89
|
|
|
85
90
|
expect(request).toHaveBeenCalledWith({
|
|
86
|
-
url:
|
|
91
|
+
url: "https://api.example.com/dispatch",
|
|
87
92
|
document: query,
|
|
88
93
|
variables: variables,
|
|
89
94
|
requestHeaders: {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
"x-raketa-project": "test-project",
|
|
96
|
+
"x-raketa-service": "test-service",
|
|
97
|
+
"x-raketa-identity": "test-token",
|
|
93
98
|
},
|
|
94
|
-
})
|
|
95
|
-
})
|
|
99
|
+
});
|
|
100
|
+
});
|
|
96
101
|
|
|
97
|
-
test(
|
|
98
|
-
const client = buildClient(buildMockConfig(),
|
|
99
|
-
const query =
|
|
102
|
+
test("uses token from constructor in headers", async () => {
|
|
103
|
+
const client = buildClient(buildMockConfig(), "custom-token-123");
|
|
104
|
+
const query = "query { test }";
|
|
100
105
|
|
|
101
|
-
request.mockResolvedValue({ data: {} })
|
|
106
|
+
request.mockResolvedValue({ data: {} });
|
|
102
107
|
|
|
103
|
-
await client.query(query)
|
|
108
|
+
await client.query(query);
|
|
104
109
|
|
|
105
110
|
expect(request).toHaveBeenCalledWith(
|
|
106
111
|
expect.objectContaining({
|
|
107
112
|
requestHeaders: expect.objectContaining({
|
|
108
|
-
|
|
113
|
+
"x-raketa-identity": "custom-token-123",
|
|
109
114
|
}),
|
|
110
|
-
})
|
|
111
|
-
)
|
|
112
|
-
})
|
|
113
|
-
})
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
114
119
|
|
|
115
|
-
describe(
|
|
116
|
-
test(
|
|
117
|
-
const client = buildClient()
|
|
118
|
-
const consoleSpy = jest.spyOn(console,
|
|
120
|
+
describe("logError", () => {
|
|
121
|
+
test("throws error with message", () => {
|
|
122
|
+
const client = buildClient();
|
|
123
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
119
124
|
|
|
120
|
-
expect(() => client.logError(
|
|
125
|
+
expect(() => client.logError("Something went wrong")).toThrow(
|
|
126
|
+
"Something went wrong",
|
|
127
|
+
);
|
|
121
128
|
|
|
122
|
-
consoleSpy.mockRestore()
|
|
123
|
-
})
|
|
129
|
+
consoleSpy.mockRestore();
|
|
130
|
+
});
|
|
124
131
|
|
|
125
|
-
test(
|
|
126
|
-
const client = buildClient()
|
|
127
|
-
const consoleSpy = jest.spyOn(console,
|
|
128
|
-
const context = { code:
|
|
132
|
+
test("logs context to console when provided", () => {
|
|
133
|
+
const client = buildClient();
|
|
134
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
135
|
+
const context = { code: "ERR_001", details: "Invalid input" };
|
|
129
136
|
|
|
130
|
-
expect(() => client.logError(
|
|
131
|
-
|
|
137
|
+
expect(() => client.logError("Error occurred", context)).toThrow(
|
|
138
|
+
"Error occurred",
|
|
139
|
+
);
|
|
140
|
+
expect(consoleSpy).toHaveBeenCalledWith(context);
|
|
132
141
|
|
|
133
|
-
consoleSpy.mockRestore()
|
|
134
|
-
})
|
|
142
|
+
consoleSpy.mockRestore();
|
|
143
|
+
});
|
|
135
144
|
|
|
136
|
-
test(
|
|
137
|
-
const client = buildClient()
|
|
138
|
-
const consoleSpy = jest.spyOn(console,
|
|
145
|
+
test("does not log when context is not provided", () => {
|
|
146
|
+
const client = buildClient();
|
|
147
|
+
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
139
148
|
|
|
140
|
-
expect(() => client.logError(
|
|
141
|
-
expect(consoleSpy).not.toHaveBeenCalled()
|
|
149
|
+
expect(() => client.logError("Error occurred")).toThrow("Error occurred");
|
|
150
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
142
151
|
|
|
143
|
-
consoleSpy.mockRestore()
|
|
144
|
-
})
|
|
145
|
-
})
|
|
152
|
+
consoleSpy.mockRestore();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
146
155
|
|
|
147
|
-
describe(
|
|
148
|
-
test(
|
|
149
|
-
const client = buildClient()
|
|
150
|
-
const errors = [{ key:
|
|
156
|
+
describe("formatErrors", () => {
|
|
157
|
+
test("formats single error into object", () => {
|
|
158
|
+
const client = buildClient();
|
|
159
|
+
const errors = [{ key: "email", message: "Email is invalid" }];
|
|
151
160
|
|
|
152
|
-
const result = client.formatErrors(errors)
|
|
161
|
+
const result = client.formatErrors(errors);
|
|
153
162
|
|
|
154
163
|
expect(result).toEqual({
|
|
155
|
-
email:
|
|
156
|
-
})
|
|
157
|
-
})
|
|
164
|
+
email: "Email is invalid",
|
|
165
|
+
});
|
|
166
|
+
});
|
|
158
167
|
|
|
159
|
-
test(
|
|
160
|
-
const client = buildClient()
|
|
168
|
+
test("formats multiple errors for different keys", () => {
|
|
169
|
+
const client = buildClient();
|
|
161
170
|
const errors = [
|
|
162
|
-
{ key:
|
|
163
|
-
{ key:
|
|
164
|
-
{ key:
|
|
165
|
-
]
|
|
171
|
+
{ key: "email", message: "Email is required" },
|
|
172
|
+
{ key: "password", message: "Password is too short" },
|
|
173
|
+
{ key: "username", message: "Username is taken" },
|
|
174
|
+
];
|
|
166
175
|
|
|
167
|
-
const result = client.formatErrors(errors)
|
|
176
|
+
const result = client.formatErrors(errors);
|
|
168
177
|
|
|
169
178
|
expect(result).toEqual({
|
|
170
|
-
email:
|
|
171
|
-
password:
|
|
172
|
-
username:
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
test(
|
|
177
|
-
const client = buildClient()
|
|
179
|
+
email: "Email is required",
|
|
180
|
+
password: "Password is too short",
|
|
181
|
+
username: "Username is taken",
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("concatenates multiple messages for same key", () => {
|
|
186
|
+
const client = buildClient();
|
|
178
187
|
const errors = [
|
|
179
|
-
{ key:
|
|
180
|
-
{ key:
|
|
181
|
-
{ key:
|
|
182
|
-
]
|
|
188
|
+
{ key: "email", message: "Email is required" },
|
|
189
|
+
{ key: "email", message: "Email format is invalid" },
|
|
190
|
+
{ key: "password", message: "Password is required" },
|
|
191
|
+
];
|
|
183
192
|
|
|
184
|
-
const result = client.formatErrors(errors)
|
|
193
|
+
const result = client.formatErrors(errors);
|
|
185
194
|
|
|
186
195
|
expect(result).toEqual({
|
|
187
|
-
email:
|
|
188
|
-
password:
|
|
189
|
-
})
|
|
190
|
-
})
|
|
196
|
+
email: "Email is required, Email format is invalid",
|
|
197
|
+
password: "Password is required",
|
|
198
|
+
});
|
|
199
|
+
});
|
|
191
200
|
|
|
192
|
-
test(
|
|
193
|
-
const client = buildClient()
|
|
201
|
+
test("remaps generalError to specified key", () => {
|
|
202
|
+
const client = buildClient();
|
|
194
203
|
const errors = [
|
|
195
|
-
{ key:
|
|
196
|
-
{ key:
|
|
197
|
-
]
|
|
204
|
+
{ key: "generalError", message: "Something went wrong" },
|
|
205
|
+
{ key: "email", message: "Email is invalid" },
|
|
206
|
+
];
|
|
198
207
|
|
|
199
|
-
const result = client.formatErrors(errors,
|
|
208
|
+
const result = client.formatErrors(errors, "base");
|
|
200
209
|
|
|
201
210
|
expect(result).toEqual({
|
|
202
|
-
base:
|
|
203
|
-
email:
|
|
204
|
-
})
|
|
205
|
-
})
|
|
211
|
+
base: "Something went wrong",
|
|
212
|
+
email: "Email is invalid",
|
|
213
|
+
});
|
|
214
|
+
});
|
|
206
215
|
|
|
207
|
-
test(
|
|
208
|
-
const client = buildClient()
|
|
209
|
-
const errors = [{ key:
|
|
216
|
+
test("keeps generalError key when no remap specified", () => {
|
|
217
|
+
const client = buildClient();
|
|
218
|
+
const errors = [{ key: "generalError", message: "Operation failed" }];
|
|
210
219
|
|
|
211
|
-
const result = client.formatErrors(errors)
|
|
220
|
+
const result = client.formatErrors(errors);
|
|
212
221
|
|
|
213
222
|
expect(result).toEqual({
|
|
214
|
-
generalError:
|
|
215
|
-
})
|
|
216
|
-
})
|
|
223
|
+
generalError: "Operation failed",
|
|
224
|
+
});
|
|
225
|
+
});
|
|
217
226
|
|
|
218
|
-
test(
|
|
219
|
-
const client = buildClient()
|
|
220
|
-
const result = client.formatErrors([])
|
|
227
|
+
test("handles empty errors array", () => {
|
|
228
|
+
const client = buildClient();
|
|
229
|
+
const result = client.formatErrors([]);
|
|
221
230
|
|
|
222
|
-
expect(result).toEqual({})
|
|
223
|
-
})
|
|
224
|
-
})
|
|
231
|
+
expect(result).toEqual({});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
225
234
|
|
|
226
|
-
describe(
|
|
227
|
-
test(
|
|
228
|
-
const client = buildClient()
|
|
235
|
+
describe("buildFiltersInput", () => {
|
|
236
|
+
test("extracts filters with f: prefix", () => {
|
|
237
|
+
const client = buildClient();
|
|
229
238
|
const query = {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
page:
|
|
233
|
-
order_by:
|
|
234
|
-
}
|
|
239
|
+
"f:status": "active",
|
|
240
|
+
"f:category": "news",
|
|
241
|
+
page: "2",
|
|
242
|
+
order_by: "created_at",
|
|
243
|
+
};
|
|
235
244
|
|
|
236
|
-
const result = client.buildFiltersInput(query)
|
|
245
|
+
const result = client.buildFiltersInput(query);
|
|
237
246
|
|
|
238
247
|
expect(result).toEqual({
|
|
239
|
-
status:
|
|
240
|
-
category:
|
|
241
|
-
})
|
|
242
|
-
})
|
|
248
|
+
status: "active",
|
|
249
|
+
category: "news",
|
|
250
|
+
});
|
|
251
|
+
});
|
|
243
252
|
|
|
244
|
-
test(
|
|
245
|
-
const client = buildClient()
|
|
253
|
+
test("ignores filters with empty values", () => {
|
|
254
|
+
const client = buildClient();
|
|
246
255
|
const query = {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
256
|
+
"f:status": "active",
|
|
257
|
+
"f:category": "",
|
|
258
|
+
"f:tag": null,
|
|
259
|
+
"f:author": undefined,
|
|
260
|
+
};
|
|
252
261
|
|
|
253
|
-
const result = client.buildFiltersInput(query)
|
|
262
|
+
const result = client.buildFiltersInput(query);
|
|
254
263
|
|
|
255
264
|
expect(result).toEqual({
|
|
256
|
-
status:
|
|
257
|
-
})
|
|
258
|
-
})
|
|
265
|
+
status: "active",
|
|
266
|
+
});
|
|
267
|
+
});
|
|
259
268
|
|
|
260
|
-
test(
|
|
261
|
-
const client = buildClient()
|
|
269
|
+
test("returns empty object when no filters present", () => {
|
|
270
|
+
const client = buildClient();
|
|
262
271
|
const query = {
|
|
263
|
-
page:
|
|
264
|
-
order_by:
|
|
265
|
-
}
|
|
272
|
+
page: "1",
|
|
273
|
+
order_by: "title",
|
|
274
|
+
};
|
|
266
275
|
|
|
267
|
-
const result = client.buildFiltersInput(query)
|
|
276
|
+
const result = client.buildFiltersInput(query);
|
|
268
277
|
|
|
269
|
-
expect(result).toEqual({})
|
|
270
|
-
})
|
|
278
|
+
expect(result).toEqual({});
|
|
279
|
+
});
|
|
271
280
|
|
|
272
|
-
test(
|
|
273
|
-
const client = buildClient()
|
|
281
|
+
test("returns empty object when query is null or undefined", () => {
|
|
282
|
+
const client = buildClient();
|
|
274
283
|
|
|
275
|
-
expect(client.buildFiltersInput(null)).toEqual({})
|
|
276
|
-
expect(client.buildFiltersInput(undefined)).toEqual({})
|
|
277
|
-
})
|
|
284
|
+
expect(client.buildFiltersInput(null)).toEqual({});
|
|
285
|
+
expect(client.buildFiltersInput(undefined)).toEqual({});
|
|
286
|
+
});
|
|
278
287
|
|
|
279
|
-
test(
|
|
280
|
-
const client = buildClient()
|
|
281
|
-
const query = { page:
|
|
288
|
+
test("returns empty object when only page param exists", () => {
|
|
289
|
+
const client = buildClient();
|
|
290
|
+
const query = { page: "3" };
|
|
282
291
|
|
|
283
|
-
const result = client.buildFiltersInput(query)
|
|
292
|
+
const result = client.buildFiltersInput(query);
|
|
284
293
|
|
|
285
|
-
expect(result).toEqual({})
|
|
286
|
-
})
|
|
287
|
-
})
|
|
294
|
+
expect(result).toEqual({});
|
|
295
|
+
});
|
|
296
|
+
});
|
|
288
297
|
|
|
289
|
-
describe(
|
|
290
|
-
test(
|
|
291
|
-
const client = buildClient()
|
|
298
|
+
describe("buildOrderInput", () => {
|
|
299
|
+
test("builds order object with field and direction", () => {
|
|
300
|
+
const client = buildClient();
|
|
292
301
|
const query = {
|
|
293
|
-
order_by:
|
|
294
|
-
order_direction:
|
|
295
|
-
}
|
|
302
|
+
order_by: "created_at",
|
|
303
|
+
order_direction: "DESC",
|
|
304
|
+
};
|
|
296
305
|
|
|
297
|
-
const result = client.buildOrderInput(query)
|
|
306
|
+
const result = client.buildOrderInput(query);
|
|
298
307
|
|
|
299
308
|
expect(result).toEqual({
|
|
300
|
-
field:
|
|
301
|
-
direction:
|
|
302
|
-
})
|
|
303
|
-
})
|
|
309
|
+
field: "created_at",
|
|
310
|
+
direction: "DESC",
|
|
311
|
+
});
|
|
312
|
+
});
|
|
304
313
|
|
|
305
|
-
test(
|
|
306
|
-
const client = buildClient()
|
|
307
|
-
const query = { order_by:
|
|
314
|
+
test("defaults to ASC when direction not provided", () => {
|
|
315
|
+
const client = buildClient();
|
|
316
|
+
const query = { order_by: "title" };
|
|
308
317
|
|
|
309
|
-
const result = client.buildOrderInput(query)
|
|
318
|
+
const result = client.buildOrderInput(query);
|
|
310
319
|
|
|
311
320
|
expect(result).toEqual({
|
|
312
|
-
field:
|
|
313
|
-
direction:
|
|
314
|
-
})
|
|
315
|
-
})
|
|
321
|
+
field: "title",
|
|
322
|
+
direction: "ASC",
|
|
323
|
+
});
|
|
324
|
+
});
|
|
316
325
|
|
|
317
|
-
test(
|
|
318
|
-
const client = buildClient()
|
|
319
|
-
const query = { order_direction:
|
|
326
|
+
test("returns null when order_by not provided", () => {
|
|
327
|
+
const client = buildClient();
|
|
328
|
+
const query = { order_direction: "DESC" };
|
|
320
329
|
|
|
321
|
-
const result = client.buildOrderInput(query)
|
|
330
|
+
const result = client.buildOrderInput(query);
|
|
322
331
|
|
|
323
|
-
expect(result).toBeNull()
|
|
324
|
-
})
|
|
332
|
+
expect(result).toBeNull();
|
|
333
|
+
});
|
|
325
334
|
|
|
326
|
-
test(
|
|
327
|
-
const client = buildClient()
|
|
328
|
-
const query = { order_by:
|
|
335
|
+
test("returns null when order_by is empty string", () => {
|
|
336
|
+
const client = buildClient();
|
|
337
|
+
const query = { order_by: "" };
|
|
329
338
|
|
|
330
|
-
const result = client.buildOrderInput(query)
|
|
339
|
+
const result = client.buildOrderInput(query);
|
|
331
340
|
|
|
332
|
-
expect(result).toBeNull()
|
|
333
|
-
})
|
|
334
|
-
})
|
|
341
|
+
expect(result).toBeNull();
|
|
342
|
+
});
|
|
343
|
+
});
|
|
335
344
|
|
|
336
|
-
describe(
|
|
337
|
-
test(
|
|
338
|
-
const client = buildClient()
|
|
339
|
-
const query = { page:
|
|
345
|
+
describe("buildPaginationInput", () => {
|
|
346
|
+
test("builds pagination with page number from query", () => {
|
|
347
|
+
const client = buildClient();
|
|
348
|
+
const query = { page: "3" };
|
|
340
349
|
|
|
341
|
-
const result = client.buildPaginationInput(query)
|
|
350
|
+
const result = client.buildPaginationInput(query);
|
|
342
351
|
|
|
343
352
|
expect(result).toEqual({
|
|
344
353
|
page: 3,
|
|
345
354
|
pageSize: 25,
|
|
346
|
-
})
|
|
347
|
-
})
|
|
355
|
+
});
|
|
356
|
+
});
|
|
348
357
|
|
|
349
|
-
test(
|
|
350
|
-
const client = buildClient()
|
|
351
|
-
const query = {}
|
|
358
|
+
test("defaults to page 1 when not provided", () => {
|
|
359
|
+
const client = buildClient();
|
|
360
|
+
const query = {};
|
|
352
361
|
|
|
353
|
-
const result = client.buildPaginationInput(query)
|
|
362
|
+
const result = client.buildPaginationInput(query);
|
|
354
363
|
|
|
355
364
|
expect(result).toEqual({
|
|
356
365
|
page: 1,
|
|
357
366
|
pageSize: 25,
|
|
358
|
-
})
|
|
359
|
-
})
|
|
367
|
+
});
|
|
368
|
+
});
|
|
360
369
|
|
|
361
|
-
test(
|
|
362
|
-
const client = buildClient()
|
|
363
|
-
const query = { page:
|
|
370
|
+
test("parses string page number to integer", () => {
|
|
371
|
+
const client = buildClient();
|
|
372
|
+
const query = { page: "10" };
|
|
364
373
|
|
|
365
|
-
const result = client.buildPaginationInput(query)
|
|
374
|
+
const result = client.buildPaginationInput(query);
|
|
366
375
|
|
|
367
|
-
expect(result.page).toBe(10)
|
|
368
|
-
expect(typeof result.page).toBe(
|
|
369
|
-
})
|
|
376
|
+
expect(result.page).toBe(10);
|
|
377
|
+
expect(typeof result.page).toBe("number");
|
|
378
|
+
});
|
|
370
379
|
|
|
371
|
-
test(
|
|
372
|
-
const client = buildClient()
|
|
380
|
+
test("handles invalid page values by defaulting to 1", () => {
|
|
381
|
+
const client = buildClient();
|
|
373
382
|
|
|
374
|
-
expect(client.buildPaginationInput({ page:
|
|
383
|
+
expect(client.buildPaginationInput({ page: "invalid" })).toEqual({
|
|
375
384
|
page: NaN,
|
|
376
385
|
pageSize: 25,
|
|
377
|
-
})
|
|
378
|
-
})
|
|
379
|
-
})
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|
|
380
389
|
|
|
381
|
-
describe(
|
|
382
|
-
test(
|
|
383
|
-
const client = buildClient()
|
|
390
|
+
describe("buildListInput", () => {
|
|
391
|
+
test("builds complete input with pagination, filters, orderBy, and locale", () => {
|
|
392
|
+
const client = buildClient();
|
|
384
393
|
const query = {
|
|
385
|
-
page:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
order_by:
|
|
389
|
-
order_direction:
|
|
390
|
-
}
|
|
394
|
+
page: "2",
|
|
395
|
+
"f:status": "published",
|
|
396
|
+
"f:category": "tech",
|
|
397
|
+
order_by: "created_at",
|
|
398
|
+
order_direction: "DESC",
|
|
399
|
+
};
|
|
391
400
|
|
|
392
|
-
const result = client.buildListInput(query,
|
|
401
|
+
const result = client.buildListInput(query, "en");
|
|
393
402
|
|
|
394
403
|
expect(result).toEqual({
|
|
395
404
|
input: {
|
|
396
405
|
pagination: { page: 2, pageSize: 25 },
|
|
397
|
-
filters: { status:
|
|
398
|
-
orderBy: { field:
|
|
399
|
-
locale:
|
|
406
|
+
filters: { status: "published", category: "tech" },
|
|
407
|
+
orderBy: { field: "created_at", direction: "DESC" },
|
|
408
|
+
locale: "en",
|
|
400
409
|
},
|
|
401
|
-
})
|
|
402
|
-
})
|
|
410
|
+
});
|
|
411
|
+
});
|
|
403
412
|
|
|
404
|
-
test(
|
|
405
|
-
const client = buildClient()
|
|
406
|
-
const query = { page:
|
|
413
|
+
test("omits orderBy when not provided", () => {
|
|
414
|
+
const client = buildClient();
|
|
415
|
+
const query = { page: "1", "f:status": "active" };
|
|
407
416
|
|
|
408
|
-
const result = client.buildListInput(query)
|
|
417
|
+
const result = client.buildListInput(query);
|
|
409
418
|
|
|
410
419
|
expect(result).toEqual({
|
|
411
420
|
input: {
|
|
412
421
|
pagination: { page: 1, pageSize: 25 },
|
|
413
|
-
filters: { status:
|
|
422
|
+
filters: { status: "active" },
|
|
414
423
|
},
|
|
415
|
-
})
|
|
416
|
-
})
|
|
424
|
+
});
|
|
425
|
+
});
|
|
417
426
|
|
|
418
|
-
test(
|
|
419
|
-
const client = buildClient()
|
|
420
|
-
const query = { page:
|
|
427
|
+
test("omits locale when not provided", () => {
|
|
428
|
+
const client = buildClient();
|
|
429
|
+
const query = { page: "1" };
|
|
421
430
|
|
|
422
|
-
const result = client.buildListInput(query)
|
|
431
|
+
const result = client.buildListInput(query);
|
|
423
432
|
|
|
424
|
-
expect(result.input).not.toHaveProperty(
|
|
425
|
-
})
|
|
433
|
+
expect(result.input).not.toHaveProperty("locale");
|
|
434
|
+
});
|
|
426
435
|
|
|
427
|
-
test(
|
|
428
|
-
const client = buildClient()
|
|
429
|
-
const query = { page:
|
|
436
|
+
test("includes locale when provided", () => {
|
|
437
|
+
const client = buildClient();
|
|
438
|
+
const query = { page: "1" };
|
|
430
439
|
|
|
431
|
-
const result = client.buildListInput(query,
|
|
440
|
+
const result = client.buildListInput(query, "de");
|
|
432
441
|
|
|
433
|
-
expect(result.input.locale).toBe(
|
|
434
|
-
})
|
|
442
|
+
expect(result.input.locale).toBe("de");
|
|
443
|
+
});
|
|
435
444
|
|
|
436
|
-
test(
|
|
437
|
-
const client = buildClient()
|
|
445
|
+
test("handles empty query object", () => {
|
|
446
|
+
const client = buildClient();
|
|
438
447
|
|
|
439
|
-
const result = client.buildListInput({})
|
|
448
|
+
const result = client.buildListInput({});
|
|
440
449
|
|
|
441
450
|
expect(result).toEqual({
|
|
442
451
|
input: {
|
|
443
452
|
pagination: { page: 1, pageSize: 25 },
|
|
444
453
|
filters: {},
|
|
445
454
|
},
|
|
446
|
-
})
|
|
447
|
-
})
|
|
448
|
-
})
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
});
|
|
449
458
|
|
|
450
|
-
describe(
|
|
451
|
-
test(
|
|
452
|
-
const client = buildClient()
|
|
459
|
+
describe("buildAllInput", () => {
|
|
460
|
+
test("builds input without pagination", () => {
|
|
461
|
+
const client = buildClient();
|
|
453
462
|
const query = {
|
|
454
|
-
page:
|
|
455
|
-
|
|
456
|
-
order_by:
|
|
457
|
-
order_direction:
|
|
458
|
-
}
|
|
463
|
+
page: "2",
|
|
464
|
+
"f:status": "active",
|
|
465
|
+
order_by: "title",
|
|
466
|
+
order_direction: "ASC",
|
|
467
|
+
};
|
|
459
468
|
|
|
460
|
-
const result = client.buildAllInput(query,
|
|
469
|
+
const result = client.buildAllInput(query, "fr");
|
|
461
470
|
|
|
462
471
|
expect(result).toEqual({
|
|
463
472
|
input: {
|
|
464
|
-
filters: { status:
|
|
465
|
-
orderBy: { field:
|
|
466
|
-
locale:
|
|
473
|
+
filters: { status: "active" },
|
|
474
|
+
orderBy: { field: "title", direction: "ASC" },
|
|
475
|
+
locale: "fr",
|
|
467
476
|
},
|
|
468
|
-
})
|
|
469
|
-
expect(result.input).not.toHaveProperty(
|
|
470
|
-
})
|
|
477
|
+
});
|
|
478
|
+
expect(result.input).not.toHaveProperty("pagination");
|
|
479
|
+
});
|
|
471
480
|
|
|
472
|
-
test(
|
|
473
|
-
const client = buildClient()
|
|
474
|
-
const query = {
|
|
481
|
+
test("omits orderBy when not provided", () => {
|
|
482
|
+
const client = buildClient();
|
|
483
|
+
const query = { "f:category": "news" };
|
|
475
484
|
|
|
476
|
-
const result = client.buildAllInput(query)
|
|
485
|
+
const result = client.buildAllInput(query);
|
|
477
486
|
|
|
478
487
|
expect(result).toEqual({
|
|
479
488
|
input: {
|
|
480
|
-
filters: { category:
|
|
489
|
+
filters: { category: "news" },
|
|
481
490
|
},
|
|
482
|
-
})
|
|
483
|
-
})
|
|
491
|
+
});
|
|
492
|
+
});
|
|
484
493
|
|
|
485
|
-
test(
|
|
486
|
-
const client = buildClient()
|
|
487
|
-
const query = {}
|
|
494
|
+
test("omits locale when not provided", () => {
|
|
495
|
+
const client = buildClient();
|
|
496
|
+
const query = {};
|
|
488
497
|
|
|
489
|
-
const result = client.buildAllInput(query)
|
|
498
|
+
const result = client.buildAllInput(query);
|
|
490
499
|
|
|
491
|
-
expect(result.input).not.toHaveProperty(
|
|
492
|
-
})
|
|
493
|
-
})
|
|
500
|
+
expect(result.input).not.toHaveProperty("locale");
|
|
501
|
+
});
|
|
502
|
+
});
|
|
494
503
|
|
|
495
|
-
describe(
|
|
496
|
-
test(
|
|
497
|
-
const client = buildClient()
|
|
498
|
-
const target = { a: 1, b: 2 }
|
|
499
|
-
const source = { b: 3, c: 4 }
|
|
504
|
+
describe("deepMerge", () => {
|
|
505
|
+
test("merges two simple objects", () => {
|
|
506
|
+
const client = buildClient();
|
|
507
|
+
const target = { a: 1, b: 2 };
|
|
508
|
+
const source = { b: 3, c: 4 };
|
|
500
509
|
|
|
501
|
-
const result = client.deepMerge(target, source)
|
|
510
|
+
const result = client.deepMerge(target, source);
|
|
502
511
|
|
|
503
|
-
expect(result).toEqual({ a: 1, b: 3, c: 4 })
|
|
512
|
+
expect(result).toEqual({ a: 1, b: 3, c: 4 });
|
|
504
513
|
// Verify original is not modified
|
|
505
|
-
expect(target).toEqual({ a: 1, b: 2 })
|
|
506
|
-
})
|
|
514
|
+
expect(target).toEqual({ a: 1, b: 2 });
|
|
515
|
+
});
|
|
507
516
|
|
|
508
|
-
test(
|
|
509
|
-
const client = buildClient()
|
|
517
|
+
test("merges nested objects deeply", () => {
|
|
518
|
+
const client = buildClient();
|
|
510
519
|
const target = {
|
|
511
|
-
settings: { theme:
|
|
512
|
-
profile: { name:
|
|
513
|
-
}
|
|
520
|
+
settings: { theme: "dark", locale: "en" },
|
|
521
|
+
profile: { name: "John" },
|
|
522
|
+
};
|
|
514
523
|
const source = {
|
|
515
|
-
settings: { locale:
|
|
524
|
+
settings: { locale: "fr", notifications: true },
|
|
516
525
|
profile: { age: 30 },
|
|
517
|
-
}
|
|
526
|
+
};
|
|
518
527
|
|
|
519
|
-
const result = client.deepMerge(target, source)
|
|
528
|
+
const result = client.deepMerge(target, source);
|
|
520
529
|
|
|
521
530
|
expect(result).toEqual({
|
|
522
|
-
settings: { theme:
|
|
523
|
-
profile: { name:
|
|
524
|
-
})
|
|
525
|
-
})
|
|
531
|
+
settings: { theme: "dark", locale: "fr", notifications: true },
|
|
532
|
+
profile: { name: "John", age: 30 },
|
|
533
|
+
});
|
|
534
|
+
});
|
|
526
535
|
|
|
527
|
-
test(
|
|
528
|
-
const client = buildClient()
|
|
529
|
-
const target = { tags: [
|
|
530
|
-
const source = { tags: [
|
|
536
|
+
test("replaces arrays instead of merging", () => {
|
|
537
|
+
const client = buildClient();
|
|
538
|
+
const target = { tags: ["javascript", "node"] };
|
|
539
|
+
const source = { tags: ["typescript", "deno"] };
|
|
531
540
|
|
|
532
|
-
const result = client.deepMerge(target, source)
|
|
541
|
+
const result = client.deepMerge(target, source);
|
|
533
542
|
|
|
534
|
-
expect(result).toEqual({ tags: [
|
|
535
|
-
})
|
|
543
|
+
expect(result).toEqual({ tags: ["typescript", "deno"] });
|
|
544
|
+
});
|
|
536
545
|
|
|
537
|
-
test(
|
|
538
|
-
const client = buildClient()
|
|
539
|
-
const target = { config: {} }
|
|
540
|
-
const source = { config: '{"enabled": true, "count": 5}' }
|
|
546
|
+
test("parses JSON strings in source", () => {
|
|
547
|
+
const client = buildClient();
|
|
548
|
+
const target = { config: {} };
|
|
549
|
+
const source = { config: '{"enabled": true, "count": 5}' };
|
|
541
550
|
|
|
542
|
-
const result = client.deepMerge(target, source)
|
|
551
|
+
const result = client.deepMerge(target, source);
|
|
543
552
|
|
|
544
|
-
expect(result.config).toEqual({ enabled: true, count: 5 })
|
|
545
|
-
})
|
|
553
|
+
expect(result.config).toEqual({ enabled: true, count: 5 });
|
|
554
|
+
});
|
|
546
555
|
|
|
547
|
-
test(
|
|
548
|
-
const client = buildClient()
|
|
549
|
-
const target = { settings: { meta: {} } }
|
|
550
|
-
const source = {
|
|
556
|
+
test("parses JSON strings in nested objects", () => {
|
|
557
|
+
const client = buildClient();
|
|
558
|
+
const target = { settings: { meta: {} } };
|
|
559
|
+
const source = {
|
|
560
|
+
settings: { meta: '{"title": "Test", "tags": ["a", "b"]}' },
|
|
561
|
+
};
|
|
551
562
|
|
|
552
|
-
const result = client.deepMerge(target, source)
|
|
563
|
+
const result = client.deepMerge(target, source);
|
|
553
564
|
|
|
554
|
-
expect(result.settings.meta).toEqual({ title:
|
|
555
|
-
})
|
|
565
|
+
expect(result.settings.meta).toEqual({ title: "Test", tags: ["a", "b"] });
|
|
566
|
+
});
|
|
556
567
|
|
|
557
|
-
test(
|
|
558
|
-
const client = buildClient()
|
|
559
|
-
const target = { a: 1, b: null }
|
|
560
|
-
const source = { b: 2, c: null }
|
|
568
|
+
test("handles null values", () => {
|
|
569
|
+
const client = buildClient();
|
|
570
|
+
const target = { a: 1, b: null };
|
|
571
|
+
const source = { b: 2, c: null };
|
|
561
572
|
|
|
562
|
-
const result = client.deepMerge(target, source)
|
|
573
|
+
const result = client.deepMerge(target, source);
|
|
563
574
|
|
|
564
|
-
expect(result).toEqual({ a: 1, b: 2, c: null })
|
|
565
|
-
})
|
|
575
|
+
expect(result).toEqual({ a: 1, b: 2, c: null });
|
|
576
|
+
});
|
|
566
577
|
|
|
567
|
-
test(
|
|
568
|
-
const client = buildClient()
|
|
569
|
-
const target = { nested: { value:
|
|
570
|
-
const source = { nested: { value:
|
|
578
|
+
test("does not modify target object", () => {
|
|
579
|
+
const client = buildClient();
|
|
580
|
+
const target = { nested: { value: "original" } };
|
|
581
|
+
const source = { nested: { value: "modified" } };
|
|
571
582
|
|
|
572
|
-
client.deepMerge(target, source)
|
|
583
|
+
client.deepMerge(target, source);
|
|
573
584
|
|
|
574
|
-
expect(target.nested.value).toBe(
|
|
575
|
-
})
|
|
585
|
+
expect(target.nested.value).toBe("original");
|
|
586
|
+
});
|
|
576
587
|
|
|
577
|
-
test(
|
|
578
|
-
const client = buildClient()
|
|
588
|
+
test("handles deeply nested JSON strings", () => {
|
|
589
|
+
const client = buildClient();
|
|
579
590
|
const target = {
|
|
580
591
|
data: {
|
|
581
592
|
user: {
|
|
582
593
|
preferences: {},
|
|
583
594
|
},
|
|
584
595
|
},
|
|
585
|
-
}
|
|
596
|
+
};
|
|
586
597
|
const source = {
|
|
587
598
|
data: {
|
|
588
599
|
user: {
|
|
589
600
|
preferences: '{"theme": "dark", "language": "en"}',
|
|
590
601
|
},
|
|
591
602
|
},
|
|
592
|
-
}
|
|
603
|
+
};
|
|
593
604
|
|
|
594
|
-
const result = client.deepMerge(target, source)
|
|
605
|
+
const result = client.deepMerge(target, source);
|
|
595
606
|
|
|
596
607
|
expect(result.data.user.preferences).toEqual({
|
|
597
|
-
theme:
|
|
598
|
-
language:
|
|
599
|
-
})
|
|
600
|
-
})
|
|
601
|
-
|
|
602
|
-
test(
|
|
603
|
-
const client = buildClient()
|
|
604
|
-
const target = { items: [] }
|
|
608
|
+
theme: "dark",
|
|
609
|
+
language: "en",
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("handles array of objects with JSON strings", () => {
|
|
614
|
+
const client = buildClient();
|
|
615
|
+
const target = { items: [] };
|
|
605
616
|
const source = {
|
|
606
|
-
items: [
|
|
607
|
-
|
|
617
|
+
items: [
|
|
618
|
+
{ id: 1, data: '{"active": true}' },
|
|
619
|
+
{ id: 2, data: '{"active": false}' },
|
|
620
|
+
],
|
|
621
|
+
};
|
|
608
622
|
|
|
609
|
-
const result = client.deepMerge(target, source)
|
|
623
|
+
const result = client.deepMerge(target, source);
|
|
610
624
|
|
|
611
|
-
expect(result.items[0].data).toEqual({ active: true })
|
|
612
|
-
expect(result.items[1].data).toEqual({ active: false })
|
|
613
|
-
})
|
|
625
|
+
expect(result.items[0].data).toEqual({ active: true });
|
|
626
|
+
expect(result.items[1].data).toEqual({ active: false });
|
|
627
|
+
});
|
|
614
628
|
|
|
615
|
-
test(
|
|
616
|
-
const client = buildClient()
|
|
617
|
-
const target = { title:
|
|
618
|
-
const source = { title:
|
|
629
|
+
test("preserves non-JSON strings", () => {
|
|
630
|
+
const client = buildClient();
|
|
631
|
+
const target = { title: "old" };
|
|
632
|
+
const source = { title: "new title", description: "just a string" };
|
|
619
633
|
|
|
620
|
-
const result = client.deepMerge(target, source)
|
|
634
|
+
const result = client.deepMerge(target, source);
|
|
621
635
|
|
|
622
636
|
expect(result).toEqual({
|
|
623
|
-
title:
|
|
624
|
-
description:
|
|
625
|
-
})
|
|
626
|
-
})
|
|
627
|
-
|
|
628
|
-
test(
|
|
629
|
-
const client = buildClient()
|
|
630
|
-
|
|
631
|
-
expect(client.deepMerge({}, { a: 1 })).toEqual({ a: 1 })
|
|
632
|
-
expect(client.deepMerge({ a: 1 }, {})).toEqual({ a: 1 })
|
|
633
|
-
expect(client.deepMerge({}, {})).toEqual({})
|
|
634
|
-
})
|
|
635
|
-
})
|
|
636
|
-
})
|
|
637
|
+
title: "new title",
|
|
638
|
+
description: "just a string",
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
test("handles empty objects", () => {
|
|
643
|
+
const client = buildClient();
|
|
644
|
+
|
|
645
|
+
expect(client.deepMerge({}, { a: 1 })).toEqual({ a: 1 });
|
|
646
|
+
expect(client.deepMerge({ a: 1 }, {})).toEqual({ a: 1 });
|
|
647
|
+
expect(client.deepMerge({}, {})).toEqual({});
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
});
|