From f3f66ea9c53e856b539411b1fe9fd9894b3f772f Mon Sep 17 00:00:00 2001 From: kseniataranov Date: Wed, 27 May 2026 10:17:00 +0300 Subject: [PATCH 1/3] docs(authorization): innitial commit --- docs/basic-guides/authorization.mdx | 3 +++ .../current/basic-guides/authorization.mdx | 1 + 2 files changed, 4 insertions(+) create mode 100644 docs/basic-guides/authorization.mdx create mode 100644 i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx diff --git a/docs/basic-guides/authorization.mdx b/docs/basic-guides/authorization.mdx new file mode 100644 index 00000000..ff2933a7 --- /dev/null +++ b/docs/basic-guides/authorization.mdx @@ -0,0 +1,3 @@ +# Authorization + +Draft diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx new file mode 100644 index 00000000..caeb8522 --- /dev/null +++ b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx @@ -0,0 +1 @@ +# Авторизация в тестах \ No newline at end of file From 66f17333c369b248653648e0e548b75b16dda004 Mon Sep 17 00:00:00 2001 From: kseniataranov Date: Fri, 19 Jun 2026 17:56:03 +0300 Subject: [PATCH 2/3] docs(authorization) create article --- .../current/basic-guides/authorization.mdx | 319 +++++++++++++++++- 1 file changed, 318 insertions(+), 1 deletion(-) diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx index caeb8522..1aac15b1 100644 --- a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx +++ b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx @@ -1 +1,318 @@ -# Авторизация в тестах \ No newline at end of file +import Admonition from "@theme/Admonition"; + +# Авторизация в тестах + + + +- Как работают команды `saveState` и `restoreState` +- Как сохранить сессию авторизации и переиспользовать её в тестах +- Как работать с несколькими аккаунтами и ролями +- Лучшие практики хранения состояния и работы с параллельными тестами + + + +## Введение + +При запуске тестов браузер не содержит данных авторизации. Выполнять вход в каждом тесте неэффективно: это увеличивает время прогона и создаёт зависимость от стабильности формы логина. + +Команды [`saveState`](../commands/browser/saveState.mdx) и [`restoreState`](../commands/browser/restoreState.mdx) позволяют сохранить состояние браузера (cookies, localStorage, sessionStorage) после однократной авторизации и восстанавливать его в последующих тестах. + +## Как работают команды + +### saveState + +Команда [`saveState`](../commands/browser/saveState.mdx) сохраняет текущее состояние браузера: cookies, localStorage и sessionStorage. Данные можно сохранить в файл или в переменную. + +```typescript +// Сохранение в файл +await browser.saveState({ path: "./state.json" }); + +// Сохранение в переменную +const state = await browser.saveState(); +``` + +Для страниц с iframe данные storage сохраняются отдельно для каждого origin. IndexedDB в сохраняемое состояние не входит. + +### restoreState + +Команда [`restoreState`](../commands/browser/restoreState.mdx) восстанавливает ранее сохранённое состояние. + +```typescript +// Восстановление из файла +await browser.restoreState({ path: "./state.json" }); + +// Восстановление из переменной +await browser.restoreState({ data: state }); +``` + + + Перед вызовом `restoreState` откройте страницу нужного домена командой + [`url`](../commands/browser/url.mdx). Это техническое ограничение браузера: localStorage и + sessionStorage привязаны к origin, а cookies можно установить только для текущего домена. + + +По умолчанию после восстановления состояния страница перезагружается (`refresh: true`). Это нужно, чтобы приложение «увидело» восстановленные данные. + +### Работа с cookies напрямую + +Если авторизация хранится только в cookies и localStorage/sessionStorage не нужны, можно использовать команды [`getCookies`](../commands/browser/getCookies.mdx), [`setCookies`](../commands/browser/setCookies.mdx) и [`deleteCookies`](../commands/browser/deleteCookies.mdx): + +```typescript +// Получить cookies +const cookies = await browser.getCookies(); + +// Установить cookies +await browser.setCookies([{ name: "session", value: "abc123", domain: ".example.com" }]); + +// Удалить конкретный cookie +await browser.deleteCookies("session"); +``` + +## Базовый сценарий: один аккаунт + +В этом варианте авторизация выполняется один раз в `beforeAll`, после чего состояние сохраняется в файл. Перед каждым тестом плагин [`@testplane/global-hook`](../plugins/testplane-global-hook.mdx) открывает приложение и восстанавливает сохранённое состояние, чтобы тесты начинались уже с готовой авторизацией. + +
+ + Пример конфигурации + + ```typescript title="testplane.config.ts" + + import path from "node:path"; + import type { ConfigInput, WdioBrowser } from "testplane"; + import { launchBrowser } from "testplane/unstable"; + + const baseUrl = "http://localhost:3000"; + const statePath = path.resolve(process.cwd(), ".testplane", "states", "user.json"); + + async function login(browser: WdioBrowser) { + await browser.url(`${baseUrl}/login`); + await browser.$("#email").setValue(process.env.E2E_USER_EMAIL); + await browser.$("#password").setValue(process.env.E2E_USER_PASSWORD); + await browser.$("#submit").click(); + await browser.$("#welcome").waitForDisplayed(); + } + + export default { + gridUrl: "local", + + browsers: { + chrome: { + desiredCapabilities: { + browserName: "chrome" + } + } + }, + + sets: { + desktop: { + files: ["testplane/**/*.e2e.ts"], + browsers: ["chrome"] + } + }, + + beforeAll: async ({ config }) => { + const browser = await launchBrowser(config.browsers.chrome!); + + await login(browser); + await browser.saveState({ path: statePath }); + + await browser.deleteSession(); + }, + + plugins: { + "@testplane/global-hook": { + enabled: true, + + beforeEach: async ({ browser }: { browser: WdioBrowser }) => { + await browser.url(baseUrl); + await browser.restoreState({ path: statePath }); + } + } + } + + } satisfies ConfigInput; + ``` + +
+ +
+ + Пример теста + + ```typescript title="example.testplane.ts" + const baseUrl = "http://localhost:3000"; + + describe("авторизованный пользователь", () => { + it("открывает дашборд без повторного логина", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("user"); + }); + + it("восстанавливает localStorage и sessionStorage", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + const email = await browser.execute(() => localStorage.getItem("auth.email")); + const role = await browser.execute(() => localStorage.getItem("auth.role")); + const bannerDismissed = await browser.execute(() => + sessionStorage.getItem("feature.bannerDismissed") + ); + + expect(email).toBe(process.env.E2E_USER_EMAIL); + expect(role).toBe("user"); + expect(bannerDismissed).toBe("true"); + }); + }); + ``` + +
+ +- `beforeAll` выполняет логин только один раз и сохраняет снимок браузера в файл. +- `@testplane/global-hook` выносит повторяющуюся логику восстановления состояния из самих тестов в общий `beforeEach`. +- Перед `restoreState` обязательно вызывается `browser.url(baseUrl)`, иначе браузер не сможет корректно восстановить cookies и storage для нужного origin. + +## Несколько аккаунтов (admin + user) + +В этом варианте в `beforeAll` подготавливаются два снимка состояния: один для обычного пользователя, второй для администратора. Нужный снимок выбирается в `beforeEach`, а в тестах можно проверять различия в правах доступа и элементах интерфейса. + +
+ + Пример конфигурации + + ```typescript title="testplane.config.ts" + + import path from "node:path"; + import type { ConfigInput, WdioBrowser } from "testplane"; + import { launchBrowser } from "testplane/unstable"; + + const baseUrl = "http://localhost:3000"; + const stateDir = path.resolve(process.cwd(), ".testplane", "states"); + const userStatePath = path.join(stateDir, "user.json"); + const adminStatePath = path.join(stateDir, "admin.json"); + + async function login(browser: WdioBrowser, email: string, password: string) { + await browser.url(`${baseUrl}/login`); + await browser.$("#email").setValue(email); + await browser.$("#password").setValue(password); + await browser.$("#submit").click(); + await browser.$("#welcome").waitForDisplayed(); + } + + async function clearClientState(browser: WdioBrowser) { + await browser.url(`${baseUrl}/login`); + await browser.execute(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + await browser.deleteCookies(); + } + + export default { + gridUrl: "local", + + browsers: { + chrome: { + desiredCapabilities: { + browserName: "chrome" + } + } + }, + + sets: { + desktop: { + files: ["testplane/**/*.e2e.ts"], + browsers: ["chrome"] + } + }, + + beforeAll: async ({ config }) => { + const browser = await launchBrowser(config.browsers.chrome!); + + await login( + browser, + process.env.E2E_USER_EMAIL, + process.env.E2E_USER_PASSWORD + ); + await browser.saveState({ path: userStatePath }); + + await clearClientState(browser); + + await login( + browser, + process.env.E2E_ADMIN_EMAIL, + process.env.E2E_ADMIN_PASSWORD + ); + await browser.saveState({ path: adminStatePath }); + + await browser.deleteSession(); + }, + + plugins: { + "@testplane/global-hook": { + enabled: true, + + beforeEach: async ({ browser, currentTest }: { + browser: WdioBrowser; + currentTest: { title: string }; + }) => { + const statePath = currentTest.title.includes("[admin]") + ? adminStatePath + : userStatePath; + + await browser.url(baseUrl); + await browser.restoreState({ path: statePath }); + } + } + } + } satisfies ConfigInput; + ``` + +
+ +
+ + Пример теста + + ```typescript title="example.testplane.ts" + const baseUrl = "http://localhost:3000"; + + describe("аккаунты с разными ролями", () => { + it("открывает дашборд для обычного пользователя", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("user"); + await expect(browser.$("#no-admin-message")).toBeDisplayed(); + }); + + it("[admin] открывает дашборд для администратора", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("admin"); + await expect(browser.$("#admin-link")).toBeDisplayed(); + }); + + it("запрещает пользователю доступ к странице администратора", async ({ browser }) => { + await browser.url(`${baseUrl}/admin`); + await expect(browser.$("#forbidden-message")).toBeDisplayed(); + }); + + it("[admin] разрешает администратору доступ к странице администратора", async ({ browser }) => { + await browser.url(`${baseUrl}/admin`); + await expect(browser.$("#manage-users")).toBeDisplayed(); + }); + }); + ``` + +
+ +- В `beforeAll` сохраняются два отдельных state-файла — по одному на роль, чтобы затем переиспользовать их в тестах. +- В `beforeEach` выбирается нужный снимок состояния; в примере выбор завязан на название теста, но в реальном проекте это может быть любая удобная схема . +- Такой подход позволяет наглядно показать, что один и тот же набор тестов можно запускать под разными ролями без повторного логина перед каждым кейсом. + +## Best practices + +- Не коммитьте state-файлы в репозиторий: в них могут содержаться cookies и другие чувствительные данные. +- Храните логины и пароли в переменных окружения, а не в коде тестов. +- Учитывайте срок жизни сессии: если снимок состояния устарел, его придётся пересоздать. +- Если тесты изменяют данные на стороне сервера, один и тот же аккаунт может стать источником конфликтов при параллельном запуске. В таких случаях лучше готовить отдельные аккаунты на каждый конфликтующий сценарий. From 715e55c54ade4f80d7703f28c260132bf5dc68f5 Mon Sep 17 00:00:00 2001 From: kseniataranov Date: Mon, 6 Jul 2026 08:38:43 +0300 Subject: [PATCH 3/3] docs(authorization): translate article, fix small issues --- docs/basic-guides/authorization.mdx | 319 +++++++++++++++++- .../current/basic-guides/authorization.mdx | 20 +- 2 files changed, 327 insertions(+), 12 deletions(-) diff --git a/docs/basic-guides/authorization.mdx b/docs/basic-guides/authorization.mdx index ff2933a7..f92dc1ea 100644 --- a/docs/basic-guides/authorization.mdx +++ b/docs/basic-guides/authorization.mdx @@ -1,3 +1,318 @@ -# Authorization +import Admonition from "@theme/Admonition"; -Draft +# Authorization in Tests + + + +- How the `saveState` and `restoreState` commands work +- How to save an authentication session and reuse it across tests +- How to work with multiple accounts and roles +- Best practices for storing state and running tests in parallel + + + +## Introduction + +When tests start, the browser has no authentication data. Logging in within every test is inefficient: it increases the overall run time and creates a dependency on the stability of the login form. + +The [`saveState`](../commands/browser/saveState.mdx) and [`restoreState`](../commands/browser/restoreState.mdx) commands let you save the browser state (cookies, localStorage, sessionStorage) after a single login and restore it in subsequent tests. + +## How the commands work + +### saveState + +The [`saveState`](../commands/browser/saveState.mdx) command saves the current browser state: cookies, localStorage, and sessionStorage. The data can be saved to a file or to a variable. + +```typescript +// Save to a file +await browser.saveState({ path: "./state.json" }); + +// Save to a variable +const state = await browser.saveState(); +``` + +For pages with iframes, storage data is saved separately for each origin. IndexedDB is not included in the saved state. + +### restoreState + +The [`restoreState`](../commands/browser/restoreState.mdx) command restores a previously saved state. + +```typescript +// Restore from a file +await browser.restoreState({ path: "./state.json" }); + +// Restore from a variable +await browser.restoreState({ data: state }); +``` + + + Before calling `restoreState`, open a page on the target domain using the + [`url`](../commands/browser/url.mdx) command. This is a browser limitation: localStorage and + sessionStorage are bound to the origin, and cookies can only be set for the current domain. + + +By default, after restoring the state the page is reloaded (`refresh: true`). This is required so that the application can "see" the restored data. + +### Working with cookies directly + +If authentication is stored only in cookies and localStorage/sessionStorage are not needed, you can use the [`getCookies`](../commands/browser/getCookies.mdx), [`setCookies`](../commands/browser/setCookies.mdx), and [`deleteCookies`](../commands/browser/deleteCookies.mdx) commands: + +```typescript +// Get cookies +const cookies = await browser.getCookies(); + +// Set cookies +await browser.setCookies([{ name: "session", value: "abc123", domain: ".example.com" }]); + +// Delete a specific cookie +await browser.deleteCookies("session"); +``` + +## Basic scenario: single account + +In this approach, authentication is performed once in `beforeAll`, and the state is saved to a file. Before each test, the [`@testplane/global-hook`](../plugins/testplane-global-hook.mdx) plugin opens the application and restores the saved state, so tests start with an already authenticated session. + +
+ + Configuration example + + ```typescript title="testplane.config.ts" + + import path from "node:path"; + import type { ConfigInput, WdioBrowser } from "testplane"; + import { launchBrowser } from "testplane/unstable"; + + const baseUrl = "http://localhost:3000"; + const statePath = path.resolve(process.cwd(), ".testplane", "states", "user.json"); + + async function login(browser: WdioBrowser) { + await browser.url(`${baseUrl}/login`); + await browser.$("#email").setValue(process.env.E2E_USER_EMAIL); + await browser.$("#password").setValue(process.env.E2E_USER_PASSWORD); + await browser.$("#submit").click(); + await browser.$("#welcome").waitForDisplayed(); + } + + export default { + gridUrl: "local", + + browsers: { + chrome: { + desiredCapabilities: { + browserName: "chrome" + } + } + }, + + sets: { + desktop: { + files: ["testplane/**/*.e2e.ts"], + browsers: ["chrome"] + } + }, + + beforeAll: async ({ config }) => { + const browser = await launchBrowser(config.browsers.chrome!); + + await login(browser); + await browser.saveState({ path: statePath }); + + await browser.deleteSession(); + }, + + plugins: { + "@testplane/global-hook": { + enabled: true, + + beforeEach: async ({ browser }: { browser: WdioBrowser }) => { + await browser.url(baseUrl); + await browser.restoreState({ path: statePath }); + } + } + } + + } satisfies ConfigInput; + ``` + +
+ +
+ + Test example + + ```typescript title="example.testplane.ts" + const baseUrl = "http://localhost:3000"; + + describe("authenticated user", () => { + it("opens the dashboard without re-logging in", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("user"); + }); + + it("restores localStorage and sessionStorage", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + const email = await browser.execute(() => localStorage.getItem("auth.email")); + const role = await browser.execute(() => localStorage.getItem("auth.role")); + const bannerDismissed = await browser.execute(() => + sessionStorage.getItem("feature.bannerDismissed") + ); + + expect(email).toBe(process.env.E2E_USER_EMAIL); + expect(role).toBe("user"); + expect(bannerDismissed).toBe("true"); + }); + }); + ``` + +
+ +- `beforeAll` performs the login only once and saves a browser snapshot to a file. +- `@testplane/global-hook` moves the repetitive state-restoration logic out of the tests themselves into a shared `beforeEach`. +- Before `restoreState`, `browser.url(baseUrl)` is always called — otherwise the browser cannot correctly restore cookies and storage for the required origin. + +## Multiple accounts (admin + user) + +In this approach, two state snapshots are prepared in `beforeAll`: one for a regular user and one for an administrator. The required snapshot is selected in `beforeEach`, and the tests can verify differences in permissions and UI elements. + +
+ + Configuration example + + ```typescript title="testplane.config.ts" + + import path from "node:path"; + import type { ConfigInput, WdioBrowser } from "testplane"; + import { launchBrowser } from "testplane/unstable"; + + const baseUrl = "http://localhost:3000"; + const stateDir = path.resolve(process.cwd(), ".testplane", "states"); + const userStatePath = path.join(stateDir, "user.json"); + const adminStatePath = path.join(stateDir, "admin.json"); + + async function login(browser: WdioBrowser, email: string, password: string) { + await browser.url(`${baseUrl}/login`); + await browser.$("#email").setValue(email); + await browser.$("#password").setValue(password); + await browser.$("#submit").click(); + await browser.$("#welcome").waitForDisplayed(); + } + + async function clearClientState(browser: WdioBrowser) { + await browser.url(`${baseUrl}/login`); + await browser.execute(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + await browser.deleteCookies(); + } + + export default { + gridUrl: "local", + + browsers: { + chrome: { + desiredCapabilities: { + browserName: "chrome" + } + } + }, + + sets: { + desktop: { + files: ["testplane/**/*.e2e.ts"], + browsers: ["chrome"] + } + }, + + beforeAll: async ({ config }) => { + const browser = await launchBrowser(config.browsers.chrome!); + + await login( + browser, + process.env.E2E_USER_EMAIL, + process.env.E2E_USER_PASSWORD + ); + await browser.saveState({ path: userStatePath }); + + await clearClientState(browser); + + await login( + browser, + process.env.E2E_ADMIN_EMAIL, + process.env.E2E_ADMIN_PASSWORD + ); + await browser.saveState({ path: adminStatePath }); + + await browser.deleteSession(); + }, + + plugins: { + "@testplane/global-hook": { + enabled: true, + + beforeEach: async ({ browser, currentTest }: { + browser: WdioBrowser; + currentTest: { title: string }; + }) => { + const statePath = currentTest.title.includes("[admin]") + ? adminStatePath + : userStatePath; + + await browser.url(baseUrl); + await browser.restoreState({ path: statePath }); + } + } + } + } satisfies ConfigInput; + ``` + +
+ +
+ + Test example + + ```typescript title="example.testplane.ts" + const baseUrl = "http://localhost:3000"; + + describe("accounts with different roles", () => { + it("opens the dashboard for a regular user", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("user"); + await expect(browser.$("#no-admin-message")).toBeDisplayed(); + }); + + it("[admin] opens the dashboard for an administrator", async ({ browser }) => { + await browser.url(`${baseUrl}/dashboard`); + + await expect(browser.$("#role")).toHaveTextContaining("admin"); + await expect(browser.$("#admin-link")).toBeDisplayed(); + }); + + it("denies the user access to the admin page", async ({ browser }) => { + await browser.url(`${baseUrl}/admin`); + await expect(browser.$("#forbidden-message")).toBeDisplayed(); + }); + + it("[admin] allows the administrator access to the admin page", async ({ browser }) => { + await browser.url(`${baseUrl}/admin`); + await expect(browser.$("#manage-users")).toBeDisplayed(); + }); + }); + ``` + +
+ +- In `beforeAll`, two separate state files are saved — one per role — so they can be reused across tests. +- In `beforeEach`, the required state snapshot is selected; in this example the choice is based on the test title, but in a real project you can use any scheme that works for you. +- This approach clearly demonstrates that the same set of tests can be run under different roles without re-logging in before each case. + +## Best practices + +- Don't commit state files to the repository: they may contain cookies and other sensitive data. +- Store logins and passwords in environment variables, not in test code. +- Keep the session lifetime in mind: if a state snapshot becomes stale, it must be recreated. +- If tests modify data on the server side, the same account can become a source of conflicts when run in parallel. In such cases, it's better to prepare separate accounts for each conflicting scenario. diff --git a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx index 1aac15b1..2215fcfb 100644 --- a/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx +++ b/i18n/ru/docusaurus-plugin-content-docs/current/basic-guides/authorization.mdx @@ -5,7 +5,7 @@ import Admonition from "@theme/Admonition"; - Как работают команды `saveState` и `restoreState` -- Как сохранить сессию авторизации и переиспользовать её в тестах +- Как сохранить сессию авторизации и переиспользовать ее в тестах - Как работать с несколькими аккаунтами и ролями - Лучшие практики хранения состояния и работы с параллельными тестами @@ -13,7 +13,7 @@ import Admonition from "@theme/Admonition"; ## Введение -При запуске тестов браузер не содержит данных авторизации. Выполнять вход в каждом тесте неэффективно: это увеличивает время прогона и создаёт зависимость от стабильности формы логина. +При запуске тестов браузер не содержит данных авторизации. Выполнять вход в каждом тесте неэффективно: это увеличивает время прогона и создает зависимость от стабильности формы логина. Команды [`saveState`](../commands/browser/saveState.mdx) и [`restoreState`](../commands/browser/restoreState.mdx) позволяют сохранить состояние браузера (cookies, localStorage, sessionStorage) после однократной авторизации и восстанавливать его в последующих тестах. @@ -35,7 +35,7 @@ const state = await browser.saveState(); ### restoreState -Команда [`restoreState`](../commands/browser/restoreState.mdx) восстанавливает ранее сохранённое состояние. +Команда [`restoreState`](../commands/browser/restoreState.mdx) восстанавливает ранее сохраненное состояние. ```typescript // Восстановление из файла @@ -70,7 +70,7 @@ await browser.deleteCookies("session"); ## Базовый сценарий: один аккаунт -В этом варианте авторизация выполняется один раз в `beforeAll`, после чего состояние сохраняется в файл. Перед каждым тестом плагин [`@testplane/global-hook`](../plugins/testplane-global-hook.mdx) открывает приложение и восстанавливает сохранённое состояние, чтобы тесты начинались уже с готовой авторизацией. +В этом варианте авторизация выполняется один раз в `beforeAll`, после чего состояние сохраняется в файл. Перед каждым тестом плагин [`@testplane/global-hook`](../plugins/testplane-global-hook.mdx) открывает приложение и восстанавливает сохраненное состояние, чтобы тесты начинались уже с готовой авторизацией.
@@ -168,7 +168,7 @@ await browser.deleteCookies("session");
-- `beforeAll` выполняет логин только один раз и сохраняет снимок браузера в файл. +- `beforeAll` выполняет логин только один раз и сохраняет снимок состояния браузера в файл. - `@testplane/global-hook` выносит повторяющуюся логику восстановления состояния из самих тестов в общий `beforeEach`. - Перед `restoreState` обязательно вызывается `browser.url(baseUrl)`, иначе браузер не сможет корректно восстановить cookies и storage для нужного origin. @@ -306,13 +306,13 @@ await browser.deleteCookies("session"); -- В `beforeAll` сохраняются два отдельных state-файла — по одному на роль, чтобы затем переиспользовать их в тестах. -- В `beforeEach` выбирается нужный снимок состояния; в примере выбор завязан на название теста, но в реальном проекте это может быть любая удобная схема . +- В `beforeAll` сохраняются два отдельных файла состояния: по одному на роль, чтобы затем переиспользовать их в тестах. +- В `beforeEach` выбирается нужный снимок состояния; в примере выбор завязан на название теста, но в реальном проекте это может быть любая удобная схема. - Такой подход позволяет наглядно показать, что один и тот же набор тестов можно запускать под разными ролями без повторного логина перед каждым кейсом. -## Best practices +## Лучшие практики -- Не коммитьте state-файлы в репозиторий: в них могут содержаться cookies и другие чувствительные данные. +- Не коммитьте файлы состояния в репозиторий: в них могут содержаться cookies и другие чувствительные данные. - Храните логины и пароли в переменных окружения, а не в коде тестов. -- Учитывайте срок жизни сессии: если снимок состояния устарел, его придётся пересоздать. +- Учитывайте срок жизни сессии: если снимок состояния устарел, его придется пересоздать. - Если тесты изменяют данные на стороне сервера, один и тот же аккаунт может стать источником конфликтов при параллельном запуске. В таких случаях лучше готовить отдельные аккаунты на каждый конфликтующий сценарий.