@saltcorn/server 0.9.6-beta.2 → 0.9.6-beta.20
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/app.js +6 -1
- package/auth/admin.js +55 -53
- package/auth/routes.js +28 -10
- package/auth/testhelp.js +86 -0
- package/help/Field label.tmd +11 -0
- package/help/Field types.tmd +39 -0
- package/help/Ownership field.tmd +76 -0
- package/help/Ownership formula.tmd +75 -0
- package/help/Table roles.tmd +20 -0
- package/help/User groups.tmd +35 -0
- package/load_plugins.js +33 -5
- package/locales/en.json +29 -1
- package/locales/it.json +3 -2
- package/markup/admin.js +1 -0
- package/markup/forms.js +5 -1
- package/package.json +9 -9
- package/public/log_viewer_utils.js +32 -0
- package/public/mermaid.min.js +705 -306
- package/public/saltcorn-builder.css +23 -0
- package/public/saltcorn-common.js +248 -80
- package/public/saltcorn.css +80 -0
- package/public/saltcorn.js +86 -2
- package/restart_watcher.js +1 -0
- package/routes/actions.js +27 -0
- package/routes/admin.js +175 -64
- package/routes/api.js +6 -0
- package/routes/common_lists.js +42 -32
- package/routes/fields.js +70 -42
- package/routes/homepage.js +2 -0
- package/routes/index.js +2 -0
- package/routes/menu.js +69 -4
- package/routes/notifications.js +90 -10
- package/routes/pageedit.js +18 -13
- package/routes/plugins.js +11 -2
- package/routes/registry.js +289 -0
- package/routes/search.js +10 -4
- package/routes/tables.js +51 -27
- package/routes/tenant.js +4 -15
- package/routes/utils.js +25 -8
- package/routes/view.js +1 -1
- package/routes/viewedit.js +11 -7
- package/serve.js +27 -5
- package/tests/edit.test.js +426 -0
- package/tests/fields.test.js +21 -0
- package/tests/filter.test.js +68 -0
- package/tests/page.test.js +2 -2
- package/tests/plugins.test.js +2 -0
- package/tests/sync.test.js +59 -0
- package/wrapper.js +4 -1
package/serve.js
CHANGED
|
@@ -235,6 +235,10 @@ module.exports =
|
|
|
235
235
|
: defaultNCPUs;
|
|
236
236
|
|
|
237
237
|
const letsEncrypt = await getConfig("letsencrypt", false);
|
|
238
|
+
const pruneSessionInterval = +(await getConfig(
|
|
239
|
+
"prune_session_interval",
|
|
240
|
+
900
|
|
241
|
+
));
|
|
238
242
|
const masterState = {
|
|
239
243
|
started: false,
|
|
240
244
|
listeningTo: new Set([]),
|
|
@@ -287,7 +291,11 @@ module.exports =
|
|
|
287
291
|
})
|
|
288
292
|
.ready((glx) => {
|
|
289
293
|
const httpsServer = glx.httpsServer();
|
|
290
|
-
setupSocket(
|
|
294
|
+
setupSocket(
|
|
295
|
+
appargs?.subdomainOffset,
|
|
296
|
+
pruneSessionInterval,
|
|
297
|
+
httpsServer
|
|
298
|
+
);
|
|
291
299
|
httpsServer.setTimeout(timeout * 1000);
|
|
292
300
|
process.on("message", workerDispatchMsg);
|
|
293
301
|
glx.serveApp(app);
|
|
@@ -344,6 +352,10 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
344
352
|
const cert = getState().getConfig("custom_ssl_certificate", "");
|
|
345
353
|
const key = getState().getConfig("custom_ssl_private_key", "");
|
|
346
354
|
const timeout = +getState().getConfig("timeout", 120);
|
|
355
|
+
const pruneSessionInterval = +(await getState().getConfig(
|
|
356
|
+
"prune_session_interval",
|
|
357
|
+
900
|
|
358
|
+
));
|
|
347
359
|
// Server with http on port 80 / https on 443
|
|
348
360
|
// todo resolve hardcode
|
|
349
361
|
if (port === 80 && cert && key) {
|
|
@@ -354,7 +366,12 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
354
366
|
// todo timeout to config
|
|
355
367
|
httpServer.setTimeout(timeout * 1000);
|
|
356
368
|
httpsServer.setTimeout(timeout * 1000);
|
|
357
|
-
setupSocket(
|
|
369
|
+
setupSocket(
|
|
370
|
+
appargs?.subdomainOffset,
|
|
371
|
+
pruneSessionInterval,
|
|
372
|
+
httpServer,
|
|
373
|
+
httpsServer
|
|
374
|
+
);
|
|
358
375
|
httpServer.listen(port, () => {
|
|
359
376
|
console.log("HTTP Server running on port 80");
|
|
360
377
|
});
|
|
@@ -367,7 +384,7 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
367
384
|
// server with http only
|
|
368
385
|
const http = require("http");
|
|
369
386
|
const httpServer = http.createServer(app);
|
|
370
|
-
setupSocket(appargs?.subdomainOffset, httpServer);
|
|
387
|
+
setupSocket(appargs?.subdomainOffset, pruneSessionInterval, httpServer);
|
|
371
388
|
|
|
372
389
|
// todo timeout to config
|
|
373
390
|
// todo refer in doc to httpserver doc
|
|
@@ -384,7 +401,7 @@ const nonGreenlockWorkerSetup = async (appargs, port) => {
|
|
|
384
401
|
*
|
|
385
402
|
* @param {...*} servers
|
|
386
403
|
*/
|
|
387
|
-
const setupSocket = (subdomainOffset, ...servers) => {
|
|
404
|
+
const setupSocket = (subdomainOffset, pruneSessionInterval, ...servers) => {
|
|
388
405
|
// https://socket.io/docs/v4/middlewares/
|
|
389
406
|
const wrap = (middleware) => (socket, next) =>
|
|
390
407
|
middleware(socket.request, {}, next);
|
|
@@ -395,7 +412,7 @@ const setupSocket = (subdomainOffset, ...servers) => {
|
|
|
395
412
|
}
|
|
396
413
|
|
|
397
414
|
const passportInit = passport.initialize();
|
|
398
|
-
const sessionStore = getSessionStore();
|
|
415
|
+
const sessionStore = getSessionStore(pruneSessionInterval);
|
|
399
416
|
const setupNamespace = (namespace) => {
|
|
400
417
|
//io.of(namespace).use(wrap(setTenant));
|
|
401
418
|
io.of(namespace).use(wrap(sessionStore));
|
|
@@ -452,6 +469,11 @@ const setupSocket = (subdomainOffset, ...servers) => {
|
|
|
452
469
|
socketIds.push(socket.id);
|
|
453
470
|
await getState().setConfig("joined_log_socket_ids", [...socketIds]);
|
|
454
471
|
callback({ status: "ok" });
|
|
472
|
+
setTimeout(() => {
|
|
473
|
+
io.of("/")
|
|
474
|
+
.to(`_logs_${tenant}_`)
|
|
475
|
+
.emit("test_conn_msg", { text: "test message" });
|
|
476
|
+
}, 1000);
|
|
455
477
|
}
|
|
456
478
|
} catch (err) {
|
|
457
479
|
getState().log(1, `Socket join_logs stream: ${err.stack}`);
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
const request = require("supertest");
|
|
2
|
+
const getApp = require("../app");
|
|
3
|
+
const { resetToFixtures, load_url_dom } = require("../auth/testhelp");
|
|
4
|
+
const db = require("@saltcorn/data/db");
|
|
5
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
6
|
+
const View = require("@saltcorn/data/models/view");
|
|
7
|
+
const Field = require("@saltcorn/data/models/field");
|
|
8
|
+
const Table = require("@saltcorn/data/models/table");
|
|
9
|
+
const { plugin_with_routes, sleep } = require("@saltcorn/data/tests/mocks");
|
|
10
|
+
|
|
11
|
+
afterAll(db.close);
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
await resetToFixtures();
|
|
14
|
+
const table = Table.findOne("books");
|
|
15
|
+
await table.update({ min_role_read: 100 });
|
|
16
|
+
await Field.create({
|
|
17
|
+
table,
|
|
18
|
+
name: "sequel_to",
|
|
19
|
+
type: "Key to books",
|
|
20
|
+
attributes: { summary_field: "author" },
|
|
21
|
+
});
|
|
22
|
+
await Field.create({
|
|
23
|
+
table,
|
|
24
|
+
label: "pagesp1",
|
|
25
|
+
type: "Integer",
|
|
26
|
+
calculated: true,
|
|
27
|
+
expression: "pages+1",
|
|
28
|
+
});
|
|
29
|
+
await table.insertRow({
|
|
30
|
+
author: "Peter Kropotkin",
|
|
31
|
+
pages: 189,
|
|
32
|
+
publisher: 1,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await table.insertRow({
|
|
36
|
+
author: "Mary Boas",
|
|
37
|
+
pages: 864,
|
|
38
|
+
publisher: 2,
|
|
39
|
+
});
|
|
40
|
+
const ptable = Table.findOne("publisher");
|
|
41
|
+
await ptable.update({ min_role_read: 100 });
|
|
42
|
+
|
|
43
|
+
//await getState().setConfig("log_level", 6);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
jest.setTimeout(30000);
|
|
47
|
+
|
|
48
|
+
const makeJoinSelectView = async ({ name, showIfFormula }) => {
|
|
49
|
+
await View.create({
|
|
50
|
+
viewtemplate: "Edit",
|
|
51
|
+
description: "",
|
|
52
|
+
min_role: 100,
|
|
53
|
+
name,
|
|
54
|
+
table_id: Table.findOne("books")?.id,
|
|
55
|
+
default_render_page: "",
|
|
56
|
+
slug: {},
|
|
57
|
+
attributes: {},
|
|
58
|
+
configuration: {
|
|
59
|
+
layout: {
|
|
60
|
+
above: [
|
|
61
|
+
{
|
|
62
|
+
gx: null,
|
|
63
|
+
gy: null,
|
|
64
|
+
style: {
|
|
65
|
+
"margin-bottom": "1.5rem",
|
|
66
|
+
},
|
|
67
|
+
aligns: ["end", "start"],
|
|
68
|
+
widths: [2, 10],
|
|
69
|
+
besides: [
|
|
70
|
+
{
|
|
71
|
+
font: "",
|
|
72
|
+
type: "blank",
|
|
73
|
+
block: false,
|
|
74
|
+
style: {},
|
|
75
|
+
inline: false,
|
|
76
|
+
contents: "Publisher",
|
|
77
|
+
labelFor: "publisher",
|
|
78
|
+
isFormula: {},
|
|
79
|
+
textStyle: "",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
above: [
|
|
83
|
+
{
|
|
84
|
+
type: "field",
|
|
85
|
+
block: false,
|
|
86
|
+
fieldview: "select",
|
|
87
|
+
textStyle: "",
|
|
88
|
+
field_name: "publisher",
|
|
89
|
+
configuration: {},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "container",
|
|
93
|
+
style: {},
|
|
94
|
+
bgType: "None",
|
|
95
|
+
hAlign: "left",
|
|
96
|
+
margin: [0, 0, 0, 0],
|
|
97
|
+
rotate: 0,
|
|
98
|
+
vAlign: "top",
|
|
99
|
+
bgColor: "#ffffff",
|
|
100
|
+
display: "block",
|
|
101
|
+
padding: [0, 0, 0, 0],
|
|
102
|
+
bgFileId: 0,
|
|
103
|
+
contents: {
|
|
104
|
+
above: [
|
|
105
|
+
{
|
|
106
|
+
font: "",
|
|
107
|
+
icon: "",
|
|
108
|
+
type: "blank",
|
|
109
|
+
block: false,
|
|
110
|
+
style: {},
|
|
111
|
+
inline: false,
|
|
112
|
+
contents: "Warning",
|
|
113
|
+
labelFor: "",
|
|
114
|
+
isFormula: {},
|
|
115
|
+
textStyle: "",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: "join_field",
|
|
119
|
+
block: false,
|
|
120
|
+
fieldview: "as_text",
|
|
121
|
+
textStyle: "",
|
|
122
|
+
join_field: "publisher.name",
|
|
123
|
+
configuration: {},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
imageSize: "contain",
|
|
128
|
+
isFormula: {},
|
|
129
|
+
minHeight: 0,
|
|
130
|
+
textColor: "#ffffff",
|
|
131
|
+
widthUnit: "px",
|
|
132
|
+
heightUnit: "px",
|
|
133
|
+
customClass: "pubwarn",
|
|
134
|
+
htmlElement: "div",
|
|
135
|
+
showForRole: [],
|
|
136
|
+
gradEndColor: "#88ff88",
|
|
137
|
+
setTextColor: false,
|
|
138
|
+
fullPageWidth: false,
|
|
139
|
+
gradDirection: "0",
|
|
140
|
+
minHeightUnit: "px",
|
|
141
|
+
showIfFormula,
|
|
142
|
+
gradStartColor: "#ff8888",
|
|
143
|
+
maxScreenWidth: "",
|
|
144
|
+
minScreenWidth: "",
|
|
145
|
+
show_for_owner: false,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
breakpoints: ["", ""],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
gx: null,
|
|
154
|
+
gy: null,
|
|
155
|
+
style: {
|
|
156
|
+
"margin-bottom": "1.5rem",
|
|
157
|
+
},
|
|
158
|
+
aligns: ["end", "start"],
|
|
159
|
+
widths: [2, 10],
|
|
160
|
+
besides: [
|
|
161
|
+
{
|
|
162
|
+
font: "",
|
|
163
|
+
type: "blank",
|
|
164
|
+
block: false,
|
|
165
|
+
style: {},
|
|
166
|
+
inline: false,
|
|
167
|
+
contents: "sequel_to",
|
|
168
|
+
labelFor: "sequel_to",
|
|
169
|
+
isFormula: {},
|
|
170
|
+
textStyle: "",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
type: "field",
|
|
174
|
+
block: false,
|
|
175
|
+
fieldview: "select",
|
|
176
|
+
textStyle: "",
|
|
177
|
+
field_name: "sequel_to",
|
|
178
|
+
configuration: {
|
|
179
|
+
where: "publisher == $publisher",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
breakpoints: ["", ""],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
gx: null,
|
|
187
|
+
gy: null,
|
|
188
|
+
style: {
|
|
189
|
+
"margin-bottom": "1.5rem",
|
|
190
|
+
},
|
|
191
|
+
aligns: ["end", "start"],
|
|
192
|
+
widths: [2, 10],
|
|
193
|
+
besides: [
|
|
194
|
+
{
|
|
195
|
+
font: "",
|
|
196
|
+
icon: "",
|
|
197
|
+
type: "blank",
|
|
198
|
+
block: false,
|
|
199
|
+
style: {},
|
|
200
|
+
inline: false,
|
|
201
|
+
contents: "Pages",
|
|
202
|
+
labelFor: "sequel_to",
|
|
203
|
+
isFormula: {},
|
|
204
|
+
textStyle: "",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
above: [
|
|
208
|
+
{
|
|
209
|
+
type: "field",
|
|
210
|
+
block: false,
|
|
211
|
+
fieldview: "edit",
|
|
212
|
+
textStyle: "",
|
|
213
|
+
field_name: "pages",
|
|
214
|
+
configuration: {
|
|
215
|
+
where: "publisher == $publisher",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: "field",
|
|
220
|
+
block: false,
|
|
221
|
+
fieldview: "show",
|
|
222
|
+
textStyle: "",
|
|
223
|
+
field_name: "pagesp1",
|
|
224
|
+
configuration: {
|
|
225
|
+
input_type: "text",
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
breakpoints: ["", ""],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
type: "action",
|
|
235
|
+
block: false,
|
|
236
|
+
rndid: "cb94bd",
|
|
237
|
+
nsteps: "",
|
|
238
|
+
minRole: 100,
|
|
239
|
+
isFormula: {},
|
|
240
|
+
action_icon: "",
|
|
241
|
+
action_name: "Save",
|
|
242
|
+
action_size: "",
|
|
243
|
+
action_bgcol: "",
|
|
244
|
+
action_label: "",
|
|
245
|
+
action_style: "btn-primary",
|
|
246
|
+
action_title: "",
|
|
247
|
+
configuration: {},
|
|
248
|
+
step_only_ifs: "",
|
|
249
|
+
action_textcol: "",
|
|
250
|
+
action_bordercol: "",
|
|
251
|
+
step_action_names: "",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
columns: [
|
|
256
|
+
{
|
|
257
|
+
type: "Field",
|
|
258
|
+
block: false,
|
|
259
|
+
fieldview: "select",
|
|
260
|
+
textStyle: "",
|
|
261
|
+
field_name: "publisher",
|
|
262
|
+
configuration: {},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
type: "JoinField",
|
|
266
|
+
block: false,
|
|
267
|
+
fieldview: "as_text",
|
|
268
|
+
textStyle: "",
|
|
269
|
+
join_field: "publisher.name",
|
|
270
|
+
configuration: {},
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
type: "Field",
|
|
274
|
+
block: false,
|
|
275
|
+
fieldview: "select",
|
|
276
|
+
textStyle: "",
|
|
277
|
+
field_name: "sequel_to",
|
|
278
|
+
configuration: {
|
|
279
|
+
where: "publisher == $publisher",
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
type: "Field",
|
|
284
|
+
block: false,
|
|
285
|
+
fieldview: "edit",
|
|
286
|
+
textStyle: "",
|
|
287
|
+
field_name: "pages",
|
|
288
|
+
configuration: {
|
|
289
|
+
where: "publisher == $publisher",
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
type: "Field",
|
|
294
|
+
block: false,
|
|
295
|
+
fieldview: "show",
|
|
296
|
+
textStyle: "",
|
|
297
|
+
field_name: "pagesp1",
|
|
298
|
+
configuration: {
|
|
299
|
+
input_type: "text",
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
type: "Action",
|
|
304
|
+
rndid: "cb94bd",
|
|
305
|
+
nsteps: "",
|
|
306
|
+
minRole: 100,
|
|
307
|
+
isFormula: {},
|
|
308
|
+
action_icon: "",
|
|
309
|
+
action_name: "Save",
|
|
310
|
+
action_size: "",
|
|
311
|
+
action_bgcol: "",
|
|
312
|
+
action_label: "",
|
|
313
|
+
action_style: "btn-primary",
|
|
314
|
+
action_title: "",
|
|
315
|
+
configuration: {},
|
|
316
|
+
step_only_ifs: "",
|
|
317
|
+
action_textcol: "",
|
|
318
|
+
action_bordercol: "",
|
|
319
|
+
step_action_names: "",
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
viewname: "AuthorEditForTest",
|
|
323
|
+
auto_save: false,
|
|
324
|
+
split_paste: false,
|
|
325
|
+
exttable_name: null,
|
|
326
|
+
page_when_done: null,
|
|
327
|
+
view_when_done: "authorlist",
|
|
328
|
+
dest_url_formula: null,
|
|
329
|
+
destination_type: "View",
|
|
330
|
+
formula_destinations: [],
|
|
331
|
+
page_group_when_done: null,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const newEvent = (dom, type) =>
|
|
337
|
+
new dom.window.CustomEvent(type, {
|
|
338
|
+
bubbles: true,
|
|
339
|
+
cancelable: true,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe("JSDOM-E2E edit test", () => {
|
|
343
|
+
it("join select should set dynamic where and show if with joinfield", async () => {
|
|
344
|
+
await makeJoinSelectView({
|
|
345
|
+
name: "AuthorEditForTest",
|
|
346
|
+
showIfFormula: 'publisher?.name == "AK Press"',
|
|
347
|
+
});
|
|
348
|
+
const dom = await load_url_dom("/view/AuthorEditForTest");
|
|
349
|
+
await sleep(1000);
|
|
350
|
+
const pubwarn = dom.window.document.querySelector("div.pubwarn");
|
|
351
|
+
//console.log(dom.serialize());
|
|
352
|
+
expect(pubwarn.style.display).toBe("none");
|
|
353
|
+
|
|
354
|
+
const select_seq = dom.window.document.querySelector(
|
|
355
|
+
"select[name=sequel_to]"
|
|
356
|
+
);
|
|
357
|
+
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
358
|
+
"",
|
|
359
|
+
"Herman Melville",
|
|
360
|
+
]);
|
|
361
|
+
const select = dom.window.document.querySelector("select[name=publisher]");
|
|
362
|
+
select.value = "1";
|
|
363
|
+
select.dispatchEvent(newEvent(dom, "change"));
|
|
364
|
+
|
|
365
|
+
await sleep(1000);
|
|
366
|
+
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
367
|
+
"",
|
|
368
|
+
"Leo Tolstoy",
|
|
369
|
+
"Peter Kropotkin",
|
|
370
|
+
]);
|
|
371
|
+
|
|
372
|
+
expect(pubwarn.style.display).toBe("");
|
|
373
|
+
|
|
374
|
+
const jf = dom.window.document.querySelector(
|
|
375
|
+
"div.pubwarn div[data-source-url]"
|
|
376
|
+
);
|
|
377
|
+
expect(jf.innerHTML).toBe("AK Press");
|
|
378
|
+
});
|
|
379
|
+
it("calculated field", async () => {
|
|
380
|
+
const dom = await load_url_dom("/view/AuthorEditForTest");
|
|
381
|
+
const input = dom.window.document.querySelector("input[name=pages]");
|
|
382
|
+
input.value = "13";
|
|
383
|
+
input.dispatchEvent(newEvent(dom, "change"));
|
|
384
|
+
await sleep(1000);
|
|
385
|
+
const cf = dom.window.document.querySelector(
|
|
386
|
+
`div[data-source-url="/field/show-calculated/books/pagesp1/show?input_type=text"]`
|
|
387
|
+
);
|
|
388
|
+
expect(cf.innerHTML).toBe("14");
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("join select should set dynamic where and show if with no joinfield", async () => {
|
|
392
|
+
await makeJoinSelectView({
|
|
393
|
+
name: "AuthorEditForTest1",
|
|
394
|
+
showIfFormula: "publisher == 1",
|
|
395
|
+
});
|
|
396
|
+
const dom = await load_url_dom("/view/AuthorEditForTest1");
|
|
397
|
+
await sleep(1000);
|
|
398
|
+
const pubwarn = dom.window.document.querySelector("div.pubwarn");
|
|
399
|
+
|
|
400
|
+
expect(pubwarn.style.display).toBe("none");
|
|
401
|
+
|
|
402
|
+
const select_seq = dom.window.document.querySelector(
|
|
403
|
+
"select[name=sequel_to]"
|
|
404
|
+
);
|
|
405
|
+
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
406
|
+
"",
|
|
407
|
+
"Herman Melville",
|
|
408
|
+
]);
|
|
409
|
+
const select = dom.window.document.querySelector("select[name=publisher]");
|
|
410
|
+
select.value = "1";
|
|
411
|
+
select.dispatchEvent(newEvent(dom, "change"));
|
|
412
|
+
|
|
413
|
+
await sleep(1000);
|
|
414
|
+
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
415
|
+
"",
|
|
416
|
+
"Leo Tolstoy",
|
|
417
|
+
"Peter Kropotkin",
|
|
418
|
+
]);
|
|
419
|
+
|
|
420
|
+
expect(pubwarn.style.display).toBe("");
|
|
421
|
+
const jf = dom.window.document.querySelector(
|
|
422
|
+
"div.pubwarn div[data-source-url]"
|
|
423
|
+
);
|
|
424
|
+
expect(jf.innerHTML).toBe("AK Press");
|
|
425
|
+
});
|
|
426
|
+
});
|
package/tests/fields.test.js
CHANGED
|
@@ -391,6 +391,27 @@ describe("Field Endpoints", () => {
|
|
|
391
391
|
})
|
|
392
392
|
.expect(toBeTrue((r) => +r.text > 2));
|
|
393
393
|
});
|
|
394
|
+
it("should show calculated field with two single joinfields", async () => {
|
|
395
|
+
const loginCookie = await getAdminLoginCookie();
|
|
396
|
+
const table = Table.findOne({ name: "patients" });
|
|
397
|
+
await Field.create({
|
|
398
|
+
table,
|
|
399
|
+
label: "pagesp12",
|
|
400
|
+
type: "Integer",
|
|
401
|
+
calculated: true,
|
|
402
|
+
stored: true,
|
|
403
|
+
expression: "favbook.pages+1+favbook.id",
|
|
404
|
+
});
|
|
405
|
+
const app = await getApp({ disableCsrf: true });
|
|
406
|
+
|
|
407
|
+
await request(app)
|
|
408
|
+
.post("/field/show-calculated/patients/pagesp12/show")
|
|
409
|
+
.set("Cookie", loginCookie)
|
|
410
|
+
.send({
|
|
411
|
+
id: 1,
|
|
412
|
+
})
|
|
413
|
+
.expect(toBeTrue((r) => +r.text > 2));
|
|
414
|
+
});
|
|
394
415
|
it("should show calculated field with double joinfield", async () => {
|
|
395
416
|
const loginCookie = await getAdminLoginCookie();
|
|
396
417
|
const table = Table.findOne({ name: "readings" });
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const request = require("supertest");
|
|
2
|
+
const getApp = require("../app");
|
|
3
|
+
const { resetToFixtures, load_url_dom } = require("../auth/testhelp");
|
|
4
|
+
const db = require("@saltcorn/data/db");
|
|
5
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
6
|
+
const View = require("@saltcorn/data/models/view");
|
|
7
|
+
const Table = require("@saltcorn/data/models/table");
|
|
8
|
+
const { plugin_with_routes, sleep } = require("@saltcorn/data/tests/mocks");
|
|
9
|
+
|
|
10
|
+
afterAll(db.close);
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
await resetToFixtures();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
jest.setTimeout(30000);
|
|
16
|
+
|
|
17
|
+
describe("JSDOM-E2E filter test", () => {
|
|
18
|
+
it("should load authorlist", async () => {
|
|
19
|
+
const dom = await load_url_dom("/view/authorlist");
|
|
20
|
+
//console.log("dom", dom);
|
|
21
|
+
});
|
|
22
|
+
it("should user filter to change url", async () => {
|
|
23
|
+
await View.create({
|
|
24
|
+
viewtemplate: "Filter",
|
|
25
|
+
description: "",
|
|
26
|
+
min_role: 100,
|
|
27
|
+
name: `authorfilter1`,
|
|
28
|
+
table_id: Table.findOne("books")?.id,
|
|
29
|
+
default_render_page: "",
|
|
30
|
+
slug: {},
|
|
31
|
+
attributes: {},
|
|
32
|
+
configuration: {
|
|
33
|
+
layout: {
|
|
34
|
+
type: "field",
|
|
35
|
+
block: false,
|
|
36
|
+
fieldview: "edit",
|
|
37
|
+
textStyle: "",
|
|
38
|
+
field_name: "author",
|
|
39
|
+
configuration: {},
|
|
40
|
+
},
|
|
41
|
+
columns: [
|
|
42
|
+
{
|
|
43
|
+
type: "Field",
|
|
44
|
+
block: false,
|
|
45
|
+
fieldview: "edit",
|
|
46
|
+
textStyle: "",
|
|
47
|
+
field_name: "author",
|
|
48
|
+
configuration: {},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const dom = await load_url_dom("/view/authorfilter1");
|
|
54
|
+
expect(dom.window.location.href).toBe(
|
|
55
|
+
"http://localhost/view/authorfilter1"
|
|
56
|
+
);
|
|
57
|
+
//console.log(dom.serialize());
|
|
58
|
+
const input = dom.window.document.querySelector("input[name=author]");
|
|
59
|
+
input.value = "Leo";
|
|
60
|
+
input.dispatchEvent(new dom.window.Event("change"));
|
|
61
|
+
await sleep(1000);
|
|
62
|
+
expect(dom.window.location.href).toBe(
|
|
63
|
+
"http://localhost/view/authorfilter1?author=Leo"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
//console.log("dom", dom);
|
|
67
|
+
});
|
|
68
|
+
});
|
package/tests/page.test.js
CHANGED
|
@@ -76,7 +76,7 @@ describe("page create", () => {
|
|
|
76
76
|
await request(app)
|
|
77
77
|
.get("/pageedit/new")
|
|
78
78
|
.set("Cookie", loginCookie)
|
|
79
|
-
.expect(toInclude("A short name that will be in
|
|
79
|
+
.expect(toInclude("A short name that will be in the page URL"));
|
|
80
80
|
});
|
|
81
81
|
it("shows new with html file selector", async () => {
|
|
82
82
|
const app = await getApp({ disableCsrf: true });
|
|
@@ -237,7 +237,7 @@ describe("pageedit", () => {
|
|
|
237
237
|
await request(app)
|
|
238
238
|
.get("/pageedit/edit-properties/a_page")
|
|
239
239
|
.set("Cookie", loginCookie)
|
|
240
|
-
.expect(toInclude("A short name that will be in
|
|
240
|
+
.expect(toInclude("A short name that will be in the page URL"));
|
|
241
241
|
|
|
242
242
|
//TODO full context
|
|
243
243
|
const ctx = encodeURIComponent(JSON.stringify({}));
|
package/tests/plugins.test.js
CHANGED
|
@@ -137,8 +137,10 @@ describe("Plugin dependency resolution and upgrade", () => {
|
|
|
137
137
|
.expect(toRedirect("/plugins"));
|
|
138
138
|
const quill = await Plugin.findOne({ name: "quill-editor" });
|
|
139
139
|
expect(quill.location).toBe("@saltcorn/quill-editor");
|
|
140
|
+
expect(quill.name).toBe("quill-editor");
|
|
140
141
|
const html = await Plugin.findOne({ location: "@saltcorn/html" });
|
|
141
142
|
expect(html.location).toBe("@saltcorn/html");
|
|
143
|
+
expect(html.name).toBe("html");
|
|
142
144
|
const html_type = getState().types.HTML;
|
|
143
145
|
expect(!!html_type.fieldviews.Quill).toBe(true);
|
|
144
146
|
});
|