Префикс в j-test-id компонента экрана

Доброго дня!

j-test-id компонентов экрана, находящихся внутри композитного компонента, содержит префикс, которого нет в id, объявленных в проекте. (возможно дело не в композитном компоненте, но в обоих наших компонентах наблюдается такое)

<div class="v-label v-disabled v-widget v-has-width" j-test-id="IgEAtAsLlI_nameOfCreatorLabel" style="width: 100%;">Начальник производственного отдела</div>
Обстановка

Jmix version: 1.5.5
Jmix Studio plugin version: 2.2.1-241
IntelliJ version: IntelliJ IDEA 2024.1 (Community Edition)
selenide:6.12.4
jmix-masquerade:1.2.0
Проект и тесты в стадии миграции с CUBA .

Дескриптор композитного компонента
<composite xmlns="http://jmix.io/schema/ui/composite">
    <hbox id="rootBox" width="100%" expand="labelBox" align="MIDDLE_LEFT">
        <hbox id="labelBox" expand="nameOfCreatorLabel" enable="false" spacing="true" align="MIDDLE_LEFT">
            <label id="captionLabel" align="MIDDLE_LEFT"
                   value="msg://<PROJECT>.web/captionForIdenticalBlocks.owner"/>
            <label id="nameOfCreatorLabel" align="MIDDLE_LEFT"/>
            <label id="dateOfCreationLabel" align="MIDDLE_LEFT"/>
        </hbox>
        <hbox id="buttonsBox" align="MIDDLE_RIGHT">
            <popupView id="readersListPopupView"/>
            <button id="readersListButton"
                icon="font-icon:EYE"
                stylename="borderless"
                align="MIDDLE_RIGHT"/>
            <popupButton id="detailedInfoButton"
                         icon="INFO_CIRCLE"
                         stylename="borderless"
                         closePopupOnOutsideClick="true">
                <popup>
<!--                    init in Controller-->
                </popup>
            </popupButton>
            <popupButton id="copyHyperlinkButton"
                         icon="FILES_O"
                         stylename="borderless"
                         autoClose="false"
                         closePopupOnOutsideClick="true">
                <popup>
<!--                    init in Controller-->
                </popup>
            </popupButton>
            <button id="openProjectBtn" visible="false" caption="msg://<PROJECT>.web/components.openProjectBtn.caption" description="msg://<PROJECT>.web/components.openProjectBtn.description" icon="LEVEL_UP" stylename="borderless"/>
        </hbox>
    </hbox>
</composite>
Контроллер композитного компонента (часть)
@CompositeDescriptor("headerinfo-component.xml")
public class HeaderInfoBox
        extends CompositeComponent<HBoxLayout>
        implements CompositeWithCaption, CompositeWithHtmlDescription, CompositeWithContextHelp {

    public static final String NAME = "headerInfoBox";

    private Label<String> captionLabel;
    private Label<String> nameOfCreatorLabel;
    private Label<String> dateOfCreationLabel;
    private PopupView readersListPopupView;
    private Button readersListButton;

    private PopupButton detailedInfoButton;
    private Label<String> createdByLabel;
    private Label<String> dateCreateLabel;
    private Label<String> changedByLabel;
    private Label<String> dateChangedLabel;

    private PopupButton copyHyperlinkButton;
    private Label<String> copyLinkLabel;
    private Button copyLinkButton;
    private TextField<String> copyLinkTextField;
    private Button openProjectBtn;

    <inject some attributes>

    public HeaderInfoBox() {
        addCreateListener(this::onCreate);
    }

    private void onCreate(CreateEvent createEvent) {

        captionLabel = getInnerComponent("captionLabel");
        nameOfCreatorLabel = getInnerComponent("nameOfCreatorLabel");
        dateOfCreationLabel = getInnerComponent("dateOfCreationLabel");
        readersListPopupView = getInnerComponent("readersListPopupView");
        readersListButton = getInnerComponent("readersListButton");
        detailedInfoButton = getInnerComponent("detailedInfoButton");
        copyHyperlinkButton = getInnerComponent("copyHyperlinkButton");
        openProjectBtn = getInnerComponent("openProjectBtn");

        <some code>
    }
<some code>
}
Загрузчик композитного компонента
public class HeaderInfoBoxLoader extends AbstractComponentLoader<HeaderInfoBox> {

    @Override
    public void createComponent() {
        resultComponent = factory.create(HeaderInfoBox.NAME);
        loadId(resultComponent, element);
    }

    @Override
    public void loadComponent() {
        resultComponent.setScreensInterface(ComponentsHelper.getScreenContext(resultComponent).getScreens());
    }
}
Дескриптор экрана, где размещен композитный компонент (часть)
<layout expand="scrollBox" spacing="true">
        <scrollBox id="scrollBox">
            <hbox id="headHbox" expand="infoHbox"
                  spacing="true"
                  width="100%">
                <hbox id="infoHbox" spacing="true" expand="headerInfo" align="MIDDLE_LEFT">
                    <app:headerInfoBox id="headerInfo"/>
                </hbox>
Описатель экрана для тестирования
public class RCDocEditWindow extends Composite<RCDocEditWindow> {
    @Wire
    public Label nameOfCreatorLabel;

