@saltcorn/server 0.9.4-beta.8 → 0.9.4

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 (184) hide show
  1. package/app.js +16 -1
  2. package/auth/admin.js +19 -3
  3. package/auth/routes.js +16 -4
  4. package/auth/testhelp.js +17 -1
  5. package/load_plugins.js +8 -2
  6. package/locales/en.json +29 -1
  7. package/markup/admin.js +22 -18
  8. package/package.json +10 -9
  9. package/public/dayjslocales/af.js +1 -0
  10. package/public/dayjslocales/am.js +1 -0
  11. package/public/dayjslocales/ar-dz.js +1 -0
  12. package/public/dayjslocales/ar-iq.js +1 -0
  13. package/public/dayjslocales/ar-kw.js +1 -0
  14. package/public/dayjslocales/ar-ly.js +1 -0
  15. package/public/dayjslocales/ar-ma.js +1 -0
  16. package/public/dayjslocales/ar-sa.js +1 -0
  17. package/public/dayjslocales/ar-tn.js +1 -0
  18. package/public/dayjslocales/ar.js +1 -0
  19. package/public/dayjslocales/az.js +1 -0
  20. package/public/dayjslocales/be.js +1 -0
  21. package/public/dayjslocales/bg.js +1 -0
  22. package/public/dayjslocales/bi.js +1 -0
  23. package/public/dayjslocales/bm.js +1 -0
  24. package/public/dayjslocales/bn-bd.js +1 -0
  25. package/public/dayjslocales/bn.js +1 -0
  26. package/public/dayjslocales/bo.js +1 -0
  27. package/public/dayjslocales/br.js +1 -0
  28. package/public/dayjslocales/bs.js +1 -0
  29. package/public/dayjslocales/ca.js +1 -0
  30. package/public/dayjslocales/cs.js +1 -0
  31. package/public/dayjslocales/cv.js +1 -0
  32. package/public/dayjslocales/cy.js +1 -0
  33. package/public/dayjslocales/da.js +1 -0
  34. package/public/dayjslocales/de-at.js +1 -0
  35. package/public/dayjslocales/de-ch.js +1 -0
  36. package/public/dayjslocales/de.js +1 -0
  37. package/public/dayjslocales/dv.js +1 -0
  38. package/public/dayjslocales/el.js +1 -0
  39. package/public/dayjslocales/en-au.js +1 -0
  40. package/public/dayjslocales/en-ca.js +1 -0
  41. package/public/dayjslocales/en-gb.js +1 -0
  42. package/public/dayjslocales/en-ie.js +1 -0
  43. package/public/dayjslocales/en-il.js +1 -0
  44. package/public/dayjslocales/en-in.js +1 -0
  45. package/public/dayjslocales/en-nz.js +1 -0
  46. package/public/dayjslocales/en-sg.js +1 -0
  47. package/public/dayjslocales/en-tt.js +1 -0
  48. package/public/dayjslocales/en.js +1 -0
  49. package/public/dayjslocales/eo.js +1 -0
  50. package/public/dayjslocales/es-do.js +1 -0
  51. package/public/dayjslocales/es-mx.js +1 -0
  52. package/public/dayjslocales/es-pr.js +1 -0
  53. package/public/dayjslocales/es-us.js +1 -0
  54. package/public/dayjslocales/es.js +1 -0
  55. package/public/dayjslocales/et.js +1 -0
  56. package/public/dayjslocales/eu.js +1 -0
  57. package/public/dayjslocales/fa.js +1 -0
  58. package/public/dayjslocales/fi.js +1 -0
  59. package/public/dayjslocales/fo.js +1 -0
  60. package/public/dayjslocales/fr-ca.js +1 -0
  61. package/public/dayjslocales/fr-ch.js +1 -0
  62. package/public/dayjslocales/fr.js +1 -0
  63. package/public/dayjslocales/fy.js +1 -0
  64. package/public/dayjslocales/ga.js +1 -0
  65. package/public/dayjslocales/gd.js +1 -0
  66. package/public/dayjslocales/gl.js +1 -0
  67. package/public/dayjslocales/gom-latn.js +1 -0
  68. package/public/dayjslocales/gu.js +1 -0
  69. package/public/dayjslocales/he.js +1 -0
  70. package/public/dayjslocales/hi.js +1 -0
  71. package/public/dayjslocales/hr.js +1 -0
  72. package/public/dayjslocales/ht.js +1 -0
  73. package/public/dayjslocales/hu.js +1 -0
  74. package/public/dayjslocales/hy-am.js +1 -0
  75. package/public/dayjslocales/id.js +1 -0
  76. package/public/dayjslocales/is.js +1 -0
  77. package/public/dayjslocales/it-ch.js +1 -0
  78. package/public/dayjslocales/it.js +1 -0
  79. package/public/dayjslocales/ja.js +1 -0
  80. package/public/dayjslocales/jv.js +1 -0
  81. package/public/dayjslocales/ka.js +1 -0
  82. package/public/dayjslocales/kk.js +1 -0
  83. package/public/dayjslocales/km.js +1 -0
  84. package/public/dayjslocales/kn.js +1 -0
  85. package/public/dayjslocales/ko.js +1 -0
  86. package/public/dayjslocales/ku.js +1 -0
  87. package/public/dayjslocales/ky.js +1 -0
  88. package/public/dayjslocales/lb.js +1 -0
  89. package/public/dayjslocales/lo.js +1 -0
  90. package/public/dayjslocales/lt.js +1 -0
  91. package/public/dayjslocales/lv.js +1 -0
  92. package/public/dayjslocales/me.js +1 -0
  93. package/public/dayjslocales/mi.js +1 -0
  94. package/public/dayjslocales/mk.js +1 -0
  95. package/public/dayjslocales/ml.js +1 -0
  96. package/public/dayjslocales/mn.js +1 -0
  97. package/public/dayjslocales/mr.js +1 -0
  98. package/public/dayjslocales/ms-my.js +1 -0
  99. package/public/dayjslocales/ms.js +1 -0
  100. package/public/dayjslocales/mt.js +1 -0
  101. package/public/dayjslocales/my.js +1 -0
  102. package/public/dayjslocales/nb.js +1 -0
  103. package/public/dayjslocales/ne.js +1 -0
  104. package/public/dayjslocales/nl-be.js +1 -0
  105. package/public/dayjslocales/nl.js +1 -0
  106. package/public/dayjslocales/nn.js +1 -0
  107. package/public/dayjslocales/oc-lnc.js +1 -0
  108. package/public/dayjslocales/pa-in.js +1 -0
  109. package/public/dayjslocales/pl.js +1 -0
  110. package/public/dayjslocales/pt-br.js +1 -0
  111. package/public/dayjslocales/pt.js +1 -0
  112. package/public/dayjslocales/rn.js +1 -0
  113. package/public/dayjslocales/ro.js +1 -0
  114. package/public/dayjslocales/ru.js +1 -0
  115. package/public/dayjslocales/rw.js +1 -0
  116. package/public/dayjslocales/sd.js +1 -0
  117. package/public/dayjslocales/se.js +1 -0
  118. package/public/dayjslocales/si.js +1 -0
  119. package/public/dayjslocales/sk.js +1 -0
  120. package/public/dayjslocales/sl.js +1 -0
  121. package/public/dayjslocales/sq.js +1 -0
  122. package/public/dayjslocales/sr-cyrl.js +1 -0
  123. package/public/dayjslocales/sr.js +1 -0
  124. package/public/dayjslocales/ss.js +1 -0
  125. package/public/dayjslocales/sv-fi.js +1 -0
  126. package/public/dayjslocales/sv.js +1 -0
  127. package/public/dayjslocales/sw.js +1 -0
  128. package/public/dayjslocales/ta.js +1 -0
  129. package/public/dayjslocales/te.js +1 -0
  130. package/public/dayjslocales/tet.js +1 -0
  131. package/public/dayjslocales/tg.js +1 -0
  132. package/public/dayjslocales/th.js +1 -0
  133. package/public/dayjslocales/tk.js +1 -0
  134. package/public/dayjslocales/tl-ph.js +1 -0
  135. package/public/dayjslocales/tlh.js +1 -0
  136. package/public/dayjslocales/tr.js +1 -0
  137. package/public/dayjslocales/tzl.js +1 -0
  138. package/public/dayjslocales/tzm-latn.js +1 -0
  139. package/public/dayjslocales/tzm.js +1 -0
  140. package/public/dayjslocales/ug-cn.js +1 -0
  141. package/public/dayjslocales/uk.js +1 -0
  142. package/public/dayjslocales/ur.js +1 -0
  143. package/public/dayjslocales/uz-latn.js +1 -0
  144. package/public/dayjslocales/uz.js +1 -0
  145. package/public/dayjslocales/vi.js +1 -0
  146. package/public/dayjslocales/x-pseudo.js +1 -0
  147. package/public/dayjslocales/yo.js +1 -0
  148. package/public/dayjslocales/zh-cn.js +1 -0
  149. package/public/dayjslocales/zh-hk.js +1 -0
  150. package/public/dayjslocales/zh-tw.js +1 -0
  151. package/public/dayjslocales/zh.js +1 -0
  152. package/public/gridedit.js +2 -2
  153. package/public/log_viewer_utils.js +156 -0
  154. package/public/saltcorn-builder.css +43 -2
  155. package/public/saltcorn-common.js +39 -29
  156. package/public/saltcorn.js +29 -8
  157. package/public/tabulator_bootstrap5.min.css +1 -0
  158. package/restart_watcher.js +1 -0
  159. package/routes/actions.js +175 -18
  160. package/routes/admin.js +83 -9
  161. package/routes/common_lists.js +344 -152
  162. package/routes/fields.js +18 -3
  163. package/routes/homepage.js +2 -1
  164. package/routes/page.js +30 -13
  165. package/routes/page_groupedit.js +104 -83
  166. package/routes/pageedit.js +23 -7
  167. package/routes/tables.js +51 -5
  168. package/routes/tag_entries.js +18 -5
  169. package/routes/tags.js +65 -12
  170. package/routes/utils.js +23 -2
  171. package/routes/view.js +12 -1
  172. package/routes/viewedit.js +46 -3
  173. package/serve.js +177 -10
  174. package/tests/admin.test.js +17 -11
  175. package/tests/api.test.js +27 -0
  176. package/tests/fields.test.js +132 -5
  177. package/tests/help.test.js +37 -0
  178. package/tests/page_group.test.js +1 -0
  179. package/tests/plugins.test.js +0 -12
  180. package/tests/table.test.js +1 -5
  181. package/tests/view.test.js +127 -15
  182. package/tests/viewedit.test.js +52 -8
  183. package/wrapper.js +9 -2
  184. package/public/relation_helpers.js +0 -351
