answer42 0.2.16__tar.gz → 0.2.18__tar.gz

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 (58) hide show
  1. {answer42-0.2.16 → answer42-0.2.18}/PKG-INFO +1 -1
  2. {answer42-0.2.16 → answer42-0.2.18}/pyproject.toml +1 -1
  3. {answer42-0.2.16 → answer42-0.2.18}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form/Module.bsl +270 -50
  4. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/assets/MCPTestManager.cf +0 -0
  5. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/server.py +35 -1
  6. {answer42-0.2.16 → answer42-0.2.18}/.gitignore +0 -0
  7. {answer42-0.2.16 → answer42-0.2.18}/LICENSE +0 -0
  8. {answer42-0.2.16 → answer42-0.2.18}/README.md +0 -0
  9. {answer42-0.2.16 → answer42-0.2.18}/credentials.example.json +0 -0
  10. {answer42-0.2.16 → answer42-0.2.18}/docs/agent-installation.md +0 -0
  11. {answer42-0.2.16 → answer42-0.2.18}/docs/architecture.md +0 -0
  12. {answer42-0.2.16 → answer42-0.2.18}/docs/assets/answer42-logo.png +0 -0
  13. {answer42-0.2.16 → answer42-0.2.18}/docs/assets/platform42-logo.svg +0 -0
  14. {answer42-0.2.16 → answer42-0.2.18}/docs/installation.md +0 -0
  15. {answer42-0.2.16 → answer42-0.2.18}/scripts/build_cf.py +0 -0
  16. {answer42-0.2.16 → answer42-0.2.18}/scripts/build_pages.py +0 -0
  17. {answer42-0.2.16 → answer42-0.2.18}/scripts/e2e_stable.py +0 -0
  18. {answer42-0.2.16 → answer42-0.2.18}/scripts/openclaw_mcp_autoreload.py +0 -0
  19. {answer42-0.2.16 → answer42-0.2.18}/scripts/rag_cli.py +0 -0
  20. {answer42-0.2.16 → answer42-0.2.18}/src/cf/ConfigDumpInfo.xml +0 -0
  21. {answer42-0.2.16 → answer42-0.2.18}/src/cf/Configuration.xml +0 -0
  22. {answer42-0.2.16 → answer42-0.2.18}/src/cf/DataProcessors/MCPTestManager/Ext/ObjectModule.bsl +0 -0
  23. {answer42-0.2.16 → answer42-0.2.18}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260/Ext/Form.xml" +0 -0
  24. {answer42-0.2.16 → answer42-0.2.18}/src/cf/DataProcessors/MCPTestManager/Forms//320/244/320/276/321/200/320/274/320/260.xml" +0 -0
  25. {answer42-0.2.16 → answer42-0.2.18}/src/cf/DataProcessors/MCPTestManager.xml +0 -0
  26. {answer42-0.2.16 → answer42-0.2.18}/src/cf/Ext/ManagedApplicationModule.bsl +0 -0
  27. {answer42-0.2.16 → answer42-0.2.18}/src/cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  28. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Catalogs//320/237/320/241_/320/222/320/273/320/260/320/264/320/265/320/273/320/265/321/206.xml" +0 -0
  29. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Catalogs//320/237/320/241_/320/241/320/277/321/200/320/260/320/262/320/276/321/207/320/275/320/270/320/2721.xml" +0 -0
  30. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/ConfigDumpInfo.xml +0 -0
  31. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Configuration.xml +0 -0
  32. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Languages//320/240/321/203/321/201/321/201/320/272/320/270/320/271.xml" +0 -0
  33. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ManagerModule.bsl" +0 -0
  34. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Ext/ObjectModule.bsl" +0 -0
  35. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205/Ext/Template.xml" +0 -0
  36. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262/Templates//320/236/321/201/320/275/320/276/320/262/320/275/320/260/321/217/320/241/321/205/320/265/320/274/320/260/320/232/320/276/320/274/320/277/320/276/320/275/320/276/320/262/320/272/320/270/320/224/320/260/320/275/320/275/321/213/321/205.xml" +0 -0
  37. {answer42-0.2.16 → answer42-0.2.18}/src/client_cf/Reports//320/237/320/241_/320/241/320/277/320/270/321/201/320/276/320/272/320/255/320/273/320/265/320/274/320/265/320/275/321/202/320/276/320/262.xml" +0 -0
  38. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/__init__.py +0 -0
  39. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/assets/MCPTestClient.cf +0 -0
  40. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/assets/__init__.py +0 -0
  41. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/assets/skills/answer42/SKILL.md +0 -0
  42. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/assets/skills/answer42-rag/SKILL.md +0 -0
  43. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/bridge.py +0 -0
  44. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/credentials.py +0 -0
  45. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/os_support.py +0 -0
  46. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/platform.py +0 -0
  47. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/protocol.py +0 -0
  48. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/__init__.py +0 -0
  49. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/detect.py +0 -0
  50. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/model.py +0 -0
  51. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/parsers.py +0 -0
  52. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/service.py +0 -0
  53. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/rag/store.py +0 -0
  54. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/recorder.py +0 -0
  55. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/release_helper.py +0 -0
  56. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/runtime.py +0 -0
  57. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/skill_installer.py +0 -0
  58. {answer42-0.2.16 → answer42-0.2.18}/src/mcp_1c/window_control.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: answer42
