@raketa-cloud/admin 0.0.88 → 0.0.91

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 (33) hide show
  1. package/jest.config.js +22 -0
  2. package/package.json +2 -2
  3. package/src/cloud/ArticleRevisions.js +33 -5
  4. package/src/cloud/Articles.js +34 -4
  5. package/src/cloud/EventRevisions.js +33 -5
  6. package/src/cloud/Events.js +35 -5
  7. package/src/cloud/PageRevisions.js +31 -3
  8. package/src/cloud/Pages.js +34 -4
  9. package/src/cloud/documents/articlesDocuments.js +44 -3
  10. package/src/cloud/documents/eventsDocuments.js +43 -2
  11. package/src/cloud/documents/pagesDocuments.js +43 -2
  12. package/src/handlers/articles/TranslationsHandler.js +81 -15
  13. package/src/handlers/events/TranslationsHandler.js +80 -14
  14. package/src/handlers/pages/TranslationsHandler.js +84 -18
  15. package/src/helpers/AuthHelper.js +2 -2
  16. package/src/tests/handlers/TagsHandler.test/create.test.js +96 -0
  17. package/src/tests/handlers/TagsHandler.test/create_form.test.js +25 -0
  18. package/src/tests/handlers/TagsHandler.test/destroy.test.js +51 -0
  19. package/src/tests/handlers/TagsHandler.test/edit_form.test.js +60 -0
  20. package/src/tests/handlers/TagsHandler.test/index.test.js +37 -0
  21. package/src/tests/handlers/TagsHandler.test/update.test.js +105 -0
  22. package/src/tests/handlers/articles/TranslationHandler.test/create.test.js +85 -79
  23. package/src/tests/handlers/events/TranslationHandler.test/create.test.js +84 -78
  24. package/src/tests/handlers/pages/TranslationHandler.test/create.test.js +83 -72
  25. package/src/tests/helpers/AuthHelper.test.js +169 -0
  26. package/src/tests/helpers/FormBuildert.test.js +357 -0
  27. package/src/tests/helpers/Inflector.test.js +75 -0
  28. package/src/views/articles/_shared/_revision_toolbar.ejs +9 -9
  29. package/src/views/articles/translations/create_form.ejs +2 -0
  30. package/src/views/events/_shared/_revision_toolbar.ejs +9 -9
  31. package/src/views/events/translations/create_form.ejs +2 -0
  32. package/src/views/pages/_shared/_revision_toolbar.ejs +9 -9
  33. package/src/views/pages/translations/create_form.ejs +3 -1
