@yuneta/gobj-ui 7.25.20 → 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 CHANGED
@@ -593,6 +593,13 @@ Two layout facts the browser taught this component, both worth keeping:
593
593
  - Output event: `EV_EXPAND_PATH {path, size}` (`EVF_OUTPUT_EVENT`) — the parent
594
594
  must declare it in its own FSM (CHILD subscription model), and ANSWER it: the
595
595
  stub shows "loading" until `EV_SUBTREE_LOADED` or `EV_SUBTREE_ERROR` arrives.
596
+ **Every** way the drill can fail is an answer: a request asked out of
597
+ session, or refused by the transport on its way out, never reaches the
598
+ backend, so no answer will ever come back for it -- the host answers it
599
+ itself (`C_YUI_TREEDB_TOPICS` / `C_YUI_TREEDB_GRAPH` since `7.25.21`: `i18n:
600
+ "no session"`, or the transport's refusal as `error`). While a path is
601
+ pending the viewer ignores every click on it, so a drill left unanswered
602
+ is a dead stub for the life of the viewer.
596
603
  A host that cannot read a subtree says so with the error, as
597
604
  `C_YUI_TREEDB_TOPIC_WITH_FORM` does for its schema, cell and table viewers
598
605
  since `7.25.19` (before, it declared nothing: *"Event NOT DEFINED"*):
@@ -25136,6 +25136,27 @@ function request_schema_file(gobj) {
25136
25136
  if (ret) (0, _yuneta_gobj_js.log_error)(ret);
25137
25137
  }
25138
25138
  /************************************************************
25139
+ * A drill (or the whole document) that will get no answer from the
25140
+ * backend is answered HERE, or its stub shows "loading" for the life
25141
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
25142
+ * `answer` is {i18n: <key>} or {error: <text>}.
25143
+ ************************************************************/
25144
+ function answer_print_tranger_refused$1(gobj, path, answer) {
25145
+ let jv = gobj.priv.json_gobj;
25146
+ if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
25147
+ if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
25148
+ else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
25149
+ }
25150
+ /************************************************************
25151
+ * The text of a transport's refusal: gobj_command() answers a string,
25152
+ * a command response or a number.
25153
+ ************************************************************/
25154
+ function refusal_text$1(ret) {
25155
+ if (typeof ret === "string" && ret) return ret;
25156
+ if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
25157
+ return "print-tranger failed";
25158
+ }
25159
+ /************************************************************
25139
25160
  * The whole tranger, for the raw-json viewer.
25140
25161
  ************************************************************/
25141
25162
  function request_print_tranger$1(gobj, path) {
@@ -25143,11 +25164,12 @@ function request_print_tranger$1(gobj, path) {
25143
25164
  let remote = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "gobj_remote_yuno");
25144
25165
  if (!remote) {
25145
25166
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
25146
- let jv = priv.json_gobj;
25147
- if (path && jv && (0, _yuneta_gobj_js.is_gobj)(jv) && !(0, _yuneta_gobj_js.gobj_is_destroying)(jv)) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", {
25148
- path,
25149
- i18n: "no session"
25150
- }, gobj);
25167
+ answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
25168
+ return;
25169
+ }
25170
+ if (!is_connected$1(gobj)) {
25171
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
25172
+ answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
25151
25173
  return;
25152
25174
  }
25153
25175
  let ret = (0, _yuneta_gobj_js.gobj_command)(remote, "print-tranger", {
@@ -25158,7 +25180,10 @@ function request_print_tranger$1(gobj, path) {
25158
25180
  path: path || "",
25159
25181
  __md_command__: { path: path || "" }
25160
25182
  }, gobj);
25161
- if (ret) (0, _yuneta_gobj_js.log_error)(ret);
25183
+ if (ret) {
25184
+ (0, _yuneta_gobj_js.log_error)(ret);
25185
+ answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
25186
+ }
25162
25187
  }