3
- Version: 0.2.16
3
+ Version: 0.2.18
4
4
  Summary: Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API
5
5
  Author: Marvin (AI Assistant), 42Clouds, and contributors
6
6
  Author-email: "Kosolapov Stanislav (proDOOMman)" <prodoomman@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "answer42"
3
- version = "0.2.16"
3
+ version = "0.2.18"
4
4
  description = "Answer42 — The Answer to Life, Universe, and 1C — UI Driver. MCP-powered 1C:Enterprise UI automation: click, fill, navigate, test, and introspect managed forms through the test-client API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -282,6 +282,10 @@
282
282
  Возврат ОткрытьНавигационнуюСсылку(Параметры);
283
283
  ИначеЕсли Метод = "current_element" Тогда
284
284
  Возврат ТекущийЭлементФормы();
285
+ ИначеЕсли Метод = "field_dropdown_state" Тогда
286
+ Возврат СостояниеВыпадающегоСпискаПоляMCP(Параметры);
287
+ ИначеЕсли Метод = "field_dropdown_close" Тогда
288
+ Возврат ЗакрытьВыпадающийСписокПоляMCP(Параметры);
285
289
  ИначеЕсли Метод = "table_current_row" Тогда
286
290
  Возврат ТекущаяСтрокаТаблицы(Параметры);
287
291
  ИначеЕсли Метод = "table_selected_rows" Тогда
@@ -434,15 +438,18 @@
434
438
  КонецЕсли;
435
439
 
436
440
  Выражение = СокрЛП(Код);
437
- Если НРег(Лев(Выражение, 7)) = "возврат" Тогда
441
+ ЭтоВозврат = НРег(Лев(Выражение, 7)) = "возврат";
442
+ Если ЭтоВозврат Тогда
438
443
  Выражение = СокрЛП(Сред(Выражение, 8));
439
- КонецЕсли;
440
- Если Прав(Выражение, 1) = ";" Тогда
441
- Выражение = Лев(Выражение, СтрДлина(Выражение) - 1);
444
+ Если Прав(Выражение, 1) = ";" Тогда
445
+ Выражение = Лев(Выражение, СтрДлина(Выражение) - 1);
446
+ КонецЕсли;
447
+ Результат = Вычислить(Выражение);
448
+ Возврат Новый Структура("value,type,presentation,executed_as", Результат, Строка(ТипЗнч(Результат)), Строка(Результат), "expression");
442
449
  КонецЕсли;
443
450
 
444
- Результат = Вычислить(Выражение);
445
- Возврат Новый Структура("value,type,presentation", Результат, Строка(ТипЗнч(Результат)), Строка(Результат));
451
+ Выполнить(Код);
452
+ Возврат Новый Структура("executed,executed_as", Истина, "statement");
446
453
  КонецФункции
447
454
 
448
455
  #КонецОбласти
@@ -3061,6 +3068,113 @@
3061
3068
  Возврат Неопределено;
3062
3069
  КонецФункции
3063
3070
 
