@yuneta/gobj-ui 7.23.29 → 7.23.31
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 +175 -2
- package/dist/gobj-ui.cjs.js +1008 -197
- package/dist/gobj-ui.es.js +1009 -198
- package/package.json +1 -1
- package/src/c_yui_gobj_tree_js.js +465 -249
- package/src/c_yui_json_graph.js +30 -4
- package/src/c_yui_treedb_graph.js +20 -6
- package/src/gclass_describe.js +234 -0
- package/src/gclass_describe.test.js +125 -0
- package/src/gobj_tree_model.js +436 -0
- package/src/gobj_tree_model.test.js +184 -0
- package/src/yui_gclass_view.js +290 -0
package/src/c_yui_json_graph.js
CHANGED
|
@@ -674,6 +674,30 @@ function make_toolbar(gobj)
|
|
|
674
674
|
return $toolbar;
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
/************************************************************
|
|
678
|
+
* The element G6 mounts its canvas in.
|
|
679
|
+
*
|
|
680
|
+
* Read from THIS gclass's own $container, never with
|
|
681
|
+
* `document.getElementById()`: a view is not always in the
|
|
682
|
+
* document when it is started -- a host that builds the
|
|
683
|
+
* viewer and mounts it into a window afterwards starts it
|
|
684
|
+
* DETACHED -- and a global lookup answers null there. It cost
|
|
685
|
+
* a resize that never worked: the ResizeObserver below was
|
|
686
|
+
* simply never attached, silently, and the canvas kept the
|
|
687
|
+
* size it was born with for the life of the window.
|
|
688
|
+
*
|
|
689
|
+
* Null only if the UI was never built; that is a bug, and it
|
|
690
|
+
* is logged where it matters rather than skipped.
|
|
691
|
+
************************************************************/
|
|
692
|
+
function canvas_mount(gobj)
|
|
693
|
+
{
|
|
694
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
695
|
+
if(!$container) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
return $container.querySelector("#" + gobj.priv.canvas_id);
|
|
699
|
+
}
|
|
700
|
+
|
|
677
701
|
/************************************************************
|
|
678
702
|
* Build a G6 graph instance
|
|
679
703
|
************************************************************/
|
|
@@ -778,8 +802,10 @@ function build_graph(gobj)
|
|
|
778
802
|
* the observed box so there is no feedback loop.
|
|
779
803
|
*/
|
|
780
804
|
if(typeof ResizeObserver !== "undefined") {
|
|
781
|
-
let $canvas =
|
|
782
|
-
if(
|
|
805
|
+
let $canvas = canvas_mount(gobj);
|
|
806
|
+
if(!$canvas) {
|
|
807
|
+
log_error(`${gobj_short_name(gobj)}: no canvas mount, the graph will never resize`);
|
|
808
|
+
} else {
|
|
783
809
|
let ro = new ResizeObserver(() => {
|
|
784
810
|
if(priv._resize_raf) {
|
|
785
811
|
cancelAnimationFrame(priv._resize_raf);
|
|
@@ -2333,7 +2359,7 @@ function ac_show(gobj, event, kw, src)
|
|
|
2333
2359
|
let graph = priv.graph;
|
|
2334
2360
|
|
|
2335
2361
|
if(graph) {
|
|
2336
|
-
let $canvas =
|
|
2362
|
+
let $canvas = canvas_mount(gobj);
|
|
2337
2363
|
if($canvas) {
|
|
2338
2364
|
/*
|
|
2339
2365
|
* Content box (clientWidth/Height), not getBoundingClientRect:
|
|
@@ -2366,7 +2392,7 @@ function ac_resize(gobj, event, kw, src)
|
|
|
2366
2392
|
let graph = priv.graph;
|
|
2367
2393
|
|
|
2368
2394
|
if(graph) {
|
|
2369
|
-
let $canvas =
|
|
2395
|
+
let $canvas = canvas_mount(gobj);
|
|
2370
2396
|
if($canvas) {
|
|
2371
2397
|
let cw = $canvas.clientWidth;
|
|
2372
2398
|
let ch = $canvas.clientHeight;
|
|
@@ -771,7 +771,14 @@ function open_json_viewer(gobj)
|
|
|
771
771
|
return;
|
|
772
772
|
}
|
|
773
773
|
priv.json_gobj = jv;
|
|
774
|
-
|
|
774
|
+
|
|
775
|
+
/* CREATED, not started. `mt_create` builds the DOM; `mt_start`
|
|
776
|
+
* RENDERS it -- and the viewer's graph view measures a canvas and
|
|
777
|
+
* puts a ResizeObserver on it, both of which need the element to be
|
|
778
|
+
* in the document, which the presenter below is what does. Started
|
|
779
|
+
* here it worked only while the reader had last used the tree view;
|
|
780
|
+
* with the graph view remembered it built detached, attached no
|
|
781
|
+
* observer, and the canvas never followed the window again. */
|
|
775
782
|
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
776
783
|
|
|
777
784
|
if(mobile) {
|
|
@@ -793,6 +800,7 @@ function open_json_viewer(gobj)
|
|
|
793
800
|
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
794
801
|
}
|
|
795
802
|
});
|
|
803
|
+
gobj_start(jv); /* mounted: now it can measure itself */
|
|
796
804
|
} else {
|
|
797
805
|
let $win_parent = (shell && yui_shell_popup_layer(shell)) ||
|
|
798
806
|
(typeof document !== "undefined" && document.getElementById("top-layer")) ||
|
|
@@ -833,6 +841,7 @@ function open_json_viewer(gobj)
|
|
|
833
841
|
return;
|
|
834
842
|
}
|
|
835
843
|
gobj_start(priv.json_win);
|
|
844
|
+
gobj_start(jv); /* mounted: now it can measure itself */
|
|
836
845
|
}
|
|
837
846
|
|
|
838
847
|
request_print_tranger(gobj, ""); /* first fetch: whole tranger, collapsed */
|
|
@@ -877,11 +886,16 @@ function close_json_viewer(gobj)
|
|
|
877
886
|
}
|
|
878
887
|
if(jv && is_gobj(jv)) {
|
|
879
888
|
try {
|
|
880
|
-
/* STOP, then destroy
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
|
|
889
|
+
/* STOP, then destroy: gobj_destroy() raises the `destroying`
|
|
890
|
+
* flag before it can stop a running gobj, so destroying it
|
|
891
|
+
* straight logs "Destroying a RUNNING gobj" + "gobj NULL or
|
|
892
|
+
* DESTROYED" and skips its mt_stop. GUARDED, because the
|
|
893
|
+
* viewer is now started AFTER its presenter is up: the two
|
|
894
|
+
* failure paths that get here arrive with it created and
|
|
895
|
+
* never started. */
|
|
896
|
+
if(gobj_is_running(jv)) {
|
|
897
|
+
gobj_stop(jv);
|
|
898
|
+
}
|
|
885
899
|
gobj_destroy(jv);
|
|
886
900
|
} catch(e) {
|
|
887
901
|
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* gclass_describe.js
|
|
3
|
+
*
|
|
4
|
+
* A gclass, as plain JSON, in the shape the BACKEND answers.
|
|
5
|
+
*
|
|
6
|
+
* There is no gclass viewer in the framework: the C kernel
|
|
7
|
+
* answers `view-gclass` with `gclass2json()`
|
|
8
|
+
* (`kernel/c/gobj-c/src/gobj.c`) and nothing draws it. This
|
|
9
|
+
* module builds the same document for a gclass of the BROWSER
|
|
10
|
+
* yuno, out of the gobj-js registry, so one viewer can show a
|
|
11
|
+
* gclass of either side:
|
|
12
|
+
*
|
|
13
|
+
* {id, gcflag, attrs, commands, gclass_methods,
|
|
14
|
+
* internal_methods, FSM: {events, states}}
|
|
15
|
+
*
|
|
16
|
+
* Two keys the backend does not carry are added at the end,
|
|
17
|
+
* because the browser can answer them for free and the reader
|
|
18
|
+
* asks them first: `instances` and `trace_levels`.
|
|
19
|
+
*
|
|
20
|
+
* Copyright (c) 2026, ArtGins.
|
|
21
|
+
* All Rights Reserved.
|
|
22
|
+
***********************************************************************/
|
|
23
|
+
import {
|
|
24
|
+
data_type_t,
|
|
25
|
+
sdata_flag_t,
|
|
26
|
+
event_flag_t,
|
|
27
|
+
gclass_flag_t,
|
|
28
|
+
gclass_find_by_name,
|
|
29
|
+
} from "@yuneta/gobj-js";
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/***************************************************************
|
|
33
|
+
* Bit tables
|
|
34
|
+
***************************************************************/
|
|
35
|
+
const DATA_TYPE_NAMES = {
|
|
36
|
+
[data_type_t.DTP_STRING]: "DTP_STRING",
|
|
37
|
+
[data_type_t.DTP_BOOLEAN]: "DTP_BOOLEAN",
|
|
38
|
+
[data_type_t.DTP_INTEGER]: "DTP_INTEGER",
|
|
39
|
+
[data_type_t.DTP_REAL]: "DTP_REAL",
|
|
40
|
+
[data_type_t.DTP_LIST]: "DTP_LIST",
|
|
41
|
+
[data_type_t.DTP_DICT]: "DTP_DICT",
|
|
42
|
+
[data_type_t.DTP_JSON]: "DTP_JSON",
|
|
43
|
+
[data_type_t.DTP_POINTER]: "DTP_POINTER",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/************************************************************
|
|
47
|
+
* The names of the bits set in `bits`, in table order.
|
|
48
|
+
************************************************************/
|
|
49
|
+
function bits2names(table, bits)
|
|
50
|
+
{
|
|
51
|
+
let names = [];
|
|
52
|
+
let value = Number(bits) || 0;
|
|
53
|
+
|
|
54
|
+
for(let name of Object.keys(table)) {
|
|
55
|
+
if((value & table[name]) !== 0) {
|
|
56
|
+
names.push(name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return names;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/************************************************************
|
|
63
|
+
* The name of a data type. An unknown type is reported as
|
|
64
|
+
* the number it is -- silently drawing "DTP_STRING" for a
|
|
65
|
+
* type nobody registered would be worse than saying "17".
|
|
66
|
+
************************************************************/
|
|
67
|
+
function data_type_name(type)
|
|
68
|
+
{
|
|
69
|
+
return DATA_TYPE_NAMES[type] || String(type);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/************************************************************
|
|
73
|
+
* One SDATA descriptor -> one row.
|
|
74
|
+
*
|
|
75
|
+
* A DTP_POINTER default is a live object (a DOM node, a
|
|
76
|
+
* gobj): it is never printed, only named, because a viewer
|
|
77
|
+
* that serializes it would either throw or dump the page.
|
|
78
|
+
************************************************************/
|
|
79
|
+
function sdatadesc2json(it)
|
|
80
|
+
{
|
|
81
|
+
let row = {
|
|
82
|
+
id: it.name,
|
|
83
|
+
type: data_type_name(it.type),
|
|
84
|
+
flag: bits2names(sdata_flag_t, it.flag),
|
|
85
|
+
description: it.description || "",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if(it.type === data_type_t.DTP_POINTER) {
|
|
89
|
+
row.default_value = (it.default_value === null ||
|
|
90
|
+
it.default_value === undefined)? null: "<pointer>";
|
|
91
|
+
} else {
|
|
92
|
+
row.default_value = (it.default_value === undefined)?
|
|
93
|
+
null: it.default_value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return row;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/************************************************************
|
|
100
|
+
* The attrs table of a gclass.
|
|
101
|
+
************************************************************/
|
|
102
|
+
function attrs2json(attrs_table)
|
|
103
|
+
{
|
|
104
|
+
let rows = [];
|
|
105
|
+
if(!Array.isArray(attrs_table)) {
|
|
106
|
+
return rows;
|
|
107
|
+
}
|
|
108
|
+
for(let it of attrs_table) {
|
|
109
|
+
if(!it || !it.name) {
|
|
110
|
+
continue; /* SDATA_END() */
|
|
111
|
+
}
|
|
112
|
+
rows.push(sdatadesc2json(it));
|
|
113
|
+
}
|
|
114
|
+
return rows;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/************************************************************
|
|
118
|
+
* The command table of a gclass, with the parameters of
|
|
119
|
+
* each command (the `schema` slot of SDATACM).
|
|
120
|
+
************************************************************/
|
|
121
|
+
function commands2json(command_table)
|
|
122
|
+
{
|
|
123
|
+
let rows = [];
|
|
124
|
+
if(!Array.isArray(command_table)) {
|
|
125
|
+
return rows;
|
|
126
|
+
}
|
|
127
|
+
for(let it of command_table) {
|
|
128
|
+
if(!it || !it.name) {
|
|
129
|
+
continue; /* SDATA_END() */
|
|
130
|
+
}
|
|
131
|
+
let row = {
|
|
132
|
+
id: it.name,
|
|
133
|
+
alias: it.alias || [],
|
|
134
|
+
description: it.description || "",
|
|
135
|
+
};
|
|
136
|
+
if(Array.isArray(it.schema)) {
|
|
137
|
+
row.parameters = attrs2json(it.schema);
|
|
138
|
+
}
|
|
139
|
+
rows.push(row);
|
|
140
|
+
}
|
|
141
|
+
return rows;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/************************************************************
|
|
145
|
+
* The methods the gclass actually implements. A table of
|
|
146
|
+
* every possible one, mostly empty, tells the reader nothing.
|
|
147
|
+
************************************************************/
|
|
148
|
+
function methods2json(mt)
|
|
149
|
+
{
|
|
150
|
+
let names = [];
|
|
151
|
+
if(!mt || typeof mt !== "object") {
|
|
152
|
+
return names;
|
|
153
|
+
}
|
|
154
|
+
for(let key of Object.keys(mt)) {
|
|
155
|
+
if(typeof mt[key] === "function") {
|
|
156
|
+
names.push(key);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return names;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/************************************************************
|
|
163
|
+
* The FSM: the events the gclass declares, and what each
|
|
164
|
+
* state does with them.
|
|
165
|
+
************************************************************/
|
|
166
|
+
function fsm2json(gclass)
|
|
167
|
+
{
|
|
168
|
+
let events = [];
|
|
169
|
+
for(let ev of (gclass.dl_events || [])) {
|
|
170
|
+
events.push({
|
|
171
|
+
event_name: ev.event_name,
|
|
172
|
+
event_flag: bits2names(event_flag_t, ev.event_flag),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let states = {};
|
|
177
|
+
for(let st of (gclass.dl_states || [])) {
|
|
178
|
+
let actions = [];
|
|
179
|
+
for(let ac of (st.dl_actions || [])) {
|
|
180
|
+
actions.push({
|
|
181
|
+
event_name: ac.event_name,
|
|
182
|
+
action: ac.action? (ac.action.name || "action"): "",
|
|
183
|
+
next_state: ac.next_state || "",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
states[st.state_name] = actions;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {events: events, states: states};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/************************************************************
|
|
193
|
+
* The description of a gclass REGISTERED IN THIS BROWSER YUNO,
|
|
194
|
+
* or null when the registry does not know that name.
|
|
195
|
+
*
|
|
196
|
+
* Null is the answer for a gclass of a BACKEND yuno too: the
|
|
197
|
+
* browser registry carries the GUI gclasses and nothing else,
|
|
198
|
+
* so the caller asks the backend `view-gclass` for those and
|
|
199
|
+
* shows whatever it answers -- this same shape.
|
|
200
|
+
************************************************************/
|
|
201
|
+
function describe_local_gclass(gclass_name)
|
|
202
|
+
{
|
|
203
|
+
if(!gclass_name) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let gclass = gclass_find_by_name(gclass_name, false);
|
|
208
|
+
if(!gclass) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
id: gclass.gclass_name,
|
|
214
|
+
gcflag: bits2names(gclass_flag_t, gclass.gclass_flag),
|
|
215
|
+
attrs: attrs2json(gclass.attrs_table),
|
|
216
|
+
commands: commands2json(gclass.command_table),
|
|
217
|
+
gclass_methods: methods2json(gclass.gmt),
|
|
218
|
+
internal_methods: methods2json(gclass.lmt),
|
|
219
|
+
FSM: fsm2json(gclass),
|
|
220
|
+
instances: Number(gclass.instances) || 0,
|
|
221
|
+
trace_levels: Array.isArray(gclass.s_user_trace_level)?
|
|
222
|
+
gclass.s_user_trace_level: [],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export {
|
|
227
|
+
bits2names,
|
|
228
|
+
data_type_name,
|
|
229
|
+
attrs2json,
|
|
230
|
+
commands2json,
|
|
231
|
+
methods2json,
|
|
232
|
+
fsm2json,
|
|
233
|
+
describe_local_gclass,
|
|
234
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {describe, it, expect, beforeAll} from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
SDATA,
|
|
4
|
+
SDATA_END,
|
|
5
|
+
SDATACM,
|
|
6
|
+
data_type_t,
|
|
7
|
+
event_flag_t,
|
|
8
|
+
gclass_create,
|
|
9
|
+
gclass_flag_t,
|
|
10
|
+
sdata_flag_t,
|
|
11
|
+
} from "@yuneta/gobj-js";
|
|
12
|
+
|
|
13
|
+
import {bits2names, describe_local_gclass} from "./gclass_describe.js";
|
|
14
|
+
|
|
15
|
+
/* A gclass with one of everything the viewer draws. */
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
const attrs_table = [
|
|
18
|
+
SDATA(data_type_t.DTP_STRING, "title", sdata_flag_t.SDF_RD, "", "The title"),
|
|
19
|
+
SDATA(data_type_t.DTP_INTEGER, "timeout",
|
|
20
|
+
sdata_flag_t.SDF_WR|sdata_flag_t.SDF_PERSIST, 1000, "Timeout"),
|
|
21
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "The DOM node"),
|
|
22
|
+
SDATA_END(),
|
|
23
|
+
];
|
|
24
|
+
const command_table = [
|
|
25
|
+
SDATACM(data_type_t.DTP_STRING, "help", ["h"], [
|
|
26
|
+
SDATA(data_type_t.DTP_STRING, "cmd", 0, "", "The command"),
|
|
27
|
+
SDATA_END(),
|
|
28
|
+
], null, "Ask for help"),
|
|
29
|
+
SDATA_END(),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
gclass_create(
|
|
33
|
+
"C_TEST_DESCRIBE",
|
|
34
|
+
[
|
|
35
|
+
["EV_GO", 0],
|
|
36
|
+
["EV_DONE", event_flag_t.EVF_OUTPUT_EVENT|event_flag_t.EVF_NO_WARN_SUBS],
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
["ST_IDLE", [["EV_GO", function ac_go() {}, "ST_WORKING"]]],
|
|
40
|
+
["ST_WORKING", [["EV_DONE", function ac_done() {}, "ST_IDLE"]]],
|
|
41
|
+
],
|
|
42
|
+
{mt_create: function() {}, mt_start: function() {}},
|
|
43
|
+
0,
|
|
44
|
+
attrs_table,
|
|
45
|
+
{},
|
|
46
|
+
0,
|
|
47
|
+
command_table,
|
|
48
|
+
0,
|
|
49
|
+
gclass_flag_t.gcflag_singleton
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("bits2names", () => {
|
|
54
|
+
it("names every bit set, and nothing else", () => {
|
|
55
|
+
expect(bits2names(sdata_flag_t,
|
|
56
|
+
sdata_flag_t.SDF_RD|sdata_flag_t.SDF_PERSIST))
|
|
57
|
+
.toEqual(["SDF_RD", "SDF_PERSIST"]);
|
|
58
|
+
expect(bits2names(sdata_flag_t, 0)).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("describe_local_gclass", () => {
|
|
63
|
+
it("an unregistered gclass is null, never an empty document", () => {
|
|
64
|
+
expect(describe_local_gclass("C_NOT_THERE")).toBe(null);
|
|
65
|
+
expect(describe_local_gclass("")).toBe(null);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("answers the shape the backend view-gclass answers", () => {
|
|
69
|
+
let g = describe_local_gclass("C_TEST_DESCRIBE");
|
|
70
|
+
expect(Object.keys(g)).toEqual([
|
|
71
|
+
"id", "gcflag", "attrs", "commands", "gclass_methods",
|
|
72
|
+
"internal_methods", "FSM", "instances", "trace_levels",
|
|
73
|
+
]);
|
|
74
|
+
expect(g.id).toBe("C_TEST_DESCRIBE");
|
|
75
|
+
expect(g.gcflag).toEqual(["gcflag_singleton"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("names the type and the flags of every attr", () => {
|
|
79
|
+
let attrs = describe_local_gclass("C_TEST_DESCRIBE").attrs;
|
|
80
|
+
expect(attrs.length).toBe(3);
|
|
81
|
+
expect(attrs[0]).toEqual({
|
|
82
|
+
id: "title",
|
|
83
|
+
type: "DTP_STRING",
|
|
84
|
+
flag: ["SDF_RD"],
|
|
85
|
+
description: "The title",
|
|
86
|
+
default_value: "",
|
|
87
|
+
});
|
|
88
|
+
expect(attrs[1].flag).toEqual(["SDF_WR", "SDF_PERSIST"]);
|
|
89
|
+
expect(attrs[1].default_value).toBe(1000);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("never prints a pointer default -- it names it", () => {
|
|
93
|
+
let attrs = describe_local_gclass("C_TEST_DESCRIBE").attrs;
|
|
94
|
+
let container = attrs.find(a => a.id === "$container");
|
|
95
|
+
expect(container.type).toBe("DTP_POINTER");
|
|
96
|
+
expect(container.default_value).toBe(null);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("carries the commands with their parameters", () => {
|
|
100
|
+
let cmds = describe_local_gclass("C_TEST_DESCRIBE").commands;
|
|
101
|
+
expect(cmds.length).toBe(1);
|
|
102
|
+
expect(cmds[0].id).toBe("help");
|
|
103
|
+
expect(cmds[0].alias).toEqual(["h"]);
|
|
104
|
+
expect(cmds[0].parameters.map(p => p.id)).toEqual(["cmd"]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("lists only the methods the gclass implements", () => {
|
|
108
|
+
let g = describe_local_gclass("C_TEST_DESCRIBE");
|
|
109
|
+
expect(g.gclass_methods).toEqual(["mt_create", "mt_start"]);
|
|
110
|
+
expect(g.internal_methods).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("draws the FSM as events plus what each state does with them", () => {
|
|
114
|
+
let fsm = describe_local_gclass("C_TEST_DESCRIBE").FSM;
|
|
115
|
+
expect(fsm.events).toEqual([
|
|
116
|
+
{event_name: "EV_GO", event_flag: []},
|
|
117
|
+
{event_name: "EV_DONE",
|
|
118
|
+
event_flag: ["EVF_NO_WARN_SUBS", "EVF_OUTPUT_EVENT"]},
|
|
119
|
+
]);
|
|
120
|
+
expect(Object.keys(fsm.states)).toEqual(["ST_IDLE", "ST_WORKING"]);
|
|
121
|
+
expect(fsm.states["ST_IDLE"]).toEqual([
|
|
122
|
+
{event_name: "EV_GO", action: "ac_go", next_state: "ST_WORKING"},
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
});
|