25163
25188
  /***************************************************************
25164
25189
  * The last gate before a write leaves for the yuno. The per-topic
@@ -68344,6 +68369,27 @@ function close_json_viewer(gobj) {
68344
68369
  }
68345
68370
  }
68346
68371
  /************************************************************
68372
+ * A drill (or the whole document) that will get no answer from the
68373
+ * backend is answered HERE, or its stub shows "loading" for the life
68374
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
68375
+ * `answer` is {i18n: <key>} or {error: <text>}.
68376
+ ************************************************************/
68377
+ function answer_print_tranger_refused(gobj, path, answer) {
68378
+ let jv = gobj.priv.json_gobj;
68379
+ if (!jv || !(0, _yuneta_gobj_js.is_gobj)(jv) || (0, _yuneta_gobj_js.gobj_is_destroying)(jv)) return;
68380
+ if (path) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
68381
+ else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t: i18next.t });
68382
+ }
68383
+ /************************************************************
68384
+ * The text of a transport's refusal: gobj_command() answers a string,
68385
+ * a command response or a number.
68386
+ ************************************************************/
68387
+ function refusal_text(ret) {
68388
+ if (typeof ret === "string" && ret) return ret;
68389
+ if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
68390
+ return "print-tranger failed";
68391
+ }
68392
+ /************************************************************
68347
68393
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
68348
68394
  * bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
68349
68395
  * small payload of `__collapsed__` stubs the viewer expands on demand.
@@ -68352,11 +68398,12 @@ function request_print_tranger(gobj, path) {
68352
68398
  let priv = gobj.priv;
68353
68399
  if (!priv.gobj_remote_yuno) {
68354
68400
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
68355
- let jv = priv.json_gobj;
68356
- if (path && jv && (0, _yuneta_gobj_js.is_gobj)(jv) && !(0, _yuneta_gobj_js.gobj_is_destroying)(jv)) (0, _yuneta_gobj_js.gobj_send_event)(jv, "EV_SUBTREE_ERROR", {
68357
- path,
68358
- i18n: "no session"
68359
- }, gobj);
68401
+ answer_print_tranger_refused(gobj, path, { i18n: "no session" });
68402
+ return;
68403
+ }
68404
+ if (!is_connected(gobj)) {
68405
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: print-tranger '${path || ""}' not asked: no session`);
68406
+ answer_print_tranger_refused(gobj, path, { i18n: "no session" });
68360
68407
  return;
68361
68408
  }
68362
68409
  let ret = (0, _yuneta_gobj_js.gobj_command)(priv.gobj_remote_yuno, "print-tranger", {
@@ -68367,7 +68414,10 @@ function request_print_tranger(gobj, path) {
68367
68414
  path: path || "",
68368
68415
  __md_command__: { path: path || "" }
68369
68416
  }, gobj);
68370
- if (ret) (0, _yuneta_gobj_js.log_error)(ret);
68417
+ if (ret) {
68418
+ (0, _yuneta_gobj_js.log_error)(ret);
68419
+ answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
68420
+ }
68371
68421
  }
68372
68422
  /************************************************************
68373
68423
  * Populate layout dropdown from child's available layouts
@@ -25131,6 +25131,27 @@ function request_schema_file(gobj) {
25131
25131
  if (ret) log_error(ret);
25132
25132
  }
25133
25133
  /************************************************************
25134
+ * A drill (or the whole document) that will get no answer from the
25135
+ * backend is answered HERE, or its stub shows "loading" for the life
25136
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
25137
+ * `answer` is {i18n: <key>} or {error: <text>}.
25138
+ ************************************************************/
25139
+ function answer_print_tranger_refused$1(gobj, path, answer) {
25140
+ let jv = gobj.priv.json_gobj;
25141
+ if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
25142
+ if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
25143
+ else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
25144
+ }
25145
+ /************************************************************
25146
+ * The text of a transport's refusal: gobj_command() answers a string,
25147
+ * a command response or a number.
25148
+ ************************************************************/
25149
+ function refusal_text$1(ret) {
25150
+ if (typeof ret === "string" && ret) return ret;
25151
+ if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
25152
+ return "print-tranger failed";
25153
+ }
25154
+ /************************************************************
25134
25155
  * The whole tranger, for the raw-json viewer.
25135
25156
  ************************************************************/