3071
+
3072
+
3073
+ &НаКлиенте
3074
+ Функция СостояниеВыпадающегоСпискаПоляMCP(Параметры)
3075
+ ИмяПоля = СвойствоСтруктуры(Параметры, "field_name", "");
3076
+ ЗаголовокПоля = СвойствоСтруктуры(Параметры, "title", "");
3077
+ ИскатьПоЗаголовку = СвойствоСтруктуры(Параметры, "search_by_title", Ложь);
3078
+ МаксимальнаяГлубина = СвойствоСтруктуры(Параметры, "max_depth", 32);
3079
+ Таймаут = СвойствоСтруктуры(Параметры, "timeout", 60);
3080
+ Если ИскатьПоЗаголовку Тогда
3081
+ ЗаголовокПоля = ИмяПоля;
3082
+ ИмяПоля = "";
3083
+ КонецЕсли;
3084
+ АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3085
+ Поле = НайтиОбъектРекурсивно(АктивноеОкно, ИмяПоля, ЗаголовокПоля, "", 0, МаксимальнаяГлубина);
3086
+ Если Поле = Неопределено Тогда
3087
+ ВызватьИсключение "Поле не найдено: " + ?(ПустаяСтрока(ИмяПоля), ЗаголовокПоля, ИмяПоля);
3088
+ КонецЕсли;
3089
+ Открыт = ОткрытВыпадающийСписокПоляMCP(Поле);
3090
+ Сформирован = Ложь;
3091
+ ПредставленияСтрокой = Новый Массив;
3092
+ Если Открыт Тогда
3093
+ Попытка
3094
+ Сформирован = Поле.ОжидатьФормированияВыпадающегоСписка(Таймаут);
3095
+ Исключение
3096
+ КонецПопытки;
3097
+ Попытка
3098
+ Представления = Поле.ПолучитьПредставлениеСпискаВыбора();
3099
+ Для Каждого Представление Из Представления Цикл
3100
+ ПредставленияСтрокой.Добавить(ПредставлениеВариантаСпискаВыбораMCP(Представление));
3101
+ КонецЦикла;
3102
+ Исключение
3103
+ КонецПопытки;
3104
+ КонецЕсли;
3105
+ Возврат Новый Структура("field,opened,formed,count,items,value", ?(ПустаяСтрока(ИмяПоля), ЗаголовокПоля, ИмяПоля), Открыт, Сформирован, ПредставленияСтрокой.Количество(), ПредставленияСтрокой, ПредставлениеДанныхПоля(Поле));
3106
+ КонецФункции
3107
+
3108
+ &НаКлиенте
3109
+ Функция ЗакрытьВыпадающийСписокПоляMCP(Параметры)
3110
+ ИмяПоля = СвойствоСтруктуры(Параметры, "field_name", "");
3111
+ ЗаголовокПоля = СвойствоСтруктуры(Параметры, "title", "");
3112
+ ИскатьПоЗаголовку = СвойствоСтруктуры(Параметры, "search_by_title", Ложь);
3113
+ МаксимальнаяГлубина = СвойствоСтруктуры(Параметры, "max_depth", 32);
3114
+ Если ИскатьПоЗаголовку Тогда
3115
+ ЗаголовокПоля = ИмяПоля;
3116
+ ИмяПоля = "";
3117
+ КонецЕсли;
3118
+ АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3119
+ Поле = НайтиОбъектРекурсивно(АктивноеОкно, ИмяПоля, ЗаголовокПоля, "", 0, МаксимальнаяГлубина);
3120
+ Если Поле = Неопределено Тогда
3121
+ ВызватьИсключение "Поле не найдено: " + ?(ПустаяСтрока(ИмяПоля), ЗаголовокПоля, ИмяПоля);
3122
+ КонецЕсли;
3123
+ БылОткрыт = ОткрытВыпадающийСписокПоляMCP(Поле);
3124
+ Если БылОткрыт Тогда
3125
+ Поле.ЗакрытьВыпадающийСписок();
3126
+ КонецЕсли;
3127
+ Возврат Новый Структура("field,closed,was_opened,value", ?(ПустаяСтрока(ИмяПоля), ЗаголовокПоля, ИмяПоля), БылОткрыт, БылОткрыт, ПредставлениеДанныхПоля(Поле));
3128
+ КонецФункции
3129
+
3130
+
3131
+ &НаКлиенте
3132
+ Функция ИндексРелевантногоВариантаСпискаВыбораMCP(Представления, ИскомоеПредставление)
3133
+ ИскомоеНРег = НРег(ИскомоеПредставление);
3134
+ Индекс = 0;
3135
+ ПервыйСодержащий = Неопределено;
3136
+ Для Каждого Представление Из Представления Цикл
3137
+ ТекстВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представление);
3138
+ ОтображаемыйТекст = "";
3139
+ Если ТипЗнч(Представление) = Тип("Структура") Тогда
3140
+ Если Не Представление.Свойство("ОтображаемыйТекст", ОтображаемыйТекст) Тогда
3141
+ ОтображаемыйТекст = "";
3142
+ КонецЕсли;
3143
+ КонецЕсли;
3144
+ ТекстНРег = НРег(ТекстВарианта);
3145
+ ОтображаемыйНРег = НРег(ОтображаемыйТекст);
3146
+ Если ТекстНРег = ИскомоеНРег Или ОтображаемыйНРег = ИскомоеНРег Тогда
3147
+ Возврат Индекс;
3148
+ КонецЕсли;
3149
+ Если ПервыйСодержащий = Неопределено Тогда
3150
+ Если СтрНайти(ТекстНРег, ИскомоеНРег) > 0 Или СтрНайти(ОтображаемыйНРег, ИскомоеНРег) > 0 Тогда
3151
+ ПервыйСодержащий = Индекс;
3152
+ КонецЕсли;
3153
+ КонецЕсли;
3154
+ Индекс = Индекс + 1;
3155
+ КонецЦикла;
3156
+ Возврат ПервыйСодержащий;
3157
+ КонецФункции
3158
+
3159
+ &НаКлиенте
3160
+ Функция ИндексВариантаСпискаВыбораMCP(Представления, ИскомоеПредставление)
3161
+ Индекс = 0;
3162
+ Для Каждого Представление Из Представления Цикл
3163
+ ТекстВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представление);
3164
+ ОтображаемыйТекст = "";
3165
+ Если ТипЗнч(Представление) = Тип("Структура") Тогда
3166
+ Если Не Представление.Свойство("ОтображаемыйТекст", ОтображаемыйТекст) Тогда
3167
+ ОтображаемыйТекст = "";
3168
+ КонецЕсли;
3169
+ КонецЕсли;
3170
+ Если ТекстВарианта = ИскомоеПредставление Или ОтображаемыйТекст = ИскомоеПредставление Тогда
3171
+ Возврат Индекс;
3172
+ КонецЕсли;
3173
+ Индекс = Индекс + 1;
3174
+ КонецЦикла;
3175
+ Возврат Неопределено;
3176
+ КонецФункции
3177
+
3064
3178
  &НаКлиенте
