Выделение текста в Playwright, Selenium и Cypress

Выделение текста — стандартное взаимодействие с пользователем, связанное с контекстно-зависимыми действиями, такими как копирование, обмен или поиск выделенного фрагмента. Могут ли Playwright, Selenium и Cypress эффективно эмулировать и верифицировать выделение текста? В этой статье мы рассмотрим, как автоматизировать выделение текста в веб-приложении, проверить его поведение и устранить возникающие при этом проблемы.

Демо-приложение

Наше демо-приложение с функцией интеллектуального выделения текста, которое распознает типы текста (электронные адреса, номера телефонов, адреса и даты), и предоставляет контекстные действия после выделения фрагмента.

Demo app

Когда пользователь выделяет фрагмент текста, JavaScript извлекает его содержимое и определяет, соответствует ли оно заданному формату. Если совпадение найдено, рядом с выделением появляется небольшое меню действий, например:

  • Электронная почта: «Отправить письмо»
  • Номер телефона: «Позвонить по номеру» или «Добавить в контакты»
  • Адрес: «Посмотреть на карте» или «Получить маршрут в точку»
  • Дата: «Добавить в календарь»

Эта функциональность имитирует действия пользователя в мобильных операционных системах и веб-браузерах. Наша цель при автоматизации — проверить, что эта функция работает так, как ожидается, имитируя выделение текста и проверяя правильность последующих действий.

Автоматизировать выделение текста может быть непросто, поскольку большинство инструментов автоматизации взаимодействуют с элементами, а не с отдельными фрагментами текста. Однако Playwright, Selenium и Cypress предлагают методы выделения текста и проверке контекстного меню действий. Ниже приведены краткие примеры кода для каждого инструмента.

Playwright

В Playwright мы имитируем выделение текста, сначала находя нужный текст в абзаце, определяя его точную позицию, а затем выполняя JavaScript для создания выделения. Мы вызываем событие mouseup для имитации взаимодействия с пользователем, обеспечивая появление контекстного меню действий. Наконец, мы проверяем, что меню содержит ожидаемую кнопку действия. Далее разберем это пошагово.

Начнем с открытия демо-приложения, и убедимся, что на странице присутствует абзац, содержащий текст, который мы хотим выделить.

import { test, expect } from "@playwright/test";

test("Select text and verify action menu", async ({ page }) => {
  await page.goto("http://localhost:3000");
                    
  await page.waitForSelector("#demo-text");

Далее мы получаем текст абзаца и определяем положение конкретной даты в нем.

const paragraphText = await page.locator("#demo-text").innerText();

const dateText = "March 15, 2025";
const datePosition = paragraphText.indexOf(dateText);
expect(datePosition).toBeGreaterThan(-1);

Теперь мы выполняем JavaScript в браузере, чтобы найти текстовый фрагмент (нод), содержащий целевую дату, и создать диапазон выделения.

await page.evaluate((dateText) => {
  const textNodes = [];
  function getTextNodes(node) {
    if (node.nodeType === Node.TEXT_NODE) {
      textNodes.push(node);
    } else {
      for (let i = 0; i < node.childNodes.length; i++) {
        getTextNodes(node.childNodes[i]);
      }
    }
  }
  getTextNodes(document.body);

Теперь пройдемся по этим нодам, чтобы найти тот, который содержит нашу дату:

let targetNode = null;
let targetOffset = -1;

for (const node of textNodes) {
  const index = node.textContent.indexOf(dateText);
  if (index >= 0) {
    targetNode = node;
    targetOffset = index;
    break;
  }
}

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

if (targetNode) {
  const range = document.createRange();
  range.setStart(targetNode, targetOffset);
  range.setEnd(targetNode, targetOffset + dateText.length);
                  
  const selection = window.getSelection();
  selection.removeAllRanges();
  selection.addRange(range);

Далее мы моделируем событие mouseup для запуска контекстного меню действий:

    const mouseupEvent = new MouseEvent("mouseup", {
      bubbles: true,
      cancelable: true,
      view: window,
    });
    document.dispatchEvent(mouseupEvent);
  }
}, dateText);

Здесь отправляется событие MouseEvent, имитирующее отпускание пользователем кнопки мыши после выделения текста. Это обеспечивает активацию логики выделения в приложении и отображение меню действий. После выделения текста мы проверяем, становится ли меню действий видимым.

await page.waitForSelector('#action-menu[style*="display: block"]', {
  timeout: 5000,
});

Наконец, мы проверяем, что контекстное меню содержит ожидаемую кнопку «Добавить в календарь».

  const actionButton = page.locator("#action-menu button");
  await expect(actionButton).toBeVisible();
  await expect(actionButton).toHaveText("Add to Calendar");
});

Selenium

Начнем с инициализации Selenium WebDriver и перехода на целевую веб-страницу.

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://localhost:3000")

WebDriverWait в Selenium проверяет, что элемент абзаца доступен, прежде чем произойдет взаимодействие с ним.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
                    
wait = WebDriverWait(driver, 10)
paragraph = wait.until(EC.presence_of_element_located((By.ID, "demo-text")))

Далее мы извлекаем текст абзаца и проверяем наличие ожидаемой даты.

paragraph_text = paragraph.text
date_text = "March 15, 2025"
if date_text not in paragraph_text:
    raise Exception(f"'{date_text}' not found in the paragraph text")

