Выполнение JavaScript
Введение
Скрипты Playwright выполняются в вашей среде Playwright. Скрипты страницы выполняются в среде браузерной страницы. Эти среды не пересекаются, они работают в разных виртуальных машинах в разных процессах и даже потенциально на разных компьютерах.
API page.evaluate() может выполнять JavaScript-функцию в контексте веб-страницы и возвращать результаты в среду Playwright. Глобальные объекты браузера, такие как window
и document
, могут использоваться в evaluate
.
- Sync
- Async
href = page.evaluate('() => document.location.href')
href = await page.evaluate('() => document.location.href')
Если результат является Promise или если функция асинхронная, evaluate автоматически дождется ее разрешения:
- Sync
- Async
status = page.evaluate("""async () => {
response = await fetch(location.href)
return response.status
}""")
status = await page.evaluate("""async () => {
response = await fetch(location.href)
return response.status
}""")
Разные среды
Выполняемые скрипты работают в среде браузера, в то время как ваш тест выполняется в тестовой среде. Это означает, что вы не можете использовать переменные из вашего теста на странице и наоборот. Вместо этого вы должны передавать их явно в качестве аргумента.
Следующий фрагмент НЕПРАВИЛЬНЫЙ, потому что он использует переменную напрямую:
- Sync
- Async
data = "some data"
result = page.evaluate("""() => {
// НЕПРАВИЛЬНО: на веб-странице нет "data".
window.myApp.use(data)
}""")
data = "some data"
result = await page.evaluate("""() => {
// НЕПРАВИЛЬНО: на веб-странице нет "data".
window.myApp.use(data)
}""")
Следующий фрагмент ПРАВИЛЬНЫЙ, потому что он передает значение явно в качестве аргумента:
- Sync
- Async
data = "some data"
# Передайте |data| в качестве параметра.
result = page.evaluate("""data => {
window.myApp.use(data)
}""", data)
data = "some data"
# Передайте |data| в качестве параметра.
result = await page.evaluate("""data => {
window.myApp.use(data)
}""", data)
Аргумент для выполнения
Методы выполнения Playwright, такие как page.evaluate(), принимают один необязательный аргумент. Этот аргумент может быть смесью значений Serializable и экземпляров JSHandle. Обработчики автоматически преобразуются в представляемое ими значение.
- Sync
- Async
# Примитивное значение.
page.evaluate('num => num', 42)
# Массив.
page.evaluate('array => array.length', [1, 2, 3])
# Объект.
page.evaluate('object => object.foo', { 'foo': 'bar' })
# Один обработчик.
button = page.evaluate_handle('window.button')
page.evaluate('button => button.textContent', button)
# Альтернативная нотация с использованием JSHandle.evaluate.
button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Объект с несколькими обработчиками.
button1 = page.evaluate_handle('window.button1')
button2 = page.evaluate_handle('.button2')
page.evaluate("""o => o.button1.textContent + o.button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Деструктуризация объекта работает. Обратите внимание, что имена свойств должны совпадать
# между деструктурированным объектом и аргументом.
# Также обратите внимание на обязательные скобки.
page.evaluate("""
({ button1, button2 }) => button1.textContent + button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Массив также работает. Для деструктуризации можно использовать произвольные имена.
# Обратите внимание на обязательные скобки.
page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Любая смесь сериализуемых и обработчиков работает.
page.evaluate("""
x => x.button1.textContent + x.list[0].textContent + String(x.foo)""",
{ 'button1': button1, 'list': [button2], 'foo': None })
# Примитивное значение.
await page.evaluate('num => num', 42)
# Массив.
await page.evaluate('array => array.length', [1, 2, 3])
# Объект.
await page.evaluate('object => object.foo', { 'foo': 'bar' })
# Один обработчик.
button = await page.evaluate_handle('button')
await page.evaluate('button => button.textContent', button)
# Альтернативная нотация с использованием JSHandle.evaluate.
await button.evaluate('(button, from) => button.textContent.substring(from)', 5)
# Объект с несколькими обработчиками.
button1 = await page.evaluate_handle('window.button1')
button2 = await page.evaluate_handle('window.button2')
await page.evaluate("""
o => o.button1.textContent + o.button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Деструктуризация объекта работает. Обратите внимание, что имена свойств должны совпадать
# между деструктурированным объектом и аргументом.
# Также обратите внимание на обязательные скобки.
await page.evaluate("""
({ button1, button2 }) => button1.textContent + button2.textContent""",
{ 'button1': button1, 'button2': button2 })
# Массив также работает. Для деструктуризации можно использовать произвольные имена.
# Обратите внимание на обязательные скобки.
await page.evaluate("""
([b1, b2]) => b1.textContent + b2.textContent""",
[button1, button2])
# Любая смесь сериализуемых и обработчиков работает.
await page.evaluate("""
x => x.button1.textContent + x.list[0].textContent + String(x.foo)""",
{ 'button1': button1, 'list': [button2], 'foo': None })
Скрипты инициализации
Иногда удобно выполнить что-то на странице до того, как она начнет загружаться. Например, вы можете захотеть настроить некоторые моки или тестовые данные.
В этом случае используйте page.add_init_script() или browser_context.add_init_script(). В примере ниже мы заменим Math.random()
на постоянное значение.
Сначала создайте файл preload.js
, содержащий мок.
// preload.js
Math.random = () => 42;
Затем добавьте скрипт инициализации на страницу.
- Sync
- Async
# В вашем тесте, предполагая, что файл "preload.js" находится в каталоге "mocks".
page.add_init_script(path="mocks/preload.js")
# В вашем тесте, предполагая, что файл "preload.js" находится в каталоге "mocks".
await page.add_init_script(path="mocks/preload.js")