3065
3179
  Функция ПереключитьПолеMCP(Параметры)
3066
3180
  Если ТестируемоеПриложение = Неопределено Тогда
@@ -3068,30 +3182,42 @@
3068
3182
  КонецЕсли;
3069
3183
  ИмяПоля = СвойствоСтруктуры(Параметры, "field_name", "");
3070
3184
  Значение = СвойствоСтруктуры(Параметры, "value", Ложь);
3185
+ МаксимальнаяГлубина = СвойствоСтруктуры(Параметры, "max_depth", 20);
3071
3186
  АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3072
- Поле = НайтиОбъектРекурсивно(АктивноеОкно, ИмяПоля, "", "", 0, 10);
3187
+ Поле = НайтиОбъектРекурсивно(АктивноеОкно, ИмяПоля, "", "", 0, МаксимальнаяГлубина);
3073
3188
  Если Поле = Неопределено Тогда
3074
3189
  ВызватьИсключение "Поле не найдено: " + ИмяПоля;
3075
3190
  КонецЕсли;
3076
3191
  НужноДа = (Значение = Истина) Или (НРег(Строка(Значение)) = "да") Или (НРег(Строка(Значение)) = "true") Или (Строка(Значение) = "1");
3192
+ ТекущееПредставление = ПредставлениеДанныхПоля(Поле);
3193
+ СейчасДа = (НРег(Строка(ТекущееПредставление)) = "да") Или (НРег(Строка(ТекущееПредставление)) = "true") Или (Строка(ТекущееПредставление) = "1");
3194
+ Если НужноДа = СейчасДа Тогда
3195
+ Возврат Новый Структура("toggled,field,value,method", Истина, ИмяПоля, Значение, "ТекущееЗначение");
3196
+ КонецЕсли;
3077
3197
  Попытка
3078
- Если НужноДа Тогда
3079
- Поле.УстановитьФлажок();
3080
- Иначе
3081
- Поле.СнятьФлажок();
3082
- КонецЕсли;
3198
+ Поле.УстановитьОтметку();
3083
3199
  Исключение
3084
- ОшибкаФлажок = ОписаниеОшибки();
3085
- Попытка
3086
- Поле.Активизировать();
3087
- Поле.Выбрать();
3088
- Исключение
3089
- ВызватьИсключение "Не удалось переключить поле " + ИмяПоля + ": " + ОшибкаФлажок + "; " + ОписаниеОшибки();
3090
- КонецПопытки;
3200
+ ВызватьИсключение "Не удалось переключить поле " + ИмяПоля + ": " + ОписаниеОшибки();
3091
3201
  КонецПопытки;
3092
- Возврат Новый Структура("toggled,field,value", Истина, ИмяПоля, Значение);
3202
+ Возврат Новый Структура("toggled,field,value,method", Истина, ИмяПоля, Значение, "УстановитьОтметку");
3093
3203
  КонецФункции
3094
3204
 
3205
+
3206
+ &НаКлиенте
3207
+ Процедура ДобавитьДиагностикуМодальногоОкнаЕслиОткрылосьMCP(Результат, ЗаголовокОкнаДоДействия)
3208
+ Попытка
3209
+ АктивноеОкноПослеДействия = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3210
+ ЗаголовокОкнаПослеДействия = БезопасноеСвойство(АктивноеОкноПослеДействия, "Заголовок", "");
3211
+ НормализованныйЗаголовокДо = СтрЗаменить(ЗаголовокОкнаДоДействия, " *", "");
3212
+ НормализованныйЗаголовокПосле = СтрЗаменить(ЗаголовокОкнаПослеДействия, " *", "");
3213
+ Если Не ПустаяСтрока(ЗаголовокОкнаПослеДействия) И НормализованныйЗаголовокПосле <> НормализованныйЗаголовокДо Тогда
3214
+ Результат.Вставить("modal_opened", Истина);
3215
+ Результат.Вставить("modal_title", ЗаголовокОкнаПослеДействия);
3216
+ КонецЕсли;
3217
+ Исключение
3218
+ КонецПопытки;
3219
+ КонецПроцедуры
3220
+
3095
3221
  &НаКлиенте