25136
25157
  function request_print_tranger$1(gobj, path) {
@@ -25138,11 +25159,12 @@ function request_print_tranger$1(gobj, path) {
25138
25159
  let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
25139
25160
  if (!remote) {
25140
25161
  log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
25141
- let jv = priv.json_gobj;
25142
- if (path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) gobj_send_event(jv, "EV_SUBTREE_ERROR", {
25143
- path,
25144
- i18n: "no session"
25145
- }, gobj);
25162
+ answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
25163
+ return;
25164
+ }
25165
+ if (!is_connected$1(gobj)) {
25166
+ log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
25167
+ answer_print_tranger_refused$1(gobj, path, { i18n: "no session" });
25146
25168
  return;
25147
25169
  }
25148
25170
  let ret = gobj_command(remote, "print-tranger", {
@@ -25153,7 +25175,10 @@ function request_print_tranger$1(gobj, path) {
25153
25175
  path: path || "",
25154
25176
  __md_command__: { path: path || "" }
25155
25177
  }, gobj);
25156
- if (ret) log_error(ret);
25178
+ if (ret) {
25179
+ log_error(ret);
25180
+ answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
25181
+ }
25157
25182
  }
25158
25183
  /***************************************************************
25159
25184
  * The last gate before a write leaves for the yuno. The per-topic
@@ -68339,6 +68364,27 @@ function close_json_viewer(gobj) {
68339
68364
  }
68340
68365
  }
68341
68366
  /************************************************************
68367
+ * A drill (or the whole document) that will get no answer from the
68368
+ * backend is answered HERE, or its stub shows "loading" for the life
68369
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
68370
+ * `answer` is {i18n: <key>} or {error: <text>}.
68371
+ ************************************************************/
68372
+ function answer_print_tranger_refused(gobj, path, answer) {
68373
+ let jv = gobj.priv.json_gobj;
68374
+ if (!jv || !is_gobj(jv) || gobj_is_destroying(jv)) return;
68375
+ if (path) gobj_send_event(jv, "EV_SUBTREE_ERROR", Object.assign({ path }, answer), gobj);
68376
+ else yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, { t });
68377
+ }
68378
+ /************************************************************
68379
+ * The text of a transport's refusal: gobj_command() answers a string,
68380
+ * a command response or a number.
68381
+ ************************************************************/
68382
+ function refusal_text(ret) {
68383
+ if (typeof ret === "string" && ret) return ret;
68384
+ if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
68385
+ return "print-tranger failed";
68386
+ }
68387
+ /************************************************************
68342
68388
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
68343
68389
  * bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
68344
68390
  * small payload of `__collapsed__` stubs the viewer expands on demand.
@@ -68347,11 +68393,12 @@ function request_print_tranger(gobj, path) {
68347
68393
  let priv = gobj.priv;
68348
68394
  if (!priv.gobj_remote_yuno) {
68349
68395
  log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
68350
- let jv = priv.json_gobj;
68351
- if (path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) gobj_send_event(jv, "EV_SUBTREE_ERROR", {
68352
- path,
68353
- i18n: "no session"
68354
- }, gobj);
68396
+ answer_print_tranger_refused(gobj, path, { i18n: "no session" });
68397
+ return;
68398
+ }
68399
+ if (!is_connected(gobj)) {
68400
+ log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' not asked: no session`);
68401
+ answer_print_tranger_refused(gobj, path, { i18n: "no session" });
68355
68402
  return;
68356
68403
  }
68357
68404
  let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger", {
@@ -68362,7 +68409,10 @@ function request_print_tranger(gobj, path) {
68362
68409
  path: path || "",
68363
68410
  __md_command__: { path: path || "" }
68364
68411
  }, gobj);
68365
- if (ret) log_error(ret);
68412
+ if (ret) {
68413
+ log_error(ret);
68414
+ answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
68415
+ }
68366
68416
  }
68367
68417
  /************************************************************
68368
68418
  * Populate layout dropdown from child's available layouts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.25.20",
3
+ "version": "7.25.21",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@rollup/plugin-terser": "^1.0.0",
32
32
  "@vitest/coverage-v8": "^4.1.10",
33
- "@yuneta/gobj-js": "^7.25.6",
33
+ "@yuneta/gobj-js": "^7.25.7",
34
34
  "vite": "^8.2.1",
35
35
  "vitest": "^4.1.10"
36
36
  },
@@ -1312,6 +1312,42 @@ function close_json_viewer(gobj)
1312
1312
  }
1313
1313
  }
1314
1314
 
1315
+ /************************************************************
1316
+ * A drill (or the whole document) that will get no answer from the
1317
+ * backend is answered HERE, or its stub shows "loading" for the life
1318
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
1319
+ * `answer` is {i18n: <key>} or {error: <text>}.
1320
+ ************************************************************/
1321
+ function answer_print_tranger_refused(gobj, path, answer)
1322
+ {
1323
+ let jv = gobj.priv.json_gobj;
1324
+ if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
1325
+ return; /* no viewer to answer: the refusal is already logged */
1326
+ }
1327
+ if(path) {
1328
+ gobj_send_event(jv, "EV_SUBTREE_ERROR",
1329
+ Object.assign({path: path}, answer), gobj);
1330
+ } else {
1331
+ yui_shell_show_error(yui_shell_of(gobj),
1332
+ answer.i18n || answer.error, {t: t});
1333
+ }
1334
+ }
1335
+
1336
+ /************************************************************
1337
+ * The text of a transport's refusal: gobj_command() answers a string,
1338
+ * a command response or a number.
1339
+ ************************************************************/
1340
+ function refusal_text(ret)
1341
+ {
1342
+ if(typeof ret === "string" && ret) {
1343
+ return ret;
1344
+ }
1345
+ if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
1346
+ return ret.comment;
1347
+ }
1348
+ return "print-tranger failed";
1349
+ }
1350
+
1315
1351
  /************************************************************
1316
1352
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
1317
1353
  * bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
@@ -1322,11 +1358,13 @@ function request_print_tranger(gobj, path)
1322
1358
  let priv = gobj.priv;
1323
1359
  if(!priv.gobj_remote_yuno) {
1324
1360
  log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
1325
- let jv = priv.json_gobj;
1326
- if(path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) {
1327
- gobj_send_event(jv, "EV_SUBTREE_ERROR",
1328
- {path: path, i18n: "no session"}, gobj);
1329
- }
1361
+ answer_print_tranger_refused(gobj, path, {i18n: "no session"});
1362
+ return;
1363
+ }
1364
+ if(!is_connected(gobj)) {
1365
+ log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
1366
+ `not asked: no session`);
1367
+ answer_print_tranger_refused(gobj, path, {i18n: "no session"});
1330
1368
  return;
1331
1369
  }
1332
1370
  let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger",
@@ -1342,6 +1380,7 @@ function request_print_tranger(gobj, path)
1342
1380
  }, gobj);
1343
1381
  if(ret) {
1344
1382
  log_error(ret);
1383
+ answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
1345
1384
  }
1346
1385
  }
1347
1386
 
@@ -1943,6 +1943,42 @@ function request_schema_file(gobj)
1943
1943
  }
1944
1944
  }
1945
1945
 
1946
+ /************************************************************
1947
+ * A drill (or the whole document) that will get no answer from the
1948
+ * backend is answered HERE, or its stub shows "loading" for the life
1949
+ * of the viewer: C_YUI_JSON ignores every click on a pending path.
1950
+ * `answer` is {i18n: <key>} or {error: <text>}.
1951
+ ************************************************************/
1952
+ function answer_print_tranger_refused(gobj, path, answer)
1953
+ {
1954
+ let jv = gobj.priv.json_gobj;
1955
+ if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
1956
+ return; /* no viewer to answer: the refusal is already logged */
1957
+ }
1958
+ if(path) {
1959
+ gobj_send_event(jv, "EV_SUBTREE_ERROR",
1960
+ Object.assign({path: path}, answer), gobj);
1961
+ } else {
1962
+ yui_shell_show_error(yui_shell_of(gobj),
1963
+ answer.i18n || answer.error, {t: t});
1964
+ }
1965
+ }
1966
+
1967
+ /************************************************************
1968
+ * The text of a transport's refusal: gobj_command() answers a string,
1969
+ * a command response or a number.
1970
+ ************************************************************/
1971
+ function refusal_text(ret)
1972
+ {
1973
+ if(typeof ret === "string" && ret) {
1974
+ return ret;
1975
+ }
1976
+ if(ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) {
1977
+ return ret.comment;
1978
+ }
1979
+ return "print-tranger failed";
1980
+ }
1981
+
1946
1982
  /************************************************************
1947
1983
  * The whole tranger, for the raw-json viewer.
1948
1984
  ************************************************************/
@@ -1952,11 +1988,13 @@ function request_print_tranger(gobj, path)
1952
1988
  let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
1953
1989
  if(!remote) {
1954
1990
  log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
1955
- let jv = priv.json_gobj;
1956
- if(path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) {
1957
- gobj_send_event(jv, "EV_SUBTREE_ERROR",
1958
- {path: path, i18n: "no session"}, gobj);
1959
- }
1991
+ answer_print_tranger_refused(gobj, path, {i18n: "no session"});
1992
+ return;
1993
+ }
1994
+ if(!is_connected(gobj)) {
1995
+ log_warning(`${gobj_short_name(gobj)}: print-tranger '${path || ""}' ` +
1996
+ `not asked: no session`);
1997
+ answer_print_tranger_refused(gobj, path, {i18n: "no session"});
1960
1998
  return;
1961
1999
  }
1962
2000
  let ret = gobj_command(remote, "print-tranger",
@@ -1972,6 +2010,7 @@ function request_print_tranger(gobj, path)
1972
2010
  }, gobj);
1973
2011
  if(ret) {
1974
2012
  log_error(ret);
2013
+ answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
1975
2014
  }
1976
2015
  }
1977
2016
 
@@ -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
+ }