    <some components>
Место в Классе теста, которое вызывает ошибку
RCDocEditWindow rcDocEditWindow = $j(RCDocEditWindow.class);
Assert.assertEquals("Начальник производственного отдела",rcDocEditWindow.nameOfCreatorLabel.getValue());
Стэктрейс
Caused by: NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[j-test-id='nameOfCreatorLabel']"}
Element not found {By.jTestId: nameOfCreatorLabel}
Expected: exist
DOM композитного компонента
<div tabindex="-1" class="v-horizontallayout v-layout v-horizontal v-widget v-has-width" j-test-id="headerInfo" style="width: 100%; height: 30px;">
 <div class="v-expand" style="padding-left: 180px;">
  <div class="v-slot v-align-middle" style="width: 100%; margin-left: -180px;">
   <div tabindex="-1" class="v-horizontallayout v-layout v-horizontal v-disabled v-widget v-has-width" j test-id="IgEAtAsLlI_labelBox" style="width: 100%; height: 19px;">
    <div class="v-expand" style="padding-left: 91px;">
     <div class="v-slot v-align-middle" style="margin-left: -91px;">
      <div class="v-label v-disabled v-widget v-label-undef-w" j-test-id="IgEAtAsLlI_captionLabel">Владелец:
      </div>
     </div>
     <div class="v-spacing">
     </div>
     <div class="v-slot v-align-middle" style="width: 100%;">
      <div class="v-label v-disabled v-widget v-has-width" j-test-id="IgEAtAsLlI_nameOfCreatorLabel" style="width: 100%;">Начальник производственного отдела
      </div>
     </div>
     <div class="v-spacing">
     </div>
     <div class="v-slot v-align-middle">
      <div class="v-label v-disabled v-widget v-label-undef-w v-label-empty" j-test-id="IgEAtAsLlI_dateOfCreationLabel">
      </div>
     </div>
    </div>
   </div>
  </div>
  <div class="v-slot v-align-right v-align-middle"><div tabindex="-1" class="v-horizontallayout v-layout v-horizontal v-widget" j-test-id="IgEAtAsLlI_buttonsBox" style="height: 30px;">
   <div class="v-slot">
    <div class="v-popupview v-widget" j-test-id="IgEAtAsLlI_readersListPopupView">
    </div>
   </div>
   <div class="v-slot v-slot-borderless v-align-right v-align-middle">
    <div tabindex="0" role="button" class="v-button v-widget borderless v-button-borderless icon" j-test-id="IgEAtAsLlI_readersListButton">
     <span class="v-button-wrap">
      <span class="v-icon FontAwesome">
      </span>
      <span class="v-button-caption">0
      </span>
     </span>
    </div>
   </div>
   <div class="v-slot v-slot-borderless v-slot-icon">
    <div tabindex="0" role="button" class="v-button v-widget borderless v-button-borderless icon v-button-icon v-popupbutton" j-test-id="IgEAtAsLlI_detailedInfoButton">
     <span class="v-button-wrap">
      <span class="v-icon FontAwesome">
      </span>
      <span class="v-button-caption">
      </span>
      <div class="v-popup-indicator">
      </div>
     </span>
    </div>
   </div>
   <div class="v-slot v-slot-borderless v-slot-icon">
    <div tabindex="0" role="button" class="v-button v-widget borderless v-button-borderless icon v-button-icon v-popupbutton" j-test-id="IgEAtAsLlI_copyHyperlinkButton">
     <span class="v-button-wrap">
      <span class="v-icon FontAwesome">
      </span>
      <span class="v-button-caption">
      </span>
      <div class="v-popup-indicator">
      </div>
     </span>
    </div>
   </div>
  </div>
 </div>
</div>
</div>
  • Пробовал добавить к аннотации @Wire параметр path, указав ближайший контейнер с корректным j-test-id - компонент определяется неверно.
  • Пробовал объявить через аннотацию @FindBy, указав в параметру id искомый id - не находит.
  • Не хочется объявлять компонент через xpath, т.к. его расположение в контейнерах экранов может меняться.

Подскажите пожалуйста, в чем может быть дело, что компоненту присваивается j-test-id с префиксом и как его можно объявить в описателе экрана тестирования? Может можно как-то объявить с частичным id (containts или regexp)?

Предположения
  • Загрузчик чиркает, что наследование от AbstractComponentLoader как нежелательное, может оно на это влияет. Композитный компонент немного сложноват (и скорее тянет на Фрагмент экрана), по этому затрудняюсь от какого еще класса загрузчика его можно наследовать.
  • Еще можно заметить, что в DOM отсутствует корневой контейнер компонента rootBox. И вообще некоторые компоненты композитного компонента имеют простые id, которые могут быть не уникальными на всем экране, но так было и в CUBA.

PS. В CUBA вся логика экранов и тестов работала в CUBA и cuba-id совпадал с id компонентов композитного компонента.

Добрый день!

Префикс ко внутренним компонентам композитного компонента добавляется не просто так. Дело в том, что без префиксов несколько композитных компнентов одного типа создают коллизию с ID внутренних компонентов. Например, в экране есть два композитных компонента, а внутренние компоненты имею одинаковый ID. В итоге в дерево компонентов Window попадут компоненты только от одного композитного компонента.

В CUBA описанное выше решение не переносилось.

Аннотация Wire ищет только по j-test-id, также как и параметр path. А атрибут id вообще не используется. Думаю здесь можно применить несколько решений.

  1. Как Вы предлагали можно искать компонент по частичному j-test-id, примерно выглядит так:

    @FindBy(xpath = "//input[contains(@j-test-id,'valueField')]")
    public TextField valueField;
    

    Решение подходит, если в экране только один композитный компонент. Плюс такой xpath не зависит от положения элемента в DOM дереве.

  2. Самим управлять генерацией префикса в компоненте. Для этого нужно переопределить один или несколько методов из CompositeComponent:

    • updateComponentIds();
    • updateIdIfNeeded();
    • getFullId();
    • getPrefixId();
1 симпатия