Я пытаюсь найти лучший способ сохранить локатор элемента, чтобы в конечном итоге использовать этот локатор для поиска родителя.
Например, в этой конкретной задаче у меня есть строка таблицы (<tr>
), в которой есть уникальный текст Foo
(как <label>
). Эта строка таблицы также имеет флажок. Выглядит в основном так:
(Представьте, что есть схема, но это, по сути, макет строки таблицы. В таблице много строк (неопределенное количество), поэтому я не могу просто сказать nth
строка. Таким образом, очевидным уникальным элементом является захват элемента через его текст и сохранение "дочерний" локатор, который я сделал так:
const tableRow = page.locator('label:has-text("Foo")')
Однако теперь мне нужно захватить его родителя, то есть <tr
>, чтобы затем я мог захватить флажок, чтобы щелкнуть по нему.
Каков наилучший способ по этому поводу? У Playwright точно нет «родительского» селектора, самый близкий, который я смог найти, это опция https://playwright.dev/docs/api/class-page#page-locator-option-hashas
.
Итак, насколько я понимаю, мне нужно сделать что-то вроде:
const tableRow = await page.locator('tr', { has: page.locator('label:has-text("Foo")') })
await tableRow.locator('input[type = "checkbox"]').check()
Или есть лучший способ?
В качестве примечания: при сохранении локатора он говорит, что ключевое слово await
не требуется (1-я строка кода выше). Разве нам не нужен await
при хранении локатора?
jsfiddle: https://jsfiddle.net/mLb50jwp/ (очевидно, самая простая его версия)
@ggorlen я добавил простой jsfiddle
Спасибо, а не могли бы вы указать код в вопросе? Внешние ссылки со временем исчезают, и у нас есть фрагменты, которые запускают код.
Код в скрипте выглядит странно — обычно метки связаны с входами, поэтому, если вы нажмете на метку, вы сможете изменить подключенный вход. В вашем случае у вас есть одна метка Foo
, которая ни с чем не связана, и другая метка, которая оборачивает ввод, но не имеет текста и, следовательно, ничего не делает. Возможно, вы слишком упростили код.
В приведенном ниже ответе показано, как получить родителя («..»), но нужно ли вам получать его с помощью родителя? На самом деле я бы рекомендовал подход has
, так как он не только более семантически получает строку на основе метки, как вы хотели, но и если туда добавляется дополнительный div или что-то в этом роде, он все равно будет работать. Относительные селекторы, такие как прямой родительский или дочерний (вместо предок/потомок), как правило, очень хрупкие и неустойчивы к изменениям разработчиков.
@DavidR, вы видели мой самый первый ответ ???, он не хрупкий, он направлен прямо на флажок без родителя и очень гибкий, поскольку это не абсолютный локатор.
@VishalAggarwal Я так и сделал, и да, это правда, так что это хорошее решение. Одна потенциальная проблема заключается в том, что он найдет строки с этим текстом в любом месте, а не только метку, но это может не иметь значения для их случая, поскольку это все, что у них есть. И вообще, я бы порекомендовал, чтобы локаторы больше соответствовали тому, как пользователь будет находить, или другим практикам или семантической структуре, которую предоставляет Playwright, а не xpath, например has
. Тем не менее, ваш лучший ответ определенно следует той же гибкой, а не абсолютной идее, как вы сказали, и представляет собой надежный селектор/подход xpath.
Кроме того, основываясь на их заявлении, что они хотели использовать локатор для ребенка, чтобы затем найти родителя, если это действительно / конкретно то, что они хотят сделать, has
, как они просили, в этом случае показалось наиболее подходящим решением.
const expectedName = "Foo"
'//tr[contains(.,"'+expectedName+'")]//input[@type = "checkbox"]'
parent_locator = page.get_by_role("button").locator('..')
Родительский элемент можно выбрать с помощью .., что является сокращением от xpath=..
Xpath=//*[@id='rt-feature']//parent::div
//span/parent::div[@class = "parent"]
-- возвращает родительский элемент только точного типа узла.
await обычно используется для развертывания обещаний путем передачи промиса в качестве выражения. Использование await приостанавливает выполнение окружающей его асинхронной функции до тех пор, пока обещание не будет выполнено (то есть выполнено или отклонено). Когда выполнение возобновляется, значение выражения ожидания становится значением выполненного обещания.
Во время объявления локатора нет обещания для разрешения, поэтому ключевое слово await не требуется, однако при выполнении операции, такой как «щелчок», обещание возвращается от драматурга, для которого требуется ключевое слово await, чтобы приостановить выполнение до обещания разрешено.
Предоставление нескольких вариантов — это хорошо. Одно замечание заключается в том, что локатор — это не просто строка, это похоже на то, что он просто определяет, как найти, а не на самом деле определяет местоположение или выполняет какое-либо действие/что-либо асинхронное.
Если вы поделитесь минимально воспроизводимым примером вашего веб-сайта/разметки и исполняемого кода, гораздо проще предоставить лучшее решение. Тем не менее, ваше второе решение выглядит разумным — получить строку с этой меткой, и у вас есть и родитель, и дочерний элемент. Начните с вершины дерева и работайте вниз, а не снизу вверх. Локаторы оцениваются лениво, поэтому вам нужно только
await
, когда вы выполняете действие, например.click()
или.check()
.