3096
3222
  Функция УстановитьЗначениеПоля(Параметры)
3097
3223
  Если ТестируемоеПриложение = Неопределено Тогда
@@ -3225,29 +3351,103 @@
3225
3351
  // Для полей с выпадающим списком (ссылочные поля, поля выбора) платформа
3226
3352
  // после ВвестиТекст() часто открывает dropdown с отфильтрованными вариантами.
3227
3353
  // В этом случае выбираем первый вариант и возвращаем его представление.
3354
+ ТекстовыйВводВыполнен = Ложь;
3355
+ НеподтвержденноеПредставлениеПослеВвода = "";
3228
3356
  Попытка
3357
+ ЗаголовокОкнаДоВвода = БезопасноеСвойство(АктивноеОкно, "Заголовок", "");
3229
3358
  Поле.ВвестиТекст(Значение);
3359
+ ТекстовыйВводВыполнен = Истина;
3230
3360
  Если ПустаяСтрока(Значение) Тогда
3231
3361
  Возврат Новый Структура("set,field,value,method", Истина, ИмяПоля, Значение, "ВвестиТекст");
3232
3362
  КонецЕсли;
3233
3363
 
3234
- // Проверяем, открылся ли выпадающий список после ввода текста.
3364
+ // ВвестиТекст() на web-клиенте может пересоздать/подменить объект поля.
3365
+ // Перед проверкой dropdown перечитываем поле из активного окна.
3366
+ АктивноеОкноПослеВвода = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3367
+ СвежееПолеПослеВвода = НайтиОбъектРекурсивно(АктивноеОкноПослеВвода, ИмяПоля, ЗаголовокПоля, "", 0, МаксимальнаяГлубина);
3368
+ Если СвежееПолеПослеВвода <> Неопределено Тогда
3369
+ Поле = СвежееПолеПослеВвода;
3370
+ КонецЕсли;
3371
+
3372
+ // Сначала проверяем, изменилось ли значение поля сразу после ввода.
3373
+ // Если да — возвращаем успех без ожидания dropdown.
3374
+ ПредставлениеСразуПослеВвода = ПредставлениеДанныхПоля(Поле);
3375
+ Если ПредставлениеСразуПослеВвода = Значение Или СтрНайти(ПредставлениеСразуПослеВвода, Значение) > 0 Тогда
3376
+ // Значение подтвердилось, но dropdown может открыться асинхронно
3377
+ // уже после этого. Коротко ждём и закрываем/выбираем, если открылся.
3378
+ НачалоПостОжидания = ТекущаяУниверсальнаяДатаВМиллисекундах();
3379
+ Пока Не ОткрытВыпадающийСписокПоляMCP(Поле)
3380
+ И ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоПостОжидания < 1000 Цикл
3381
+ АктивноеОкноПослеВвода = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3382
+ СвежееПолеПослеВвода = НайтиОбъектРекурсивно(АктивноеОкноПослеВвода, ИмяПоля, ЗаголовокПоля, "", 0, МаксимальнаяГлубина);
3383
+ Если СвежееПолеПослеВвода <> Неопределено Тогда
3384
+ Поле = СвежееПолеПослеВвода;
3385
+ КонецЕсли;
3386
+ КонецЦикла;
3387
+ Если ОткрытВыпадающийСписокПоляMCP(Поле) Тогда
3388
+ Попытка
3389
+ Поле.ОжидатьФормированияВыпадающегоСписка(60);
3390
+ Представления = Поле.ПолучитьПредставлениеСпискаВыбора();
3391
+ Если Представления.Количество() > 0 Тогда
3392
+ ИндексВыбора = ИндексРелевантногоВариантаСпискаВыбораMCP(Представления, Значение);
3393
+ Если ИндексВыбора <> Неопределено Тогда
3394
+ ПредставлениеВыбираемогоВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представления[ИндексВыбора]);
3395
+ Попытка
3396
+ Поле.ВыполнитьВыборИзСпискаВыбора(ПредставлениеВыбираемогоВарианта);
3397
+ Исключение
3398
+ Поле.ВыполнитьВыборИзСпискаВыбора(ИндексВыбора);
3399
+ КонецПопытки;
3400
+ Иначе
3401
+ Поле.ЗакрытьВыпадающийСписок();
3402
+ КонецЕсли;
3403
+ Иначе
3404
+ Поле.ЗакрытьВыпадающийСписок();
3405
+ КонецЕсли;
3406
+ Исключение
3407
+ КонецПопытки;
3408
+ КонецЕсли;
3409
+ Возврат Новый Структура("set,field,value,method", Истина, ИмяПоля, Значение, "ВвестиТекст");
3410
+ КонецЕсли;
3411
+
3412
+ // Значение не изменилось — ждём, не откроется ли dropdown асинхронно.
3235
3413
  // Это характерно для ссылочных полей и полей с выбором из справочника.