@@ -0,0 +1,105 @@
1
+ import Tags from "../../../cloud/Tags";
2
+ import TagsHandler from "../../../handlers/TagsHandler";
3
+ import Factory from "../../support/factory";
4
+
5
+ describe("Tags Handler", () => {
6
+ function buildCtx(tag) {
7
+ return {
8
+ client: {
9
+ tags: new Tags(() => ({}), "token"),
10
+ },
11
+ params: { id: tag.id },
12
+ resourceParams: {
13
+ tag: {
14
+ name: "Updated Tag",
15
+ slug: "updated-tag",
16
+ },
17
+ },
18
+ locale: "en",
19
+ };
20
+ }
21
+
22
+ test("update successful", async () => {
23
+ const tag = Factory.build("tag", {
24
+ id: "1",
25
+ name: "Original Tag",
26
+ slug: "original-tag",
27
+ });
28
+ const ctx = buildCtx(tag);
29
+
30
+ const mockedUpdate = jest
31
+ .spyOn(ctx.client.tags, "update")
32
+ .mockImplementation(() => ({
33
+ tag: { ...tag, name: "Updated Tag", slug: "updated-tag" },
34
+ errors: null,
35
+ }));
36
+
37
+ const result = await TagsHandler.update(ctx);
38
+
39
+ expect(mockedUpdate).toHaveBeenCalledWith(
40
+ "1",
41
+ {
42
+ name: "Updated Tag",
43
+ slug: "updated-tag",
44
+ },
45
+ "en"
46
+ );
47
+
48
+ expect(result).toEqual({
49
+ redirect: "/en/tags",
50
+ });
51
+ });
52
+
53
+ test("update failed", async () => {
54
+ const tag = Factory.build("tag", {
55
+ id: "1",
56
+ name: "Original Tag",
57
+ slug: "original-tag",
58
+ });
59
+ const ctx = buildCtx(tag);
60
+
61
+ const errors = [{ key: "name", message: "already taken" }];
62
+
63
+ const mockedUpdate = jest
64
+ .spyOn(ctx.client.tags, "update")
65
+ .mockImplementation(() => ({
66
+ tag: null,
67
+ errors,
68
+ }));
69
+
70
+ const mockedFormatErrors = jest
71
+ .spyOn(ctx.client.tags, "formatErrors")
72
+ .mockImplementation(() => ({
73
+ name: "already taken",
74
+ }));
75
+
76
+ const result = await TagsHandler.update(ctx);
77
+
78
+ expect(mockedUpdate).toHaveBeenCalledWith(
79
+ "1",
80
+ {
81
+ name: "Updated Tag",
82
+ slug: "updated-tag",
83
+ },
84
+ "en"
85
+ );
86
+
87
+ expect(mockedFormatErrors).toHaveBeenCalledWith(errors);
88
+
89
+ expect(result).toEqual({
90
+ assigns: {
91
+ tag: {
92
+ id: "1",
93
+ name: "Updated Tag",
94
+ slug: "updated-tag",
95
+ },
96
+ errors: {
97
+ tag: {
98
+ name: "already taken",
99
+ },
100
+ },
101
+ _translations: [],
102
+ },
103
+ });
104
+ });
105
+ });
@@ -38,12 +38,14 @@ describe("Articles Translation Handler", () => {
38
38
  .mockImplementation(() => article);
39
39
 
40
40
  const translationErrors = [{ key: "title", message: "cannot be blank" }];
41
- const mockTranslate = jest
42
- .spyOn(ctx.client.articles, "translate")
43
- .mockImplementation(() => ({
44
- article: null,
45
- errors: translationErrors,
46
- }));
41
+ const mockTranslateBatch = jest
42
+ .spyOn(ctx.client.articles, "translateBatch")
43
+ .mockImplementation((parentId, locales) =>
44
+ locales.map((locale) => ({
45
+ article: null,
46
+ errors: translationErrors,
47
+ }))
48
+ );
47
49
 
48
50
  const mockFormatErrors = jest
49
51
  .spyOn(ctx.client.articles, "formatErrors")
@@ -52,7 +54,7 @@ describe("Articles Translation Handler", () => {
52
54
  const result = await TranslationsHandler.create(ctx);
53
55
 
54
56
  expect(mockFind).toHaveBeenCalledWith(article.id, "en");
55
- expect(mockTranslate).toHaveBeenCalled();
57
+ expect(mockTranslateBatch).toHaveBeenCalled();
56
58
  expect(mockFormatErrors).toHaveBeenCalledWith(translationErrors);
57
59
 
58
60
  expect(result).toEqual({
@@ -92,35 +94,32 @@ describe("Articles Translation Handler", () => {
92
94
  .spyOn(ctx.client.articles, "find")
93
95
  .mockImplementation(() => article);
94
96
 
95
- const mockTranslate = jest
96
- .spyOn(ctx.client.articles, "translate")
97
- .mockImplementation((parentId, locale) => ({
98
- article: { id: `translated-article-${locale}` },
99
- errors: null,
100
- }));
101
-
102
- const mockCreateRevision = jest
103
- .spyOn(ctx.client.articleRevisions, "create")
104
- .mockImplementation(() => ({ errors: null }));
97
+ const mockTranslateBatch = jest
98
+ .spyOn(ctx.client.articles, "translateBatch")
99
+ .mockImplementation((parentId, locales) =>
100
+ locales.map((locale) => ({
101
+ article: { id: `translated-article-${locale}` },
102
+ errors: null,
103
+ }))
104
+ );
105
+
106
+ const mockCreateRevisionBatch = jest
107
+ .spyOn(ctx.client.articleRevisions, "createBatch")
108
+ .mockImplementation((revisionInputs) =>
109
+ revisionInputs.map(({ locale }) => ({
110
+ revision: { id: `revision-${locale}` },
111
+ errors: null,
112
+ }))
113
+ );
105
114
 
106
115
  const result = await TranslationsHandler.create(ctx);
107
116
 
108
117
  expect(mockFind).toHaveBeenCalledWith(article.id, "en");
109
118
 
110
- expect(mockTranslate).toHaveBeenCalledTimes(2);
111
- expect(mockTranslate).toHaveBeenCalledWith(
112
- article.id,
113
- "fr",
114
- expect.objectContaining({
115
- title: article.title,
116
- tags: [],
117
- visibility: "private",
118
- slug: article.slug,
119
- })
120
- );
121
- expect(mockTranslate).toHaveBeenCalledWith(
119
+ expect(mockTranslateBatch).toHaveBeenCalledTimes(1);
120
+ expect(mockTranslateBatch).toHaveBeenCalledWith(
122
121
  article.id,
123
- "es",
122
+ ["fr", "es"],
124
123
  expect.objectContaining({
125
124
  title: article.title,
126
125
  tags: [],
@@ -129,22 +128,18 @@ describe("Articles Translation Handler", () => {
129
128
  })
130
129
  );
131
130
 
132
- expect(mockCreateRevision).toHaveBeenCalledTimes(2);
133
- expect(mockCreateRevision).toHaveBeenCalledWith(
134
- "translated-article-fr",
135
- "fr"
136
- );
137
- expect(mockCreateRevision).toHaveBeenCalledWith(
138
- "translated-article-es",
139
- "es"
140
- );
131
+ expect(mockCreateRevisionBatch).toHaveBeenCalledTimes(1);
132
+ expect(mockCreateRevisionBatch).toHaveBeenCalledWith([
133
+ { articleId: "translated-article-fr", locale: "fr" },
134
+ { articleId: "translated-article-es", locale: "es" },
135
+ ]);
141
136
 
142
137
  expect(result).toEqual({
143
138
  redirect: `/en/articles/${article.id}/translations?translations=fr:translated-article-fr;es:translated-article-es`,
144
139
  });
145
140
  });
146
141
 
147
- test("create translations fails if any translation fails", async () => {
142
+ test("create translations if any translation fails", async () => {
148
143
  const article = Factory.build("article", {
149
144
  translations: {},
150
145
  categories: [{ id: "some-category-uno" }],
@@ -160,17 +155,22 @@ describe("Articles Translation Handler", () => {
160
155
  .spyOn(ctx.client.articles, "find")
161
156
  .mockImplementation(() => article);
162
157
 
163
- const translationResponses = {
164
- fr: { article: { id: "translated-article-fr" }, errors: null },
165
- es: {
166
- article: null,
167
- errors: [{ key: "title", message: "invalid for spanish" }],
168
- },
169
- };
170
-
171
- const mockTranslate = jest
172
- .spyOn(ctx.client.articles, "translate")
173
- .mockImplementation((parentId, locale) => translationResponses[locale]);
158
+ const mockTranslateBatch = jest
159
+ .spyOn(ctx.client.articles, "translateBatch")
160
+ .mockImplementation((parentId, locales) => [
161
+ {
162
+ article: {
163
+ id: "translated-article-fr",
164
+ slug: "article-revision",
165
+ visibility: "public",
166
+ },
167
+ errors: null,
168
+ },
169
+ {
170
+ article: null,
171
+ errors: [{ key: "title", message: "invalid for spanish" }],
172
+ },
173
+ ]);
174
174
 
175
175
  const mockFormatErrors = jest
176
176
  .spyOn(ctx.client.articles, "formatErrors")
@@ -186,20 +186,9 @@ describe("Articles Translation Handler", () => {
186
186
 
187
187
  expect(mockFind).toHaveBeenCalledWith(article.id, "en");
188
188
 
189
- expect(mockTranslate).toHaveBeenCalledWith(
189
+ expect(mockTranslateBatch).toHaveBeenCalledWith(
190
190
  article.id,
191
- "fr",
192
- expect.objectContaining({
193
- title: article.title,
194
- tags: [],
195
- visibility: "private",
196
- slug: article.slug,
197
- })
198
- );
199
-
200
- expect(mockTranslate).toHaveBeenCalledWith(
201
- article.id,
202
- "es",
191
+ ["fr", "es"],
203
192
  expect.objectContaining({
204
193
  title: article.title,
205
194
  tags: [],
@@ -219,7 +208,13 @@ describe("Articles Translation Handler", () => {
219
208
  widgets: article.widgets,
220
209
  categories: ["some-category-uno"],
221
210
  publishAt: article.publishAt,
222
- translations: {},
211
+ translations: {
212
+ fr: {
213
+ id: "translated-article-fr",
214
+ slug: article.slug,
215
+ visibility: article.visibility,
216
+ },
217
+ },
223
218
  languages: ["fr", "es"],
224
219
  },
225
220
  blueprint: article,
@@ -244,17 +239,23 @@ describe("Articles Translation Handler", () => {
244
239
  .spyOn(ctx.client.articles, "find")
245
240
  .mockImplementation(() => article);
246
241
 
247
- const mockTranslate = jest
248
- .spyOn(ctx.client.articles, "translate")
249
- .mockImplementation(() => ({
250
- article: { id: "translated-article-id" },
251
- errors: null,
252
- }));
242
+ const mockTranslateBatch = jest
243
+ .spyOn(ctx.client.articles, "translateBatch")
244
+ .mockImplementation(() => [
245
+ {
246
+ article: {
247
+ id: "translated-article-fr",
248
+ slug: "article-revision",
249
+ visibility: "public",
250
+ },
251
+ errors: null,
252
+ },
253
+ ]);
253
254
 
254
255
  const revisionErrors = [{ key: "base", message: "something went wrong" }];
255
- const mockCreateRevision = jest
256
- .spyOn(ctx.client.articleRevisions, "create")
257
- .mockImplementation(() => ({ errors: revisionErrors }));
256
+ const mockCreateRevisionBatch = jest
257
+ .spyOn(ctx.client.articleRevisions, "createBatch")
258
+ .mockImplementation(() => [{ errors: revisionErrors }]);
258
259
 
259
260
  const mockFormatErrors = jest
260
261
  .spyOn(ctx.client.articles, "formatErrors")
@@ -262,10 +263,9 @@ describe("Articles Translation Handler", () => {
262
263
 
263
264
  const result = await TranslationsHandler.create(ctx);
264
265
 
265
- expect(mockCreateRevision).toHaveBeenCalledWith(
266
- "translated-article-id",
267
- "fr"
268
- );
266
+ expect(mockCreateRevisionBatch).toHaveBeenCalledWith([
267
+ { articleId: "translated-article-fr", locale: "fr" },
268
+ ]);
269
269
  expect(mockFormatErrors).toHaveBeenCalledWith(revisionErrors);
270
270
 
271
271
  expect(result).toEqual({
@@ -279,7 +279,13 @@ describe("Articles Translation Handler", () => {
279
279
  widgets: article.widgets,
280
280
  categories: ["cat1"],
281
281
  publishAt: article.publishAt,
282
- translations: {},
282
+ translations: {
283
+ fr: {
284
+ id: "translated-article-fr",
285
+ slug: article.slug,
286
+ visibility: article.visibility,
287
+ },
288
+ },
283
289
  languages: ["fr"],
284
290
  },
285
291
  blueprint: article,
@@ -38,12 +38,14 @@ describe("Events Translation Handler", () => {
38
38
  .mockImplementation(() => event);
39
39
 
40
40
  const translationErrors = [{ key: "title", message: "cannot be blank" }];
41
- const mockTranslate = jest
42
- .spyOn(ctx.client.events, "translate")
43
- .mockImplementation(() => ({
44
- event: null,
45
- errors: translationErrors,
46
- }));
41
+ const mockTranslateBatch = jest
42
+ .spyOn(ctx.client.events, "translateBatch")
43
+ .mockImplementation((parentId, locales) =>
44
+ locales.map((locale) => ({
45
+ event: null,
46
+ errors: translationErrors,
47
+ }))
48
+ );
47
49
 
48
50
  const mockFormatErrors = jest
49
51
  .spyOn(ctx.client.events, "formatErrors")
@@ -52,7 +54,7 @@ describe("Events Translation Handler", () => {
52
54
  const result = await TranslationsHandler.create(ctx);
53
55
 
54
56
  expect(mockFind).toHaveBeenCalledWith(event.id, "en");
55
- expect(mockTranslate).toHaveBeenCalled();
57
+ expect(mockTranslateBatch).toHaveBeenCalled();
56
58
  expect(mockFormatErrors).toHaveBeenCalledWith(translationErrors);
57
59
 
58
60
  expect(result).toEqual({
@@ -94,35 +96,32 @@ describe("Events Translation Handler", () => {
94
96
  .spyOn(ctx.client.events, "find")
95
97
  .mockImplementation(() => event);
96
98
 
97
- const mockTranslate = jest
98
- .spyOn(ctx.client.events, "translate")
99
- .mockImplementation((parentId, locale) => ({
100
- event: { id: `translated-event-${locale}` },
101
- errors: null,
102
- }));
103
-
104
- const mockCreateRevision = jest
105
- .spyOn(ctx.client.eventRevisions, "create")
106
- .mockImplementation(() => ({ errors: null }));
99
+ const mockTranslateBatch = jest
100
+ .spyOn(ctx.client.events, "translateBatch")
101
+ .mockImplementation((parentId, locales) =>
102
+ locales.map((locale) => ({
103
+ event: { id: `translated-event-${locale}` },
104
+ errors: null,
105
+ }))
106
+ );
107
+
108
+ const mockCreateRevisionBatch = jest
109
+ .spyOn(ctx.client.eventRevisions, "createBatch")
110
+ .mockImplementation((revisionInputs) =>
111
+ revisionInputs.map(({ locale }) => ({
112
+ revision: { id: `revision-${locale}` },
113
+ errors: null,
114
+ }))
115
+ );
107
116
 
108
117
  const result = await TranslationsHandler.create(ctx);
109
118
 
110
119
  expect(mockFind).toHaveBeenCalledWith(event.id, "en");
111
120
 
112
- expect(mockTranslate).toHaveBeenCalledTimes(2);
113
- expect(mockTranslate).toHaveBeenCalledWith(
114
- event.id,
115
- "fr",
116
- expect.objectContaining({
117
- title: event.title,
118
- tags: [],
119
- visibility: "private",
120
- slug: event.slug,
121
- })
122
- );
123
- expect(mockTranslate).toHaveBeenCalledWith(
121
+ expect(mockTranslateBatch).toHaveBeenCalledTimes(1);
122
+ expect(mockTranslateBatch).toHaveBeenCalledWith(
124
123
  event.id,
125
- "es",
124
+ ["fr", "es"],
126
125
  expect.objectContaining({
127
126
  title: event.title,
128
127
  tags: [],
@@ -131,15 +130,11 @@ describe("Events Translation Handler", () => {
131
130
  })
132
131
  );
133
132
 
134
- expect(mockCreateRevision).toHaveBeenCalledTimes(2);
135
- expect(mockCreateRevision).toHaveBeenCalledWith(
136
- "translated-event-fr",
137
- "fr"
138
- );
139
- expect(mockCreateRevision).toHaveBeenCalledWith(
140
- "translated-event-es",
141
- "es"
142
- );
133
+ expect(mockCreateRevisionBatch).toHaveBeenCalledTimes(1);
134
+ expect(mockCreateRevisionBatch).toHaveBeenCalledWith([
135
+ { eventId: "translated-event-fr", locale: "fr" },
136
+ { eventId: "translated-event-es", locale: "es" },
137
+ ]);
143
138
 
144
139
  expect(result).toEqual({
145
140
  redirect: `/en/events/${event.id}/translations?translations=fr:translated-event-fr;es:translated-event-es`,
@@ -162,17 +157,22 @@ describe("Events Translation Handler", () => {
162
157
  .spyOn(ctx.client.events, "find")
163
158
  .mockImplementation(() => event);
164
159
 
165
- const translationResponses = {
166
- fr: { event: { id: "translated-event-fr" }, errors: null },
167
- es: {
168
- event: null,
169
- errors: [{ key: "title", message: "invalid for spanish" }],
170
- },
171
- };
172
-
173
- const mockTranslate = jest
174
- .spyOn(ctx.client.events, "translate")
175
- .mockImplementation((parentId, locale) => translationResponses[locale]);
160
+ const mockTranslateBatch = jest
161
+ .spyOn(ctx.client.events, "translateBatch")
162
+ .mockImplementation((parentId, locales) => [
163
+ {
164
+ event: {
165
+ id: "translated-event-fr",
166
+ slug: "event-revision",
167
+ visibility: "public",
168
+ },
169
+ errors: null,
170
+ },
171
+ {
172
+ event: null,
173
+ errors: [{ key: "title", message: "invalid for spanish" }],
174
+ },
175
+ ]);
176
176
 
177
177
  const mockFormatErrors = jest
178
178
  .spyOn(ctx.client.events, "formatErrors")
@@ -188,20 +188,9 @@ describe("Events Translation Handler", () => {
188
188
 
189
189
  expect(mockFind).toHaveBeenCalledWith(event.id, "en");
190
190
 
191
- expect(mockTranslate).toHaveBeenCalledWith(
191
+ expect(mockTranslateBatch).toHaveBeenCalledWith(
192
192
  event.id,
193
- "fr",
194
- expect.objectContaining({
195
- title: event.title,
196
- tags: [],
197
- visibility: "private",
198
- slug: event.slug,
199
- })
200
- );
201
-
202
- expect(mockTranslate).toHaveBeenCalledWith(
203
- event.id,
204
- "es",
193
+ ["fr", "es"],
205
194
  expect.objectContaining({
206
195
  title: event.title,
207
196
  tags: [],
@@ -223,7 +212,13 @@ describe("Events Translation Handler", () => {
223
212
  publishAt: event.publishAt,
224
213
  startsAt: event.startsAt,
225
214
  endsAt: event.endsAt,
226
- translations: {},
215
+ translations: {
216
+ fr: {
217
+ id: "translated-event-fr",
218
+ slug: event.slug,
219
+ visibility: event.visibility,
220
+ },
221
+ },
227
222
  languages: ["fr", "es"],
228
223
  },
229
224
  blueprint: event,
@@ -248,17 +243,23 @@ describe("Events Translation Handler", () => {
248
243
  .spyOn(ctx.client.events, "find")
249
244
  .mockImplementation(() => event);
250
245
 
251
- const mockTranslate = jest
252
- .spyOn(ctx.client.events, "translate")
253
- .mockImplementation(() => ({
254
- event: { id: "translated-event-id" },
255
- errors: null,
256
- }));
246
+ const mockTranslateBatch = jest
247
+ .spyOn(ctx.client.events, "translateBatch")
248
+ .mockImplementation(() => [
249
+ {
250
+ event: {
251
+ id: "translated-event-fr",
252
+ slug: "event-revision",
253
+ visibility: "public",
254
+ },
255
+ errors: null,
256
+ },
257
+ ]);
257
258
 
258
259
  const revisionErrors = [{ key: "base", message: "something went wrong" }];
259
- const mockCreateRevision = jest
260
- .spyOn(ctx.client.eventRevisions, "create")
261
- .mockImplementation(() => ({ errors: revisionErrors }));
260
+ const mockCreateRevisionBatch = jest
261
+ .spyOn(ctx.client.eventRevisions, "createBatch")
262
+ .mockImplementation(() => [{ errors: revisionErrors }]);
262
263
 
263
264
  const mockFormatErrors = jest
264
265
  .spyOn(ctx.client.events, "formatErrors")
@@ -266,10 +267,9 @@ describe("Events Translation Handler", () => {
266
267
 
267
268
  const result = await TranslationsHandler.create(ctx);
268
269
 
269
- expect(mockCreateRevision).toHaveBeenCalledWith(
270
- "translated-event-id",
271
- "fr"
272
- );
270
+ expect(mockCreateRevisionBatch).toHaveBeenCalledWith([
271
+ { eventId: "translated-event-fr", locale: "fr" },
272
+ ]);
273
273
  expect(mockFormatErrors).toHaveBeenCalledWith(revisionErrors);
274
274
 
275
275
  expect(result).toEqual({
@@ -285,7 +285,13 @@ describe("Events Translation Handler", () => {
285
285
  publishAt: event.publishAt,
286
286
  startsAt: event.startsAt,
287
287
  endsAt: event.endsAt,
288
- translations: {},
288
+ translations: {
289
+ fr: {
290
+ id: "translated-event-fr",
291
+ slug: event.slug,
292
+ visibility: event.visibility,
293
+ },
294
+ },
289
295
  languages: ["fr"],
290
296
  },
291
297
  blueprint: event,