Поскольку логика выполнения JavaScript идентична примеру Playwright, она здесь не повторяется. Сценарий использует driver.execute_script() для запуска JavaScript, который выбирает дату и запускает событие mouseup.

После выбора текста мы некоторое время ждем, пока меню действий станет видимым.

import time

time.sleep(0.5)
                    
action_menu = wait.until(EC.visibility_of_element_located((By.ID, "action-menu")))
assert action_menu.is_displayed(), "Action menu is not displayed after text selection"

Наконец, мы находим кнопку внутри меню действий и валидируем ее текст.

action_button = action_menu.find_element(By.TAG_NAME, "button")
assert action_button.text == "Add to Calendar", f"Expected 'Add to Calendar' button, got '{action_button.text}'"

Для очистки мы закрываем сессию браузера после завершения теста.

print("Text selection and action menu verification successful!")

driver.quit()

Cypress

Общий ход выполнения этого теста Cypress похож на примеры в Playwright и Selenium.

Cypress предоставляет простой метод cy.visit() для открытия целевой веб-страницы.

describe("Text Selection Test", () => {
  it("should select text and verify action menu appears", () => {
    cy.visit("http://localhost:3000");
  });
});

Cypress использует cy.get(selector).should("be.visible"), чтобы убедиться, что абзац существует и отображается.

cy.get("#demo-text").should("be.visible");

Сохраняем текст абзаца и проверяем, содержит ли он ожидаемую дату.

const dateText = "March 15, 2025";
                    
cy.get("#demo-text").then(($paragraph) => {
  const paragraphText = $paragraph.text();
  expect(paragraphText).to.include(dateText);
});

Поскольку логика JavaScript для выбора текста идентична предыдущим примерам, мы не будем повторять ее здесь. Cypress выполняет JavaScript через cy.window().then((win) => { ... }) для имитации процесса выделения.

После выбора текста мы убеждаемся, что меню действий становится видимым.

cy.get("#action-menu")
  .should("be.visible")
  .and("have.css", "display", "block");

Мы также проверяем, что кнопка действий внутри меню содержит правильный текст.

cy.get("#action-menu button")
  .should("be.visible")
  .and("have.text", "Add to Calendar");

Проблемы и решения

Автоматизация выделения текста и проверка контекстных меню могут вызвать ряд сложностей, например с управлением фокусом, нечетким выделением и кроссбраузерным поведением. Ниже приведены возможные решения.

1. Потеря фокуса после выделения

Проблема:

Некоторые браузеры или веб-приложения могут сбрасывать выделение, если тестовый сценарий взаимодействует с другим элементом или запускает событие JavaScript.

Это может помешать меню действий отобразиться так, как ожидалось.

Решение:

После выполнения сценария выделения явно перефокусируйтесь на целевом элементе перед проверкой меню действий.

Используйте JavaScript для проверки того, что выделение все еще активно:

cy.window().then((win) => {
  const selection = win.getSelection();
  expect(selection.rangeCount).to.be.greaterThan(0);
});

В Selenium добавление короткого ожидания перед взаимодействием с меню может помочь предотвратить преждевременную потерю фокуса:

time.sleep(0.5)  # Give the UI a moment to render the action menu

2. Непоследовательное поведение при выделении текста

Проблема:

Некоторые браузеры могут не распознавать текстовые выделения, созданные с помощью JavaScript, не позволяя срабатывать ожидаемым событиям.

Это может произойти, если выделение чисто визуальное и не вызывает внутреннего события выделения.

Решение:

После выделения текста отправьте событие mouseup вручную, чтобы убедиться в срабатывании обработчиков выделения:

cy.window().then((win) => {
  const mouseupEvent = new MouseEvent("mouseup", {
    bubbles: true,
    cancelable: true,
    view: win,
  });
  win.document.dispatchEvent(mouseupEvent);
});

В Selenium убедитесь, что скрипт явно вызывает addRange(range) после установки выделения:

selection.removeAllRanges();
selection.addRange(range);

3. Кроссбраузерные проблемы

Проблема:

Поведение при выделении текста может отличаться в Chrome, Firefox и Safari.

Некоторые браузеры (например, Safari) могут не вызывать события выделения, если выделение не происходит при взаимодействии с пользователем.

Решение:

При необходимости используйте условия для конкретного браузера:

cy.window().then((win) => {
  const userAgent = win.navigator.userAgent;
  if (userAgent.includes("Safari") && !userAgent.includes("Chrome")) {
    cy.log("Safari detected - applying workaround");
    // Adjust selection handling for Safari
  }
});

В Playwright активируйте реальное взаимодействие с пользователем после выделения:

await page.mouse.up(); // Mimic the user releasing the mouse

4. Выделение снимается после прокрутки или клика в другом месте

Проблема:

Если страница прокручивается или скрипт кликает на другом элементе, выделение может быть потеряно.

Некоторые приложения автоматически очищают текстовые выделения при переключении фокуса.

Решение:

Повторно активируйте выделение перед работой с меню действий.

Вместо того чтобы щелкать в другом месте, используйте сочетания клавиш (например, нажатие Enter) для работы с меню.

Заключение

У каждого фреймворка автоматизации есть свои сильные стороны, но основной подход остается неизменным: выделите текст программно, запустите все необходимые события выделения и проверьте полученные изменения пользовательского интерфейса. Полные примеры кода вместе с демо-приложением вы можете найти на нашей GitHub-странице

thegreenreport

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас