dataops-testgen 2.2.0__py3-none-any.whl

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 (270) hide show
  1. dataops_testgen-2.2.0.dist-info/LICENSE +203 -0
  2. dataops_testgen-2.2.0.dist-info/METADATA +287 -0
  3. dataops_testgen-2.2.0.dist-info/NOTICE +5 -0
  4. dataops_testgen-2.2.0.dist-info/RECORD +270 -0
  5. dataops_testgen-2.2.0.dist-info/WHEEL +5 -0
  6. dataops_testgen-2.2.0.dist-info/entry_points.txt +2 -0
  7. dataops_testgen-2.2.0.dist-info/top_level.txt +1 -0
  8. testgen/__init__.py +0 -0
  9. testgen/__main__.py +770 -0
  10. testgen/commands/__init__.py +0 -0
  11. testgen/commands/queries/__init__.py +0 -0
  12. testgen/commands/queries/execute_cat_tests_query.py +95 -0
  13. testgen/commands/queries/execute_tests_query.py +160 -0
  14. testgen/commands/queries/generate_tests_query.py +94 -0
  15. testgen/commands/queries/profiling_query.py +366 -0
  16. testgen/commands/queries/test_parameter_validation_query.py +88 -0
  17. testgen/commands/run_execute_cat_tests.py +162 -0
  18. testgen/commands/run_execute_tests.py +168 -0
  19. testgen/commands/run_generate_tests.py +107 -0
  20. testgen/commands/run_get_entities.py +122 -0
  21. testgen/commands/run_launch_db_config.py +84 -0
  22. testgen/commands/run_observability_exporter.py +330 -0
  23. testgen/commands/run_profiling_bridge.py +495 -0
  24. testgen/commands/run_quick_start.py +168 -0
  25. testgen/commands/run_setup_profiling_tools.py +96 -0
  26. testgen/commands/run_test_definition.py +146 -0
  27. testgen/commands/run_test_parameter_validation.py +135 -0
  28. testgen/commands/run_upgrade_db_config.py +156 -0
  29. testgen/common/__init__.py +8 -0
  30. testgen/common/clean_sql.py +53 -0
  31. testgen/common/credentials.py +25 -0
  32. testgen/common/database/__init__.py +0 -0
  33. testgen/common/database/database_service.py +629 -0
  34. testgen/common/database/flavor/__init__.py +0 -0
  35. testgen/common/database/flavor/flavor_service.py +75 -0
  36. testgen/common/database/flavor/mssql_flavor_service.py +34 -0
  37. testgen/common/database/flavor/postgresql_flavor_service.py +5 -0
  38. testgen/common/database/flavor/redshift_flavor_service.py +22 -0
  39. testgen/common/database/flavor/snowflake_flavor_service.py +69 -0
  40. testgen/common/database/flavor/trino_flavor_service.py +21 -0
  41. testgen/common/date_service.py +68 -0
  42. testgen/common/display_service.py +85 -0
  43. testgen/common/docker_service.py +76 -0
  44. testgen/common/encrypt.py +55 -0
  45. testgen/common/get_pipeline_parms.py +57 -0
  46. testgen/common/logs.py +79 -0
  47. testgen/common/process_service.py +62 -0
  48. testgen/common/read_file.py +69 -0
  49. testgen/settings.py +440 -0
  50. testgen/template/dbsetup/010_create_base_schema.sql +2 -0
  51. testgen/template/dbsetup/020_create_standard_functions_sprocs.sql +179 -0
  52. testgen/template/dbsetup/030_initialize_new_schema_structure.sql +735 -0
  53. testgen/template/dbsetup/040_populate_new_schema_project.sql +59 -0
  54. testgen/template/dbsetup/050_populate_new_schema_metadata.sql +1517 -0
  55. testgen/template/dbsetup/060_create_standard_views.sql +248 -0
  56. testgen/template/dbsetup/070_create_default_users.sql +17 -0
  57. testgen/template/dbsetup/075_grant_role_rights.sql +43 -0
  58. testgen/template/dbsetup/080_set_current_revision.sql +5 -0
  59. testgen/template/dbupgrade/0100_incremental_upgrade.sql +5 -0
  60. testgen/template/dbupgrade/0101_incremental_upgrade.sql +15 -0
  61. testgen/template/dbupgrade/0102_incremental_upgrade.sql +4 -0
  62. testgen/template/dbupgrade/0103_incremental_upgrade.sql +22 -0
  63. testgen/template/dbupgrade/0104_incremental_upgrade.sql +44 -0
  64. testgen/template/dbupgrade/0105_incremental_upgrade.sql +1 -0
  65. testgen/template/dbupgrade/0106_incremental_upgrade.sql +5 -0
  66. testgen/template/dbupgrade/0107_incremental_upgrade.sql +3 -0
  67. testgen/template/dbupgrade_helpers/get_tg_revision.sql +2 -0
  68. testgen/template/exec_cat_tests/ex_cat_build_agg_table_tests.sql +116 -0
  69. testgen/template/exec_cat_tests/ex_cat_get_distinct_tables.sql +11 -0
  70. testgen/template/exec_cat_tests/ex_cat_results_parse.sql +69 -0
  71. testgen/template/exec_cat_tests/ex_cat_retrieve_agg_test_parms.sql +6 -0
  72. testgen/template/exec_cat_tests/ex_cat_test_query.sql +8 -0
  73. testgen/template/execution/ex_finalize_test_run_results.sql +37 -0
  74. testgen/template/execution/ex_get_tests_non_cat.sql +47 -0
  75. testgen/template/execution/ex_update_test_record_in_testrun_table.sql +27 -0
  76. testgen/template/execution/ex_write_test_record_to_testrun_table.sql +6 -0
  77. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_no_drops_generic.sql +48 -0
  78. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_num_incr_generic.sql +34 -0
  79. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_above_generic.sql +49 -0
  80. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_within_generic.sql +49 -0
  81. testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_same_generic.sql +49 -0
  82. testgen/template/flavors/generic/exec_query_tests/ex_custom_query_generic.sql +39 -0
  83. testgen/template/flavors/generic/exec_query_tests/ex_data_match_2way_generic.sql +58 -0
  84. testgen/template/flavors/generic/exec_query_tests/ex_data_match_generic.sql +44 -0
  85. testgen/template/flavors/generic/exec_query_tests/ex_prior_match_generic.sql +37 -0
  86. testgen/template/flavors/generic/exec_query_tests/ex_relative_entropy_generic.sql +53 -0
  87. testgen/template/flavors/generic/exec_query_tests/ex_window_match_no_drops_generic.sql +46 -0
  88. testgen/template/flavors/generic/exec_query_tests/ex_window_match_same_generic.sql +59 -0
  89. testgen/template/flavors/generic/profiling/contingency_counts.sql +3 -0
  90. testgen/template/flavors/generic/validate_tests/ex_get_project_column_list_generic.sql +3 -0
  91. testgen/template/flavors/mssql/exec_query_tests/ex_relative_entropy_mssql.sql +53 -0
  92. testgen/template/flavors/mssql/profiling/project_ddf_query_mssql.sql +35 -0
  93. testgen/template/flavors/mssql/profiling/project_profiling_query_mssql.yaml +246 -0
  94. testgen/template/flavors/mssql/profiling/project_secondary_profiling_query_mssql.sql +36 -0
  95. testgen/template/flavors/mssql/setup_profiling_tools/00_drop_existing_functions_mssql.sql +8 -0
  96. testgen/template/flavors/mssql/setup_profiling_tools/01_create_functions_mssql.sql +12 -0
  97. testgen/template/flavors/mssql/setup_profiling_tools/02_create_functions_mssql.sql +54 -0
  98. testgen/template/flavors/mssql/setup_profiling_tools/create_qc_schema_mssql.sql +4 -0
  99. testgen/template/flavors/mssql/setup_profiling_tools/grant_execute_privileges_mssql.sql +1 -0
  100. testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_no_drops_postgresql.sql +46 -0
  101. testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_same_postgresql.sql +59 -0
  102. testgen/template/flavors/postgresql/profiling/project_ddf_query_postgresql.sql +42 -0
  103. testgen/template/flavors/postgresql/profiling/project_profiling_query_postgresql.yaml +225 -0
  104. testgen/template/flavors/postgresql/profiling/project_secondary_profiling_query_postgresql.sql +28 -0
  105. testgen/template/flavors/postgresql/setup_profiling_tools/create_functions_postgresql.sql +157 -0
  106. testgen/template/flavors/postgresql/setup_profiling_tools/create_qc_schema_postgresql.sql +1 -0
  107. testgen/template/flavors/postgresql/setup_profiling_tools/grant_execute_privileges_postgresql.sql +2 -0
  108. testgen/template/flavors/redshift/profiling/project_ddf_query_redshift.sql +38 -0
  109. testgen/template/flavors/redshift/profiling/project_profiling_query_redshift.yaml +221 -0
  110. testgen/template/flavors/redshift/profiling/project_secondary_profiling_query_redshift.sql +29 -0
  111. testgen/template/flavors/redshift/setup_profiling_tools/create_functions_redshift.sql +115 -0
  112. testgen/template/flavors/redshift/setup_profiling_tools/create_qc_schema_redshift.sql +1 -0
  113. testgen/template/flavors/redshift/setup_profiling_tools/grant_execute_privileges_redshift.sql +2 -0
  114. testgen/template/flavors/snowflake/profiling/project_ddf_query_snowflake.sql +38 -0
  115. testgen/template/flavors/snowflake/profiling/project_profiling_query_snowflake.yaml +220 -0
  116. testgen/template/flavors/snowflake/profiling/project_secondary_profiling_query_snowflake.sql +29 -0
  117. testgen/template/flavors/snowflake/setup_profiling_tools/create_functions_snowflake.sql +69 -0
  118. testgen/template/flavors/snowflake/setup_profiling_tools/create_qc_schema_snowflake.sql +1 -0
  119. testgen/template/flavors/snowflake/setup_profiling_tools/grant_execute_privileges_snowflake.sql +6 -0
  120. testgen/template/flavors/trino/profiling/project_profiling_query_trino.yaml +219 -0
  121. testgen/template/flavors/trino/setup_profiling_tools/create_functions_trino.sql +92 -0
  122. testgen/template/flavors/trino/setup_profiling_tools/create_qc_schema_trino.sql +1 -0
  123. testgen/template/gen_funny_cat_tests/gen_test_constant.sql +104 -0
  124. testgen/template/gen_funny_cat_tests/gen_test_distinct_value_ct.sql +98 -0
  125. testgen/template/gen_funny_cat_tests/gen_test_row_ct.sql +57 -0
  126. testgen/template/gen_funny_cat_tests/gen_test_row_ct_pct.sql +59 -0
  127. testgen/template/generation/gen_delete_old_tests.sql +5 -0
  128. testgen/template/generation/gen_insert_test_suite.sql +5 -0
  129. testgen/template/generation/gen_retrieve_or_insert_test_suite.sql +58 -0
  130. testgen/template/generation/gen_standard_test_type_list.sql +13 -0
  131. testgen/template/generation/gen_standard_tests.sql +48 -0
  132. testgen/template/get_entities/get_connection.sql +21 -0
  133. testgen/template/get_entities/get_connections_list.sql +9 -0
  134. testgen/template/get_entities/get_latest.sql +4 -0
  135. testgen/template/get_entities/get_profile.sql +12 -0
  136. testgen/template/get_entities/get_profile_info.sql +17 -0
  137. testgen/template/get_entities/get_profile_list.sql +17 -0
  138. testgen/template/get_entities/get_profile_screen.sql +275 -0
  139. testgen/template/get_entities/get_project_list.sql +6 -0
  140. testgen/template/get_entities/get_table_group_list.sql +10 -0
  141. testgen/template/get_entities/get_test_generation_list.sql +18 -0
  142. testgen/template/get_entities/get_test_info.sql +41 -0
  143. testgen/template/get_entities/get_test_results_for_run_cli.sql +16 -0
  144. testgen/template/get_entities/get_test_run_list.sql +24 -0
  145. testgen/template/get_entities/get_test_suite.sql +13 -0
  146. testgen/template/get_entities/get_test_suite_list.sql +18 -0
  147. testgen/template/get_entities/list_test_types.sql +4 -0
  148. testgen/template/observability/get_event_data.sql +23 -0
  149. testgen/template/observability/get_test_results.sql +41 -0
  150. testgen/template/observability/update_test_results_exported_to_observability.sql +12 -0
  151. testgen/template/parms/parms_profiling.sql +34 -0
  152. testgen/template/parms/parms_test_execution.sql +13 -0
  153. testgen/template/parms/parms_test_gen.sql +23 -0
  154. testgen/template/profiling/contingency_columns.sql +7 -0
  155. testgen/template/profiling/datatype_suggestions.sql +56 -0
  156. testgen/template/profiling/functional_datatype.sql +523 -0
  157. testgen/template/profiling/functional_tabletype_stage.sql +48 -0
  158. testgen/template/profiling/functional_tabletype_update.sql +8 -0
  159. testgen/template/profiling/pii_flag.sql +133 -0
  160. testgen/template/profiling/profile_anomalies_screen_column.sql +22 -0
  161. testgen/template/profiling/profile_anomalies_screen_multi_column.sql +58 -0
  162. testgen/template/profiling/profile_anomalies_screen_table.sql +22 -0
  163. testgen/template/profiling/profile_anomalies_screen_table_dates.sql +30 -0
  164. testgen/template/profiling/profile_anomalies_screen_variants.sql +40 -0
  165. testgen/template/profiling/profile_anomaly_types_get.sql +3 -0
  166. testgen/template/profiling/project_get_table_sample_count.sql +22 -0
  167. testgen/template/profiling/project_profile_run_record_insert.sql +8 -0
  168. testgen/template/profiling/project_profile_run_record_update.sql +5 -0
  169. testgen/template/profiling/project_profile_run_record_update_status.sql +5 -0
  170. testgen/template/profiling/project_update_profile_results_to_estimates.sql +32 -0
  171. testgen/template/profiling/refresh_anomalies.sql +33 -0
  172. testgen/template/profiling/refresh_data_chars_from_profiling.sql +156 -0
  173. testgen/template/profiling/secondary_profiling_columns.sql +12 -0
  174. testgen/template/profiling/secondary_profiling_delete.sql +4 -0
  175. testgen/template/profiling/secondary_profiling_update.sql +18 -0
  176. testgen/template/quick_start/populate_target_data.sql +1077 -0
  177. testgen/template/quick_start/recreate_target_data_schema.sql +167 -0
  178. testgen/template/quick_start/update_target_data.sql +100 -0
  179. testgen/template/updates/create_tmp_test_definition.sql +19 -0
  180. testgen/template/updates/get_test_def_parms.sql +38 -0
  181. testgen/template/updates/populate_stg_test_definitions.sql +184 -0
  182. testgen/template/validate_tests/ex_disable_tests_test_definitions.sql +5 -0
  183. testgen/template/validate_tests/ex_flag_tests_test_definitions.sql +64 -0
  184. testgen/template/validate_tests/ex_get_project_column_list_generic.sql +3 -0
  185. testgen/template/validate_tests/ex_get_test_column_list_tg.sql +65 -0
  186. testgen/template/validate_tests/ex_write_test_val_errors.sql +22 -0
  187. testgen/ui/__init__.py +0 -0
  188. testgen/ui/app.py +98 -0
  189. testgen/ui/assets/dk_logo.svg +46 -0
  190. testgen/ui/assets/question_mark.png +0 -0
  191. testgen/ui/assets/scripts.js +68 -0
  192. testgen/ui/assets/style.css +140 -0
  193. testgen/ui/bootstrap.py +109 -0
  194. testgen/ui/components/__init__.py +0 -0
  195. testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 +0 -0
  196. testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 +0 -0
  197. testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu4mxK.woff2 +0 -0
  198. testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 +0 -0
  199. testgen/ui/components/frontend/css/material-symbols-rounded.css +24 -0
  200. testgen/ui/components/frontend/css/material-symbols-rounded.woff2 +0 -0
  201. testgen/ui/components/frontend/css/roboto-font-faces.css +35 -0
  202. testgen/ui/components/frontend/css/shared.css +36 -0
  203. testgen/ui/components/frontend/img/dk_logo.svg +46 -0
  204. testgen/ui/components/frontend/index.html +17 -0
  205. testgen/ui/components/frontend/js/components/breadcrumbs.js +86 -0
  206. testgen/ui/components/frontend/js/components/button.js +66 -0
  207. testgen/ui/components/frontend/js/components/location.js +62 -0
  208. testgen/ui/components/frontend/js/components/select.js +75 -0
  209. testgen/ui/components/frontend/js/components/sidebar.js +358 -0
  210. testgen/ui/components/frontend/js/main.js +99 -0
  211. testgen/ui/components/frontend/js/streamlit.js +19 -0
  212. testgen/ui/components/frontend/js/van.min.js +1 -0
  213. testgen/ui/components/utils/__init__.py +0 -0
  214. testgen/ui/components/utils/callbacks.py +51 -0
  215. testgen/ui/components/utils/component.py +13 -0
  216. testgen/ui/components/widgets/__init__.py +6 -0
  217. testgen/ui/components/widgets/breadcrumbs.py +32 -0
  218. testgen/ui/components/widgets/location.py +65 -0
  219. testgen/ui/components/widgets/modal.py +97 -0
  220. testgen/ui/components/widgets/sidebar.py +69 -0
  221. testgen/ui/navigation/__init__.py +0 -0
  222. testgen/ui/navigation/menu.py +42 -0
  223. testgen/ui/navigation/page.py +20 -0
  224. testgen/ui/navigation/router.py +63 -0
  225. testgen/ui/queries/__init__.py +0 -0
  226. testgen/ui/queries/authentication_queries.py +47 -0
  227. testgen/ui/queries/connection_queries.py +121 -0
  228. testgen/ui/queries/profiling_queries.py +148 -0
  229. testgen/ui/queries/project_queries.py +9 -0
  230. testgen/ui/queries/table_group_queries.py +186 -0
  231. testgen/ui/queries/test_definition_queries.py +270 -0
  232. testgen/ui/queries/test_run_queries.py +32 -0
  233. testgen/ui/queries/test_suite_queries.py +145 -0
  234. testgen/ui/scripts/__init__.py +0 -0
  235. testgen/ui/scripts/patch_streamlit.py +111 -0
  236. testgen/ui/services/__init__.py +0 -0
  237. testgen/ui/services/authentication_service.py +119 -0
  238. testgen/ui/services/connection_service.py +220 -0
  239. testgen/ui/services/database_service.py +282 -0
  240. testgen/ui/services/form_service.py +1008 -0
  241. testgen/ui/services/javascript_service.py +44 -0
  242. testgen/ui/services/query_service.py +316 -0
  243. testgen/ui/services/string_service.py +12 -0
  244. testgen/ui/services/table_group_service.py +130 -0
  245. testgen/ui/services/test_definition_service.py +117 -0
  246. testgen/ui/services/test_run_service.py +13 -0
  247. testgen/ui/services/test_suite_service.py +76 -0
  248. testgen/ui/services/toolbar_service.py +77 -0
  249. testgen/ui/session.py +46 -0
  250. testgen/ui/views/__init__.py +0 -0
  251. testgen/ui/views/app_log_modal.py +92 -0
  252. testgen/ui/views/connections.py +72 -0
  253. testgen/ui/views/connections_base.py +367 -0
  254. testgen/ui/views/login.py +40 -0
  255. testgen/ui/views/not_found.py +16 -0
  256. testgen/ui/views/overview.py +34 -0
  257. testgen/ui/views/profiling_anomalies.py +501 -0
  258. testgen/ui/views/profiling_details.py +335 -0
  259. testgen/ui/views/profiling_modal.py +40 -0
  260. testgen/ui/views/profiling_results.py +206 -0
  261. testgen/ui/views/profiling_summary.py +177 -0
  262. testgen/ui/views/project_settings.py +74 -0
  263. testgen/ui/views/table_groups.py +530 -0
  264. testgen/ui/views/test_definitions.py +1020 -0
  265. testgen/ui/views/test_results.py +908 -0
  266. testgen/ui/views/test_runs.py +195 -0
  267. testgen/ui/views/test_suites.py +545 -0
  268. testgen/utils/__init__.py +0 -0
  269. testgen/utils/plugins.py +17 -0
  270. testgen/utils/singleton.py +14 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @typedef Breadcrumb
3
+ * @type {object}
4
+ * @property {string} path
5
+ * @property {string} label
6
+ *
7
+ * @typedef Properties
8
+ * @type {object}
9
+ * @property {Array.<Breadcrumb>} breadcrumbs
10
+ */
11
+ import van from '../van.min.js';
12
+ import { Streamlit } from '../streamlit.js';
13
+
14
+ const { a, div, span } = van.tags;
15
+
16
+ const Breadcrumbs = (/** @type Properties */ props) => {
17
+ Streamlit.setFrameHeight(24);
18
+
19
+ if (!window.testgen.loadedStylesheets.breadcrumbs) {
20
+ document.adoptedStyleSheets.push(stylesheet);
21
+ window.testgen.loadedStylesheets.breadcrumbs = true;
22
+ }
23
+
24
+ return div(
25
+ {class: 'tg-breadcrumbs-wrapper'},
26
+ () => {
27
+ const breadcrumbs = van.val(props.breadcrumbs);
28
+
29
+ return div(
30
+ { class: 'tg-breadcrumbs' },
31
+ breadcrumbs.reduce((items, b, idx) => {
32
+ const isLastItem = idx === breadcrumbs.length - 1;
33
+ items.push(a({ class: `tg-breadcrumbs--${ isLastItem ? 'current' : 'active'}`, href: `#/${b.path}`, onclick: () => navigate(b.path) }, b.label))
34
+ if (!isLastItem) {
35
+ items.push(span({class: 'tg-breadcrumbs--arrow'}, '>'));
36
+ }
37
+ return items;
38
+ }, [])
39
+ );
40
+ }
41
+ )
42
+ };
43
+
44
+ function navigate(/** @type string */ path) {
45
+ window.parent.postMessage({ type: 'TestgenNavigationRequest', path: path }, '*');
46
+ return false;
47
+ }
48
+
49
+ const stylesheet = new CSSStyleSheet();
50
+ stylesheet.replace(`
51
+ .tg-breadcrumbs-wrapper {
52
+ height: 100%;
53
+ }
54
+
55
+ .tg-breadcrumbs {
56
+ display: flex;
57
+ align-items: center;
58
+ color: var(--secondary-text-color);
59
+ height: 100%;
60
+ }
61
+
62
+ .tg-breadcrumbs > a {
63
+ text-decoration: unset;
64
+ }
65
+
66
+ .tg-breadcrumbs--arrow {
67
+ margin-left: 4px;
68
+ margin-right: 4px;
69
+ }
70
+
71
+ .tg-breadcrumbs--active {
72
+ cursor: pointer;
73
+ color: var(--secondary-text-color);
74
+ }
75
+
76
+ .tg-breadcrumbs--active:hover {
77
+ text-decoration: underline;
78
+ }
79
+
80
+ .tg-breadcrumbs--current {
81
+ pointer-events: none;
82
+ color: var(--secondary-text-color);
83
+ }
84
+ `);
85
+
86
+ export { Breadcrumbs };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @typedef Properties
3
+ * @type {object}
4
+ * @property {(string|null)} icon
5
+ * @property {(string|null)} class
6
+ * @property {(Function|null)} onclick
7
+ */
8
+ import van from '../van.min.js';
9
+ import { Streamlit } from '../streamlit.js';
10
+
11
+ const { button, i, span } = van.tags;
12
+
13
+ const Button = (/** @type Properties */ props, /** @type string */ text) => {
14
+ Streamlit.setFrameHeight();
15
+
16
+ if (!window.testgen.loadedStylesheets.button) {
17
+ document.adoptedStyleSheets.push(stylesheet);
18
+ window.testgen.loadedStylesheets.button = true;
19
+ }
20
+
21
+ return button(
22
+ {
23
+ class: `tg-button ${props.icon ? 'tg-icon-button' : ''} ${props.class ?? ''}`,
24
+ onclick: props.onclick,
25
+ },
26
+ props.icon ? i({class: 'material-symbols-rounded'}, props.icon) : undefined,
27
+ span(text),
28
+ );
29
+ };
30
+
31
+ const stylesheet = new CSSStyleSheet();
32
+ stylesheet.replace(`
33
+ button.tg-button {
34
+ position: relative;
35
+ overflow: hidden;
36
+
37
+ display: flex;
38
+ flex-direction: row;
39
+ align-items: center;
40
+ justify-content: center;
41
+
42
+ outline: 0;
43
+ border: unset;
44
+ background: transparent;
45
+ border-radius: 4px;
46
+ padding: 8px 16px;
47
+
48
+ color: var(--primary-text-color);
49
+ cursor: pointer;
50
+
51
+ font-size: 14px;
52
+
53
+ transition: background 400ms;
54
+ }
55
+
56
+ button.tg-button:hover {
57
+ background: rgba(0, 0, 0, 0.04);
58
+ }
59
+
60
+ button.tg-button.tg-icon-button > i {
61
+ font-size: 18px;
62
+ margin-right: 8px;
63
+ }
64
+ `);
65
+
66
+ export { Button };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @typedef Properties
3
+ * @type {object}
4
+ * @property {boolean} initialized
5
+ * @property {string} current_page_code
6
+ */
7
+ import van from '../van.min.js';
8
+ import { Streamlit } from '../streamlit.js';
9
+
10
+ const Location = (/** @type Properties */ props) => {
11
+ Streamlit.setFrameHeight('0');
12
+
13
+ van.derive(() => {
14
+ syncHashToCurrentPage(van.val(props.current_page_code));
15
+ });
16
+
17
+ if (!van.val(props.initialized)) {
18
+ Streamlit.sendData(extractLocation());
19
+ }
20
+
21
+ window.top.addEventListener("hashchange", function(event) {
22
+ if (event.newURL.includes('login')) {
23
+ return;
24
+ }
25
+
26
+ const urlChanged = event.oldURL !== event.newURL;
27
+ if (urlChanged) {
28
+ Streamlit.sendData(extractLocation());
29
+ }
30
+ });
31
+
32
+ return '';
33
+ };
34
+
35
+
36
+ function extractLocation() {
37
+ const hash = decodeURI(window.top.location.hash).replace('#', '');
38
+ const parts = hash.split('?')
39
+ const page = parts[0] ? `${parts[0]}` : undefined;
40
+ const args = (parts[1] || '').split('&').filter(pair => !!pair).reduce((allArgs, pair) => {
41
+ const pairParts = pair.split('=');
42
+ allArgs[pairParts[0].trim()] = pairParts[1].trim() || '';
43
+ return allArgs;
44
+ }, {});
45
+
46
+ return { page: page, args: args };
47
+ }
48
+
49
+ function isHashSynchronized(/** @type string */ hash, /** @type string */ currentPageCode) {
50
+ return btoa(decodeURI(hash || '').replace('#', '')) === currentPageCode;
51
+ }
52
+
53
+ function syncHashToCurrentPage(/** @type string */ currentPageCode) {
54
+ if (!currentPageCode) {
55
+ return;
56
+ }
57
+
58
+ const path = atob(currentPageCode);
59
+ window.parent.postMessage({ type: 'TestgenNavigationRequest', path: path }, '*');
60
+ }
61
+
62
+ export { Location };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @typedef Option
3
+ * @type {object}
4
+ * @property {string} label
5
+ * @property {string} value
6
+ * @property {boolean} selected
7
+ *
8
+ * @typedef Properties
9
+ * @type {object}
10
+ * @property {string} label
11
+ * @property {Array.<Option>} options
12
+ * @property {Function|null} onChange
13
+ */
14
+ import van from '../van.min.js';
15
+ import { Streamlit } from '../streamlit.js';
16
+
17
+ const { div, label, option, select } = van.tags;
18
+
19
+ const Select = (/** @type {Properties} */ props) => {
20
+ Streamlit.setFrameHeight();
21
+
22
+ if (!window.testgen.loadedStylesheets.select) {
23
+ document.adoptedStyleSheets.push(stylesheet);
24
+ window.testgen.loadedStylesheets.select = true;
25
+ }
26
+
27
+ const domId = Math.random().toString(36).substring(2);
28
+ const changeHandler = props.onChange || post;
29
+ return div(
30
+ {class: 'tg-select'},
31
+ label({for: domId, class: 'tg-select--label'}, van.val(props.label)),
32
+ () => {
33
+ const options = van.val(props.options);
34
+ return select(
35
+ {id: domId, class: 'tg-select--field', onchange: changeHandler},
36
+ options.map(op => option({class: 'tg-select--field--option', value: op.value, selected: op.selected}, op.label)),
37
+ );
38
+ },
39
+ );
40
+ };
41
+
42
+ function post(/** @type string */ value) {
43
+ Streamlit.sendData({ value: value });
44
+ }
45
+
46
+ const stylesheet = new CSSStyleSheet();
47
+ stylesheet.replace(`
48
+ div.tg-select {
49
+ display: flex;
50
+ flex-direction: column;
51
+ }
52
+
53
+ div.tg-select > .tg-select--label {
54
+ color: var(--secondary-text-color);
55
+ margin-bottom: 4px;
56
+ }
57
+
58
+ div.tg-select > .tg-select--field {
59
+ border: unset;
60
+ border-bottom: 1px solid var(--field-underline-color);
61
+
62
+ font-size: inherit;
63
+ font-family: inherit;
64
+ color: var(--primary-text-color);
65
+
66
+ background-color: inherit;
67
+ }
68
+
69
+ div.tg-select > .tg-select--field:focus-visible {
70
+ outline: unset;
71
+ border-bottom-color: var(--primary-color);
72
+ }
73
+ `);
74
+
75
+ export { Select };
@@ -0,0 +1,358 @@
1
+ /**
2
+ * @typedef MenuItem
3
+ * @type {object}
4
+ * @property {(string|null)} id
5
+ * @property {(string|null)} icon
6
+ * @property {string} label
7
+ * @property {(string|null)} page
8
+ * @property {(Array.<MenuItem>|null)} items
9
+ *
10
+ * @typedef Version
11
+ * @type {object}
12
+ * @property {string} current
13
+ * @property {string} latest
14
+ * @property {string} schema
15
+ *
16
+ * @typedef Menu
17
+ * @type {object}
18
+ * @property {Array.<MenuItem>} items
19
+ * @property {Version} version
20
+ *
21
+ * @typedef Project
22
+ * @type {object}
23
+ * @property {string} code
24
+ * @property {string} name
25
+ *
26
+ * @typedef Properties
27
+ * @type {object}
28
+ * @property {Menu} menu
29
+ * @property {string} username
30
+ * @property {string} current_page
31
+ * @property {string} current_project
32
+ * @property {string} auth_cookie_name
33
+ */
34
+ const van = window.top.van;
35
+ const { a, button, div, i, img, label, option, select, span } = van.tags;
36
+
37
+ const Sidebar = (/** @type {Properties} */ props) => {
38
+ if (Sidebar.StreamlitInstance) {
39
+ Sidebar.StreamlitInstance.setFrameHeight(1);
40
+ }
41
+
42
+ if (!window.testgen.loadedStylesheets.sidebar) {
43
+ document.adoptedStyleSheets.push(stylesheet);
44
+ window.testgen.loadedStylesheets.sidebar = true;
45
+ }
46
+
47
+ return div(
48
+ {class: 'menu'},
49
+ a({class: 'logo', href: `/#overview`, onclick: () => navigate('overview')}, img({ src: logo })),
50
+ () => {
51
+ const menuItems = van.val(props.menu).items;
52
+ return div(
53
+ {class: 'content'},
54
+ menuItems.map(item =>
55
+ item.items?.length > 0
56
+ ? MenuSection(item, props.current_page)
57
+ : MenuItem(item, props.current_page))
58
+ );
59
+ },
60
+ button(
61
+ { class: `tg-button logout`, onclick: () => Sidebar.onLogout(van.val(props.auth_cookie_name)) },
62
+ i({class: 'material-symbols-rounded'}, 'logout'),
63
+ span('Logout'),
64
+ ),
65
+ span({class: 'menu--username'}, props.username),
66
+ () => Version(van.val(props.menu).version),
67
+ );
68
+ };
69
+
70
+ const MenuSection = (/** @type {MenuItem} */ item, /** @type {string} */ currentPage) => {
71
+ return div(
72
+ {class: 'menu--section'},
73
+ div({class: 'menu--section--label'}, item.label),
74
+ div(
75
+ {class: 'menu--section--items'},
76
+ ...item.items.map(child => MenuItem(child, currentPage)),
77
+ )
78
+ );
79
+ }
80
+
81
+ const MenuItem = (/** @type {MenuItem} */ item, /** @type {string} */ currentPage) => {
82
+ const classes = van.derive(() => {
83
+ if (isCurrentPage(item.page, van.val(currentPage))) {
84
+ return 'menu--item active';
85
+ }
86
+ return 'menu--item';
87
+ });
88
+
89
+ return a(
90
+ {class: classes, href: `/#${item.page}`, onclick: () => navigate(item.page)},
91
+ i({class: 'menu--item--icon material-symbols-rounded'}, item.icon),
92
+ span({class: 'menu--item--label'}, item.label),
93
+ );
94
+ };
95
+
96
+ const Version = (/** @type {Version} */ version) => {
97
+ const expanded = van.state(false);
98
+
99
+ const icon = van.derive(() => expanded.val ? 'expand_less' : 'expand_more');
100
+ const classes = van.derive(() => expanded.val ? ' version expanded' : 'version');
101
+
102
+ return div(
103
+ {class: classes, onclick: () => { expanded.val = !expanded.val; }},
104
+ VersionRow(
105
+ 'version',
106
+ version.current,
107
+ i({class: 'material-symbols-rounded version--dropdown-icon'}, icon),
108
+ ),
109
+ div(
110
+ {class: 'version--details'},
111
+ VersionRow('latest version', version.latest),
112
+ VersionRow('schema revision', version.schema),
113
+ ),
114
+ );
115
+ };
116
+
117
+ const VersionRow = (/** @type string */ label, /** @type string */ version, iconEl = undefined) => {
118
+ return div(
119
+ {class: 'version--row'},
120
+ span({class: 'version--row--label'}, `${label}:`),
121
+ span({class: 'version--row--value'}, version),
122
+ iconEl,
123
+ );
124
+ };
125
+
126
+ function navigate(/** @type string */ path) {
127
+ window.parent.postMessage({ type: 'TestgenNavigationRequest', path: path }, '*');
128
+ return false;
129
+ }
130
+
131
+ function isCurrentPage(itemPath, currentPage) {
132
+ const normalizedItemPath = normalizePath(itemPath);
133
+ const normalizedCurrentPagePath = normalizePath(currentPage);
134
+ const isTheSamePage = normalizedItemPath === normalizedCurrentPagePath;
135
+ const isASubPage = normalizedCurrentPagePath.includes(normalizedItemPath);
136
+
137
+ return isTheSamePage || isASubPage;
138
+ }
139
+
140
+ function normalizePath(path) {
141
+ if (!path) {
142
+ return '';
143
+ }
144
+
145
+ return path.split('/').filter(p => p.length > 0).join('/');
146
+ }
147
+
148
+ const b64LogoString = `PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRv
149
+ ciAyNC4xLjIsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF
150
+ 5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMH
151
+ B4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA2MDIuMiAxMTcuNyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjAyLjIgMTE3Ljc7IiB4b
152
+ Ww6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNBQUQwNDY7fQ0KCS5zdDF7ZmlsbDojMDZBMDRBO30N
153
+ Cjwvc3R5bGU+DQo8cGF0aCBkPSJNMzcuMywxMDcuNmMwLjYsMCwxLjEsMCwxLjYtMC4xYy0yLjIsMC00LjUsMC4xLTYuOSwwLjFIMzcuM3oiLz4NCjxwYXR
154
+ oIGQ9Ik0zNy4zLDkuOUgzMmMyLjQsMCw0LjcsMCw2LjksMC4xQzM4LjMsOS45LDM3LjgsOS45LDM3LjMsOS45eiIvPg0KPHBhdGggY2xhc3M9InN0MCIgZD
155
+ 0iTTg1LjYsNTguM2MwLTEuNy0wLjEtMy4zLTAuMy00LjljLTAuNi03LjgtMi41LTE0LjgtNS45LTIwLjhDNzYsMjYuNSw3MS41LDIxLjcsNjUuOSwxOA0KC
156
+ WMtMS4yLTAuOC0yLjMtMS42LTMuNi0yLjJjLTIuNy0xLjQtNS41LTIuNS04LjUtMy40TDExLjcsNTguN2w0Mi4yLDQ2LjRjMi45LTAuOSw1LjgtMiw4LjUt
157
+ My40YzAuOS0wLjUsMS44LTEuMSwyLjctMS43DQoJYzYtMy44LDEwLjktOC44LDE0LjQtMTUuMmMzLjYtNi40LDUuNS0xMy45LDUuOS0yMi40YzAuMS0wLjk
158
+ sMC4xLTEuOCwwLjEtMi43YzAtMC40LDAuMS0wLjcsMC4xLTEuMWMwLTAuMSwwLTAuMSwwLTAuMg0KCUM4NS41LDU4LjUsODUuNiw1OC40LDg1LjYsNTguM3
159
+ oiLz4NCjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik01My44LDEyLjNjLTQuNi0xLjQtOS42LTIuMi0xNC45LTIuNGMtMi4yLDAtNC41LTAuMS02LjktMC4xSDE2L
160
+ jVjLTIuNywwLTQuOCwyLjItNC44LDQuOHYyOS44djE0LjNMNTMuOCwxMi4zDQoJQzUzLjgsMTIuMyw1My44LDEyLjMsNTMuOCwxMi4zeiIvPg0KPHBhdGgg
161
+ Y2xhc3M9InN0MSIgZD0iTTExLjcsNzN2MjkuOGMwLDIuNywyLjIsNC44LDQuOCw0LjhIMzJjMi40LDAsNC43LDAsNi45LTAuMWM1LjMtMC4xLDEwLjMtMSw
162
+ xNC45LTIuNGMwLDAsMCwwLDAsMEwxMS43LDU4LjdWNzMNCgl6Ii8+DQo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNOTQuNSw5LjlINjkuM2MwLjMsMC4yLDAuNS
163
+ wwLjQsMC44LDAuN2M2LjQsNC40LDExLjcsMTAsMTUuNiwxNy4xYzQsNy4yLDYuMywxNS41LDcsMjQuOA0KCWMwLjIsMS45LDAuMywzLjksMC4zLDUuOWMwL
164
+ DAuMSwwLDAuMiwwLDAuM2MwLDAuMSwwLDAuMSwwLDAuMmMwLDAuNC0wLjEsMC44LTAuMSwxLjNjMCwxLjEtMC4xLDIuMS0wLjIsMy4yDQoJYy0wLjUsMTAu
165
+ MS0yLjgsMTktNy4xLDI2LjdjLTQuMSw3LjQtOS43LDEzLjItMTYuNSwxNy42YzAsMCwwLDAuMSwwLDAuMWgyNS40YzIuNywwLDQuOC0yLjIsNC44LTQuOHY
166
+ tODgNCglDOTkuNCwxMi4xLDk3LjIsOS45LDk0LjUsOS45eiIvPg0KPHBhdGggY2xhc3M9InN0MSIgZD0iTTEzMiwyOGgyNC4xYzE3LjYsMCwyNy44LDEzLj
167
+ QsMjcuOCwzMC45YzAsMTcuNC0xMC4zLDMwLjYtMjcuOCwzMC42SDEzMlYyOHogTTE1Ni4xLDc5LjcNCgljMTEuMiwwLDE2LjktOS41LDE2LjktMjAuOWMwL
168
+ TExLjUtNS43LTIxLjEtMTYuOS0yMS4xaC0xMy4ydjQySDE1Ni4xeiIvPg0KPHBhdGggY2xhc3M9InN0MSIgZD0iTTIzMS45LDQ3Ljh2NDEuN2gtMTAuNHYt
169
+ NS42Yy0yLjksNC41LTguNyw2LjUtMTMuOCw2LjVjLTExLDAtMjAuNS04LjUtMjAuNS0yMS44YzAtMTMuNCw5LjUtMjEuNywyMC40LTIxLjcNCgljNS4zLDA
170
+ sMTEuMSwyLjEsMTMuOSw2LjR2LTUuNUgyMzEuOXogTTIyMS40LDY4LjVjMC03LjMtNi4xLTEyLTEyLTEyYy02LjQsMC0xMS43LDUtMTEuNywxMmMwLDcsNS
171
+ 4zLDEyLjEsMTEuNywxMi4xDQoJQzIxNS43LDgwLjYsMjIxLjQsNzUuOCwyMjEuNCw2OC41eiIvPg0KPHBhdGggY2xhc3M9InN0MSIgZD0iTTI2Myw1Ni4xa
172
+ C04Ljh2MzMuNGgtMTAuNFY1Ni4xaC03LjV2LTguM2g3LjVWMzIuNWgxMC40djE1LjRoOC44VjU2LjF6Ii8+DQo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNMzA5
173
+ LDQ3Ljh2NDEuN2gtMTAuNHYtNS42Yy0yLjksNC41LTguNyw2LjUtMTMuOCw2LjVjLTExLDAtMjAuNS04LjUtMjAuNS0yMS44YzAtMTMuNCw5LjUtMjEuNyw
174
+ yMC40LTIxLjcNCgljNS4zLDAsMTEuMSwyLjEsMTMuOSw2LjR2LTUuNUgzMDl6IE0yOTguNCw2OC41YzAtNy4zLTYuMS0xMi0xMi0xMmMtNi40LDAtMTEuNy
175
+ w1LTExLjcsMTJjMCw3LDUuMywxMi4xLDExLjcsMTIuMQ0KCUMyOTIuNyw4MC42LDI5OC40LDc1LjgsMjk4LjQsNjguNXoiLz4NCjxwYXRoIGNsYXNzPSJzd
176
+ DEiIGQ9Ik0zNTQuNiw4OS41bC0yMS4yLTIzLjR2MjMuNGgtMTAuOFYyOGgxMC44djIzLjNMMzUwLjcsMjhoMTMuNWwtMjMuNSwzMC42bDI4LjYsMzAuOUgz
177
+ NTQuNnoiLz4NCjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0zNzMuNywzMy4xYzAtMy43LDMuMS02LjMsNi44LTYuM2MzLjcsMCw2LjcsMi43LDYuNyw2LjNjMCw
178
+ zLjYtMi45LDYuMy02LjcsNi4zDQoJQzM3Ni45LDM5LjQsMzczLjcsMzYuNiwzNzMuNywzMy4xeiBNMzc1LjMsNDcuOGgxMC40djQxLjdoLTEwLjRWNDcuOH
179
+ oiLz4NCjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik00MTcuNCw1Ni4xaC04Ljh2MzMuNGgtMTAuNFY1Ni4xaC03LjV2LTguM2g3LjVWMzIuNWgxMC40djE1LjRoO
180
+ C44VjU2LjF6Ii8+DQo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNNDE3LjYsNjguNmMwLTEzLjIsMTAuNi0yMS43LDIyLjctMjEuN2M3LjIsMCwxMy4xLDMuMSwx
181
+ Nyw4bC03LjQsNS44Yy0yLjEtMi42LTUuNy00LjItOS40LTQuMg0KCWMtNy4yLDAtMTIuNCw1LTEyLjQsMTJjMCw3LDUuMiwxMiwxMi40LDEyYzMuNywwLDc
182
+ uMi0xLjYsOS40LTQuMmw3LjQsNS44Yy0zLjgsNC44LTkuNyw4LTE3LDhDNDI4LjMsOTAuMyw0MTcuNiw4MS44LDQxNy42LDY4LjZ6Ig0KCS8+DQo8cGF0aC
183
+ BjbGFzcz0ic3QxIiBkPSJNNTAwLjcsNjYuMXYyMy40aC0xMC40VjY3LjFjMC02LjYtNC0xMC04LjctMTBjLTQuNywwLTEwLjYsMi42LTEwLjYsMTAuNnYyM
184
+ S44aC0xMC40di02NGgxMC40djI4LjYNCgljMi4xLTUsOC43LTcuMiwxMi45LTcuMkM0OTQuOCw0Ni45LDUwMC43LDU0LDUwMC43LDY2LjF6Ii8+DQo8cGF0
185
+ aCBjbGFzcz0ic3QxIiBkPSJNNTQ3LjUsNzIuM2gtMzIuMmMxLjIsNS44LDUuNiw4LjcsMTEuOCw4LjdjNC42LDAsOC44LTEuOCwxMS4zLTUuMmw2LjksNS4
186
+ zYy0zLjgsNi4xLTExLjIsOS4zLTE4LjcsOS4zDQoJYy0xMi41LDAtMjItOC43LTIyLTIxLjhjMC0xMy4zLDEwLTIxLjcsMjEuOS0yMS43YzEyLDAsMjEuMy
187
+ w4LjMsMjEuMywyMS4zQzU0Ny44LDY5LjQsNTQ3LjcsNzAuNyw1NDcuNSw3Mi4zeiBNNTM3LjQsNjUNCgljLTAuNi01LjctNS05LTEwLjgtOWMtNS42LDAtM
188
+ TAuMSwyLjctMTEuMyw5SDUzNy40eiIvPg0KPHBhdGggY2xhc3M9InN0MSIgZD0iTTU5MS45LDY2LjF2MjMuNGgtMTAuNFY2Ny4xYzAtNi42LTQtMTAtOC43
189
+ LTEwYy00LjcsMC0xMC42LDIuNi0xMC42LDEwLjZ2MjEuOGgtMTAuNFY0Ny44aDEwLjR2Ni42DQoJYzIuMS01LjIsOC43LTcuNSwxMi45LTcuNUM1ODUuOSw
190
+ 0Ni45LDU5MS45LDU0LDU5MS45LDY2LjF6Ii8+DQo8L3N2Zz4NCg==`
191
+ const logo = `data:image/svg+xml;base64,${b64LogoString}`;
192
+
193
+ const stylesheet = new CSSStyleSheet();
194
+ stylesheet.replace(`
195
+ .menu {
196
+ position: relative;
197
+ display: flex;
198
+ flex-direction: column;
199
+ height: 100%;
200
+ background: var(--sidebar-background-color);
201
+ }
202
+
203
+ .menu .logo {
204
+ margin: 24px 16px 16px;
205
+ }
206
+
207
+ .menu > .menu--username {
208
+ position: absolute;
209
+
210
+ left: 0;
211
+ bottom: 0;
212
+
213
+ padding-left: 16px;
214
+ padding-bottom: 8px;
215
+
216
+ max-width: 35%;
217
+ overflow-x: hidden;
218
+ text-overflow: ellipsis;
219
+ text-wrap: nowrap;
220
+
221
+ color: var(--secondary-text-color);
222
+ }
223
+
224
+ .menu > .menu--username:before {
225
+ content: 'User: ';
226
+ }
227
+
228
+ .menu > .content > .menu--section > .menu--section--label {
229
+ padding: 16px;
230
+ font-weight: 500;
231
+ color: var(--secondary-text-color);
232
+ }
233
+
234
+ .menu .menu--item {
235
+ height: 40px;
236
+ display: flex;
237
+ align-items: center;
238
+ padding: 0 16px;
239
+ color: var(--secondary-text-color);
240
+ border-left: 4px solid transparent;
241
+ font-weight: 500;
242
+ text-decoration: unset;
243
+ }
244
+
245
+ .menu .menu--item.active {
246
+ color: var(--primary-color);
247
+ background: var(--sidebar-active-item-color);
248
+ border-left-color: var(--sidebar-active-item-border-color);
249
+ }
250
+
251
+ .menu .menu--item > .menu--item--icon {
252
+ font-size: 20px;
253
+ line-height: 20px;
254
+ }
255
+
256
+ .menu .menu--item > .menu--item--label {
257
+ margin-left: 16px;
258
+ }
259
+
260
+ .menu .menu--item:hover {
261
+ cursor: pointer;
262
+ background: var(--sidebar-item-hover-color);
263
+ }
264
+
265
+ .menu .version {
266
+ color: var(--secondary-text-color);
267
+ display: flex;
268
+ flex-direction: column;
269
+ padding: 8px 16px;
270
+ cursor: pointer;
271
+ }
272
+
273
+ .menu .version .version--dropdown-icon {
274
+ font-size: 19px;
275
+ }
276
+
277
+ .menu .version .version--row {
278
+ display: flex;
279
+ align-items: center;
280
+ justify-content: flex-end;
281
+ }
282
+
283
+ .menu .version .version--row .version--row--label {
284
+ font-weight: 500;
285
+ margin-right: 4px;
286
+ }
287
+
288
+ .menu .version .version--details {
289
+ display: none;
290
+ flex-direction: column;
291
+ }
292
+
293
+ .menu .version .version--details {
294
+ display: none;
295
+ margin-top: 4px;
296
+ }
297
+
298
+ .menu .version.expanded .version--details {
299
+ display: block;
300
+ }
301
+
302
+ .version--row + .version--row {
303
+ margin-top: 4px;
304
+ }
305
+
306
+ .menu > :nth-child(1 of button) {
307
+ margin-top: auto !important;
308
+ }
309
+
310
+ .menu > button {
311
+ margin: 16px;
312
+ color: var(--secondary-text-color) !important;
313
+ }
314
+
315
+ .menu > button.logout {
316
+ margin-top: 8px;
317
+ }
318
+
319
+ .menu > button.users {
320
+ margin-bottom: 0px;
321
+ }
322
+
323
+ /* Intentionally duplicate from button.js */
324
+ button.tg-button {
325
+ position: relative;
326
+ overflow: hidden;
327
+
328
+ display: flex;
329
+ flex-direction: row;
330
+ align-items: center;
331
+ justify-content: center;
332
+
333
+ outline: 0;
334
+ border: unset;
335
+ background: transparent;
336
+ border-radius: 4px;
337
+ padding: 8px 16px;
338
+
339
+ color: var(--primary-text-color);
340
+ cursor: pointer;
341
+
342
+ font-size: 14px;
343
+
344
+ transition: background 400ms;
345
+ }
346
+
347
+ button.tg-button:hover {
348
+ background: rgba(0, 0, 0, 0.04);
349
+ }
350
+
351
+ button.tg-button > i {
352
+ font-size: 18px;
353
+ margin-right: 8px;
354
+ }
355
+ /* ... */
356
+ `);
357
+
358
+ window.testgen.components.Sidebar = Sidebar;