3414
+ // На web-клиенте список может открыться асинхронно уже после возврата
3415
+ // ВвестиТекст(), поэтому коротко ждём именно факта открытия dropdown.
3416
+ НачалоОжиданияСписка = ТекущаяУниверсальнаяДатаВМиллисекундах();
3417
+ Пока Не ОткрытВыпадающийСписокПоляMCP(Поле)
3418
+ И ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоОжиданияСписка < 2000 Цикл
3419
+ АктивноеОкноПослеВвода = ТестируемоеПриложение.ПолучитьАктивноеОкно();
3420
+ СвежееПолеПослеВвода = НайтиОбъектРекурсивно(АктивноеОкноПослеВвода, ИмяПоля, ЗаголовокПоля, "", 0, МаксимальнаяГлубина);
3421
+ Если СвежееПолеПослеВвода <> Неопределено Тогда
3422
+ Поле = СвежееПолеПослеВвода;
3423
+ КонецЕсли;
3424
+ КонецЦикла;
3236
3425
  Если ОткрытВыпадающийСписокПоляMCP(Поле) Тогда
3426
+ СписокСформирован = Ложь;
3237
3427
  Попытка
3238
- Поле.ОжидатьФормированияВыпадающегоСписка(10);
3428
+ СписокСформирован = Поле.ОжидатьФормированияВыпадающегоСписка(60);
3239
3429
  Исключение
3240
3430
  КонецПопытки;
3241
3431
  Представления = Поле.ПолучитьПредставлениеСпискаВыбора();
3242
3432
  Если Представления.Количество() > 0 Тогда
3243
- // Выбираем первый вариант (индекс 0).
3244
- ПредставлениеВыбираемогоВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представления[0]);
3245
- Поле.ВыполнитьВыборИзСпискаВыбора(0);
3433
+ ИндексВыбора = ИндексРелевантногоВариантаСпискаВыбораMCP(Представления, Значение);
3434
+ Если ИндексВыбора = Неопределено Тогда
3435
+ Поле.ЗакрытьВыпадающийСписок();
3436
+ ВызватьИсключение "Выпадающий список открыт, но релевантный вариант для значения " + Значение + " не найден";
3437
+ КонецЕсли;
3438
+ ПредставлениеВыбираемогоВарианта = ПредставлениеВариантаСпискаВыбораMCP(Представления[ИндексВыбора]);
3439
+ Попытка
3440
+ Поле.ВыполнитьВыборИзСпискаВыбора(ПредставлениеВыбираемогоВарианта);
3441
+ Исключение
3442
+ Поле.ВыполнитьВыборИзСпискаВыбора(ИндексВыбора);
3443
+ КонецПопытки;
3246
3444
  ВыбранноеПредставление = ПредставлениеДанныхПоля(Поле);
3247
3445
  Если ПустаяСтрока(ВыбранноеПредставление) Тогда
3248
3446
  ВыбранноеПредставление = ПредставлениеВыбираемогоВарианта;
3249
3447
  КонецЕсли;
3250
- Возврат Новый Структура("set,field,value,method,selected", Истина, ИмяПоля, Значение, "ВвестиТекст+ВыполнитьВыборИзСпискаВыбора(0)", ВыбранноеПредставление);
3448
+ РезультатВыбора = Новый Структура("set,field,value,method,selected,dropdown_formed,dropdown_index", Истина, ИмяПоля, Значение, "ВвестиТекст+ВыполнитьВыборИзСпискаВыбора", ВыбранноеПредставление, СписокСформирован, ИндексВыбора);
3449
+ ДобавитьДиагностикуМодальногоОкнаЕслиОткрылосьMCP(РезультатВыбора, ЗаголовокОкнаДоВвода);
3450
+ Возврат РезультатВыбора;
3251
3451
  Иначе
3252
3452
  // Список выбора пуст — просто закрываем dropdown.
3253
3453
  Попытка
@@ -3255,22 +3455,18 @@
3255
3455
  Исключение
3256
3456
  КонецПопытки;
3257
3457
  КонецЕсли;
