Поэтому я пытаюсь понять/объяснить, почему часть кода работает именно так. Я знаком с асинхронностью по большей части и понимаю, как работают промисы на базовом уровне, но я пытаюсь объяснить, почему блок кода работает так, как в Playwright.
import { test, expect } from "@playwright/test";
test.only("Basic Login", async ({ page }) => {
//Basic test that demonstrates logging into a webpage
await page.goto("/");
page
.getByPlaceholder("Email Address")
.fill("[email protected]");
await page.getByPlaceholder("Password").fill("bar");
await page.getByRole("button", { name: "Sign In" }).click();
await expect(page).toHaveTitle("The Foobar Page");
});
Так что, очевидно, это не сработает, потому что первое «заполнение» для ввода адреса электронной почты не awaiting. В настоящее время поведение заключается в том, что он заполняет поле пароля строками электронной почты и пароля вместе (я полагаю, я не вижу его, потому что это поле пароля, но количество * больше, поэтому я предполагаю, что он помещает оба fill строки в поле пароля.
Однако я не могу понять, почему. getByPlaceholder будет повторять попытки до тех пор, пока не произойдет сбой. и я также считаю, что fill ждет проверки (IE: поле заполняемое/видимое/и т.д...)
Однако драматург конкретно не указывает, обещают ли они возвращение конкретно. Я ПРЕДПОЛАГАЮ, что они это делают, но в документах конкретно не говорится.
Так что же на самом деле происходит здесь шаг за шагом? Я действительно не могу «пройти» через это, поэтому это затрудняет объяснение другим. Например, я понимаю, почему это не работает на высоком уровне, но не совсем понимаю, почему это не работает в деталях.
Это также приводит меня к побочному вопросу: Дожидается ли Драматург ОБОИХ обещаний при цепочке функций? Или только последний?
@VishalAggarwal Да, я определенно больше знаком с Cypress, но перехожу к драматургу.





getByPlaceholder будет повторять попытки до тех пор, пока не произойдет сбой.
Локаторы не асинхронны. Они просто возвращают локатор. Функция fill будет повторять попытку.
Я думаю, что это также отвечает на вопрос: «Это также приводит меня к побочному вопросу: Драматург ждет ОБА обещаний при цепочке функций? Или только последнее?».
Так что же на самом деле происходит здесь шаг за шагом? Я действительно не могу «пройти» через это, поэтому это затрудняет объяснение другим. Например, я понимаю, почему это не работает на высоком уровне, но не совсем понимаю, почему это не работает в деталях.
Все дело в цикле событий:
getByPlaceholder("Email Address") просто вернет локатор.fill("[email protected]") будет первым, кто выполнит асинхронную задачу. Допустим, он ищет элемент, соответствующий локатору.getByPlaceholder("Password") вернет локатор (действие синхронизации)fill("bar") инициирует другой асинхронный вызов.fill("bar") выполнение возобновится независимо от статуса fill("[email protected]").Это прохождение высокого уровня. Я думаю, что это могло бы стать более «глубоким» и конкретным, но это объясняет потоки.
Я предполагаю, что драматург отслеживает 1 «локатор», поэтому исходный локатор переопределяется? Я предположил, что он может отслеживать оба локатора в своем цикле.
Локаторы являются декларативными. Он просто говорит: «Когда вы вызываете действие, подобное .fill(), для этого локатора, мы запускаем запрос для этого локатора, а затем запускаем действие для самого последнего совпадения элемента на основе последней версии страницы». Локаторы — это не действия, это просто стратегии выбора, которые вы можете использовать всякий раз, когда хотите определить, над какими элементами DOM будет выполняться действие.
Это также приводит меня к побочному вопросу: Дожидается ли Драматург ОБОИХ обещаний при цепочке функций? Или только последний?
Локаторы не возвращают обещания, только методы «действия», такие как .fill(), .click(), .evaluate() и т. д., возвращают обещания.
Думайте о локаторах как о конструкторах в обычном JS:
const bike = new Bicycle(); // locator declaration
await bike.ride(); // asynchronous action
Вы можете написать это в цепном стиле, например:
await new Bicycle().ride();
new Bike не асинхронный, а .ride() есть. Разница между надуманным примером выше и Playwright заключается в том, что локаторы не используют синтаксис new Locator:
await bicycle().ride();
// or with an intermediate variable:
const bike = bicycle();
await bike.ride();
// or with some declarative chained options, like Playwright's .filter:
await bicycle()
.withRedPaint()
.withFatTires()
.ride(); // .ride() is the only method that returns
// a promise; the rest return `this`
Я не могу понять, почему
getByPlaceholderбудет повторять попытки, пока не потерпит неудачу.
Так устроена библиотека. В коде Playwright есть цикл повтора для выбора локатора, потому что ожидание — это обычно то, что вы хотите сделать.
Когда вы создаете локатор, Playwright не выполняет никаких действий на странице. Вы просто создали декларативный план, стратегию того, как что-то выбрать, но еще не действовали в соответствии с этой стратегией.
Когда вы вызываете действие, такое как .click(), на локаторе, Playwright начинает действовать, просматривает строку параметров локатора и параметры и переходит на страницу, которую вы автоматизируете, чтобы найти элементы. Он повторяет попытку, пока не найдет что-то, а затем запускает действие над этим. Теперь вы выполняете объявленный вами селектор/план действий с комбинацией локатора и метода вроде .fill().
Однако драматург конкретно не указывает, обещают ли они возвращение конкретно. Я ПРЕДПОЛАГАЮ, что они это делают, но в документах конкретно не говорится.
Это может быть улучшено в документах Playwright. Документы используют Promise<SomeType>, когда метод возвращает обещание, которое разрешается в значение, но, похоже, опускают Promise<void> для асинхронных методов, таких как .click, которые необходимо ожидать, но не разрешают обещание со значением, что сбивает с толку.
В настоящее время поведение заключается в том, что он заполняет поле пароля одновременно строками электронной почты и пароля.
Без await вы вводите параллелизм, и Playwright пытается заполнить два поля одновременно. Заполнение поля требует фокусировки на нем, поэтому вполне вероятно, что какое бы .fill() ни произошло вторым (обычно это пароль .fill()), это приведет к потере фокуса первого .fill(), и оба действия будут бороться за один и тот же ввод. Но когда есть состояние гонки, это почти наверняка ошибка, поэтому я просто обязательно await все сделаю и не трачу слишком много энергии, пытаясь рассуждать о том, что может произойти, если вы пренебрегаете этим.
В отличие от Playwright, Puppeteer использует более императивный стиль (на момент написания статьи) и не имеет локаторов. У Playwright есть ряд устаревших методов, основанных на Puppeteer, которые являются более обязательными и теперь не рекомендуются или устарели. Если вы предпочитаете императивный код, вы можете использовать Puppeteer.
Если вы не уверены, почему API Playwright и Puppeteer вообще асинхронны, см. этот пост.
Я предполагаю, что в случае click он не указывал, что возвращает обещание (но это происходит в VSCode), что, как я предполагал, все равно было. Я предполагаю, что мое замешательство в том, что Locator - это просто конструктор..... он предположительно будет иметь 2 локатора с правильным "элементом", на который он указывал, даже без ожидания. Так зачем же ему заполнять оба на одном и том же элементе? (Даже в журнале трассировки/отладки указано, что он находит правильные элементы электронной почты/пароля)
Вы правы насчет .click — может быть, вы сможете прорекламировать улучшение. Я думаю, если это Promise<void>, они опускают это. Отсутствие ожидания действия над локатором просто означает, что у вас есть состояние гонки и вы ничего не можете сделать с результатом. Обычно это ошибка, поэтому я не уверен, что есть смысл рассуждать о том, что происходит, когда вы не ждете обещания. То, что запускает локатор, — это вызов метода действия, а не await.
Я не уверен, что вы имеете в виду под «Так зачем же заполнять оба элемента одним и тем же элементом?» -- Мне нужно увидеть минимальный воспроизводимый пример, если у вас возникли проблемы с конкретной страницей. Я бы задал это как новый вопрос.
Я вижу, думаю, я вижу, что вы спрашиваете сейчас, и обновил ответ, чтобы решить эту проблему.
Нет такого понятия, как «Обещания драматурга», есть только Обещания.
Я думаю, это просто вопрос понимания того, что все действия локатора драматурга (определения локатора, которые возвращают простые объекты) возвращают обещания, а ключевое слово not требуется для ожидания завершения обещаний, как обычно в любом коде JavaScript.
И когда мы комбинируем оба (определение локатора + действие в одной цепочке операторов), нам все еще нужно await для возможного разрешения обещания действия.
кстати, вы похожи на кого-то из мира кипариса :)