diff --git a/docs/quickstart/index.mdx b/docs/quickstart/index.mdx index b30ea94..11d5702 100644 --- a/docs/quickstart/index.mdx +++ b/docs/quickstart/index.mdx @@ -2,10 +2,6 @@ sidebar_position: 1 --- -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Admonition from "@theme/Admonition"; - # Installation and Setup ## System requirements @@ -20,7 +16,7 @@ To run the Testplane installer using `npm`, execute the following command: npm init testplane@latest YOUR_PROJECT_PATH ``` -To configure the project instead of using defaults during initialization, specify the `-v` option. +To configure the project in interactive mode with additional parameters (package manager selection, plugin installation), specify the `-v` option. After running the installation command, the following set of files and folders will appear in the project directory: @@ -38,25 +34,23 @@ testplane.config.ts The `testplane.config.ts` file contains a basic set of settings for running tests: -```typescript +```typescript title="testplane.config.ts" +import { setupBrowser } from "@testplane/testing-library"; +import type { WdioBrowser } from "testplane"; + export default { gridUrl: "local", baseUrl: "http://localhost", - pageLoadTimeout: 0, - httpTimeout: 60000, + pageLoadTimeout: 20000, + httpTimeout: 20000, testTimeout: 90000, resetCursor: false, - - // The `sets` parameter contains information about the directory where the tests are located - // and a list of browsers in which they will be run: sets: { desktop: { files: ["testplane-tests/**/*.testplane.(t|j)s"], browsers: ["chrome", "firefox"], }, }, - - // The `browsers` field describes the configuration of the browsers used: browsers: { chrome: { headless: true, @@ -71,7 +65,9 @@ export default { }, }, }, - + prepareBrowser: (browser: WdioBrowser) => { + setupBrowser(browser); + }, plugins: { "html-reporter/testplane": { enabled: true, @@ -80,9 +76,21 @@ export default { diffMode: "3-up-scaled", }, }, -}; +} satisfies import("testplane").ConfigInput; ``` +### Key configuration parameters + +- `gridUrl` — where to run browsers: `"local"` for local execution or a URL for Selenium Grid or cloud (e.g., BrowserStack, Sauce Labs). +- `sets` — test groups for different browsers (e.g., a test set for desktop Chrome or Firefox and a set for mobile Safari). Learn more: [Sets reference](../reference/config/sets.mdx). +- `testTimeout` — maximum test execution time in milliseconds; the test is aborted after exceeding this limit. +- `pageLoadTimeout` — maximum page load wait time. +- `browsers` — browser configuration; `headless: true` — run without UI. Learn more: [Browsers](../basic-guides/browsers-overview.mdx). +- `prepareBrowser` — hook before tests; in the config above it calls `setupBrowser(browser)` and adds Testing Library methods. +- `plugins` — plugins; in the config above `html-reporter` for interactive reports (screenshots, logs, debugging). Learn more: [HTML Reporter](../html-reporter/overview.mdx). + +For a complete list of configuration parameters, see the [configuration reference](../reference/config/main.mdx). + To download the browsers described in the config separately from running Testplane itself, execute the command: ```bash @@ -90,3 +98,55 @@ npx testplane install-deps ``` Without running this command beforehand, missing browsers will be automatically downloaded the first time Testplane is launched. + +## First run {#first-run} + +After installation and setup, run the test example that was created automatically: + +```bash +npx testplane +``` + +### What happens during the first run + +Testplane will automatically perform the following actions: + +1. Download browsers: if you haven't run `install-deps`, Testplane will download Chrome and Firefox +2. Run the test: execute the example from `testplane-tests/example.testplane.ts` +3. Generate a report: create an HTML report in the `testplane-report/` folder + +### Viewing results + +After the tests finish, open the interactive report: + +```bash +npx testplane gui +``` + +The command will start a local server and open the report in your browser. In the interface you will see: + +- List of passed tests +- Execution statistics +- Screenshots (if the test uses `assertView`) +- Execution logs + +### Expected outcome + +If everything is configured correctly, you will see: + +``` +✔ test examples › docs search test [chrome] - 3.2s +✔ test examples › docs search test [firefox] - 3.5s + +Total: 2 Passed: 2 Failed: 0 Skipped: 0 Retries: 0 +``` + +For the tests to pass successfully, they need access to [testplane.io](https://testplane.io/). + +## What's next? + +Now that Testplane is set up and running, you can: + +- [Write your own tests](./writing-tests.mdx): test structure, selectors, and basic commands +- [Run and debug tests](./running-tests.mdx): filtering, GUI mode, debugging +- [Set up CI/CD](./usage-in-ci.mdx): automatic test runs in GitHub Actions diff --git a/docs/quickstart/running-tests.mdx b/docs/quickstart/running-tests.mdx index 81a4364..2b60287 100644 --- a/docs/quickstart/running-tests.mdx +++ b/docs/quickstart/running-tests.mdx @@ -2,158 +2,232 @@ sidebar_position: 3 --- -# Running and Debugging Tests +import Admonition from "@theme/Admonition"; + +# Running and Debugging ## Running tests -To run tests, use the following command: +To run tests, use the command: ```bash npx testplane ``` -You can also run tests in `gui` mode. To do this, execute the command: +You can also run tests in GUI mode with a visual interface: ```bash npx testplane gui ``` -### Running a specific test +In the GUI you can: + +- Observe test execution in real time +- View screenshots, compare and update them +- Restart individual tests +- See detailed errors with command history + +![Testplane GUI interface](/gif/docs/ui/run-debug.gif) -Consider the following set of tests: +Learn more about the GUI in the [HTML Reporter](../html-reporter/overview.mdx) section. -```javascript -const assert = require("assert"); +## Filtering tests at runtime -describe("tests", () => { - it("Checking the display of the main page", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - const title = await browser.getTitle(); - assert.ok(title.includes("Testplane")); +Suppose you have the following tests: + +```typescript +describe("Main page", () => { + it("Check title", async ({ browser }) => { + // ... }); - it("Checking for the presence of a search field", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - const searchButton = await browser.$("button.DocSearch"); - const isExisting = await searchButton.isExisting(); - assert.strictEqual(isExisting, true); + it("Check search field presence", async ({ browser }) => { + // ... }); }); ``` -If you want to run just one of them, use `--grep` option: +### By name {#the_grep_option} + +The [`--grep`](../reference/cli.mdx) option allows running tests by name match: + +```bash +npx testplane --grep "Check search field presence" +``` + +Regular expressions are supported: ```bash -testplane --grep "Checking for the presence of a search field" +npx testplane --grep "search|title" ``` -You can pass the whole test name, some part of it or regex pattern to run only those tests whose names will match. +The `--grep` option filters tests by full name (including all levels of `describe` and `it`). The provided string is interpreted as a regular expression. -### Running tests in specific browsers +### By tags -By default, tests run in the browsers specified in the `testplane.config.ts` file. +Tags help group tests, for example, to separate fast smoke tests from full ones: + +```typescript +describe("Authorization", { tag: "auth" }, () => { + it("Successful login", { tag: "smoke" }, async ({ browser }) => { + // ... + }); +}); +``` + +Run only smoke tests: ```bash -browsers: ["chrome", "firefox"]; +npx testplane --tag "smoke" # only smoke tests +npx testplane --tag "auth&smoke" # tests with both tags ``` -When you run the `npx testplane` command, tests will run in Google Chrome and Mozilla Firefox. +### By browser + +By default, tests run in all browsers from the config. To run in only one: ```bash -# Run in all browsers (default) -testplane +npx testplane --browser chrome ``` -To run tests in a specific browser, use the command: +The `--browser` option can be specified multiple times to run in several browsers: ```bash -# Run only in Chrome -testplane --browser chrome +npx testplane -b chrome -b firefox ``` -You can also specify a specific browser for use within the test body. +You can also specify a specific browser in the test code: -```javascript -// tests/browser-specific.test.js -describe("Browser specific tests", () => { - it("should work in all browsers", async ({ browser }) => { - await browser.url("https://example.com"); +```typescript +describe("Running tests in different browsers", () => { + it("Works in all browsers", async ({ browser }) => { + await browser.url("https://testplane.io/"); }); - // Skip the test in Safari + // Skip test in Safari testplane.skip.in("safari", "Feature not supported in Safari"); - it("should work only in Chrome and Firefox", async ({ browser }) => { - await browser.url("https://example.com"); - // ... test body + it("Works only in Chrome and Firefox", async ({ browser }) => { + await browser.url("https://testplane.io/"); + // ... }); // Run only in Chrome testplane.only.in("chrome"); - it("should work only in Chrome", async ({ browser }) => { - await browser.url("https://example.com"); - // ... test body + it("Works only in Chrome", async ({ browser }) => { + await browser.url("https://testplane.io/"); + // ... }); }); ``` -### Running a test from a specific file +### By file {#running_a_specific_file} -To run tests from a specific file, execute the command: +To run tests from a specific file: ```bash -# Running a specific file -testplane ../testplane-tests/example.testplane.ts +npx testplane tests/login.testplane.ts ``` -Where `../testplane-tests/example.testplane.ts` is the path to the test file. +### Quick run of a single test -### User interface mode +During development, it's convenient to use `.only()`: -In Testplane, you can work with tests in UI format using Testplane UI. - -![](/img/docs/html-reporter/html-reporter-demo.png) +```typescript +it.only("Check search field", async ({ browser }) => { + // Only this test will run +}); +``` -You can read about the installation and setup processes for Testplane UI in the [UI.](..//html-reporter//overview.mdx) section. +Remove `.only()` before committing, otherwise only one test will run in CI! ## Debugging -It’s very easy to track the test execution process if you run tests in `gui` mode. In this mode, the HTML reporter will show which tests were executed successfully, which ones have errors, and what kind of errors they are. +### GUI mode + +The easiest way to debug is to run tests in GUI: + +```bash +npx testplane gui +``` + +In the GUI you will see test execution in real time, screenshots, and errors. You can restart individual tests and observe their execution. + +### Local browser with DevTools + +For debugging, you can open a visible browser window with DevTools: -![Testplane GUI](/img/docs/guides/testplane-gui.png) +```bash +npx testplane --local --devtools --headless false --grep "Test name" +``` -### Browser.debug() +What will happen: -Testplane has a built‑in debugging tool — `browser.debug`. +1. A visible browser window will open +2. Chrome DevTools will automatically open +3. You can observe the test execution -```javascript -it("debugging with a pause", async ({ browser }) => { - // Open the page being tested - await browser.url("/page"); +Firefox browser does not support DevTools. - // browser.debug() stops test execution - // and opens an interactive console (REPL — Read-Eval-Print Loop) - await browser.debug(); +### REPL mode - // After calling debug(), the test pauses - // In the console, you can enter WebdriverIO commands in real time: +REPL (Read-Eval-Print Loop) is an interactive console for executing browser commands during a test. There are several ways to enter REPL: - // Examples of commands you can enter in REPL: - // > await browser.$('.button').click() - click the button - // > await browser.getTitle() - get the page title - // > await browser.$$('.items') - find all elements - // > .exit - exit debug mode +1. Command line options: - // This code will run only after exiting debug() - await browser.$(".button").click(); -}); + - `--repl` — enables REPL mode. To enter the console, call [`browser.switchToRepl()`](../commands/browser/switchToRepl.mdx) in the test code + - `--repl-before-test` — automatically opens REPL before test execution + - `--repl-on-fail` — automatically opens REPL when a test fails + + ```bash + # Open REPL on test failure + npx testplane --repl-on-fail --grep "Test name" --browser chrome + + # Open REPL before test execution + npx testplane --repl-before-test --grep "Test name" --browser chrome + ``` + +2. `browser.switchToRepl()` command in code: + + ```typescript + it("Debugging", async ({ browser }) => { + await browser.url("/page"); + await browser.switchToRepl(); // Test will stop here + + // In the console you can execute commands: + // > await browser.$(".button").click() + // > await browser.getTitle() + // > Cmd+D or Ctrl+D — exit REPL and continue the test + + await browser.$(".button").click(); + }); + ``` + +### Keeping the browser after test + +By default, the browser closes immediately after the test. To examine the final page state: + +```bash +npx testplane --keep-browser --grep "Test name" +npx testplane --keep-browser-on-fail # only on failure ``` -### Debugging via Testplane UI +Useful for checking DOM, cookies, or localStorage after test execution, and also in combination with [MCP](../testplane-mcp.mdx) to allow an AI agent to connect to an already prepared browser. -The most convenient way to debug tests is using the UI mode, where you can observe test execution in real time. +## Useful commands -![](/gif/docs/ui/run-debug.gif) +```bash +# Run all tests +npx testplane + +# Run in GUI with visual interface +npx testplane gui -You can find unstable, slow tests, or other issues using the «sorting» and «grouping» options. +# Run a specific test +npx testplane --grep "test name" -![](/gif/docs/ui/analytics.gif) +# Run only in one browser +npx testplane --browser chrome + +# Interactive debugging on failure +npx testplane --repl-on-fail --grep "test name" +``` diff --git a/docs/quickstart/usage-in-ci.mdx b/docs/quickstart/usage-in-ci.mdx index 0c89c4b..e3699c4 100644 --- a/docs/quickstart/usage-in-ci.mdx +++ b/docs/quickstart/usage-in-ci.mdx @@ -1,6 +1,49 @@ --- sidebar_position: 4 -draft: true --- # Usage in CI + +Testplane can be integrated into any CI system. For GitHub Actions, we recommend using the official action [gemini-testing/gh-actions-testplane](https://github.com/gemini-testing/gh-actions-testplane), which automatically caches browsers and generates reports. + +## GitHub Actions setup + +Create a `.github/workflows/testplane.yml` file in the root of your repository: + +```yaml title=".github/workflows/testplane.yml" +name: Testplane Tests + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - run: npm ci + + - uses: gemini-testing/gh-actions-testplane@v1 + with: + browser: "chrome,firefox" # Optional: list of browsers +``` + +## Additional capabilities + +The action supports additional parameters for configuration: + +- `config-path` — path to the configuration file (default `testplane.config.ts`) +- `set` — run specific test sets +- `grep` — filter tests by pattern +- `storybook` — integration with [@testplane/storybook](https://github.com/gemini-testing/testplane-storybook) + +For more details on parameters and advanced usage scenarios, see the [GitHub Actions setup guide](../guides/how-to-run-on-github.mdx), and for configuring Testplane for other CI systems, refer to the [configuration documentation](../reference/config/main.mdx). diff --git a/docs/quickstart/writing-tests.mdx b/docs/quickstart/writing-tests.mdx index cd7e945..9e03de5 100644 --- a/docs/quickstart/writing-tests.mdx +++ b/docs/quickstart/writing-tests.mdx @@ -6,430 +6,315 @@ sidebar_position: 2 ## Test structure -The `describe` block is used to group related tests. - -```javascript +```typescript describe("Test group name", () => { - it("description of what should happen", async ({ browser }) => { + it("Description of what should happen", async ({ browser }) => { // Test body }); }); ``` -The `it` block describes test scenarios. +- `describe` groups related tests +- `it` describes a specific test scenario +- `browser` — browser object for interacting with the page -```javascript -it("description of what should happen", async ({ browser }) => { - // Test body -}); -``` +After installing Testplane, you can review a test example in the `testplane-tests/example.testplane.ts` file. -After installing Testplane, you can view a test example by navigating to the `testplane-tests` folder and opening the `example.testplane.ts file`. +
-```javascript -describe("test examples", () => { - it("docs search test", async ({ browser }) => { - await browser.openAndWait("https://testplane.io/"); + Test example - // Find by tag name - const navBar = await browser.$("nav"); + ```typescript title="example.testplane.ts" - // Find by aria-label - await navBar.$("aria/Search").click(); + describe("test examples", () => { + it("docs search test", async ({browser}) => { + await browser.openAndWait("https://testplane.io/"); - // Find by placeholder - const fileSearchInput = await browser.findByPlaceholderText("Search docs"); - await fileSearchInput.waitForDisplayed(); - await fileSearchInput.setValue("config"); + // Find by tag name + const navBar = await browser.$("nav"); - // Find by id - const fileSearchResults = await browser.$("#docsearch-list"); + // Find by aria-label + await navBar.$("aria/Search").click(); - // Find by role - const fileSearchResultsItems = await fileSearchResults.findAllByRole("option"); + // Find by placeholder + const fileSearchInput = await browser.findByPlaceholderText("Search docs"); + await fileSearchInput.waitForDisplayed(); + await fileSearchInput.setValue("config"); - await expect(fileSearchResultsItems.length).toBeGreaterThan(1); - }); -}); -``` + // Find by id + const fileSearchResults = await browser.$("#docsearch-list"); -## Basic syntax + // Find by role + const fileSearchResultsItems = await fileSearchResults.findAllByRole("option"); -### Navigation + await expect(fileSearchResultsItems.length).toBeGreaterThan(1); + }); + }); -To navigate between pages, use the method: + ``` -```javascript -await browser.url("https://testplane.io/"); -``` +
-If the page contains elements that appear with a delay, specify an explicit wait for the tests to run correctly: +## Basic syntax -```javascript -await browser.url("https://testplane.io/"); -await browser.$("h1").waitForExist({ timeout: 5000 }); -const title = await browser.$("h1").getText(); -``` +### Navigation -Alternatively, use the command: +To navigate between pages, use the [`browser.url()`](../commands/browser/url.mdx) method: -```javascript -await browser.openAndWait("https://testplane.io/"); +```typescript +await browser.url("https://testplane.io/"); ``` -The await `browser.openAndWait()` command waits for all necessary elements on the page to load by default. +You can also use the [`browser.openAndWait()`](../commands/browser/openAndWait.mdx) command, which provides more settings related to page static loading. ### Selectors -Testplane supports various element-finding strategies: `CSS` selectors (the most common), text selectors (by content), and `XPath` for complex queries. The `$()` method returns the first matching element, while `$$()` returns an array of all matching elements: +Testplane supports various element-finding strategies: CSS selectors (most common), text selectors (by content), XPath for complex queries. The [`$()`](../commands/browser/$.mdx) method returns the first found element, while [`$$()`](../commands/browser/$$.mdx) returns an array of all matching elements. -```javascript -const assert = require("assert"); +There is also support for Testing Library selectors, which allows writing more readable tests focused on user behavior. It is already included when initializing via `npm init testplane`. If you created the project differently, you can [install it manually](../guides/how-to-add-testing-library.mdx). -describe("tests", () => { - it("Checking the display of the main page", async ({ browser }) => { - await browser.url("https://testplane.io/"); - const title = await browser.getTitle(); - assert.ok(title.includes("Testplane")); +```typescript +describe("Selector examples", () => { + it("First found element", async ({ browser }) => { + await browser.url("https://testplane-bookstore.website.yandexcloud.net/"); + + const heading = await browser.$("h1"); + const input = await browser.$('[data-testid="search-input"]'); + const inputTL = await browser.findByTestId("search-input"); }); - it("Checking for the logo on the main page", async ({ browser }) => { + it("Finding multiple elements", async ({ browser }) => { await browser.url("https://testplane.io/"); - const logo = await browser.$("a.navbar__brand"); - const isDisplayed = await logo.isDisplayed(); - assert.strictEqual(isDisplayed, true); + + const links = await browser.$$("a"); + expect(links.length).toBeGreaterThan(0); }); +}); +``` + +#### Selector types + +```typescript +describe("Selector types", () => { + it("CSS selectors", async ({ browser }) => { + await browser.url("https://testplane-bookstore.website.yandexcloud.net/"); - it("Checking the navigation menu", async ({ browser }) => { + // data attributes + const input = await browser.$('[data-testid="search-input"]'); + const inputTL = await browser.findByTestId("search-input"); + + // CSS classes and tags + const navbar = await browser.$(".navbar"); + const heading = await browser.$("h1"); + }); + + it("Text selectors", async ({ browser }) => { await browser.url("https://testplane.io/"); - const menuItems = await browser.$$("nav.navbar a.navbar__item"); - assert.ok(menuItems.length > 0); + + // Exact text match + const match = await browser.$("=Docs"); + + // Partial text match + const partial = await browser.$("*=Doc"); }); - it("Checking for a search field", async ({ browser }) => { + it("XPath", async ({ browser }) => { await browser.url("https://testplane.io/"); - const searchButton = await browser.$("button.DocSearch"); - const isExisting = await searchButton.isExisting(); - assert.strictEqual(isExisting, true); + + // Search by text inside element + const link = await browser.$('//a[text()="Docs"]'); }); }); ``` -### Interactions with elements +Learn more about selectors in [this article](../basic-guides/selectors.mdx). -After learning about selectors and finding an element, you can perform various actions: clicking, typing text, double-clicking. +### Interacting with elements -To click on an element, use the `element.click()` method. +#### Click -```javascript +```typescript const assert = require("assert"); -describe("Click test", () => { - it("Example of a click — opening search", async ({ browser }) => { +describe("Click", () => { + it("Click and double click", async ({ browser }) => { await browser.url("https://testplane.io/"); - // Click on the search button + // Click on search button const searchButton = await browser.$("button.DocSearch"); await searchButton.waitForClickable({ timeout: 5000 }); await searchButton.click(); - // Check that the search modal appears + // Double click on search button + await searchButton.doubleClick(); + + // Verification const searchModal = await browser.$(".DocSearch-Modal"); + await searchModal.waitForDisplayed({ timeout: 5000 }); const isDisplayed = await searchModal.isDisplayed(); assert.strictEqual(isDisplayed, true); }); }); ``` -To fill in a text input field, use the `element.setValue("text")` method. +Learn more in the articles about [`click()`](../commands/element/click.mdx) and [`doubleClick()`](../commands/element/doubleClick.mdx). + +#### Text input -```javascript +```typescript const assert = require("assert"); -describe("Text input test", () => { - it("Example of text input — searching documentation", async ({ browser }) => { +describe("Text input", () => { + it("Enter text into search field", async ({ browser }) => { await browser.url("https://testplane.io/"); - // Open search + // Click on search button const searchButton = await browser.$("button.DocSearch"); await searchButton.waitForClickable({ timeout: 5000 }); await searchButton.click(); - // Enter text into the search field + // Enter text into search field const searchInput = await browser.$("input.DocSearch-Input"); await searchInput.waitForDisplayed({ timeout: 5000 }); await searchInput.setValue("browser"); - // Check that the text is entered + // Verification const inputValue = await searchInput.getValue(); assert.strictEqual(inputValue, "browser"); }); }); ``` -To double-click on an element, use the `element.doubleClick()` method. +Learn more in the article about [`setValue()`](../commands/element/setValue.mdx). -```javascript -const assert = require("assert"); - -describe("Double-click test", () => { - it("Example of a double click — highlighting the heading text", async ({ browser }) => { - await browser.url("https://testplane.io/"); +#### Form interactions - // Find the heading on the main page - const heading = await browser.$("h1"); - await heading.waitForDisplayed({ timeout: 5000 }); - await heading.scrollIntoView(); - - // Double-click on the heading - await heading.doubleClick(); - - // Check that the element exists and is displayed - const isDisplayed = await heading.isDisplayed(); - assert.strictEqual(isDisplayed, true); - }); -}); -``` - -Testplane provides special methods for working with various form elements. For example, checkboxes and radio buttons are controlled via clicking. - -```javascript +```typescript const assert = require("assert"); describe("Form interactions", () => { - it("Working with checkboxes via click", async ({ browser }) => { - // For demonstration, use a page with a form - await browser.url("https://the-internet.herokuapp.com/checkboxes"); + it("Checkbox", async ({ browser }) => { + await browser.url("https://testplane-bookstore.website.yandexcloud.net/login"); - const checkbox1 = await browser.$("#checkboxes input:nth-child(1)"); - await checkbox1.waitForDisplayed({ timeout: 5000 }); + const checkbox = await browser.$('[data-testid="remember-checkbox"]'); + await checkbox.waitForDisplayed({ timeout: 5000 }); // Check initial state - let isSelected = await checkbox1.isSelected(); + let isSelected = await checkbox.isSelected(); assert.strictEqual(isSelected, false); - // Click to select - await checkbox1.click(); - isSelected = await checkbox1.isSelected(); + // Select + await checkbox.click(); + isSelected = await checkbox.isSelected(); assert.strictEqual(isSelected, true); - // Click again to deselect - await checkbox1.click(); - isSelected = await checkbox1.isSelected(); + // Deselect + await checkbox.click(); + isSelected = await checkbox.isSelected(); assert.strictEqual(isSelected, false); }); - it("Working with radio buttons via click", async ({ browser }) => { - await browser.url("https://the-internet.herokuapp.com/"); - - // Navigate to the examples page - const link = await browser.$("a[href='/forgot_password']"); - await link.click(); - - // Work with the email field (as an example of radio buttons) - const emailInput = await browser.$("#email"); - await emailInput.waitForDisplayed({ timeout: 5000 }); - await emailInput.setValue("test@example.com"); - - const value = await emailInput.getValue(); - assert.ok(value.includes("test@example.com")); - }); -}); -``` - -For dropdown lists `()` есть удобные методы выбора опций по видимому тексту или значению атрибута. - -```javascript -const assert = require("assert"); - -describe("Работа с выпадающим списком", () => { - it("Работа с выпадающим списком (select) — выбор по тексту", async ({ browser }) => { - await browser.url("https://the-internet.herokuapp.com/dropdown"); - - const dropdown = await browser.$("#dropdown"); + const dropdown = await browser.$('[data-testid="sort-select"]'); await dropdown.waitForDisplayed({ timeout: 5000 }); // Выбор опции по видимому тексту - await dropdown.selectByVisibleText("Option 1"); + await dropdown.selectByVisibleText("Highest Rated"); - // Проверяем выбранное значение + // Проверка выбранного значения let selectedValue = await dropdown.getValue(); - assert.strictEqual(selectedValue, "1"); + assert.strictEqual(selectedValue, "rating"); - // Получаем текст выбранной опции + // Проверка текста выбранной опции let selectedText = await dropdown.$("option:checked").getText(); - assert.strictEqual(selectedText, "Option 1"); + assert.strictEqual(selectedText, "Highest Rated"); }); }); ``` -### Assertions - -Testplane задействует `expect API` из `WebdriverIO` для проверки состояния элементов и страниц — это позволяет формулировать утверждения (`assertions`) о том, какими должны быть свойства элементов или страницы в целом. - -```javascript -const assert = require("assert"); - -describe("tests", () => { - it("WebdriverIO assert — проверка URL", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - - // WebdriverIO expect для browser - await expect(browser).toHaveUrl("https://testplane.io/ru/"); - }); - - it("WebdriverIO assert — проверка существования элемента", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); +Подробнее см. в статьях о методах [`isSelected()`](../commands/element/isSelected.mdx) и [`selectByVisibleText()`](../commands/element/selectByVisibleText.mdx). - const logo = await browser.$("a.navbar__brand"); - - // WebdriverIO expect для элемента - await expect(logo).toExist(); - }); - - it("WebdriverIO assert — проверка видимости элемента", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - - const searchButton = await browser.$("button.DocSearch"); - - // WebdriverIO expect - await expect(searchButton).toBeDisplayed(); - }); - - // Примеры с Jest ассертами - it("Jest assert — проверка заголовка страницы", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - - const title = await browser.getTitle(); - - // Jest expect - expect(title).toContain("Testplane"); - }); - - it("Jest assert — проверка количества элементов", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - - const menuItems = await browser.$$("nav.navbar a.navbar__item"); - - // Jest expect - expect(menuItems.length).toBeGreaterThan(0); - }); +### Assertions - it("Jest assert — проверка атрибута элемента", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); +Assertions — это проверки, которые сравнивают фактический результат с ожидаемым. - const docsLink = await browser.$("a[href='/ru/docs/v8/']"); - const href = await docsLink.getAttribute("href"); +```typescript +describe("Assertions", () => { + it("Проверка элементов", async ({ browser }) => { + await browser.url("https://testplane.io/"); - // Jest expect - expect(href).toBe("/ru/docs/v8/"); - }); + const heading = await browser.$("h1"); - it("Jest assert — проверка URL с регулярным выражением", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); + // Проверка существования и видимости + await expect(heading).toExist(); + await expect(heading).toBeDisplayed(); - const currentUrl = await browser.getUrl(); + // Проверка текста + await expect(heading).toHaveTextContaining("testplane"); - // Jest expect с regex - expect(currentUrl).toMatch(/testplane\.io/); + // Проверка атрибутов + await expect(heading).toHaveAttribute("class"); }); }); ``` -### Хуки +Подробнее см. в [документации по assertions](../commands/expect/overview.mdx). -Хуки — это специальные функции, которые автоматически выполняются в определенные моменты жизненного цикла тестов. Они позволяют подготовить окружение перед тестами и очистить его после выполнения. По умолчанию доступны два вида хуков — `beforeEach` и `afterEach`, первый выполняется перед каждым тестом, а второй — после. +### Хуки -```javascript -const assert = require("assert"); +Хуки позволяют подготовить окружение перед тестами и очистить его после выполнения. -describe("Пример работы с хуками", () => { +```typescript +describe("Работа с хуками", () => { beforeEach(async ({ browser }) => { - console.log("--- OUTER BEFOREEACH ---"); - await browser.url("https://testplane.io/ru/"); + // Выполняется перед каждым тестом + await browser.url("https://testplane.io/"); }); afterEach(async ({ browser }) => { - console.log("--- OUTER AFTEREACH ---"); + // Выполняется после каждого теста + await browser.deleteAllCookies(); }); - it("Внешний тест", async ({ browser }) => { + it("Первый тест", async ({ browser }) => { + // Страница уже открыта благодаря beforeEach const title = await browser.getTitle(); - assert.ok(title.length > 0); + expect(title).toContain("Fast"); }); - describe("Внутренний блок тестов", () => { - beforeEach(async ({ browser }) => { - console.log("--- INNER BEFOREEACH ---"); - // Сначала выполнится outer beforeEach, потом этот - await browser.url("https://testplane.io/ru/docs/v8/"); - }); - - afterEach(async ({ browser }) => { - console.log("--- INNER AFTEREACH ---"); - // Сначала выполнится этот afterEach, потом outer - }); - - it("Внутренний тест 1", async ({ browser }) => { - const currentUrl = await browser.getUrl(); - assert.ok(currentUrl.includes("docs")); - }); - - it("Внутренний тест 2", async ({ browser }) => { - const heading = await browser.$("h1"); - const isDisplayed = await heading.isDisplayed(); - assert.strictEqual(isDisplayed, true); - }); + it("Второй тест", async ({ browser }) => { + // Страница снова открыта, cookies очищены + const heading = await browser.$("h1"); + await expect(heading).toExist(); }); }); ``` -### Ожидания - -Явные ожидания необходимы для работы с динамическим контентом, который загружается или изменяется асинхронно. Testplane автоматически ждет появления элементов, но для сложных сценариев можно использовать специальные методы ожидания. +Подробнее о хуках см. в [документации](../basic-guides/setup-and-teardown.mdx). -```javascript -const assert = require("assert"); - -describe("Примеры ожиданий в Testplane", () => { - it("Ожидание появления и кликабельности элемента", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); - - // Ожидаем, что кнопка поиска появится на странице - const searchButton = await browser.$("button.DocSearch"); - await searchButton.waitForDisplayed({ - timeout: 5000, - timeoutMsg: "Кнопка поиска не появилась в течение 5 секунд", - }); +### Ожидания - // Ожидаем, что элемент станет кликабельным - await searchButton.waitForClickable({ - timeout: 3000, - timeoutMsg: "Кнопка поиска не стала кликабельной", - }); +Явные ожидания (например, [`waitForDisplayed()`](../commands/element/waitForDisplayed.mdx) и [`waitUntil()`](../commands/element/waitUntil.mdx)) необходимы для работы с динамическим контентом: - await searchButton.click(); +```typescript +describe("Ожидание", () => { + it("Ожидание элементов", async ({ browser }) => { + await browser.url("https://testplane.io/"); - // Ожидаем появления модального окна поиска - const searchModal = await browser.$(".DocSearch-Modal"); - await searchModal.waitForDisplayed({ timeout: 3000 }); + const button = await browser.$("button*=Get"); - const isDisplayed = await searchModal.isDisplayed(); - assert.strictEqual(isDisplayed, true); + // Ожидание появления + await button.waitForDisplayed({ timeout: 5000 }); }); - it("Ожидание изменения текста элемента", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); + it("Кастомное ожидание", async ({ browser }) => { + await browser.url("https://testplane.io/"); const heading = await browser.$("h1"); - // Ожидаем, что элемент будет существовать - await heading.waitForExist({ - timeout: 5000, - timeoutMsg: "Заголовок не найден на странице", - }); - - // Ожидаем, что у элемента будет определенный текст + // Ожидание появления текста заголовка await heading.waitUntil( async function () { const text = await this.getText(); @@ -440,84 +325,63 @@ describe("Примеры ожиданий в Testplane", () => { timeoutMsg: "Текст заголовка не появился", }, ); - - const text = await heading.getText(); - assert.ok(text.length > 0); }); +}); +``` - it("Ожидание с использованием browser.waitUntil для проверки URL", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); +## Визуальное тестирование - const docsLink = await browser.$("a[href='/ru/docs/v8/']"); - await docsLink.waitForExist({ timeout: 5000 }); +Testplane позволяет делать скриншоты элементов и сравнивать их с эталонными изображениями с помощью команды [`assertView()`](../commands/browser/assertView.mdx). - // Используем JavaScript click для надежности - await browser.execute(el => el.click(), docsLink); +При первом запуске теста создаются эталонные скриншоты. При последующих запусках Testplane сравнивает новые скриншоты с эталонами и сообщает о различиях. Если различий нет, тест проходит. Если есть различия, тест падает, а diff-изображение сохраняется для анализа. - // Ожидаем изменения URL с помощью browser.waitUntil - await browser.waitUntil( - async () => { - const currentUrl = await browser.getUrl(); - return currentUrl.includes("docs"); - }, - { - timeout: 5000, - timeoutMsg: "URL не изменился на страницу документации", - }, - ); +```typescript +describe("Визуальная проверка", () => { + it("Логотип", async ({ browser }) => { + await browser.url("https://testplane.io/"); + + // Ожидание появления элемента + const brand = await browser.$(".navbar__brand"); + await brand.waitForDisplayed({ timeout: 10000 }); - const finalUrl = await browser.getUrl(); - assert.ok(finalUrl.includes("docs")); + // Скриншот элемента + await browser.assertView("default", ".navbar__brand"); }); }); ``` -### Работа с JavaScript кодом +## Выполнение JavaScript -Иногда нужно выполнить произвольный JavaScript-код в контексте страницы — например, для работы с `localStorage`, вызова функций или манипуляции `DOM` напрямую. Метод `execute()` выполняет код в браузере и может возвращать результат. - -```javascript -const assert = require("assert"); +Метод [`execute()`](../commands/browser/execute.mdx) выполняет JavaScript-код в контексте страницы: -describe("Примеры работы с JavaScript кодом", () => { - it("Выполнение JavaScript кода в контексте страницы", async ({ browser }) => { - await browser.url("https://testplane.io/ru/"); +```typescript +describe("Выполнение JavaScript", () => { + it("Пример", async ({ browser }) => { + await browser.url("https://testplane.io/"); - // Пример 1: Получение данных из localStorage + // Работа с localStorage await browser.execute(() => { - localStorage.setItem("testKey", "testValue"); - localStorage.setItem("userName", "John Doe"); + localStorage.setItem("token", "abc123"); }); - const storageValue = await browser.execute(() => { - return localStorage.getItem("testKey"); + const token = await browser.execute(() => { + return localStorage.getItem("token"); }); - assert.strictEqual(storageValue, "testValue"); - - // Пример 2: Манипуляция DOM — изменение текста элемента - const newText = await browser.execute(() => { - const heading = document.querySelector("h1"); - if (heading) { - const originalText = heading.textContent; - heading.textContent = "Измененный заголовок"; - return originalText; - } - return ""; - }); - - const modifiedHeading = await browser.$("h1"); - const currentText = await modifiedHeading.getText(); - assert.strictEqual(currentText, "Измененный заголовок"); + expect(token).toBe("abc123"); - // Пример 3: Вызов функции с параметрами - const sum = await browser.execute( - (a, b) => { - return a + b; - }, - 5, - 10, - ); - assert.strictEqual(sum, 15); + // Передача параметров + const sum = await browser.execute((a, b) => a + b, 5, 10); + expect(sum).toBe(15); }); }); ``` + +## Что дальше? + +Теперь, когда вы знаете основы написания тестов, изучите: + +- [Запуск и отладка](./running-tests.mdx): как запускать тесты и отлаживать проблемы +- [Использование в CI](./usage-in-ci.mdx): автоматический запуск в GitHub Actions +- [Конфигурация](../reference/config/main.mdx): полный список настроек +- [Команды браузера](../commands/browser/url.mdx): все доступные команды +- [HTML Reporter](../html-reporter/overview.mdx): работа с отчетами