3258
- Иначе
3259
- // Dropdown не открылся автоматически — пробуем открыть явно и выбрать.
3260
- Попытка
3261
- Поле.ОткрытьВыпадающийСписок();
3262
- Поле.ОжидатьФормированияВыпадающегоСписка(10);
3263
- Поле.ВыполнитьВыборИзСпискаВыбора(Значение);
3264
- ВыбранноеПредставление = ПредставлениеДанныхПоля(Поле);
3265
- Если ПустаяСтрока(ВыбранноеПредставление) Тогда
3266
- ВыбранноеПредставление = Значение;
3267
- КонецЕсли;
3268
- Возврат Новый Структура("set,field,value,method,selected", Истина, ИмяПоля, Значение, "ВвестиТекст+ОткрытьВыпадающийСписок+ВыполнитьВыборИзСпискаВыбора", ВыбранноеПредставление);
3269
- Исключение
3270
- // Не удалось открыть/выбрать — продолжаем к циклу ожидания представления.
3271
- КонецПопытки;
3272
3458
  КонецЕсли;
3273
3459
 
3460
+ // Смещаем фокус на следующий элемент, чтобы сработали обработчики
3461
+ // ПриИзменении() и платформа обновила представление данных.
3462
+ Попытка
3463
+ ФормаПоля = НайтиРодительскуюФорму(Поле);
3464
+ Если ФормаПоля <> Неопределено Тогда
3465
+ ФормаПоля.ПерейтиКСледующемуЭлементу();
3466
+ КонецЕсли;
3467
+ Исключение
3468
+ КонецПопытки;
3469
+
3274
3470
  НачалоОжиданияВвода = ТекущаяУниверсальнаяДатаВМиллисекундах();
3275
3471
  Пока ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоОжиданияВвода < 2000 Цикл
3276
3472
  АктивноеОкно = ТестируемоеПриложение.ПолучитьАктивноеОкно();
@@ -3283,11 +3479,18 @@
3283
3479
  Возврат Новый Структура("set,field,value,method", Истина, ИмяПоля, Значение, "ВвестиТекст");
3284
3480
  КонецЕсли;
3285
3481
  КонецЦикла;
3286
- Ошибки.Добавить("ВвестиТекст=значение введено, но представление не подтвердилось; current_value=" + ПредставлениеПослеВвода);
3482
+
3483
+ // Текст введён, но представление не подтвердилось. Если dropdown сам
3484
+ // не открылся — не открываем его кодом, а возвращаем честную ошибку.
3485
+ НеподтвержденноеПредставлениеПослеВвода = ПредставлениеПослеВвода;
3287
3486
  Исключение
3288
3487
  Ошибки.Добавить("ВвестиТекст=" + ОписаниеОшибки());
3289
3488
  КонецПопытки;
3290
3489
 
3490
+ Если ТекстовыйВводВыполнен Тогда
3491
+ ВызватьИсключение "Не удалось установить поле " + ИмяПоля + ": ВвестиТекст выполнен, но значение не подтвердилось; текущее представление: " + НеподтвержденноеПредставлениеПослеВвода;
3492
+ КонецЕсли;
3493
+
3291
3494
  // Переключатели/радиокнопки и поля со списком выбора: после активации
3292
3495
  // форма умеет выбирать значение из текущего открытого списка выбора.
3293
3496
  Попытка
@@ -3339,10 +3542,23 @@
3339
3542
  КонецПопытки;
3340
3543
 
3341
3544
  Попытка
3545
+ ЗаголовокОкнаДоВыбора = БезопасноеСвойство(ТестируемоеПриложение.ПолучитьАктивноеОкно(), "Заголовок", "");
3342
3546
  Поле.ОткрытьВыпадающийСписок();
3343
3547
  Поле.ОжидатьФормированияВыпадающегоСписка(10);
3344
- Поле.ВыполнитьВыборИзСпискаВыбора(Значение);
3345
- Возврат Новый Структура("set,field,value,method", Истина, ИмяПоля, Значение, "ОткрытьВыпадающийСписок");
3548
+ Представления = Поле.ПолучитьПредставлениеСпискаВыбора();
3549
+ ИндексВыбора = ИндексВариантаСпискаВыбораMCP(Представления, Значение);
3550
+ Если ИндексВыбора = Неопределено Тогда
3551
+ Поле.ВыполнитьВыборИзСпискаВыбора(Значение);
3552
+ Иначе
3553
+ Поле.ВыполнитьВыборИзСпискаВыбора(ИндексВыбора);
3554
+ КонецЕсли;
3555
+ ВыбранноеПредставление = ПредставлениеДанныхПоля(Поле);
3556
+ Если ПустаяСтрока(ВыбранноеПредставление) Тогда
3557
+ ВыбранноеПредставление = Значение;
3558
+ КонецЕсли;
3559
+ РезультатВыбора = Новый Структура("set,field,value,method,selected", Истина, ИмяПоля, Значение, "ОткрытьВыпадающийСписок+ВыполнитьВыборИзСпискаВыбора", ВыбранноеПредставление);
3560
+ ДобавитьДиагностикуМодальногоОкнаЕслиОткрылосьMCP(РезультатВыбора, ЗаголовокОкнаДоВыбора);
3561
+ Возврат РезультатВыбора;
3346
3562
  Исключение
