Изоляция состояния между контекстами

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

Контексты и их роль в изоляции состояния

Контекст в Playwright представляет собой независимую среду для взаимодействия с браузером. Каждый контекст может содержать свои cookies, локальное хранилище (localStorage), сессионные данные и другие ресурсы, что делает его идеальным для работы с многими параллельными сессиями.

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

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

Создание контекстов и использование их в тестах

Чтобы создать новый контекст в Playwright, необходимо воспользоваться методом browser.newContext(). Это позволит изолировать все данные, связанные с данным контекстом, от остальных.

Пример создания контекста:

const { webkit } = require('playwright');  // или другой браузер, например, chromium или firefox

(async () => {
  const browser = await webkit.launch();
  
  // Создание контекста
  const context1 = await browser.newContext();
  const context2 = await browser.newContext();
  
  // Создание страниц в контекстах
  const page1 = await context1.newPage();
  const page2 = await context2.newPage();

  // Действия с page1 и page2, которые не будут влиять друг на друга
  await page1.goto('https://example.com');
  await page2.goto('https://example.org');

  // Закрытие браузера
  await browser.close();
})();

Каждый контекст работает независимо, и данные, например cookies или локальное хранилище, не пересекаются между контекстами.

Изоляция cookies и данных сессий

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

Пример использования нескольких контекстов для проверки авторизации с разными учетными записями:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();

  // Контекст для первого пользователя
  const context1 = await browser.newContext();
  const page1 = await context1.newPage();
  await page1.goto('https://example.com/login');
  await page1.fill('#username', 'user1');
  await page1.fill('#password', 'password1');
  await page1.click('button[type="submit"]');

  // Контекст для второго пользователя
  const context2 = await browser.newContext();
  const page2 = await context2.newPage();
  await page2.goto('https://example.com/login');
  await page2.fill('#username', 'user2');
  await page2.fill('#password', 'password2');
  await page2.click('button[type="submit"]');

  // Действия с обоими пользователями, не влияющие друг на друга
  await page1.goto('https://example.com/dashboard');
  await page2.goto('https://example.com/dashboard');

  await browser.close();
})();

В этом примере каждый контекст создаёт свою собственную сессию с независимыми cookies, что позволяет им авторизоваться с разными учетными записями.

Изоляция данных в локальном хранилище и sessionStorage

Важной особенностью контекстов является их способность изолировать данные локального хранилища (localStorage) и сессионного хранилища (sessionStorage). Это необходимо, например, когда требуется протестировать, как приложение ведет себя при изменении этих данных в разных контекстах.

Пример работы с локальным хранилищем в разных контекстах:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();

  // Контекст для первого пользователя
  const context1 = await browser.newContext();
  const page1 = await context1.newPage();
  await page1.goto('https://example.com');
  await page1.evaluate(() => {
    localStorage.setItem('user_data', JSON.stringify({ user: 'user1' }));
  });

  // Контекст для второго пользователя
  const context2 = await browser.newContext();
  const page2 = await context2.newPage();
  await page2.goto('https://example.com');
  await page2.evaluate(() => {
    localStorage.setItem('user_data', JSON.stringify({ user: 'user2' }));
  });

  // Проверка изоляции
  const user1Data = await page1.evaluate(() => {
    return localStorage.getItem('user_data');
  });

  const user2Data = await page2.evaluate(() => {
    return localStorage.getItem('user_data');
  });

  console.log(user1Data); // {"user": "user1"}
  console.log(user2Data); // {"user": "user2"}

  await browser.close();
})();

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

Использование контекстов в тестировании многозадачных сценариев

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

Пример:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();

  // Контекст для первого пользователя
  const context1 = await browser.newContext();
  const page1 = await context1.newPage();
  await page1.goto('https://example.com/chat');
  await page1.fill('#message', 'Привет, как дела?');
  await page1.click('#send');

  // Контекст для второго пользователя
  const context2 = await browser.newContext();
  const page2 = await context2.newPage();
  await page2.goto('https://example.com/chat');
  await page2.fill('#message', 'Все хорошо, а у тебя?');
  await page2.click('#send');

  // Проверка сообщений на обеих страницах
  const messages1 = await page1.locator('.messages').allTextContents();
  const messages2 = await page2.locator('.messages').allTextContents();

  console.log(messages1); // ['Привет, как дела?']
  console.log(messages2); // ['Все хорошо, а у тебя?']

  await browser.close();
})();

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

Заключение

Изоляция состояния между контекстами является важным инструментом при тестировании веб-приложений с использованием Playwright. Она позволяет создавать независимые сессии для различных пользователей, эмулировать параллельные действия и работать с изолированными данными, такими как cookies, localStorage и sessionStorage. Правильное использование контекстов помогает повысить точность и надежность тестов, а также облегчить тестирование сложных многозадачных и многопользовательских сценариев.