Понимание обещаний драматурга

Поэтому я пытаюсь понять/объяснить, почему часть кода работает именно так. Я знаком с асинхронностью по большей части и понимаю, как работают промисы на базовом уровне, но я пытаюсь объяснить, почему блок кода работает так, как в 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: поле заполняемое/видимое/и т.д...)

Однако драматург конкретно не указывает, обещают ли они возвращение конкретно. Я ПРЕДПОЛАГАЮ, что они это делают, но в документах конкретно не говорится.

Так что же на самом деле происходит здесь шаг за шагом? Я действительно не могу «пройти» через это, поэтому это затрудняет объяснение другим. Например, я понимаю, почему это не работает на высоком уровне, но не совсем понимаю, почему это не работает в деталях.

Это также приводит меня к побочному вопросу: Дожидается ли Драматург ОБОИХ обещаний при цепочке функций? Или только последний?

кстати, вы похожи на кого-то из мира кипариса :)

Vishal Aggarwal 02.05.2023 17:40

@VishalAggarwal Да, я определенно больше знаком с Cypress, но перехожу к драматургу.

msmith1114 02.05.2023 18:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
279
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

getByPlaceholder будет повторять попытки до тех пор, пока не произойдет сбой.

Локаторы не асинхронны. Они просто возвращают локатор. Функция fill будет повторять попытку.

Я думаю, что это также отвечает на вопрос: «Это также приводит меня к побочному вопросу: Драматург ждет ОБА обещаний при цепочке функций? Или только последнее?».

Так что же на самом деле происходит здесь шаг за шагом? Я действительно не могу «пройти» через это, поэтому это затрудняет объяснение другим. Например, я понимаю, почему это не работает на высоком уровне, но не совсем понимаю, почему это не работает в деталях.

Все дело в цикле событий:

  • getByPlaceholder("Email Address") просто вернет локатор.
  • fill("[email protected]") будет первым, кто выполнит асинхронную задачу. Допустим, он ищет элемент, соответствующий локатору.
  • Как только обещание будет создано, выполнение продолжится.
  • getByPlaceholder("Password") вернет локатор (действие синхронизации)
  • fill("bar") инициирует другой асинхронный вызов.
  • Оттуда Node назначит время выполнения для обоих промисов.
  • После завершения fill("bar") выполнение возобновится независимо от статуса fill("[email protected]").

Это прохождение высокого уровня. Я думаю, что это могло бы стать более «глубоким» и конкретным, но это объясняет потоки.

Я предполагаю, что драматург отслеживает 1 «локатор», поэтому исходный локатор переопределяется? Я предположил, что он может отслеживать оба локатора в своем цикле.

msmith1114 02.05.2023 16:40

Локаторы являются декларативными. Он просто говорит: «Когда вы вызываете действие, подобное .fill(), для этого локатора, мы запускаем запрос для этого локатора, а затем запускаем действие для самого последнего совпадения элемента на основе последней версии страницы». Локаторы — это не действия, это просто стратегии выбора, которые вы можете использовать всякий раз, когда хотите определить, над какими элементами DOM будет выполняться действие.

ggorlen--on LLM strike 02.05.2023 16:54
Ответ принят как подходящий

Это также приводит меня к побочному вопросу: Дожидается ли Драматург ОБОИХ обещаний при цепочке функций? Или только последний?

Локаторы не возвращают обещания, только методы «действия», такие как .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 локатора с правильным "элементом", на который он указывал, даже без ожидания. Так зачем же ему заполнять оба на одном и том же элементе? (Даже в журнале трассировки/отладки указано, что он находит правильные элементы электронной почты/пароля)

msmith1114 02.05.2023 18:13

Вы правы насчет .click — может быть, вы сможете прорекламировать улучшение. Я думаю, если это Promise<void>, они опускают это. Отсутствие ожидания действия над локатором просто означает, что у вас есть состояние гонки и вы ничего не можете сделать с результатом. Обычно это ошибка, поэтому я не уверен, что есть смысл рассуждать о том, что происходит, когда вы не ждете обещания. То, что запускает локатор, — это вызов метода действия, а не await.

ggorlen--on LLM strike 02.05.2023 18:47

Я не уверен, что вы имеете в виду под «Так зачем же заполнять оба элемента одним и тем же элементом?» -- Мне нужно увидеть минимальный воспроизводимый пример, если у вас возникли проблемы с конкретной страницей. Я бы задал это как новый вопрос.

ggorlen--on LLM strike 02.05.2023 18:56

Я вижу, думаю, я вижу, что вы спрашиваете сейчас, и обновил ответ, чтобы решить эту проблему.

ggorlen--on LLM strike 03.05.2023 01:27

Нет такого понятия, как «Обещания драматурга», есть только Обещания.

Я думаю, это просто вопрос понимания того, что все действия локатора драматурга (определения локатора, которые возвращают простые объекты) возвращают обещания, а ключевое слово not требуется для ожидания завершения обещаний, как обычно в любом коде JavaScript.

И когда мы комбинируем оба (определение локатора + действие в одной цепочке операторов), нам все еще нужно await для возможного разрешения обещания действия.

Другие вопросы по теме