3347
3563
  Ошибки.Добавить("ОткрытьВыпадающийСписок=" + ОписаниеОшибки());
3348
3564
  КонецПопытки;
@@ -3373,16 +3589,20 @@
3373
3589
  &НаКлиенте
3374
3590
  Функция ПредставлениеДанныхПоля(Поле)
3375
3591
  Попытка
3376
- Возврат Поле.ПолучитьПредставлениеДанных();
3592
+ ПредставлениеДанных = Поле.ПолучитьПредставлениеДанных();
3593
+ Если Не ПустаяСтрока(ПредставлениеДанных) Тогда
3594
+ Возврат ПредставлениеДанных;
3595
+ КонецЕсли;
3596
+ Исключение
3597
+ КонецПопытки;
3598
+
3599
+ Попытка
3600
+ Возврат Поле.ПолучитьОтображаемыйТекст();
3377
3601
  Исключение
3378
3602
  Попытка
3379
- Возврат Поле.ПолучитьОтображаемыйТекст();
3603
+ Возврат Строка(БезопасноеСвойство(Поле, "Данные", ""));
3380
3604
  Исключение
3381
- Попытка
3382
- Возврат Строка(БезопасноеСвойство(Поле, "Данные", ""));
3383
- Исключение
3384
- Возврат "";
3385
- КонецПопытки;
3605
+ Возврат "";
3386
3606
  КонецПопытки;
3387
3607
  КонецПопытки;
3388
3608
  КонецФункции
@@ -3126,6 +3126,39 @@ async def current_element(*,
3126
3126
  return await _bridge_call_recorded(session_id, "current_element", {})
3127
3127
 
3128
3128
 
3129
+ @mcp.tool()
3130
+ async def field_dropdown_state(
3131
+ field_name: Annotated[str, "Field technical name or title"],
3132
+ search_by_title: Annotated[bool, "Search field by visible title"] = False,
3133
+ timeout: Annotated[int, "Wait timeout for dropdown formation"] = 60,
3134
+ max_depth: Annotated[int, "Maximum search depth"] = 32,
3135
+ *,
3136
+ session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3137
+ ) -> Any:
3138
+ """Return whether a field dropdown is open and its visible choice items."""
3139
+ return await _bridge_call_recorded(
3140
+ session_id,
3141
+ "field_dropdown_state",
3142
+ {"field_name": field_name, "search_by_title": search_by_title, "timeout": timeout, "max_depth": max_depth},
3143
+ )
3144
+
3145
+
3146
+ @mcp.tool()
3147
+ async def field_dropdown_close(
3148
+ field_name: Annotated[str, "Field technical name or title"],
3149
+ search_by_title: Annotated[bool, "Search field by visible title"] = False,
3150
+ max_depth: Annotated[int, "Maximum search depth"] = 32,
3151
+ *,
3152
+ session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3153
+ ) -> Any:
3154
+ """Close an open dropdown for a form field, if it is open."""
3155
+ return await _bridge_call_recorded(
3156
+ session_id,
3157
+ "field_dropdown_close",
3158
+ {"field_name": field_name, "search_by_title": search_by_title, "max_depth": max_depth},
3159
+ )
3160
+
3161
+
3129
3162
  @mcp.tool()
3130
3163
  async def table_current_row(
3131
3164
  name: Annotated[str, "Table technical name"] = "",
@@ -3204,11 +3237,12 @@ async def tabular_document_save(
3204
3237
  async def toggle_field(
3205
3238
  field_name: Annotated[str, "Field technical name"],
3206
3239
  value: Annotated[bool, "Desired checkbox value"] = False,
3240
+ max_depth: Annotated[int, "Max UI tree depth for nested fields"] = 20,
3207
3241
  *,
3208
3242
  session_id: Annotated[str, "Session id"] = DEFAULT_SESSION,
3209
3243
  ) -> Any:
3210
3244
  """Set a checkbox-like field using the 1C test client API."""
3211
- return await _bridge_call_recorded(session_id, "toggle_field", {"field_name": field_name, "value": value})
3245
+ return await _bridge_call_recorded(session_id, "toggle_field", {"field_name": field_name, "value": value, "max_depth": max_depth})
3212
3246
 
3213
3247
 
3214
3248
  @mcp.tool()
File without changes
File without changes
File without changes
File without changes
File without changes