@yuneta/gobj-ui 7.25.17 → 7.25.19

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.
@@ -29,6 +29,7 @@ const {register_c_yui_shell, yui_shell_set_connection_state} = await import("./c
29
29
  const {register_c_yui_treedb_topics} = await import("./c_yui_treedb_topics.js");
30
30
 
31
31
  const logged = [];
32
+ const written = []; /* the EV_RECORD_WRITTEN the host heard */
32
33
  const answers = []; /* what the fake forms were told */
33
34
  const loaded = []; /* the rows each fake form was loaded with */
34
35
  const commands = []; /* what the fake transport was asked */
@@ -103,7 +104,10 @@ beforeAll(() => {
103
104
  "C_TEST_VIEW_HOST",
104
105
  [["EV_RECORD_WRITTEN", 0], ["EV_TOPIC_SELECTED", 0]],
105
106
  [["ST_IDLE", [
106
- ["EV_RECORD_WRITTEN", () => 0, null],
107
+ ["EV_RECORD_WRITTEN", (gobj, event, kw) => {
108
+ written.push(kw);
109
+ return 0;
110
+ }, null],
107
111
  ["EV_TOPIC_SELECTED", () => 0, null]
108
112
  ]]],
109
113
  {}, 0, [SDATA_END()], {}, 0, 0, 0, 0
@@ -121,6 +125,7 @@ beforeAll(() => {
121
125
 
122
126
  beforeEach(() => {
123
127
  logged.length = 0;
128
+ written.length = 0;
124
129
  answers.length = 0;
125
130
  commands.length = 0;
126
131
  loaded.length = 0;
@@ -228,16 +233,26 @@ describe("a Save in flight when the backend drops", () => {
228
233
  save(topics, forms.roles, 1);
229
234
 
230
235
  /* The answer of the users write, the way C_IEVENT_CLI delivers it:
231
- * the request's frame on top of the command_stack. */
232
- const ok = {result: 0, comment: "", data: [{id: "x"}], __md_iev__: {
233
- command_stack: [{command: "update-node", kw: {
234
- topic_name: "users", treedb_name: "treedb_test", form_write: 1, record: {id: "x"}
235
- }}]
236
+ * the request's `__md_command__` as the frame on top of the
237
+ * command_stack, and nothing else of the request. */
238
+ const users_write = commands.find((c) => c.command === "update-node" &&
239
+ c.kw.topic_name === "users");
240
+ const ok = {result: 0, comment: "", data: [{id: "x", name: "stored"}], __md_iev__: {
241
+ command_stack: [{command: "update-node", kw: users_write.kw.__md_command__}]
236
242
  }};
237
243
  gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", ok, remote);
238
244
  expect(answers).toEqual([
239
245
  {topic: "users", event: "EV_WRITE_DONE", form_write: 1},
240
246
  ]);
247
+ /* What the host is told names the treedb and carries the record
248
+ * the store answered: read off that frame, both arrived empty. */
249
+ expect(written).toEqual([{
250
+ treedb_name: "treedb_test",
251
+ topic_name: "users",
252
+ record: {id: "x", name: "stored"},
253
+ created: false,
254
+ command: "update-node"
255
+ }]);
241
256
 
242
257
  yui_shell_set_connection_state(shell, false);
243
258
  expect(answers).toEqual([
@@ -247,13 +262,102 @@ describe("a Save in flight when the backend drops", () => {
247
262
  });
248
263
  });
249
264
 
265
+ /*
266
+ * The success answer of a request, the way C_IEVENT_CLI delivers it: the
267
+ * request's `__md_command__` as the frame, and nothing else of it.
268
+ */
269
+ function answer_ok(request, data)
270
+ {
271
+ return {result: 0, comment: "", data: data, __md_iev__: {
272
+ command_stack: [{command: request.command, kw: request.kw.__md_command__}]
273
+ }};
274
+ }
275
+
276
+ /*
277
+ * Before gobj-ui 7.25.19 a delete answered published nothing (the case
278
+ * was an empty `break`), and `created` compared the command with
279
+ * "create-node" -- which the view never sends: its +New goes out as an
280
+ * update-node with `create_only`, so `created` was always false.
281
+ */
282
+ describe("EV_RECORD_WRITTEN of a create and of a delete", () => {
283
+
284
+ test("a +New answered says created: true", () => {
285
+ const {shell, topics, forms} = build("tc1");
286
+ yui_shell_set_connection_state(shell, true);
287
+ gobj_send_event(topics, "EV_CREATE_RECORD",
288
+ {record: {id: "n1"}, form_write: 1}, forms.users);
289
+
290
+ const request = commands.find((c) => c.command === "update-node");
291
+ expect(request.kw.options.create_only).toBe(true);
292
+ gobj_send_event(topics, "EV_MT_COMMAND_ANSWER",
293
+ answer_ok(request, [{id: "n1", name: "new"}]), remote);
294
+
295
+ expect(written).toEqual([{
296
+ treedb_name: "treedb_test",
297
+ topic_name: "users",
298
+ record: {id: "n1", name: "new"},
299
+ created: true,
300
+ command: "update-node"
301
+ }]);
302
+ expect(answers).toEqual([
303
+ {topic: "users", event: "EV_WRITE_DONE", form_write: 1},
304
+ ]);
305
+ expect(errors()).toEqual([]);
306
+ });
307
+
308
+ test("an edit answered says created: false", () => {
309
+ const {shell, topics, forms} = build("tc2");
310
+ yui_shell_set_connection_state(shell, true);
311
+ save(topics, forms.users, 1);
312
+ const request = commands.find((c) => c.command === "update-node");
313
+ gobj_send_event(topics, "EV_MT_COMMAND_ANSWER",
314
+ answer_ok(request, [{id: "x"}]), remote);
315
+ expect(written.map((w) => w.created)).toEqual([false]);
316
+ });
317
+
318
+ test("a delete answered is published with the node deleted", () => {
319
+ const {shell, topics, forms} = build("tc3");
320
+ yui_shell_set_connection_state(shell, true);
321
+ gobj_send_event(topics, "EV_DELETE_RECORD", {record: {id: "x"}}, forms.roles);
322
+
323
+ const request = commands.find((c) => c.command === "delete-node");
324
+ expect(request).toBeTruthy();
325
+ gobj_send_event(topics, "EV_MT_COMMAND_ANSWER",
326
+ answer_ok(request, [{id: "x", name: "gone"}]), remote);
327
+
328
+ expect(written).toEqual([{
329
+ treedb_name: "treedb_test",
330
+ topic_name: "roles",
331
+ record: {id: "x", name: "gone"},
332
+ created: false,
333
+ command: "delete-node"
334
+ }]);
335
+ /* A delete has no form waiting for its answer. */
336
+ expect(answers).toEqual([]);
337
+ expect(errors()).toEqual([]);
338
+ });
339
+
340
+ test("a delete refused publishes nothing", () => {
341
+ const {shell, topics, forms} = build("tc4");
342
+ yui_shell_set_connection_state(shell, true);
343
+ gobj_send_event(topics, "EV_DELETE_RECORD", {record: {id: "x"}}, forms.roles);
344
+ const request = commands.find((c) => c.command === "delete-node");
345
+ gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", {
346
+ result: -1, comment: "linked", data: null, __md_iev__: {
347
+ command_stack: [{command: "delete-node", kw: request.kw.__md_command__}]
348
+ }
349
+ }, remote);
350
+ expect(written).toEqual([]);
351
+ });
352
+ });
353
+
250
354
  /*
251
355
  * The answer the routing adapter gives a write it had in flight when
252
356
  * its session closed: a failure, settled by the adapter itself.
253
357
  */
254
358
  function failed_write(topic_name, form_write)
255
359
  {
256
- const md = {topic_name: topic_name, treedb_name: "treedb_test", record: {id: "x"}};
360
+ const md = {topic_name: topic_name}; /* the request's __md_command__, and only it */
257
361
  if(form_write) {
258
362
  md.form_write = form_write;
259
363
  }
@@ -0,0 +1,92 @@
1
+ /***********************************************************************
2
+ * uplot_add_serie.test.js
3
+ *
4
+ * C_YUI_UPLOT's EV_ADD_SERIE gives a series the palette's stroke
5
+ * and fill only when the host gave none. It did it through
6
+ * kw_get_str(KW_CREATE), which reads a stroke that is a FUNCTION
7
+ * (uPlot takes one, and a gradient) as a value of the wrong type:
8
+ * since gobj-js 7.25.5, which logs that as C does, every series
9
+ * added with one logged an error.
10
+ *
11
+ * Copyright (c) 2026, ArtGins.
12
+ * All Rights Reserved.
13
+ ***********************************************************************/
14
+ import {describe, test, expect, beforeAll, beforeEach, vi} from "vitest";
15
+ import {install_dom_double} from "../test/dom_double.js";
16
+
17
+ install_dom_double();
18
+
19
+ /* uPlot draws on a canvas the document double does not have: the
20
+ * chart gets a stand-in, and the test writes its own over it. */
21
+ vi.mock("uplot", () => ({
22
+ default: class {
23
+ constructor() {
24
+ this.series = [{}];
25
+ }
26
+ setSize() {}
27
+ addSeries() {}
28
+ destroy() {}
29
+ }
30
+ }));
31
+
32
+ const {
33
+ gobj_start_up, gobj_create_yuno, gobj_create_pure_child, gclass_create,
34
+ gobj_send_event, gobj_write_attr, set_log_callback, SDATA_END,
35
+ } = await import("@yuneta/gobj-js");
36
+ const {register_c_yui_uplot} = await import("./c_yui_uplot.js");
37
+
38
+ const logged = [];
39
+ let yuno = null;
40
+
41
+ beforeAll(() => {
42
+ gobj_start_up(null, null, null, null, null, null, null);
43
+ set_log_callback((level, msg) => {
44
+ logged.push({level: String(level), msg: String(msg)});
45
+ });
46
+ gclass_create("C_TEST_UPLOT_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
47
+ register_c_yui_uplot();
48
+ yuno = gobj_create_yuno("uplot_yuno", "C_TEST_UPLOT_HOST", {});
49
+ });
50
+
51
+ beforeEach(() => {
52
+ logged.length = 0;
53
+ });
54
+
55
+ let seq = 0;
56
+
57
+ /* The chart, with uPlot replaced by what EV_ADD_SERIE asks of it. */
58
+ function build()
59
+ {
60
+ const chart = gobj_create_pure_child(`chart_${++seq}`, "C_YUI_UPLOT", {}, yuno);
61
+ const added = [];
62
+ gobj_write_attr(chart, "uplot", {
63
+ series: [{}],
64
+ addSeries: (kw) => {
65
+ added.push(kw);
66
+ }
67
+ });
68
+ logged.length = 0;
69
+ return {chart, added};
70
+ }
71
+
72
+ describe("EV_ADD_SERIE", () => {
73
+
74
+ test("a series with no colours takes the palette's", () => {
75
+ const {chart, added} = build();
76
+ gobj_send_event(chart, "EV_ADD_SERIE", {id: "a", label: "A"}, chart);
77
+ expect(added.length).toBe(1);
78
+ expect(typeof added[0].stroke).toBe("string");
79
+ expect(added[0].stroke.length).toBeGreaterThan(0);
80
+ expect(logged.filter((l) => l.level === "error")).toEqual([]);
81
+ });
82
+
83
+ test("a stroke and a fill that are functions are kept, and nothing is logged", () => {
84
+ const {chart, added} = build();
85
+ const stroke = () => "red";
86
+ const fill = () => "pink";
87
+ gobj_send_event(chart, "EV_ADD_SERIE", {id: "b", stroke, fill}, chart);
88
+ expect(added[0].stroke).toBe(stroke);
89
+ expect(added[0].fill).toBe(fill);
90
+ expect(logged.filter((l) => l.level === "error")).toEqual([]);
91
+ });
92
+ });