@saltcorn/server 0.8.7 → 0.8.8-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth/admin.js +20 -16
- package/auth/routes.js +18 -13
- package/locales/en.json +45 -1
- package/locales/ru.json +145 -100
- package/locales/si.json +1197 -0
- package/package.json +8 -8
- package/public/diagram_utils.js +21 -1
- package/public/saltcorn-common.js +40 -19
- package/public/saltcorn.js +11 -5
- package/routes/actions.js +4 -3
- package/routes/admin.js +2 -0
- package/routes/common_lists.js +34 -19
- package/routes/diagram.js +214 -199
- package/routes/fields.js +118 -7
- package/routes/index.js +2 -0
- package/routes/menu.js +16 -9
- package/routes/models.js +492 -0
- package/routes/page.js +10 -6
- package/routes/tables.js +67 -41
- package/routes/view.js +10 -6
- package/routes/viewedit.js +27 -5
- package/tests/view.test.js +217 -0
package/routes/tables.js
CHANGED
|
@@ -11,6 +11,7 @@ const Table = require("@saltcorn/data/models/table");
|
|
|
11
11
|
const File = require("@saltcorn/data/models/file");
|
|
12
12
|
const View = require("@saltcorn/data/models/view");
|
|
13
13
|
const User = require("@saltcorn/data/models/user");
|
|
14
|
+
const Model = require("@saltcorn/data/models/model");
|
|
14
15
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
15
16
|
const {
|
|
16
17
|
mkTable,
|
|
@@ -54,7 +55,7 @@ const {
|
|
|
54
55
|
} = require("@saltcorn/data/models/discovery");
|
|
55
56
|
const { getState } = require("@saltcorn/data/db/state");
|
|
56
57
|
const { cardHeaderTabs } = require("@saltcorn/markup/layout_utils");
|
|
57
|
-
const { tablesList } = require("./common_lists");
|
|
58
|
+
const { tablesList, viewsList } = require("./common_lists");
|
|
58
59
|
const {
|
|
59
60
|
InvalidConfiguration,
|
|
60
61
|
removeAllWhiteSpace,
|
|
@@ -654,7 +655,14 @@ const attribBadges = (f) => {
|
|
|
654
655
|
let s = "";
|
|
655
656
|
if (f.attributes) {
|
|
656
657
|
Object.entries(f.attributes).forEach(([k, v]) => {
|
|
657
|
-
if (
|
|
658
|
+
if (
|
|
659
|
+
[
|
|
660
|
+
"summary_field",
|
|
661
|
+
"on_delete_cascade",
|
|
662
|
+
"on_delete",
|
|
663
|
+
"unique_error_msg",
|
|
664
|
+
].includes(k)
|
|
665
|
+
)
|
|
658
666
|
return;
|
|
659
667
|
if (v || v === 0) s += badge("secondary", k);
|
|
660
668
|
});
|
|
@@ -785,37 +793,10 @@ router.get(
|
|
|
785
793
|
);
|
|
786
794
|
var viewCardContents;
|
|
787
795
|
if (views.length > 0) {
|
|
788
|
-
viewCardContents =
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
key: (r) => link(`/view/${encodeURIComponent(r.name)}`, r.name),
|
|
793
|
-
},
|
|
794
|
-
{ label: req.__("Pattern"), key: "viewtemplate" },
|
|
795
|
-
{
|
|
796
|
-
label: req.__("Configure"),
|
|
797
|
-
key: (r) =>
|
|
798
|
-
link(
|
|
799
|
-
`/viewedit/config/${encodeURIComponent(
|
|
800
|
-
r.name
|
|
801
|
-
)}?on_done_redirect=${encodeURIComponent(
|
|
802
|
-
`table/${table.name}`
|
|
803
|
-
)}`,
|
|
804
|
-
req.__("Configure")
|
|
805
|
-
),
|
|
806
|
-
},
|
|
807
|
-
{
|
|
808
|
-
label: req.__("Delete"),
|
|
809
|
-
key: (r) =>
|
|
810
|
-
post_delete_btn(
|
|
811
|
-
`/viewedit/delete/${encodeURIComponent(r.id)}`,
|
|
812
|
-
req
|
|
813
|
-
),
|
|
814
|
-
},
|
|
815
|
-
],
|
|
816
|
-
views,
|
|
817
|
-
{ hover: true }
|
|
818
|
-
);
|
|
796
|
+
viewCardContents = await viewsList(views, req, {
|
|
797
|
+
on_done_redirect: encodeURIComponent(`table/${table.name}`),
|
|
798
|
+
notable: true,
|
|
799
|
+
});
|
|
819
800
|
} else {
|
|
820
801
|
viewCardContents = div(
|
|
821
802
|
h4(req.__("No views defined")),
|
|
@@ -838,6 +819,33 @@ router.get(
|
|
|
838
819
|
),
|
|
839
820
|
};
|
|
840
821
|
}
|
|
822
|
+
const models = await Model.find({ table_id: table.id });
|
|
823
|
+
const modelCard = div(
|
|
824
|
+
mkTable(
|
|
825
|
+
[
|
|
826
|
+
{
|
|
827
|
+
label: req.__("Name"),
|
|
828
|
+
key: (r) => link(`/models/show/${r.id}`, r.name),
|
|
829
|
+
},
|
|
830
|
+
{ label: req.__("Pattern"), key: "modelpattern" },
|
|
831
|
+
{
|
|
832
|
+
label: req.__("Delete"),
|
|
833
|
+
key: (r) =>
|
|
834
|
+
post_delete_btn(
|
|
835
|
+
`/models/delete/${encodeURIComponent(r.id)}`,
|
|
836
|
+
req
|
|
837
|
+
),
|
|
838
|
+
},
|
|
839
|
+
],
|
|
840
|
+
models
|
|
841
|
+
),
|
|
842
|
+
a(
|
|
843
|
+
{ href: `/models/new/${table.id}`, class: "btn btn-primary" },
|
|
844
|
+
i({ class: "fas fa-plus-square me-1" }),
|
|
845
|
+
req.__("Create model")
|
|
846
|
+
)
|
|
847
|
+
);
|
|
848
|
+
|
|
841
849
|
// Table Data card
|
|
842
850
|
const dataCard = div(
|
|
843
851
|
{ class: "d-flex text-center" },
|
|
@@ -997,6 +1005,15 @@ router.get(
|
|
|
997
1005
|
titleAjaxIndicator: true,
|
|
998
1006
|
contents: renderForm(tblForm, req.csrfToken()),
|
|
999
1007
|
},
|
|
1008
|
+
...(Model.has_templates
|
|
1009
|
+
? [
|
|
1010
|
+
{
|
|
1011
|
+
type: "card",
|
|
1012
|
+
title: req.__("Models"),
|
|
1013
|
+
contents: modelCard,
|
|
1014
|
+
},
|
|
1015
|
+
]
|
|
1016
|
+
: []),
|
|
1000
1017
|
],
|
|
1001
1018
|
});
|
|
1002
1019
|
})
|
|
@@ -1389,11 +1406,19 @@ const constraintForm = (req, table_id, fields, type) => {
|
|
|
1389
1406
|
blurb: req.__(
|
|
1390
1407
|
"Tick the boxes for the fields that should be jointly unique"
|
|
1391
1408
|
),
|
|
1392
|
-
fields:
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1409
|
+
fields: [
|
|
1410
|
+
...fields.map((f) => ({
|
|
1411
|
+
name: f.name,
|
|
1412
|
+
label: f.label,
|
|
1413
|
+
type: "Bool",
|
|
1414
|
+
})),
|
|
1415
|
+
{
|
|
1416
|
+
name: "errormsg",
|
|
1417
|
+
label: "Error message",
|
|
1418
|
+
sublabel: "Shown the user if joint uniqueness is violated",
|
|
1419
|
+
type: "String",
|
|
1420
|
+
},
|
|
1421
|
+
],
|
|
1397
1422
|
});
|
|
1398
1423
|
case "Index":
|
|
1399
1424
|
return new Form({
|
|
@@ -1485,11 +1510,12 @@ router.post(
|
|
|
1485
1510
|
if (form.hasErrors) req.flash("error", req.__("An error occurred"));
|
|
1486
1511
|
else {
|
|
1487
1512
|
let configuration = {};
|
|
1488
|
-
if (type === "Unique")
|
|
1513
|
+
if (type === "Unique") {
|
|
1489
1514
|
configuration.fields = fields
|
|
1490
1515
|
.map((f) => f.name)
|
|
1491
1516
|
.filter((f) => form.values[f]);
|
|
1492
|
-
|
|
1517
|
+
configuration.errormsg = form.values.errormsg;
|
|
1518
|
+
} else configuration = form.values;
|
|
1493
1519
|
await TableConstraint.create({
|
|
1494
1520
|
table_id: table.id,
|
|
1495
1521
|
type,
|
|
@@ -1873,7 +1899,7 @@ const get_provider_workflow = (table, req) => {
|
|
|
1873
1899
|
|
|
1874
1900
|
return {
|
|
1875
1901
|
redirect: `/table/${table.id}`,
|
|
1876
|
-
flash: ["success", `Table ${
|
|
1902
|
+
flash: ["success", `Table ${table.name || ""} saved`],
|
|
1877
1903
|
};
|
|
1878
1904
|
};
|
|
1879
1905
|
return workflow;
|
package/routes/view.js
CHANGED
|
@@ -8,6 +8,7 @@ const Router = require("express-promise-router");
|
|
|
8
8
|
|
|
9
9
|
const View = require("@saltcorn/data/models/view");
|
|
10
10
|
const Table = require("@saltcorn/data/models/table");
|
|
11
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
11
12
|
|
|
12
13
|
const { text, style } = require("@saltcorn/markup/tags");
|
|
13
14
|
const {
|
|
@@ -51,7 +52,7 @@ router.get(
|
|
|
51
52
|
res.redirect("/");
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
const tic =
|
|
55
|
+
const tic = new Date();
|
|
55
56
|
|
|
56
57
|
view.rewrite_query_from_slug(query, req.params);
|
|
57
58
|
if (
|
|
@@ -85,11 +86,14 @@ router.get(
|
|
|
85
86
|
res.set("SaltcornModalSaveIndicator", `true`);
|
|
86
87
|
if (isModal && view.attributes?.popup_link_out)
|
|
87
88
|
res.set("SaltcornModalLinkOut", `true`);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const tock = new Date();
|
|
90
|
+
const ms = tock.getTime() - tic.getTime();
|
|
91
|
+
Trigger.emitEvent("PageLoad", null, req.user, {
|
|
92
|
+
text: req.__("View '%s' was loaded", viewname),
|
|
93
|
+
type: "view",
|
|
94
|
+
name: viewname,
|
|
95
|
+
render_time: ms,
|
|
96
|
+
});
|
|
93
97
|
if (typeof contents === "object" && contents.goto)
|
|
94
98
|
res.redirect(contents.goto);
|
|
95
99
|
else
|
package/routes/viewedit.js
CHANGED
|
@@ -13,7 +13,12 @@ const { p, a, div, script, text, domReady, code, pre, tbody, tr, th, td } =
|
|
|
13
13
|
tags;
|
|
14
14
|
|
|
15
15
|
const { getState } = require("@saltcorn/data/db/state");
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
isAdmin,
|
|
18
|
+
error_catcher,
|
|
19
|
+
addOnDoneRedirect,
|
|
20
|
+
is_relative_url,
|
|
21
|
+
} = require("./utils.js");
|
|
17
22
|
const { setTableRefs, viewsList } = require("./common_lists");
|
|
18
23
|
const Form = require("@saltcorn/data/models/form");
|
|
19
24
|
const Field = require("@saltcorn/data/models/field");
|
|
@@ -675,7 +680,11 @@ router.post(
|
|
|
675
680
|
view.name
|
|
676
681
|
)
|
|
677
682
|
);
|
|
678
|
-
|
|
683
|
+
let redirectTarget =
|
|
684
|
+
req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
|
|
685
|
+
? `/${req.query.on_done_redirect}`
|
|
686
|
+
: "/viewedit";
|
|
687
|
+
res.redirect(redirectTarget);
|
|
679
688
|
})
|
|
680
689
|
);
|
|
681
690
|
|
|
@@ -696,7 +705,11 @@ router.post(
|
|
|
696
705
|
"success",
|
|
697
706
|
req.__("View %s duplicated as %s", view.name, newview.name)
|
|
698
707
|
);
|
|
699
|
-
|
|
708
|
+
let redirectTarget =
|
|
709
|
+
req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
|
|
710
|
+
? `/${req.query.on_done_redirect}`
|
|
711
|
+
: "/viewedit";
|
|
712
|
+
res.redirect(redirectTarget);
|
|
700
713
|
})
|
|
701
714
|
);
|
|
702
715
|
|
|
@@ -713,7 +726,11 @@ router.post(
|
|
|
713
726
|
const { id } = req.params;
|
|
714
727
|
await View.delete({ id });
|
|
715
728
|
req.flash("success", req.__("View deleted"));
|
|
716
|
-
|
|
729
|
+
let redirectTarget =
|
|
730
|
+
req.query.on_done_redirect && is_relative_url(req.query.on_done_redirect)
|
|
731
|
+
? `/${req.query.on_done_redirect}`
|
|
732
|
+
: "/viewedit";
|
|
733
|
+
res.redirect(redirectTarget);
|
|
717
734
|
})
|
|
718
735
|
);
|
|
719
736
|
|
|
@@ -805,7 +822,12 @@ router.post(
|
|
|
805
822
|
: req.__(`Minimum role updated`);
|
|
806
823
|
if (!req.xhr) {
|
|
807
824
|
req.flash("success", message);
|
|
808
|
-
|
|
825
|
+
let redirectTarget =
|
|
826
|
+
req.query.on_done_redirect &&
|
|
827
|
+
is_relative_url(req.query.on_done_redirect)
|
|
828
|
+
? `/${req.query.on_done_redirect}`
|
|
829
|
+
: "/viewedit";
|
|
830
|
+
res.redirect(redirectTarget);
|
|
809
831
|
} else res.json({ okay: true, responseText: message });
|
|
810
832
|
})
|
|
811
833
|
);
|
package/tests/view.test.js
CHANGED
|
@@ -8,11 +8,14 @@ const {
|
|
|
8
8
|
toInclude,
|
|
9
9
|
toNotInclude,
|
|
10
10
|
resetToFixtures,
|
|
11
|
+
respondJsonWith,
|
|
11
12
|
} = require("../auth/testhelp");
|
|
12
13
|
const db = require("@saltcorn/data/db");
|
|
13
14
|
const { getState } = require("@saltcorn/data/db/state");
|
|
14
15
|
const View = require("@saltcorn/data/models/view");
|
|
15
16
|
const Table = require("@saltcorn/data/models/table");
|
|
17
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
18
|
+
const Page = require("@saltcorn/data/models/page");
|
|
16
19
|
|
|
17
20
|
const { plugin_with_routes } = require("@saltcorn/data/tests/mocks");
|
|
18
21
|
|
|
@@ -178,6 +181,220 @@ describe("render view with slug", () => {
|
|
|
178
181
|
});
|
|
179
182
|
});
|
|
180
183
|
|
|
184
|
+
describe("action row_variable", () => {
|
|
185
|
+
const createFilterView = async ({
|
|
186
|
+
configuration,
|
|
187
|
+
rowVariable,
|
|
188
|
+
rndid,
|
|
189
|
+
actionName,
|
|
190
|
+
viewName,
|
|
191
|
+
rowLimit,
|
|
192
|
+
}) => {
|
|
193
|
+
const table = Table.findOne({ name: "books" });
|
|
194
|
+
const filterCfg = {
|
|
195
|
+
layout: {
|
|
196
|
+
type: "action",
|
|
197
|
+
configuration: configuration,
|
|
198
|
+
action_name: actionName,
|
|
199
|
+
action_row_variable: rowVariable,
|
|
200
|
+
action_style: "btn-primary",
|
|
201
|
+
minRole: 10,
|
|
202
|
+
rndid: rndid,
|
|
203
|
+
},
|
|
204
|
+
columns: [
|
|
205
|
+
{
|
|
206
|
+
type: "Action",
|
|
207
|
+
action_name: actionName,
|
|
208
|
+
action_row_variable: rowVariable,
|
|
209
|
+
action_style: "btn-primary",
|
|
210
|
+
minRole: 100,
|
|
211
|
+
configuration: configuration,
|
|
212
|
+
rndid: rndid,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
if (rowLimit) {
|
|
217
|
+
filterCfg.layout.action_row_limit = rowLimit;
|
|
218
|
+
filterCfg.columns[0].action_row_limit = rowLimit;
|
|
219
|
+
}
|
|
220
|
+
await View.create({
|
|
221
|
+
table_id: table.id,
|
|
222
|
+
name: viewName,
|
|
223
|
+
viewtemplate: "Filter",
|
|
224
|
+
configuration: filterCfg,
|
|
225
|
+
min_role: 100,
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
it("run_code_none_row_var", async () => {
|
|
230
|
+
const app = await getApp({ disableCsrf: true });
|
|
231
|
+
const loginCookie = await getAdminLoginCookie();
|
|
232
|
+
await createFilterView({
|
|
233
|
+
configuration: {
|
|
234
|
+
run_where: "Client page",
|
|
235
|
+
code: 'console.log("hello");',
|
|
236
|
+
},
|
|
237
|
+
rowVariable: "none",
|
|
238
|
+
rndid: "q6b06q",
|
|
239
|
+
actionName: "run_js_code",
|
|
240
|
+
viewName: "run_code_none_row_var",
|
|
241
|
+
});
|
|
242
|
+
await request(app)
|
|
243
|
+
.post("/view/run_code_none_row_var/run_action")
|
|
244
|
+
.set("Cookie", loginCookie)
|
|
245
|
+
.send({
|
|
246
|
+
rndid: "q6b06q",
|
|
247
|
+
})
|
|
248
|
+
.set("Content-Type", "application/json")
|
|
249
|
+
.set("Accept", "application/json")
|
|
250
|
+
.expect(
|
|
251
|
+
respondJsonWith(200, (resp) => {
|
|
252
|
+
return (
|
|
253
|
+
resp.success === "ok" && resp.eval_js === 'console.log("hello");'
|
|
254
|
+
);
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
it("insert_row_from_state", async () => {
|
|
259
|
+
const app = await getApp({ disableCsrf: true });
|
|
260
|
+
const loginCookie = await getAdminLoginCookie();
|
|
261
|
+
await createFilterView({
|
|
262
|
+
configuration: {
|
|
263
|
+
table: "books",
|
|
264
|
+
row_expr: `{
|
|
265
|
+
author: row.author,
|
|
266
|
+
pages: row.pages,
|
|
267
|
+
}`,
|
|
268
|
+
},
|
|
269
|
+
rowVariable: "state",
|
|
270
|
+
rndid: "u6b06u",
|
|
271
|
+
actionName: "insert_any_row",
|
|
272
|
+
viewName: "insert_row_from_state",
|
|
273
|
+
});
|
|
274
|
+
await request(app)
|
|
275
|
+
.post(
|
|
276
|
+
"/view/insert_row_from_state/run_action?author=author_from_state&pages=234"
|
|
277
|
+
)
|
|
278
|
+
.set("Cookie", loginCookie)
|
|
279
|
+
.send({
|
|
280
|
+
rndid: "u6b06u",
|
|
281
|
+
})
|
|
282
|
+
.set("Content-Type", "application/json")
|
|
283
|
+
.set("Accept", "application/json")
|
|
284
|
+
.expect(respondJsonWith(200, (resp) => resp.success === "ok"));
|
|
285
|
+
const books = Table.findOne({ name: "books" });
|
|
286
|
+
const actual = await books.getRows({ author: "author_from_state" });
|
|
287
|
+
expect(actual.length).toBe(1);
|
|
288
|
+
});
|
|
289
|
+
it("run_code_eatch_matching", async () => {
|
|
290
|
+
const app = await getApp({ disableCsrf: true });
|
|
291
|
+
const loginCookie = await getAdminLoginCookie();
|
|
292
|
+
await createFilterView({
|
|
293
|
+
configuration: {
|
|
294
|
+
run_where: "Client page",
|
|
295
|
+
code: 'console.log("hello");',
|
|
296
|
+
},
|
|
297
|
+
rowVariable: "each_matching_row",
|
|
298
|
+
rndid: "b6b06b",
|
|
299
|
+
actionName: "run_js_code",
|
|
300
|
+
viewName: "run_code_eatch_matching",
|
|
301
|
+
});
|
|
302
|
+
const testHelper = async (query, resultCount) => {
|
|
303
|
+
await request(app)
|
|
304
|
+
.post(`/view/run_code_eatch_matching/run_action?${query}`)
|
|
305
|
+
.set("Cookie", loginCookie)
|
|
306
|
+
.send({ rndid: "b6b06b" })
|
|
307
|
+
.set("Content-Type", "application/json")
|
|
308
|
+
.set("Accept", "application/json")
|
|
309
|
+
.expect(
|
|
310
|
+
respondJsonWith(200, (resp) => {
|
|
311
|
+
if (resp.success !== "ok") return false;
|
|
312
|
+
return (
|
|
313
|
+
(resultCount === 0 && !resp.eval_js) ||
|
|
314
|
+
(Array.isArray(resp.eval_js) &&
|
|
315
|
+
resp.eval_js.length === resultCount)
|
|
316
|
+
);
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
};
|
|
320
|
+
await testHelper("author=le", 2);
|
|
321
|
+
await testHelper("author=Herman", 1);
|
|
322
|
+
await testHelper("author=Christian", 0);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("insert_row_each_matching", async () => {
|
|
326
|
+
const app = await getApp({ disableCsrf: true });
|
|
327
|
+
const loginCookie = await getAdminLoginCookie();
|
|
328
|
+
await createFilterView({
|
|
329
|
+
configuration: {
|
|
330
|
+
table: "books",
|
|
331
|
+
row_expr: `{
|
|
332
|
+
author: row.author + "_copy",
|
|
333
|
+
pages: row.pages,
|
|
334
|
+
}`,
|
|
335
|
+
},
|
|
336
|
+
rowVariable: "each_matching_row",
|
|
337
|
+
rndid: "a6b06a",
|
|
338
|
+
actionName: "insert_any_row",
|
|
339
|
+
viewName: "insert_row_each_matching",
|
|
340
|
+
});
|
|
341
|
+
const testHelper = async (query) => {
|
|
342
|
+
await request(app)
|
|
343
|
+
.post(`/view/insert_row_each_matching/run_action?${query}`)
|
|
344
|
+
.set("Cookie", loginCookie)
|
|
345
|
+
.send({ rndid: "a6b06a" })
|
|
346
|
+
.set("Content-Type", "application/json")
|
|
347
|
+
.set("Accept", "application/json")
|
|
348
|
+
.expect(
|
|
349
|
+
respondJsonWith(200, (resp) => {
|
|
350
|
+
return resp.success === "ok";
|
|
351
|
+
})
|
|
352
|
+
);
|
|
353
|
+
};
|
|
354
|
+
const books = Table.findOne({ name: "books" });
|
|
355
|
+
let oldLength = (await books.getRows()).length;
|
|
356
|
+
await testHelper("author=le");
|
|
357
|
+
let newLength = (await books.getRows()).length;
|
|
358
|
+
expect(newLength).toBe(oldLength + 2);
|
|
359
|
+
oldLength = newLength;
|
|
360
|
+
await testHelper("author=_copy");
|
|
361
|
+
newLength = (await books.getRows()).length;
|
|
362
|
+
expect(newLength).toBe(oldLength + 2);
|
|
363
|
+
oldLength = newLength;
|
|
364
|
+
await testHelper("author=Christian");
|
|
365
|
+
newLength = (await books.getRows()).length;
|
|
366
|
+
expect(newLength).toBe(oldLength);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("each_matching_row: action_row_limit", async () => {
|
|
370
|
+
const app = await getApp({ disableCsrf: true });
|
|
371
|
+
const loginCookie = await getAdminLoginCookie();
|
|
372
|
+
await createFilterView({
|
|
373
|
+
configuration: {
|
|
374
|
+
run_where: "Client page",
|
|
375
|
+
code: 'console.log("hello");',
|
|
376
|
+
},
|
|
377
|
+
rowVariable: "each_matching_row",
|
|
378
|
+
rndid: "c6b06c",
|
|
379
|
+
actionName: "run_js_code",
|
|
380
|
+
viewName: "author_filter_row_limit",
|
|
381
|
+
rowLimit: 2,
|
|
382
|
+
});
|
|
383
|
+
await request(app)
|
|
384
|
+
.post("/view/author_filter_row_limit/run_action?author=le")
|
|
385
|
+
.set("Cookie", loginCookie)
|
|
386
|
+
.send({ rndid: "c6b06c" })
|
|
387
|
+
.set("Content-Type", "application/json")
|
|
388
|
+
.set("Accept", "application/json")
|
|
389
|
+
.expect(
|
|
390
|
+
respondJsonWith(200, (resp) => {
|
|
391
|
+
if (resp.success !== "ok") return false;
|
|
392
|
+
return Array.isArray(resp.eval_js) && resp.eval_js.length === 2;
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
181
398
|
describe("inbound relations", () => {
|
|
182
399
|
it("view with inbound relation", async () => {
|
|
183
400
|
const app = await getApp({ disableCsrf: true });
|