@@ -382,13 +382,13 @@ describe("actions", () => {
382
382
  .send("table_id=2")
383
383
  .send("name=mynewaction")
384
384
  .send("when_trigger=Insert")
385
- .expect(toRedirect("/actions/configure/1"));
385
+ .expect(toRedirect("/actions/configure/3"));
386
386
  });
387
387
  it("show edit", async () => {
388
388
  const app = await getApp({ disableCsrf: true });
389
389
  const loginCookie = await getAdminLoginCookie();
390
390
  await request(app)
391
- .get("/actions/edit/1")
391
+ .get("/actions/edit/3")
392
392
  .set("Cookie", loginCookie)
393
393
  .expect(toInclude("Edit trigger"))
394
394
  .expect(toInclude("run_js_code"));
@@ -397,7 +397,7 @@ describe("actions", () => {
397
397
  const app = await getApp({ disableCsrf: true });
398
398
  const loginCookie = await getAdminLoginCookie();
399
399
  await request(app)
400
- .get("/actions/configure/1")
400
+ .get("/actions/configure/3")
401
401
  .set("Cookie", loginCookie)
402
402
  .expect(toInclude("Configure trigger"))
403
403
  .expect(toInclude("Code"));
@@ -406,7 +406,7 @@ describe("actions", () => {
406
406
  const app = await getApp({ disableCsrf: true });
407
407
  const loginCookie = await getAdminLoginCookie();
408
408
  await request(app)
409
- .post("/actions/configure/1")
409
+ .post("/actions/configure/3")
410
410
  .set("Cookie", loginCookie)
411
411
  .send("code=console.log(12345678)")
412
412
  .expect(toRedirect("/actions/"));
@@ -415,7 +415,7 @@ describe("actions", () => {
415
415
  const app = await getApp({ disableCsrf: true });
416
416
  const loginCookie = await getAdminLoginCookie();
417
417
  await request(app)
418
- .get("/actions/testrun/1")
418
+ .get("/actions/testrun/3")
419
419
  .set("Cookie", loginCookie)
420
420
  .expect(toInclude("12345678"));
421
421
  });
@@ -423,7 +423,7 @@ describe("actions", () => {
423
423
  const app = await getApp({ disableCsrf: true });
424
424
  const loginCookie = await getAdminLoginCookie();
425
425
  await request(app)
426
- .post("/actions/configure/1")
426
+ .post("/actions/configure/3")
427
427
  .set("Cookie", loginCookie)
428
428
  .send("code=1")
429
429
  .expect(toRedirect("/actions/"));
@@ -432,7 +432,7 @@ describe("actions", () => {
432
432
  const app = await getApp({ disableCsrf: true });
433
433
  const loginCookie = await getAdminLoginCookie();
434
434
  await request(app)
435
- .get("/actions/testrun/1")
435
+ .get("/actions/testrun/2")
436
436
  .set("Cookie", loginCookie)
437
437
  .expect(toRedirect("/actions/"));
438
438
  });
@@ -446,9 +446,9 @@ describe("actions", () => {
446
446
  .send("action=run_js_code")
447
447
  .send("when_trigger=API+call")
448
448
  .send("min_role=1")
449
- .expect(toRedirect("/actions/configure/2"));
449
+ .expect(toRedirect("/actions/configure/4"));
450
450
  await request(app)
451
- .post("/actions/configure/2")
451
+ .post("/actions/configure/4")
452
452
  .set("Cookie", loginCookie)
453
453
  .send("code=return 27")
454
454
  .expect(toRedirect("/actions/"));
@@ -463,7 +463,7 @@ describe("actions", () => {
463
463
  const app = await getApp({ disableCsrf: true });
464
464
  const loginCookie = await getAdminLoginCookie();
465
465
  await request(app)
466
- .post("/actions/delete/1")
466
+ .post("/actions/delete/3")
467
467
  .set("Cookie", loginCookie)
468
468
  .expect(toRedirect("/actions/"));
469
469
  });
@@ -555,7 +555,7 @@ describe("diagram", () => {
555
555
  });
556
556
 
557
557
  /**
558
- * Diagram tests
558
+ * Tags tests
559
559
  */
560
560
  describe("tags", () => {
561
561
  itShouldRedirectUnauthToLogin("/tag");
@@ -604,6 +604,12 @@ describe("tags", () => {
604
604
  .expect(toRedirect("/tag"));
605
605
  });
606
606
  });
607
+
608
+ describe("server logs viewer", () => {
609
+ itShouldRedirectUnauthToLogin("/admin/dev/logs_viewer");
610
+ itShouldIncludeTextForAdmin("/admin/dev/logs_viewer", "Server logs");
611
+ });
612
+
607
613
  /**
608
614
  * Clear all tests
609
615
  */
package/tests/api.test.js CHANGED
@@ -161,6 +161,33 @@ describe("API read", () => {
161
161
  succeedJsonWith((rows) => rows.length == 2 && +rows[0]._versions === 0)
162
162
  );
163
163
  });
164
+ it("should get distinct authors for public", async () => {
165
+ const app = await getApp({ disableCsrf: true });
166
+ await request(app)
167
+ .get("/api/books/distinct/author")
168
+ .expect(
169
+ succeedJsonWith((vals) => {
170
+ return vals.length == 2 && vals.includes("Herman Melville");
171
+ })
172
+ );
173
+ });
174
+ it("should not allow public access to distinct patients", async () => {
175
+ const app = await getApp({ disableCsrf: true });
176
+ await request(app).get("/api/patients/distinct/name").expect(notAuthorized);
177
+ });
178
+ it("should allow staff access to distinct patients", async () => {
179
+ const loginCookie = await getStaffLoginCookie();
180
+
181
+ const app = await getApp({ disableCsrf: true });
182
+ await request(app)
183
+ .get("/api/patients/distinct/name")
184
+ .set("Cookie", loginCookie)
185
+ .expect(
186
+ succeedJsonWith(
187
+ (rows) => rows.length == 2 && rows.includes("Kirk Douglas")
188
+ )
189
+ );
190
+ });
164
191
  });
165
192
  describe("API post", () => {
166
193
  it("should post books", async () => {
@@ -8,6 +8,7 @@ const {
8
8
  getAdminLoginCookie,
9
9
  itShouldRedirectUnauthToLogin,
10
10
  toInclude,
11
+ toBeTrue,
11
12
  toNotInclude,
12
13
  toRedirect,
13
14
  resetToFixtures,
@@ -364,9 +365,12 @@ describe("Field Endpoints", () => {
364
365
  await request(app)
365
366
  .post("/field/show-calculated/books/pagesp1/show")
366
367
  .set("Cookie", loginCookie)
367
- .expect((r) => +r.body > 2);
368
+ .send({
369
+ id: 1,
370
+ })
371
+ .expect(toBeTrue((r) => +r.text > 500 && +r.text < 1500));
368
372
  });
369
- it("should show calculated with single joinfield", async () => {
373
+ it("should show calculated field with single joinfield", async () => {
370
374
  const loginCookie = await getAdminLoginCookie();
371
375
  const table = Table.findOne({ name: "patients" });
372
376
  await Field.create({
@@ -382,9 +386,12 @@ describe("Field Endpoints", () => {
382
386
  await request(app)
383
387
  .post("/field/show-calculated/patients/pagesp1/show")
384
388
  .set("Cookie", loginCookie)
385
- .expect((r) => +r.body > 2);
389
+ .send({
390
+ id: 1,
391
+ })
392
+ .expect(toBeTrue((r) => +r.text > 2));
386
393
  });
387
- it("should show calculated with double joinfield", async () => {
394
+ it("should show calculated field with double joinfield", async () => {
388
395
  const loginCookie = await getAdminLoginCookie();
389
396
  const table = Table.findOne({ name: "readings" });
390
397
  await Field.create({
@@ -400,7 +407,127 @@ describe("Field Endpoints", () => {
400
407
  await request(app)
401
408
  .post("/field/show-calculated/readings/pagesp1/show")
402
409
  .set("Cookie", loginCookie)
403
- .expect((r) => +r.body > 2);
410
+ .send({
411
+ id: 1,
412
+ })
413
+ .expect(toBeTrue((r) => +r.text > 2));
414
+ });
415
+ it("should show-calculated on join field value", async () => {
416
+ const loginCookie = await getAdminLoginCookie();
417
+
418
+ const app = await getApp({ disableCsrf: true });
419
+
420
+ await request(app)
421
+ .post("/field/show-calculated/books/publisher.name/as_text")
422
+ .set("Cookie", loginCookie)
423
+ .send({
424
+ publisher: 1,
425
+ })
426
+ .expect(toBeTrue((r) => r.text === "AK Press"));
427
+ });
428
+ it("should show-calculated on join field value", async () => {
429
+ const loginCookie = await getAdminLoginCookie();
430
+
431
+ const app = await getApp({ disableCsrf: true });
432
+
433
+ await request(app)
434
+ .post("/field/show-calculated/books/publisher.name/code")
435
+ .set("Cookie", loginCookie)
436
+ .send({
437
+ publisher: 1,
438
+ })
439
+ .expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
440
+ });
441
+ it("should show-calculated on join field value", async () => {
442
+ const loginCookie = await getAdminLoginCookie();
443
+
444
+ const app = await getApp({ disableCsrf: true });
445
+
446
+ await request(app)
447
+ .post("/field/show-calculated/books/publisher.name/code")
448
+ .set("Cookie", loginCookie)
449
+ .send({
450
+ publisher: 1,
451
+ })
452
+ .expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
453
+ });
454
+ it("should show-calculated on join field value", async () => {
455
+ const loginCookie = await getAdminLoginCookie();
456
+
457
+ const app = await getApp({ disableCsrf: true });
458
+
459
+ await request(app)
460
+ .post(
461
+ "/field/show-calculated/books/publisher.name/show_with_html?code=pub%3A%7B%7Bit.toLowerCase()%7D%7D"
462
+ )
463
+ .set("Cookie", loginCookie)
464
+ .send({
465
+ publisher: 1,
466
+ })
467
+ .expect(toBeTrue((r) => r.text === "pub:ak press"));
468
+ });
469
+ it("should show-calculated on double-join field value", async () => {
470
+ const loginCookie = await getAdminLoginCookie();
471
+
472
+ const app = await getApp({ disableCsrf: true });
473
+
474
+ await request(app)
475
+ .post("/field/show-calculated/patients/favbook.publisher.name/as_text")
476
+ .set("Cookie", loginCookie)
477
+ .send({
478
+ favbook: 2,
479
+ })
480
+ .expect(toBeTrue((r) => r.text === "AK Press"));
481
+ });
482
+ it("should not show unauth show-calculated on double-join field value", async () => {
483
+ const app = await getApp({ disableCsrf: true });
484
+
485
+ await request(app)
486
+ .post("/field/show-calculated/patients/favbook.publisher.name/as_text")
487
+
488
+ .send({
489
+ favbook: 2,
490
+ })
491
+ .expect(401);
492
+ });
493
+ it("should preview field", async () => {
494
+ const loginCookie = await getAdminLoginCookie();
495
+
496
+ const app = await getApp({ disableCsrf: true });
497
+
498
+ await request(app)
499
+ .post("/field/preview/books/author/code")
500
+ .set("Cookie", loginCookie)
501
+ .send({})
502
+ .expect(
503
+ toBeTrue((r) => r.text === "<pre><code>Herman Melville</code></pre>")
504
+ );
505
+ });
506
+ it("should preview joinfield", async () => {
507
+ const loginCookie = await getAdminLoginCookie();
508
+
509
+ const app = await getApp({ disableCsrf: true });
510
+
511
+ await request(app)
512
+ .post("/field/preview/books/publisher.name/code")
513
+ .set("Cookie", loginCookie)
514
+ .send({})
515
+ .expect(toBeTrue((r) => r.text === "<pre><code>AK Press</code></pre>"));
516
+ });
517
+ it("should preview joinfield with cfg", async () => {
518
+ const loginCookie = await getAdminLoginCookie();
519
+
520
+ const app = await getApp({ disableCsrf: true });
521
+
522
+ await request(app)
523
+ .post("/field/preview/books/publisher.name/show_with_html")
524
+ .set("Cookie", loginCookie)
525
+ .send({
526
+ configuration: {
527
+ code: "pub:{{it.toLowerCase()}}",
528
+ },
529
+ })
530
+ .expect(toBeTrue((r) => r.text === "pub:ak press"));
404
531
  });
405
532
  });
406
533
 
@@ -0,0 +1,37 @@
1
+ const request = require("supertest");
2
+ const getApp = require("../app");
3
+ const {
4
+ toRedirect,
5
+ getAdminLoginCookie,
6
+ getStaffLoginCookie,
7
+ itShouldRedirectUnauthToLogin,
8
+ toInclude,
9
+ toNotInclude,
10
+ resetToFixtures,
11
+ succeedJsonWith,
12
+ } = require("../auth/testhelp");
13
+ const db = require("@saltcorn/data/db");
14
+ const View = require("@saltcorn/data/models/view");
15
+ const Table = require("@saltcorn/data/models/table");
16
+
17
+ beforeAll(async () => {
18
+ await resetToFixtures();
19
+ });
20
+ afterAll(db.close);
21
+
22
+ jest.setTimeout(30000);
23
+
24
+ describe("Help topics", () => {
25
+ itShouldRedirectUnauthToLogin("/admin/help/View%20patterns");
26
+
27
+ it("should show view patterns help", async () => {
28
+ const loginCookie = await getAdminLoginCookie();
29
+
30
+ const app = await getApp({ disableCsrf: true });
31
+ await request(app)
32
+ .get("/admin/help/View patterns")
33
+ .set("Cookie", loginCookie)
34
+ .expect(toInclude("<title>Help: View patterns</title>"))
35
+ .expect(toInclude("The view pattern is a fundamental concept"));
36
+ });
37
+ });
@@ -73,6 +73,7 @@ describe("edit Page groups", () => {
73
73
  name: nameAfterUpdate,
74
74
  description: null,
75
75
  min_role: 100,
76
+ random_allocation: false,
76
77
  });
77
78
  });
78
79
 
@@ -280,19 +280,7 @@ describe("Pack clash detection", () => {
280
280
  .set("Cookie", loginCookie)
281
281
  .expect(toRedirect("/"));
282
282
  });
283
- it("should install issues", async () => {
284
- const loginCookie = await getAdminLoginCookie();
285
283
 
286
- const app = await getApp({ disableCsrf: true });
287
- await request(app)
288
- .post("/packs/install-named/Blog")
289
- .set("Cookie", loginCookie)
290
- .expect(toRedirect("/plugins"));
291
- await request(app)
292
- .get("/plugins")
293
- .set("Cookie", loginCookie)
294
- .expect(toInclude("Tables already exist: comments"));
295
- });
296
284
  it("should reset again", async () => {
297
285
  await resetToFixtures();
298
286
  });
@@ -254,11 +254,7 @@ Gordon Kane, 218`;
254
254
  await request(app)
255
255
  .get("/table/")
256
256
  .set("Cookie", loginCookie)
257
- .expect(
258
- toInclude(
259
- "cannot drop table books because other objects depend on it"
260
- )
261
- );
257
+ .expect(toInclude("has views. Delete these first"));
262
258
  });
263
259
  });
264
260
  describe("deletion to table with row ownership", () => {
@@ -37,6 +37,18 @@ describe("view list endpoint", () => {
37
37
  describe("nonexisting view", () => {
38
38
  itShouldRedirectUnauthToLogin("/view/patlist", "/");
39
39
  });
40
+ describe("preview view", () => {
41
+ it("should show previewview to auth", async () => {
42
+ const loginCookie = await getAdminLoginCookie();
43
+
44
+ const app = await getApp({ disableCsrf: true });
45
+ await request(app)
46
+ .post("/view/authorlist/preview")
47
+ .set("Cookie", loginCookie)
48
+ .expect(toInclude("Tolstoy"))
49
+ .expect(toNotInclude(">728<"));
50
+ });
51
+ });
40
52
  describe("view patients list endpoint", () => {
41
53
  itShouldRedirectUnauthToLogin("/view/patientlist");
42
54
 
@@ -419,6 +431,10 @@ describe("update matching rows", () => {
419
431
  await field.update({ is_unique: false });
420
432
  });
421
433
 
434
+ afterAll(async () => {
435
+ await resetToFixtures();
436
+ });
437
+
422
438
  it("update matching books normal", async () => {
423
439
  const table = Table.findOne({ name: "books" });
424
440
  await updateMatchingRows({
@@ -755,7 +771,7 @@ describe("relation path to query and state", () => {
755
771
  .expect(toNotInclude("album B"));
756
772
  });
757
773
 
758
- it("OneToOneSHow", async () => {
774
+ it("OneToOneShow", async () => {
759
775
  const app = await getApp({ disableCsrf: true });
760
776
  const loginCookie = await getAdminLoginCookie();
761
777
  await request(app)
@@ -805,7 +821,7 @@ describe("relation path to query and state", () => {
805
821
  .expect(toInclude(`value="artist B"`));
806
822
  });
807
823
 
808
- it("Parent", async () => {
824
+ it("Parent one layer", async () => {
809
825
  const app = await getApp({ disableCsrf: true });
810
826
  const loginCookie = await getAdminLoginCookie();
811
827
  await request(app)
@@ -833,6 +849,28 @@ describe("relation path to query and state", () => {
833
849
  .expect(toInclude("No row selected"));
834
850
  });
835
851
 
852
+ it("Parent two layers", async () => {
853
+ const app = await getApp({ disableCsrf: true });
854
+ const loginCookie = await getAdminLoginCookie();
855
+ await request(app)
856
+ .get(`/view/show_patient_with_publisher?id=2`)
857
+ .set("Cookie", loginCookie)
858
+ // view link
859
+ .expect(toInclude("/view/show_publisher?.patients.favbook.publisher=2"))
860
+ // embedded show
861
+ .expect(toInclude("Michael Douglas"))
862
+ .expect(toInclude("AK Press"));
863
+
864
+ await request(app)
865
+ .get(`/view/show_patient_with_publisher?id=1`)
866
+ .set("Cookie", loginCookie)
867
+ // view link
868
+ .expect(toInclude("/view/show_publisher?.patients.favbook.publisher=1"))
869
+ // embedded show
870
+ .expect(toInclude("Kirk Douglas"))
871
+ .expect(toInclude("No row selected"));
872
+ });
873
+
836
874
  it("RelationPath", async () => {
837
875
  const app = await getApp({ disableCsrf: true });
838
876
  const loginCookie = await getAdminLoginCookie();
@@ -873,11 +911,23 @@ describe("edit-in-edit with relation path and legacy", () => {
873
911
  const app = await getApp({ disableCsrf: true });
874
912
  const loginCookie = await getAdminLoginCookie();
875
913
  await request(app)
876
- .get("/view/edit_department_with_edit_in_edit_legacy?id=1")
914
+ .get("/view/edit_department_with_edit_in_edit_relation_path?id=1")
877
915
  .set("Cookie", loginCookie)
878
916
  .expect(toInclude("add_repeater"));
879
-
880
- // TODO post
917
+ await request(app)
918
+ .post("/view/edit_department_with_edit_in_edit_relation_path?id=1")
919
+ .set("Cookie", loginCookie)
920
+ .send({
921
+ department_0: "1",
922
+ department_1: "1",
923
+ id: "1",
924
+ id_0: "1",
925
+ id_1: "2",
926
+ name: "my_department",
927
+ name_0: "manager",
928
+ name_1: "my_employee",
929
+ })
930
+ .expect(toRedirect("/"));
881
931
  });
882
932
 
883
933
  it("edit-in-edit with relation path two layer", async () => {
@@ -887,10 +937,21 @@ describe("edit-in-edit with relation path and legacy", () => {
887
937
  .get("/view/edit_cover_with_edit_artist_on_album_rel_path?id=1")
888
938
  .set("Cookie", loginCookie)
889
939
  .expect(toInclude("add_repeater"));
890
-
891
- // TODO post
940
+ await request(app)
941
+ .post("/view/edit_cover_with_edit_artist_on_album_rel_path?id=1")
942
+ .set("Cookie", loginCookie)
943
+ .send({
944
+ album_0: "1",
945
+ album_1: "1",
946
+ artist_0: "1",
947
+ artist_1: "2",
948
+ id: "1",
949
+ id_0: "1",
950
+ id_1: "3",
951
+ name: "green cover",
952
+ })
953
+ .expect(toRedirect("/"));
892
954
  });
893
-
894
955
  it("edit-in-edit legacy one layer", async () => {
895
956
  const app = await getApp({ disableCsrf: true });
896
957
  const loginCookie = await getAdminLoginCookie();
@@ -898,8 +959,20 @@ describe("edit-in-edit with relation path and legacy", () => {
898
959
  .get("/view/edit_department_with_edit_in_edit_legacy?id=1")
899
960
  .set("Cookie", loginCookie)
900
961
  .expect(toInclude("add_repeater"));
901
-
902
- // TODO post
962
+ await request(app)
963
+ .post("/view/edit_department_with_edit_in_edit_legacy?id=1")
964
+ .set("Cookie", loginCookie)
965
+ .send({
966
+ department_0: "1",
967
+ department_1: "1",
968
+ id: "1",
969
+ id_0: "1",
970
+ id_1: "2",
971
+ name: "my_department",
972
+ name_0: "manager",
973
+ name_1: "my_employee",
974
+ })
975
+ .expect(toRedirect("/"));
903
976
  });
904
977
 
905
978
  it("edit-in-edit with relation path two layer", async () => {
@@ -909,8 +982,20 @@ describe("edit-in-edit with relation path and legacy", () => {
909
982
  .get("/view/edit_cover_with_edit_artist_on_album_rel_path?id=1")
910
983
  .set("Cookie", loginCookie)
911
984
  .expect(toInclude("add_repeater"));
912
-
913
- // TODO post
985
+ await request(app)
986
+ .post("/view/edit_cover_with_edit_artist_on_album_rel_path?id=1")
987
+ .set("Cookie", loginCookie)
988
+ .send({
989
+ album_0: "1",
990
+ album_1: "1",
991
+ artist_0: "1",
992
+ artist_1: "2",
993
+ id: "1",
994
+ id_0: "1",
995
+ id_1: "3",
996
+ name: "green cover",
997
+ })
998
+ .expect(toRedirect("/"));
914
999
  });
915
1000
 
916
1001
  it("edit-in-edit legacy two layer", async () => {
@@ -920,8 +1005,20 @@ describe("edit-in-edit with relation path and legacy", () => {
920
1005
  .get("/view/edit_cover_with_edit_artist_on_album_legacy?id=1")
921
1006
  .set("Cookie", loginCookie)
922
1007
  .expect(toInclude("add_repeater"));
923
-
924
- // TODO post
1008
+ await request(app)
1009
+ .post("/view/edit_cover_with_edit_artist_on_album_legacy?id=1")
1010
+ .set("Cookie", loginCookie)
1011
+ .send({
1012
+ album_0: "1",
1013
+ album_1: "1",
1014
+ artist_0: "1",
1015
+ artist_1: "2",
1016
+ id: "1",
1017
+ id_0: "1",
1018
+ id_1: "3",
1019
+ name: "green cover",
1020
+ })
1021
+ .expect(toRedirect("/"));
925
1022
  });
926
1023
  });
927
1024
 
@@ -982,6 +1079,21 @@ describe("legacy relations with relation path", () => {
982
1079
  await request(app)
983
1080
  .get("/view/authoredit_with_show?id=1")
984
1081
  .set("Cookie", loginCookie)
985
- .expect(toInclude(["Herman Melville", "agi"]));
1082
+ .expect(toInclude("Herman Melville"));
1083
+ });
1084
+
1085
+ it("edit-view with independent list", async () => {
1086
+ const app = await getApp({ disableCsrf: true });
1087
+ const loginCookie = await getAdminLoginCookie();
1088
+ await request(app)
1089
+ .get("/view/authoredit_with_independent_list")
1090
+ .set("Cookie", loginCookie)
1091
+ .expect(toInclude("Herman Melville"))
1092
+ .expect(toInclude("Delete"));
1093
+ await request(app)
1094
+ .get("/view/authoredit_with_independent_list?id=1")
1095
+ .set("Cookie", loginCookie)
1096
+ .expect(toInclude("Herman Melville"))
1097
+ .expect(toInclude("Delete"));
986
1098
  });
987
1099
  });
@@ -78,6 +78,37 @@ describe("viewedit edit endpoint", () => {
78
78
  });
79
79
 
80
80
  describe("viewedit new List", () => {
81
+ const columns = [
82
+ { type: "Field", field_name: "author" },
83
+ { type: "Field", field_name: "pages" },
84
+ ];
85
+ const layout = {
86
+ besides: [
87
+ {
88
+ contents: {
89
+ type: "field",
90
+ fieldview: "as_text",
91
+ field_name: "author",
92
+ configuration: {},
93
+ },
94
+ alignment: "Default",
95
+ header_label: "Author",
96
+ col_width_units: "px",
97
+ },
98
+ {
99
+ contents: {
100
+ type: "field",
101
+ fieldview: "show",
102
+ field_name: "pages",
103
+ configuration: {},
104
+ },
105
+ alignment: "Default",
106
+ header_label: "Pages",
107
+ col_width_units: "px",
108
+ },
109
+ ],
110
+ list_columns: true,
111
+ };
81
112
  itShouldRedirectUnauthToLogin("/viewedit/new");
82
113
 
83
114
  it("show new view", async () => {
@@ -117,11 +148,8 @@ describe("viewedit new List", () => {
117
148
  .post("/viewedit/config/mybooklist")
118
149
  .send("contextEnc=" + ctx)
119
150
  .send("stepName=Columns")
120
- .send("type_0=Field")
121
- .send("field_name_0=author")
122
- .send("type_1=Field")
123
- .send("field_name_1=pages")
124
- .send("create_view_display=Link")
151
+ .send("columns=" + encodeURIComponent(JSON.stringify(columns)))
152
+ .send("layout=" + encodeURIComponent(JSON.stringify(layout)))
125
153
  .set("Cookie", loginCookie)
126
154
  .expect(toInclude("Default state"));
127
155
  });
@@ -205,6 +233,23 @@ describe("viewedit new List", () => {
205
233
  });
206
234
 
207
235
  describe("viewedit new List with one field", () => {
236
+ const columns = [{ type: "Field", field_name: "author", state_field: "on" }];
237
+ const layout = {
238
+ besides: [
239
+ {
240
+ contents: {
241
+ type: "field",
242
+ fieldview: "as_text",
243
+ field_name: "author",
244
+ configuration: {},
245
+ },
246
+ alignment: "Default",
247
+ header_label: "Author",
248
+ col_width_units: "px",
249
+ },
250
+ ],
251
+ list_columns: true,
252
+ };
208
253
  it("submit new view", async () => {
209
254
  const loginCookie = await getAdminLoginCookie();
210
255
 
@@ -234,9 +279,8 @@ describe("viewedit new List with one field", () => {
234
279
  .post("/viewedit/config/mybooklist1")
235
280
  .send("contextEnc=" + ctx)
236
281
  .send("stepName=Columns")
237
- .send("type_0=Field")
238
- .send("field_name_0=author")
239
- .send("create_view_display=Link")
282
+ .send("columns=" + encodeURIComponent(JSON.stringify(columns)))
283
+ .send("layout=" + encodeURIComponent(JSON.stringify(layout)))
240
284
  .set("Cookie", loginCookie)
241
285
  .expect(toInclude("Default state"));
242
286
  });