@yuneta/gobj-ui 7.25.19 → 7.25.21
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/README.md +56 -7
- package/dist/gobj-ui.cjs.js +138 -33
- package/dist/gobj-ui.es.js +138 -33
- package/package.json +2 -2
- package/src/c_g6_nodes_tree.js +29 -4
- package/src/c_yui_json.js +28 -5
- package/src/c_yui_json_pad.js +3 -2
- package/src/c_yui_schema_editor.js +14 -1
- package/src/c_yui_treedb_graph.js +67 -14
- package/src/c_yui_treedb_topic_with_form.js +3 -2
- package/src/c_yui_treedb_topics.js +44 -5
- package/src/form_json_expand.wiring.test.js +86 -3
- package/src/g6_graphs_write_owed.wiring.test.js +77 -0
- package/src/schema_editor_transport.wiring.test.js +89 -5
- package/src/treedb_graph_writes.wiring.test.js +38 -4
- package/src/treedb_json_drill.wiring.test.js +232 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* treedb_json_drill.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* The raw-tranger viewer of C_YUI_TREEDB_TOPICS and of
|
|
5
|
+
* C_YUI_TREEDB_GRAPH drills into a `__collapsed__` stub by asking
|
|
6
|
+
* its host (EV_EXPAND_PATH), and the stub shows "loading" until the
|
|
7
|
+
* host answers EV_SUBTREE_LOADED or EV_SUBTREE_ERROR. While the
|
|
8
|
+
* path is pending, C_YUI_JSON ignores every further click on it.
|
|
9
|
+
*
|
|
10
|
+
* Before gobj-ui 7.25.21 the host answered only when it had no
|
|
11
|
+
* transport at all. A drill asked out of session, or refused by the
|
|
12
|
+
* transport on the way out, was logged and never answered: the stub
|
|
13
|
+
* stayed on "loading" for the life of the viewer, and no click --
|
|
14
|
+
* not even after the reconnect -- asked again.
|
|
15
|
+
*
|
|
16
|
+
* Driven through the real hosts and the real viewer on a document
|
|
17
|
+
* double, with a fake transport whose state is the one the hosts
|
|
18
|
+
* read and which refuses on demand.
|
|
19
|
+
*
|
|
20
|
+
* Copyright (c) 2026, ArtGins.
|
|
21
|
+
* All Rights Reserved.
|
|
22
|
+
***********************************************************************/
|
|
23
|
+
import {describe, test, expect, beforeAll, beforeEach} from "vitest";
|
|
24
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
25
|
+
|
|
26
|
+
install_dom_double();
|
|
27
|
+
|
|
28
|
+
if(!globalThis.ResizeObserver) {
|
|
29
|
+
globalThis.ResizeObserver = class {
|
|
30
|
+
observe() {}
|
|
31
|
+
unobserve() {}
|
|
32
|
+
disconnect() {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
SDATA, SDATA_END, data_type_t,
|
|
38
|
+
gclass_create,
|
|
39
|
+
gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
|
|
40
|
+
gobj_create_pure_child,
|
|
41
|
+
gobj_start, gobj_send_event,
|
|
42
|
+
gobj_read_pointer_attr, gobj_subscribe_event,
|
|
43
|
+
gobj_change_state, gobj_current_state,
|
|
44
|
+
set_log_callback,
|
|
45
|
+
} = await import("@yuneta/gobj-js");
|
|
46
|
+
|
|
47
|
+
const logged = [];
|
|
48
|
+
const commands = []; /* what the fake transport was asked */
|
|
49
|
+
let refuse_in_session = false;
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* The transport. Out of session it refuses, as gui_agent's routing
|
|
53
|
+
* adapter (C_AGENT_TREEDB_LINK) does; in session it refuses only when
|
|
54
|
+
* the test says so (a transport that cannot route the command).
|
|
55
|
+
*/
|
|
56
|
+
function remote_command_parser(gobj, command, kw, src)
|
|
57
|
+
{
|
|
58
|
+
commands.push({command, kw});
|
|
59
|
+
if(gobj_current_state(gobj) !== "ST_SESSION") {
|
|
60
|
+
return `cannot route '${command}' -- not in session`;
|
|
61
|
+
}
|
|
62
|
+
if(refuse_in_session) {
|
|
63
|
+
return `cannot route '${command}'`;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let yuno = null;
|
|
69
|
+
|
|
70
|
+
beforeAll(async () => {
|
|
71
|
+
const i18next = (await import("i18next")).default;
|
|
72
|
+
await i18next.init({lng: "en", resources: {}});
|
|
73
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
74
|
+
set_log_callback((level, msg) => {
|
|
75
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
gclass_create("C_TEST_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
79
|
+
gclass_create(
|
|
80
|
+
"C_TEST_VIEW_HOST",
|
|
81
|
+
[["EV_RECORD_WRITTEN", 0], ["EV_TOPIC_SELECTED", 0], ["EV_OPERATION_MODE_CHANGED", 0]],
|
|
82
|
+
[["ST_IDLE", [
|
|
83
|
+
["EV_RECORD_WRITTEN", () => 0, null],
|
|
84
|
+
["EV_TOPIC_SELECTED", () => 0, null],
|
|
85
|
+
["EV_OPERATION_MODE_CHANGED", () => 0, null]
|
|
86
|
+
]]],
|
|
87
|
+
{}, 0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
88
|
+
);
|
|
89
|
+
gclass_create(
|
|
90
|
+
"C_TEST_REMOTE",
|
|
91
|
+
[],
|
|
92
|
+
[["ST_SESSION", []], ["ST_DISCONNECTED", []]],
|
|
93
|
+
{mt_command_parser: remote_command_parser},
|
|
94
|
+
0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
/* The graph's engine, as a silent double: this test is about the
|
|
98
|
+
* viewer, not about the graph. */
|
|
99
|
+
const ENGINE_EVENTS = [
|
|
100
|
+
"EV_DESCS", "EV_CLEAR_DATA", "EV_LOAD_DATA", "EV_NODE_CREATED",
|
|
101
|
+
"EV_NODE_UPDATED", "EV_NODE_DELETED", "EV_GRAPHS_WRITE_REFUSED",
|
|
102
|
+
"EV_SHOW", "EV_HIDE", "EV_RESIZE", "EV_THEME", "EV_LANGUAGE_CHANGED",
|
|
103
|
+
"EV_SET_OPERATION_MODE", "EV_SET_LAYOUT"
|
|
104
|
+
];
|
|
105
|
+
gclass_create(
|
|
106
|
+
"C_G6_NODES_TREE",
|
|
107
|
+
ENGINE_EVENTS.map((e) => [e, 0]).concat([["EV_UPDATE_NODE", 2]]),
|
|
108
|
+
[["ST_IDLE", ENGINE_EVENTS.map((e) => [e, () => 0, null])]],
|
|
109
|
+
{
|
|
110
|
+
mt_create: (gobj) => {
|
|
111
|
+
const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
112
|
+
if(subscriber) {
|
|
113
|
+
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
0,
|
|
118
|
+
[
|
|
119
|
+
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, ""),
|
|
120
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, ""),
|
|
121
|
+
SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, ""),
|
|
122
|
+
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, "", ""),
|
|
123
|
+
SDATA(data_type_t.DTP_JSON, "layout_names", 0, "[]", ""),
|
|
124
|
+
SDATA(data_type_t.DTP_STRING, "layout", 0, "", ""),
|
|
125
|
+
SDATA_END()
|
|
126
|
+
],
|
|
127
|
+
{}, 0, 0, 0, 0
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const {register_c_yui_json} = await import("./c_yui_json.js");
|
|
131
|
+
const {register_c_yui_treedb_topics} = await import("./c_yui_treedb_topics.js");
|
|
132
|
+
const {register_c_yui_treedb_graph} = await import("./c_yui_treedb_graph.js");
|
|
133
|
+
register_c_yui_json();
|
|
134
|
+
register_c_yui_treedb_topics();
|
|
135
|
+
register_c_yui_treedb_graph();
|
|
136
|
+
|
|
137
|
+
yuno = gobj_create_yuno("json_drill_yuno", "C_TEST_HOST", {});
|
|
138
|
+
gobj_start(yuno);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
logged.length = 0;
|
|
143
|
+
commands.length = 0;
|
|
144
|
+
refuse_in_session = false;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* A host of each kind, with its viewer open on a tranger that carries
|
|
149
|
+
* one stub. The viewer is the one the host created: it publishes
|
|
150
|
+
* EV_EXPAND_PATH to the host, and the host answers it.
|
|
151
|
+
*/
|
|
152
|
+
function build(name, gclass)
|
|
153
|
+
{
|
|
154
|
+
const remote = gobj_create_service(`${name}_remote`, "C_TEST_REMOTE", {}, yuno);
|
|
155
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
156
|
+
const view_host = gobj_create(`${name}_view_host`, "C_TEST_VIEW_HOST", {}, yuno);
|
|
157
|
+
const host = gobj_create_service(`${name}_host`, gclass, {
|
|
158
|
+
gobj_remote_yuno: remote,
|
|
159
|
+
treedb_name: "treedb_test",
|
|
160
|
+
subscriber: view_host
|
|
161
|
+
}, view_host);
|
|
162
|
+
if(gclass === "C_YUI_TREEDB_TOPICS") {
|
|
163
|
+
gobj_start(host);
|
|
164
|
+
}
|
|
165
|
+
const viewer = gobj_create_pure_child(`${name}_jv`, "C_YUI_JSON", {
|
|
166
|
+
subscriber: host,
|
|
167
|
+
json_data: {topics: {__collapsed__: {size: 3, path: "topics"}}}
|
|
168
|
+
}, host);
|
|
169
|
+
gobj_start(viewer);
|
|
170
|
+
host.priv.json_gobj = viewer;
|
|
171
|
+
logged.length = 0;
|
|
172
|
+
commands.length = 0;
|
|
173
|
+
return {remote, host, viewer};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function click_stub(viewer)
|
|
177
|
+
{
|
|
178
|
+
gobj_send_event(viewer, "EV_EXPAND_COLLAPSED", {path: "topics", size: 3}, viewer);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function drills()
|
|
182
|
+
{
|
|
183
|
+
return commands.filter((c) => c.command === "print-tranger").map((c) => c.kw.path);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for(const gclass of ["C_YUI_TREEDB_TOPICS", "C_YUI_TREEDB_GRAPH"]) {
|
|
187
|
+
describe(`${gclass}: a drill that cannot go out is answered`, () => {
|
|
188
|
+
|
|
189
|
+
test("out of session: the stub says 'no session', and a click after the reconnect asks", () => {
|
|
190
|
+
const {remote, viewer} = build(`${gclass}_a`, gclass);
|
|
191
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
192
|
+
|
|
193
|
+
click_stub(viewer);
|
|
194
|
+
|
|
195
|
+
expect(drills()).toEqual([]); /* not even tried */
|
|
196
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
197
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
198
|
+
{error: "no session", is_key: true});
|
|
199
|
+
|
|
200
|
+
/* The reconnect: the same stub asks again. */
|
|
201
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
202
|
+
click_stub(viewer);
|
|
203
|
+
expect(drills()).toEqual(["topics"]);
|
|
204
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("refused by the transport: the stub says why, and can be clicked again", () => {
|
|
208
|
+
const {viewer} = build(`${gclass}_b`, gclass);
|
|
209
|
+
refuse_in_session = true;
|
|
210
|
+
|
|
211
|
+
click_stub(viewer);
|
|
212
|
+
|
|
213
|
+
expect(drills()).toEqual(["topics"]);
|
|
214
|
+
expect(viewer.priv.pending.has("topics")).toBe(false);
|
|
215
|
+
expect(viewer.priv.errors.get("topics")).toEqual(
|
|
216
|
+
{error: "cannot route 'print-tranger'", is_key: false});
|
|
217
|
+
|
|
218
|
+
refuse_in_session = false;
|
|
219
|
+
click_stub(viewer);
|
|
220
|
+
expect(drills()).toEqual(["topics", "topics"]);
|
|
221
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("in session and sent: the stub waits for its answer", () => {
|
|
225
|
+
const {viewer} = build(`${gclass}_c`, gclass);
|
|
226
|
+
click_stub(viewer);
|
|
227
|
+
expect(drills()).toEqual(["topics"]);
|
|
228
|
+
expect(viewer.priv.pending.has("topics")).toBe(true);
|
|
229
|
+
expect(logged.filter((l) => l.level === "error")).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|