Я написал код для переноса Playwright Page
, чтобы упростить несколько операций. В одной из моих операций я хотел сделать что-то вроде набора текста и табуляции (это лучше имитирует поведение пользователя, даже если для Playwright оно медленнее, поэтому в первую очередь это проверка того, что порядок табуляции работает должным образом).
Класс для переноса будет примерно таким:
import { Locator, Page } from "@playwright/test";
class SimplePage {
constructor(private readonly page: Page) {}
async type(text: string): Promise<this> {
await this.page.keyboard.type(text);
return this;
}
async tab(): Promise<this> {
await this.page.keyboard.press("Tab");
return this;
}
}
В настоящее время единственный способ использовать это в своем коде заключается в следующем:
await myPage.type(programName)
.then((p) => p.tab())
.then((p) => p.type(shortName))
.then((p) => p.tab());
или
await myPage.type(programName);
await myPage.tab();
await myPage.type(shortName);
await myPage.tab();
Мне было интересно, есть ли способ его построить, чтобы работало следующее:
await myPage
.type(programName)
.tab()
.type(shortName)
.tab();
Есть github.com/hdorgeval/playwright-fluent, но на самом деле я бы не стал использовать этот слишком умный трюк — код непонятен обычным людям, его сложно отлаживать и поддерживать. Просто используйте ванильный синтаксис Playwright, чтобы он был читаемым и неудивительным.
Честно говоря, мне нужна только клавиатура, а не все это, как вы сказали, было непонятно
Вы можете присвоить своему классу SimplePage
свойство promise
и при каждом вызове tab
или text
заменять это обещание новым связанным обещанием.
Наконец, сделайте SimplePage
thenable, определив метод then
. Таким образом, он будет реагировать на оператор await
, как и ожидалось:
class SimplePage {
private promise: Promise<void> = Promise.resolve();
constructor(private readonly page: Page) {}
type(text: string): SimplePage {
this.promise = this.promise.then(() => this.page.keyboard.type(text));
return this;
}
tab(): SimplePage {
this.promise = this.promise.then(() => this.page.keyboard.press("Tab"));
return this;
}
then(onFulfill: (value: any) => void, onReject: (error: any) => void) {
return this.promise.then(onFulfill, onReject);
}
}
PromiseLike
:Объявление типа для then
будет таким:
class SimplePage implements PromiseLike<void> {
private promise: Promise<void> = Promise.resolve();
constructor(private readonly page: Page) {}
type(text: string): SimplePage {
this.promise = this.promise.then(() => this.page.keyboard.type(text));
return this;
}
tab(): SimplePage {
this.promise = this.promise.then(() => this.page.keyboard.press("Tab"));
return this;
}
then<TResult1, TResult2>(
onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
): PromiseLike<TResult1|TResult2> {
return this.promise.then(onfulfilled, onrejected);
}
}
Можете ли вы привести пример кода, который это использует?
Код, который вы указали в вопросе, должен работать с этим. См. уменьшенный пример на сайте replit.com, где я использовал макетную реализацию Page
.
Потребовалось немного времени, чтобы понять, как это работает, но интересно насчет then
в конце.
Интересно, как правильно было бы набирать then
, чтобы это прошло с implemented PromiseLike<void>
Смотрите дополнение к ответу.
Да, это возможно, но методы должны будут вернуться
SimplePage & Promise
и правильно управлять цепочкой внутри.