{"componentChunkName":"component---src-templates-docs-js","path":"/sdks-for-web-apps/selenium-python-sdk","result":{"data":{"site":{"siteMetadata":{"title":"Documentation for developers | Evinced, Inc.","docsLocation":"https://github.com/GetEvinced/evinced-kb"}},"mdx":{"fields":{"id":"04ffdc00-1dc2-5e1b-a497-5b5e8fe18c92","title":"Selenium Python SDK","slug":"/sdks-for-web-apps/selenium-python-sdk"},"body":"var _excluded = [\"components\"];\nfunction _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) { ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } } return n; }, _extends.apply(null, arguments); }\nfunction _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) { o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } } return i; }\nfunction _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) { if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } } return t; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Selenium Python SDK\",\n  \"metaTitle\": \"Selenium Python SDK Documentation\",\n  \"index\": 8\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"The Evinced Selenium Python SDK integrates with new or existing\\nSelenium WebDriver tests to automatically detect accessibility issues. By\\nadding a few lines of code to your Selenium WebDriver project, you can\\nbegin analyzing all the web pages and DOM changes to provide a dynamic view of\\nhow your site can become more accessible. As a result of the test, a rich and\\ncomprehensive report is generated to easily track issues to resolution.\"), mdx(\"p\", null, \"Interested in seeing this in action? \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://e7gfbzr3c1n.typeform.com/to/vWy2sruY?utm_source=website&utm_medium=xxxxx&utm_campaign=devdocs&utm_term=xxxxx&utm_content=xxxxx#hidden4=xxxxx\"\n  }, \"Contact us\"), \" to get started!\"), mdx(\"h2\", null, \"Prerequisites\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Python 3.10 or higher (tested on 3.10, 3.11, and 3.12) - check with\\n\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"python3 --version\"), \" on macOS / Linux or \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"py --version\"), \" on Windows\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Selenium 4.46 or higher\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"pytest 8 or higher for the examples in this guide (optional - the SDK\\nworks with any test runner)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"A Chrome, Edge, or Firefox browser and matching WebDriver (Selenium Manager resolves drivers automatically)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Evinced credentials (service account ID and either a JWT or a secret)\")), mdx(\"h2\", null, \"Get started\"), mdx(\"h3\", null, \"Installation\"), mdx(\"p\", null, \"To install Selenium Python SDK you will need either a file provided by Evinced Support\\nor access to a remote repository that provides it. If you have neither, \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://e7gfbzr3c1n.typeform.com/to/vWy2sruY?utm_source=website&utm_medium=xxxxx&utm_campaign=devdocs&utm_term=xxxxx&utm_content=xxxxx#hidden4=xxxxx\"\n  }, \"contact us\"), \" to get started.\"), mdx(\"h4\", null, \"Adding Evinced to an existing Python project\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced-selenium-sdk\"), \" is a standard Python package with ordinary\\ndependencies, so it does not impose any project\\nlayout or tooling. Install it into your existing setup exactly the way you\\ninstall any other dependency.\"), mdx(Disclosure, {\n    summary: \"Starting a Python project from scratch\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"p\", null, \"The standard way to work on a Python project is one\\n\", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"virtual environment\"), \" (venv) per project: a private Python environment inside\\nthe project folder that keeps its packages separate from other projects and\\nfrom the system-wide Python. Create it once, then activate it in every new\\nterminal you open for this project.\"), mdx(\"p\", null, \"On macOS / Linux:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir my-a11y-tests\\ncd my-a11y-tests\\npython3 -m venv .venv\\nsource .venv/bin/activate\\n\")), mdx(\"p\", null, \"On Windows:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir my-a11y-tests\\ncd my-a11y-tests\\npy -m venv .venv\\n.venv\\\\Scripts\\\\activate\\n\")), mdx(\"p\", null, \"While the environment is active, your prompt is prefixed with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"(.venv)\"), \" and the\\nplain \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python\"), \" command refers to the project's own interpreter - from this\\npoint on, the commands in this documentation are identical on every operating\\nsystem.\"), mdx(DisclaimerBox, {\n    mdxType: \"DisclaimerBox\"\n  }, mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Running pip from your project folder does not install into that folder.\"), \"\\nWithout an activated virtual environment, the package goes into your global\\nPython installation. Activate your project's environment first (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".venv\"), \" in\\nthis guide), then check where pip will install to:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip -V\\n\")), mdx(\"p\", null, \"The printed path should point inside your project's environment.\"), mdx(InfoBox, {\n    mdxType: \"InfoBox\"\n  }, mdx(\"p\", null, \"This documentation uses \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python -m pip\"), \" rather than the bare \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"pip\"), \" command. On\\nWindows in particular, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"pip\"), \" may be missing from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PATH\"), \" or bound to a different\\nPython installation than the one you intend to use - \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python -m pip\"), \" always\\nruns pip for exactly the interpreter you invoke it with, on every operating\\nsystem. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python\"), \" command itself is guaranteed inside an activated virtual\\nenvironment; outside one, substitute \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python3 -m pip\"), \" on macOS / Linux or\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"py -m pip\"), \" on Windows.\")))), mdx(\"p\", null, \"A typical pytest project for accessibility testing looks like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-text\"\n  }, \"my-a11y-tests/\\n\\u251C\\u2500\\u2500 .venv/               # virtual environment - never commit; add to .gitignore\\n\\u251C\\u2500\\u2500 requirements.txt     # the project's dependencies\\n\\u251C\\u2500\\u2500 conftest.py          # shared pytest fixtures: credentials and driver setup\\n\\u2514\\u2500\\u2500 tests/\\n    \\u2514\\u2500\\u2500 test_home_page.py\\n\")), mdx(\"p\", null, \"Declare your dependencies in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requirements.txt\"), \" so teammates and CI can\\nrecreate the same environment:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-text\"\n  }, \"selenium>=4.46\\npytest>=8.0\\n\")), mdx(\"p\", null, \"and install them all at once with:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip install -r requirements.txt\\n\")), mdx(\"p\", null, \"How the Evinced SDK itself is added depends on which installation method below\\napplies to you: with \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"remote repository\"), \" access, add\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced-selenium-sdk==<version>\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requirements.txt\"), \" together with the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"--extra-index-url\"), \" line shown in that section; with a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"locally provided\\nfile\"), \", either reference the wheel by path\\n(\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./evinced_selenium_sdk-<version>-py3-none-any.whl\"), \" on its own line) or\\ninstall it as a separate step on each machine.\"), mdx(\"h4\", null, \"Installation with a locally provided file\"), mdx(\"p\", null, \"Selenium Python SDK is distributed as a pre-built wheel - the accessibility\\nengine and report generators are vendored inside it, so \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"no Node.js is\\nrequired\"), \". This is the supported way to install the SDK today.\"), mdx(\"p\", null, \"Obtain the distribution file (a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".whl\"), \" wheel or a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".tar.gz\"), \" source archive)\\nfrom Evinced Support and place it in your project folder. Then install it -\\nif your project uses a virtual environment, \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"activate it first\"), \".\"), mdx(\"p\", null, \"On macOS / Linux / Windows:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip install evinced_selenium_sdk-<version>-py3-none-any.whl\\n\")), mdx(\"p\", null, \"Verify the installation:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip show evinced-selenium-sdk\\n\")), mdx(\"h4\", null, \"Installation from a remote repository\"), mdx(\"p\", null, \"Evinced Customers have the option of accessing Selenium Python SDK from a remote repository -\\nthe Evinced JFrog Artifactory - to keep their SDK version up-to-date and to share the SDK internally\\nat their organization.\"), mdx(\"p\", null, \"When access is enabled, Selenium Python SDK is published to the Evinced\\nJFrog Artifactory. The repository requires authentication - Evinced provides a\\nJFrog username and access token, which you embed in the index URL. Activate\\nyour project's virtual environment if you use one, then install by pointing\\npip at the Evinced index:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip install evinced-selenium-sdk --extra-index-url \\\"https://<JFROG_USER>:<JFROG_TOKEN>@evinced.jfrog.io/artifactory/api/pypi/restricted-python/simple\\\"\\n\")), mdx(\"p\", null, \"If your JFrog username is an email address, URL-encode the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@\"), \" as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"%40\"), \"\\n(for example \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"jane.doe%40example.com\"), \").\"), mdx(\"p\", null, \"Verify the installation:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"python -m pip show evinced-selenium-sdk\\n\")), mdx(\"h3\", null, \"AI Skills\"), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced-selenium-sdk\"), \" package ships built-in AI agent skills that guide your AI assistant through using the Evinced Selenium Python SDK - from initial setup and test writing to configuration, reporting, and CI integration. They work with any AI assistant that reads project context files (Claude Code, Cursor, Copilot, Windsurf, Gemini, and others).\"), mdx(\"h4\", null, \"Installing the skills\"), mdx(\"p\", null, \"After installing the SDK, run the bundled installer from your project root:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"evinced-ai install\\n\")), mdx(\"p\", null, \"The command detects your assistant and writes the skills pointer into the matching context file - \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".cursor/rules/evinced-ai.mdc\"), \" for Cursor, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CLAUDE.md\"), \" for Claude Code, or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"AGENTS.md\"), \" otherwise. The pointer locates the skills inside the installed package rather than at a machine-specific path, so it is safe to commit and keeps working for every team member, on any operating system, after a plain package install.\"), mdx(\"h4\", null, \"Manual installation\"), mdx(\"p\", null, \"To add the pointer yourself, copy the following block into your project's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"AGENTS.md\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CLAUDE.md\"), \" (for Cursor, put it in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".cursor/rules/evinced-ai.mdc\"), \"). Running \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced-ai snippet\"), \" prints the same block:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"<!-- evinced-ai:begin (managed by `evinced-ai install`) -->\\n\\n## Evinced accessibility SDK \\u2014 AI skills\\n\\nThis project uses the Evinced Selenium Python SDK (`evinced-selenium-sdk`), which ships AI skills for setting up and using accessibility testing. Before working on any web accessibility task:\\n\\n1. **Make sure the SDK is installed** in this project's Python environment:\\n\\n   ```bash\\n   python -c \\\"import evinced_selenium_sdk\\\"\\n   ```\\n\\n   If that fails, install `evinced-selenium-sdk` **the way this project manages dependencies**, and add it as a project dependency \\u2014 match the existing setup, don't switch package managers:\\n   - **pip / venv** (`requirements.txt` or a `.venv/`): activate the venv, then `pip install evinced-selenium-sdk`, and add it to `requirements.txt`.\\n   - **Poetry** (`pyproject.toml` with `[tool.poetry]`): `poetry add evinced-selenium-sdk`.\\n   - **uv** (`uv.lock` / `[tool.uv]`): `uv add evinced-selenium-sdk`.\\n   - **Pipenv** (`Pipfile`): `pipenv install evinced-selenium-sdk`.\\n   - **conda**: `pip install evinced-selenium-sdk` inside the active conda env.\\n\\n2. **Find the skills entry point** (run in the same environment):\\n\\n   ```bash\\n   python -c \\\"import evinced_selenium_sdk, pathlib; print(pathlib.Path(evinced_selenium_sdk.__file__).parent / 'evinced_ai' / 'entry.mdc')\\\"\\n   ```\\n\\n3. **Read the file it prints, then follow it.** It routes you to the right skill (setup, writing tests, reporting, configuration) and to the SDK integration rule.\\n\\nThe skills only add to the project and never overwrite existing code.\\n\\n<!-- evinced-ai:end -->\\n\")), mdx(\"h4\", null, \"What the skills do\"), mdx(\"p\", null, \"The entry point routes the assistant by intent:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"install\\\"\"), \" - installs the SDK and creates a standalone demo test (one-shot and continuous) that showcases it, without touching your existing tests.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"integrate\\\"\"), \" - a guided wizard that wires the SDK into your existing suite: it asks whether you authenticate online or offline, places the credential call and driver fixture in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"conftest.py\"), \", keeps your \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"test_*.py\"), \" files clean, and finishes with the exact credential steps and a copy-paste verification command.\")), mdx(\"p\", null, \"The skills can also:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Write accessibility tests using \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_analyze\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_start\"), \", and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_stop\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Configure accessibility rules, skip validations, and scope analysis to a DOM subtree\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Generate HTML, JSON, SARIF, or CSV reports\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Configure the SDK through \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"EvincedConfig\"), \" - screenshots, iframes, and network-idle waiting\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Enable and tune SDK logging and log levels\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Control SDK toggles and the kill switch\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Integrate accessibility checks into CI/CD pipelines\")), mdx(\"p\", null, \"Credentials and driver setup always go in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"conftest.py\"), \" (pytest's fixture and configuration file), so your test files contain only tests.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note.\"), \" The skills entry point resolves the SDK in the active Python environment. If your assistant cannot find it, activate the project's virtual environment (or select the correct interpreter) and try again.\"), mdx(\"p\", null, \"Example prompts:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"em\", {\n    parentName: \"li\"\n  }, \"\\\"Install Evinced and show me a demo test\\\"\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"em\", {\n    parentName: \"li\"\n  }, \"\\\"Integrate Evinced into my existing Selenium WebDriver tests\\\"\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"em\", {\n    parentName: \"li\"\n  }, \"\\\"Fail my tests when critical accessibility issues are found\\\"\"))), mdx(\"h3\", null, \"Authentication\"), mdx(\"p\", null, \"To launch Selenium Python SDK, you need to have a Service ID and an API Key.\"), mdx(Disclosure, {\n    summary: \"Where to find your Evinced SDK credentials\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"p\", null, \"These credentials are available via the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://hub.evinced.com/\"\n  }, \"Evinced Product Hub\"), \" in the\\n\\u201CAutomation for Web\\u201D or \\u201CAutomation for Mobile\\u201D product areas. Click the \\u201CGet SDK\\u201D button to see the Service Account ID and API Key at the bottom of the page.\")), mdx(Disclosure, {\n    summary: \"Authenticate for Offline Testing\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"p\", null, \"There are two methods to provide the token: \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"online\"), \" mode and \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"offline\"), \" mode.\\nOnline mode contacts the Evinced Licensing Server. Offline mode assumes that an\\nEvinced employee has supplied a JSON Web Token (JWT). If an offline token is required,\\nplease reach out to your account team or \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"mailto:support@evinced.com\"\n  }, \"support@evinced.com\"), \".\")), mdx(\"p\", null, \"Please set credentials in environment variables and reference the environment variables in code.\"), mdx(\"p\", null, \"On macOS / Linux:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Online mode\\nexport AUTH_SERVICE_ID=YOUR_SERVICE_ID\\nexport AUTH_SECRET=YOUR_API_SECRET\\n\\n# Offline mode - when a JWT has been provided by Evinced\\nexport AUTH_SERVICE_ID=YOUR_SERVICE_ID\\nexport AUTH_TOKEN=YOUR_JWT\\n\")), mdx(\"p\", null, \"On Windows:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Online mode\\n$env:AUTH_SERVICE_ID = \\\"YOUR_SERVICE_ID\\\"\\n$env:AUTH_SECRET = \\\"YOUR_API_SECRET\\\"\\n\\n# Offline mode - when a JWT has been provided by Evinced\\n$env:AUTH_SERVICE_ID = \\\"YOUR_SERVICE_ID\\\"\\n$env:AUTH_TOKEN = \\\"YOUR_JWT\\\"\\n\")), mdx(\"p\", null, \"Both forms last only for the current terminal session. To persist them, add the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"export\"), \" lines to your shell profile (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"~/.zshrc\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"~/.bashrc\"), \") on macOS /\\nLinux, or the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$env:\"), \" lines to your PowerShell profile (open it with\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"notepad $PROFILE\"), \") on Windows.\"), mdx(\"h4\", null, \"Setting credentials, an example:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# conftest.py - project root\\nimport os\\n\\nimport pytest\\n\\nfrom evinced_selenium_sdk import set_credentials\\n\\n\\n@pytest.fixture(scope=\\\"session\\\", autouse=True)\\ndef evinced_credentials():\\n    # Online mode - exchange a service-account secret for a JWT\\n    set_credentials(\\n        service_id=os.environ[\\\"AUTH_SERVICE_ID\\\"],\\n        secret=os.environ[\\\"AUTH_SECRET\\\"],\\n    )\\n\")), mdx(\"p\", null, \"If Evinced has supplied you with a signed JWT (offline mode), use the same\\nfixture with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_offline_credentials\"), \" instead:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# conftest.py - project root\\nimport os\\n\\nimport pytest\\n\\nfrom evinced_selenium_sdk import set_offline_credentials\\n\\n\\n@pytest.fixture(scope=\\\"session\\\", autouse=True)\\ndef evinced_credentials():\\n    # Offline mode - you already hold a signed JWT from Evinced\\n    set_offline_credentials(\\n        service_id=os.environ[\\\"AUTH_SERVICE_ID\\\"],\\n        token=os.environ[\\\"AUTH_TOKEN\\\"],\\n    )\\n\")), mdx(\"h3\", null, \"Your First Test\"), mdx(\"h4\", null, \"SDK Initialization\"), mdx(\"p\", null, \"To use Selenium Python SDK, you first need to authenticate. Please refer to\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#authentication\"\n  }, \"Authentication\"), \" for details.\"), mdx(\"p\", null, \"The examples in this guide use pytest, but nothing in the SDK requires it -\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedWebDriver\"), \" wraps a plain Selenium WebDriver, so unittest, Robot\\nFramework, Behave, or a bare script all work the same. Framework-neutral, the\\ncontract is: call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_credentials\"), \" (or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_offline_credentials\"), \") once per\\nprocess before creating the first \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedWebDriver\"), \", and save any reports you\\nneed before calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"quit()\"), \".\\nEverything else is regular Selenium.\"), mdx(Disclosure, {\n    summary: \"Using the SDK without pytest\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"import os\\n\\nfrom selenium import webdriver\\n\\nfrom evinced_selenium_sdk import EvincedWebDriver, SaveFileFormat, set_credentials\\n\\nset_credentials(\\n    service_id=os.environ[\\\"AUTH_SERVICE_ID\\\"],\\n    secret=os.environ[\\\"AUTH_SECRET\\\"],\\n)\\n\\ndriver = EvincedWebDriver(webdriver.Chrome())\\ntry:\\n    driver.get(\\\"https://demo.evinced.com\\\")\\n    report = driver.ev_analyze()\\n    driver.ev_save_file(report, \\\"report.html\\\", SaveFileFormat.HTML)\\nfinally:\\n    driver.quit()\\n\"))), mdx(\"p\", null, \"Add the import at the top of your test module:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedWebDriver\\nfrom selenium import webdriver\\n\")), mdx(\"p\", null, \"The Evinced SDK wraps your Selenium driver and intercepts navigation and interactions for continuous analysis. Initialize it with a Chrome (or other) WebDriver instance:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"raw = webdriver.Chrome()\\ndriver = EvincedWebDriver(raw)\\n\")), mdx(\"p\", null, \"Chrome, Edge, and Firefox are all supported - pass \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"webdriver.Edge()\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"webdriver.Firefox()\"), \" the same way. Chromium-only capabilities are called out where they apply (for example \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScreenshotMode.SDK\"), \").\"), mdx(Disclosure, {\n    summary: \"Using Evinced with RemoteWebDriver or Selenium Grid\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"p\", null, \"Use the same \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedWebDriver\"), \" constructor with any Selenium \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WebDriver\"), \", including \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"webdriver.Remote\"), \" (Selenium Grid, BrowserStack, Sauce Labs, and other remote endpoints). There is no separate remote wrapper class:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium import webdriver\\nfrom evinced_selenium_sdk import EvincedWebDriver\\n\\nremote = webdriver.Remote(command_executor=\\\"http://localhost:4444\\\", options=webdriver.ChromeOptions())\\ndriver = EvincedWebDriver(remote)\\n\"))), mdx(\"h4\", null, \"Add Evinced Accessibility Checks (Single Run Mode)\"), mdx(\"p\", null, \"This is a simple pytest example of a single-page Evinced accessibility scan.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# tests/test_home_page.py\\nfrom selenium import webdriver\\n\\nfrom evinced_selenium_sdk import EvincedWebDriver\\n\\n\\ndef test_home_page_accessibility():\\n    driver = EvincedWebDriver(webdriver.Chrome())\\n    try:\\n        driver.get(\\\"https://demo.evinced.com\\\")\\n        report = driver.ev_analyze()\\n        # demo.evinced.com intentionally contains issues, so this assertion\\n        # fails there - point the test at your own application\\n        assert len(report.get_failed_validations()) == 0\\n    finally:\\n        driver.quit()\\n\")), mdx(\"h4\", null, \"Add Evinced Accessibility Checks (Continuous Mode)\"), mdx(\"p\", null, \"This is a simple pytest example of continuous accessibility scanning. Using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \", the Evinced engine scans in the background as your test runs, capturing DOM changes and navigations.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# tests/test_continuous_scan.py\\nimport pytest\\nfrom selenium import webdriver\\nfrom selenium.webdriver.common.by import By\\n\\nfrom evinced_selenium_sdk import EvincedWebDriver, SaveFileFormat\\n\\n\\n@pytest.fixture\\ndef driver():\\n    d = EvincedWebDriver(webdriver.Chrome())\\n    yield d\\n    d.quit()\\n\\n\\ndef test_trip_planner_accessibility(driver):\\n    driver.get(\\\"https://demo.evinced.com\\\")\\n    driver.ev_start()\\n\\n    # Interact with the page - each step below reveals new UI\\n    # (dropdown options, calendar) that is scanned in the background\\n    driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(1) > div > div.dropdown.line\\\").click()\\n    driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(2) > div > div.dropdown.line\\\").click()\\n    driver.find_element(By.CSS_SELECTOR, \\\".react-date-picker\\\").click()\\n\\n    report = driver.ev_stop()\\n\\n    driver.ev_save_file(report, \\\"test-results.html\\\", SaveFileFormat.HTML)\\n    driver.ev_save_file(report, \\\"test-results.json\\\", SaveFileFormat.JSON)\\n    # the TRVL demo site intentionally contains issues, so this assertion\\n    # fails there - point the test at your own application\\n    assert len(report.get_failed_validations()) == 0\\n\")), mdx(Disclosure, {\n    summary: \"Using Evinced with Selenium BiDi\",\n    mdxType: \"Disclosure\"\n  }, mdx(\"p\", null, \"You can configure the Evinced SDK to use \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Selenium BiDi\"), \" (Bidirectional Protocol). BiDi reduces communication overhead between the test runner and the browser, which can speed up continuous accessibility scanning.\"), mdx(\"p\", null, \"By default, continuous mode uses \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"interaction-based\"), \" transport (clicks and navigation hooks). BiDi is \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"opt-in\"), \": set \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"continuous_transport=\\\"bidi\\\"\"), \" on a driver that advertises the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"webSocketUrl\"), \" capability.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium import webdriver\\nfrom selenium.webdriver.chrome.options import Options\\nfrom evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\n\\noptions = Options()\\noptions.set_capability(\\\"webSocketUrl\\\", True)\\nraw = webdriver.Chrome(options=options)\\nconfig = EvincedConfig(continuous_transport=\\\"bidi\\\")\\ndriver = EvincedWebDriver(raw, config)\\ndriver.ev_start()\\n\"))), mdx(\"h2\", null, \"API\"), mdx(\"hr\", null), mdx(\"h3\", null, mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"EvincedWebDriver(driver, config)\")), mdx(\"p\", null, \"Prepares the Evinced object for use in the project.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(root_selector=\\\"#main\\\", include_iframes=True)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"p\", null, \"Refer to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#configuration\"\n  }, \"Configuration\"), \" to see examples of initializing with options.\"), mdx(\"hr\", null), mdx(\"h3\", null, mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"ev_analyze(config, upload_to_platform=None)\")), mdx(\"p\", null, \"Scans the current page and returns a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \" with the accessibility issues found.\\nThis is the recommended method for static page analysis.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Note: This method is not supported if \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"ev_start()\"), \" is already running.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, Report\\n\\n# Basic usage - uses the driver-level EvincedConfig (if any)\\nreport: Report = driver.ev_analyze()\\nfailed = report.get_failed_validations()\\n\\n# Optional per-call config override\\nreport = driver.ev_analyze(EvincedConfig(root_selector=\\\"#main\\\"))\\n\")), mdx(\"p\", null, \"Both parameters are optional. The optional \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"upload_to_platform\"), \" parameter overrides platform upload for this call - see \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#uploadingreportstoevincedplatform\"\n  }, \"Uploading Reports to Evinced Platform\"), \".\"), mdx(\"p\", null, \"Calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" during an active \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start\"), \" session raises \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SessionError\"), \" (importable with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"from evinced_selenium_sdk import SessionError\"), \"). Call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" first.\"), mdx(\"p\", null, \"Returns \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \".\"), mdx(\"p\", null, \"The returned report object contains a list of accessibility issues.\"), mdx(\"p\", null, \"For more information regarding reports as well as the report object itself,\\nplease refer to our detailed \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../web-reports\"\n  }, \"Web Reports\"), \" page.\"), mdx(\"hr\", null), mdx(\"h3\", null, mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"ev_start(config)\")), mdx(\"p\", null, \"Continually watches for DOM mutations and page navigation, recording accessibility issues\\nuntil the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" method is called. This method is recommended for dynamic page flows.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# Uses the EvincedConfig passed to EvincedWebDriver(driver, config), if any\\ndriver.ev_start()\\n\\n# Optional per-call override for this session only\\ndriver.ev_start(EvincedConfig(include_iframes=False))\\n\")), mdx(\"p\", null, \"The optional \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"config\"), \" parameter accepts an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedConfig\"), \" instance or a plain dict. It overrides the driver-level settings for this continuous session only.\"), mdx(\"p\", null, \"Returns \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"None\"), \".\"), mdx(\"hr\", null), mdx(\"h3\", null, mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"ev_stop(upload_to_platform=None)\")), mdx(\"p\", null, \"Stops the issue-gathering process started by \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import Report\\n\\ndriver.ev_start()\\nreport: Report = driver.ev_stop()\\nfailed = report.get_failed_validations()\\n\")), mdx(\"p\", null, \"The optional \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"upload_to_platform\"), \" parameter overrides platform upload for this call - see \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#uploadingreportstoevincedplatform\"\n  }, \"Uploading Reports to Evinced Platform\"), \".\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_passed_validations=True\"), \" was enabled for the session, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"report.get_passed_validations()\"), \" is also populated.\"), mdx(\"p\", null, \"Returns \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \".\"), mdx(\"p\", null, \"The returned report object includes all accessibility issues detected between\\nthe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" method calls.\"), mdx(\"p\", null, \"For more information regarding reports as well as the report object itself,\\nplease refer to our detailed \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../web-reports\"\n  }, \"Web Reports\"), \" page.\"), mdx(\"hr\", null), mdx(\"h3\", null, mdx(\"inlineCode\", {\n    parentName: \"h3\"\n  }, \"ev_save_file(report, destination, file_format)\")), mdx(\"p\", null, \"Saves issues in a file with the specified format and location.\\nSupported formats are \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"json\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"html\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sarif\"), \", and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"csv\"), \".\\nFind detailed information in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../web-reports\"\n  }, \"Web Reports\"), \" page.\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_save_file\"), \" has two forms - the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"first argument\"), \" decides which one you get:\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Goal\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Call\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Save one specific report\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ev_save_file(report, path, format)\"))), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Save the aggregated run (every \", mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ev_analyze\"), \" + \", mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ev_stop\"), \" on this driver)\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ev_save_file(path, format)\"))))), mdx(\"h4\", null, \"Save a single report\"), mdx(\"p\", null, \"Pass the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \" from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import Report, SaveFileFormat\\n\\nreport: Report = driver.ev_stop()\\n# or: report = driver.ev_analyze()\\n\\ndriver.ev_save_file(report, \\\"jsonReport.json\\\", SaveFileFormat.JSON)\\ndriver.ev_save_file(report, \\\"htmlReport.html\\\", SaveFileFormat.HTML)\\ndriver.ev_save_file(report, \\\"sarifReport.sarif.json\\\", SaveFileFormat.SARIF)\\ndriver.ev_save_file(report, \\\"csvReport.csv\\\", SaveFileFormat.CSV)\\n\")), mdx(\"p\", null, \"String format names (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"json\\\"\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"html\\\"\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"sarif\\\"\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"csv\\\"\"), \") are also accepted. A plain \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"list[dict]\"), \" of failed issues is still accepted for backward compatibility.\"), mdx(\"p\", null, \"Pass a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"scale\"), \" keyword argument (float) to adjust HTML report scaling.\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_passed_validations=True\"), \", pass the full \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \" to include passed validations in \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"JSON\"), \" output.\"), mdx(\"h4\", null, \"Save an aggregated run report\"), mdx(\"p\", null, \"After multiple \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \" calls on the same driver, save one combined deduplicated report without passing a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \" object:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.ev_save_file(\\\"combined.html\\\", SaveFileFormat.HTML)\\ndriver.ev_save_file(\\\"combined.json\\\", SaveFileFormat.JSON)\\n\")), mdx(\"p\", null, \"See \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#aggregatedreport\"\n  }, \"Aggregated Report\"), \" for a full example.\"), mdx(\"h4\", null, \"SaveFileFormat\"), mdx(\"p\", null, \"Defines the report file type. Options are \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"JSON\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HTML\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SARIF\"), \", and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CSV\"), \" (via the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SaveFileFormat\"), \" enum or equivalent strings).\"), mdx(\"p\", null, \"Returns \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"None\"), \".\"), mdx(\"h2\", null, \"Aggregated Report\"), mdx(\"p\", null, \"The aggregated report feature allows you to have a general aggregated report for\\nthe whole run (not only for one test or suite). This report will contain all the\\nissues found by the tests where \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \" and\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" commands were called. It is still possible to use the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_save_file()\"), \" command in any place of your code along with this\\nAggregated Report feature.\"), mdx(\"p\", null, \"Each \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedWebDriver\"), \" keeps its own aggregation store. Every \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \" call on that driver contributes deduplicated issues you can save as one combined report.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import SaveFileFormat\\n\\ndriver.get(\\\"https://demo.evinced.com/page1\\\")\\ndriver.ev_analyze()\\n\\ndriver.get(\\\"https://demo.evinced.com/page2\\\")\\ndriver.ev_start()\\ndriver.find_element(\\\"id\\\", \\\"next\\\").click()\\ndriver.ev_stop()\\n\\ndriver.ev_save_file(\\\"evinced-combined.html\\\", SaveFileFormat.HTML)\\ndriver.ev_save_file(\\\"evinced-combined.json\\\", SaveFileFormat.JSON)\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Save the aggregated report before calling \", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"quit()\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"close()\")), \" - quitting the driver clears the aggregation store, and issues collected during the run are lost.\"), mdx(\"p\", null, \"To save a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"single\"), \" analyze/stop result instead, pass the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Report\"), \" as the first argument:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"report = driver.ev_analyze()\\ndriver.ev_save_file(report, \\\"page-only.json\\\", SaveFileFormat.JSON)\\n\")), mdx(\"p\", null, \"Merge arbitrary issue lists (for example from separate drivers) with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_merge_issues\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"merged = driver.ev_merge_issues(report_page1, report_page2)\\ndriver.ev_save_file(merged, \\\"merged.json\\\", SaveFileFormat.JSON)\\n\")), mdx(\"h2\", null, \"Configuration\"), mdx(\"p\", null, \"The same configuration object can be used when initializing the Evinced object\\nusing \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedConfig()\"), \" and when calling the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \"\\nand \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" methods but with a bit different consequences.\\nProviding options when initializing defines a global configuration for all calls\\nof \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \", while providing options to\\neither of those methods affect only the test in which they are called.\"), mdx(\"p\", null, \"Options provided in either \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \" override\\nthose set in Evinced engine initialization.\"), mdx(\"h3\", null, \"Engines Configuration\"), mdx(\"p\", null, \"Evinced uses two separate engines when scanning for accessibility issues:\\nthe \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Axe\"), \" (axe-core) engine and the proprietary Evinced engine. By default, Evinced\\ndisables Axe \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Needs Review\"), \" and \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Best Practices\"), \" rules because they are mostly false positives.\\nKeep this in mind when comparing issue counts with other tools. See \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#toggles\"\n  }, \"Toggles\"), \" to enable them.\"), mdx(\"h3\", null, \"Configuration Object\"), mdx(\"p\", null, \"Evinced configuration is passed as an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedConfig\"), \" dataclass (or a plain dict with the same keys). You can set it in two places:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Per driver\"), \" - pass config when constructing \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"EvincedWebDriver\"), \". These values apply to every \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_analyze\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_start\"), \" on that driver unless overridden.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Per call\"), \" - pass config to a single \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_analyze(config)\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_start(config)\"), \" to override the driver-level settings for that command only.\\n\", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Precedence:\"), \" SDK defaults \\u2192 per-driver config \\u2192 per-call config.\")), mdx(\"p\", null, \"Both \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"snake_case\"), \" (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_iframes\"), \") and \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"camelCase\"), \" (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"includeIframes\"), \") keys are accepted in dicts. When both forms of the same option are set, camelCase wins. Note that keys inside nested options (for example \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"skip_validations\"), \" entries such as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"urlRegex\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"validationTypes\"), \") are passed to the engine as-is and are always camelCase, as are the keyword arguments of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_upload_to_platform_config\"), \".\"), mdx(\"h4\", null, \"Per-driver configuration\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    include_iframes=False,\\n    root_selector=\\\"#app\\\",\\n    screenshot_mode=\\\"sdk\\\",\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h4\", null, \"Per-call override\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig\\n\\n# Include passed validations for one analyze call\\nreport = driver.ev_analyze(EvincedConfig(include_passed_validations=True))\\n\\n# Override root selector for one continuous session\\ndriver.ev_start(EvincedConfig(root_selector=\\\"#content\\\"))\\n\")), mdx(\"p\", null, \"The sections below cover each option in detail.\"), mdx(\"h3\", null, \"Root Selector\"), mdx(\"p\", null, \"Sets a CSS selector to limit the Evinced Engine to scan only the selected element and its children.\\nMust be a valid CSS selector. If not set, the Evinced Engine will scan the entire document.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" no value\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(root_selector=\\\".some-selector\\\")\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h3\", null, \"Axe Configuration\"), mdx(\"p\", null, \"Configures Axe open-source accessibility toolkit, which the Evinced engine includes with\\nits own, more extensive accessibility detection. For full Axe config options,\\nsee \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure\"\n  }, \"Axe Core API\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    axe_config={\\\"rules\\\": {\\\"html-has-lang\\\": {\\\"enabled\\\": False}}},\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h3\", null, \"Engine Logging\"), mdx(\"p\", null, \"Set level of messages the Evinced engine will print to the console.\\nValid levels are \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"debug\\\"\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"info\\\"\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"warn\\\"\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"error\\\"\"), \".\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"error\\\"\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(logging={\\\"loggingLevel\\\": \\\"debug\\\"})\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h3\", null, \"Reports Screenshots\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \", the Evinced SDK will include screenshots in its reports that\\nhighlight elements with accessibility issues.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \".\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Note: Enabling screenshots may affect test run performance.\")), mdx(\"p\", null, mdx(\"img\", {\n    parentName: \"p\",\n    \"src\": \"../../images/entries/web-sdks/screenshots_feature.png\",\n    \"alt\": \"screenshot of a demo.evinced.com page with an issue highlighted\"\n  })), mdx(\"p\", null, \"Screenshots are \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"disabled by default\"), \" because capturing and embedding element images increases test runtime and report size. Enable them when visual context in HTML or SARIF reports is worth the overhead - for example when triaging issues offline or sharing reports with designers.\"), mdx(\"p\", null, \"The SDK supports three screenshot modes via the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"screenshot_mode\"), \" option (the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScreenshotMode\"), \" enum, or the equivalent string):\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Mode\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Description\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ScreenshotMode.PAGE\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"In-page capture via html2canvas (default when screenshots are enabled)\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ScreenshotMode.SDK\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Driver-side capture via Chrome DevTools (\", mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"Page.captureScreenshot\"), \") - pixel-true, \", mdx(\"strong\", {\n    parentName: \"td\"\n  }, \"Chromium-only\"))), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"ScreenshotMode.DISABLED\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Screenshots off (default)\")))), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScreenshotMode.SDK\"), \" is recommended when you need pixel-accurate images: it uses the real browser compositor, avoiding the html2canvas artifacts that can appear on overlays or fixed elements. It is \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Chromium-only\"), \" - on other browsers the SDK logs a warning and falls back to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PAGE\"), \" (html2canvas).\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver, ScreenshotMode\\nfrom selenium import webdriver\\n\\n# Recommended: driver-side (CDP) capture, pixel-true\\nconfig = EvincedConfig(screenshot_mode=ScreenshotMode.SDK)   # or screenshot_mode=\\\"sdk\\\"\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\\n# Per-call - screenshots for one analyze only\\nreport = driver.ev_analyze(EvincedConfig(screenshot_mode=\\\"page\\\"))\\n\")), mdx(\"p\", null, \"Tune SDK-side capture with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"screenshot_config\"), \" (all optional): \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"quality\"), \" (JPEG 1\\u2013100, default 70), \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"timeout\"), \" (per-capture bound in ms, default 5000), \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"scale\"), \" (capture scale, default 1.0).\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"config = EvincedConfig(\\n    screenshot_mode=\\\"sdk\\\",\\n    screenshot_config={\\\"quality\\\": 70, \\\"timeout\\\": 5000, \\\"scale\\\": 1.0},\\n)\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Backward compatibility:\"), \" the flag options still work - \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"enable_screenshots=True\"), \" is equivalent to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScreenshotMode.PAGE\"), \", and adding \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sdk_side_screenshots=True\"), \" is equivalent to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScreenshotMode.SDK\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"# Legacy flags - still supported\\nEvincedConfig(enable_screenshots=True)                             # == ScreenshotMode.PAGE\\nEvincedConfig(enable_screenshots=True, sdk_side_screenshots=True)  # == ScreenshotMode.SDK\\n\")), mdx(\"h3\", null, \"Toggles\"), mdx(\"p\", null, \"Enables experimental features. Feature names and values may vary from release to release.\"), mdx(\"p\", null, \"Example:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    toggles={\\n        \\\"USE_AXE_NEEDS_REVIEW\\\": True,\\n        \\\"USE_AXE_BEST_PRACTICES\\\": True,\\n    },\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h3\", null, \"Skip Validations\"), mdx(\"p\", null, \"Sets validation types to be skipped for specified URL pattern and CSS selector.\\nIssue type IDs can be found by inspecting a JSON report as described in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../web-reports\"\n  }, \"Web Reports\"), \".\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" no validations skipped.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    skip_validations=[\\n        {\\n            \\\"selector\\\": \\\"test.1--selector\\\",\\n            \\\"urlRegex\\\": \\\"http://url.to.skip/path1\\\",\\n            \\\"validationTypes\\\": [\\\"NO_DESCRIPTIVE_TEXT\\\", \\\"NOT_FOCUSABLE\\\"],\\n        },\\n        {\\n            \\\"selector\\\": \\\"test.2--selector\\\",\\n            \\\"urlRegex\\\": \\\"http://url.to.skip/path2\\\",\\n            \\\"validationTypes\\\": [\\\"NOT_FOCUSABLE\\\", \\\"ONE_MORE_TYPE_TO_EXCLUDE\\\", \\\"NO_DESCRIPTIVE_TEXT\\\"],\\n        },\\n    ],\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"h4\", null, \"A selector matches elements, not subtrees\"), mdx(\"p\", null, \"Skip rules are evaluated against each reported element on its own, so a selector suppresses issues\\nonly on the elements it matches \\u2014 not on anything nested inside them. To cover a container \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"and\"), \" its\\ncontents, list both the container and its descendants:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-text\"\n  }, \"#parent, #parent *\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"#parent\"), \" alone suppresses only issues reported on \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"#parent\"), \" itself.\"), mdx(\"h3\", null, \"Recording Service\"), mdx(\"p\", null, \"This setting controls how frequently event-triggered functions are executed, helping to optimize performance and responsiveness. The option does not enable or disable the event-handling service itself but determines how frequently events are processed based on the selected mode.\"), mdx(\"p\", null, \"Available modes:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"debounce\"), \" - Delays execution until a set time has passed since the last event. Useful for actions triggered by continuous input, such as typing in a search box, to avoid excessive function calls.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"throttle\"), \" - Ensures execution at fixed intervals, regardless of event frequency. Ideal for scenarios like handling window resize events or scroll tracking, where limiting execution prevents performance degradation.\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default\"), \": Functions execute as triggering events occur.\"), mdx(\"p\", null, \"An example of how to modify settings:\\nThe config keys map to the modes above: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DELAY_MODE\"), \" selects \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"debounce\\\"\"), \" or\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"throttle\\\"\"), \", and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ENABLE_DEBOUNCE_NEW_SELECTORS\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DEBOUNCE_NEW_SELECTORS_MS\"), \"\\ndebounce the analysis of newly appearing elements:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    recording_service={\\n        \\\"ENABLE_DEBOUNCE_NEW_SELECTORS\\\": True,\\n        \\\"DEBOUNCE_NEW_SELECTORS_MS\\\": 1000,\\n        \\\"DELAY_MODE\\\": \\\"throttle\\\",\\n    },\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"p\", null, \"These settings apply only during continuous mode (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \"); they have no effect on \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \". They tune scanning driven by the page's own \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"DOM changes\"), \"; scans triggered by your test's \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"interactions\"), \" are tuned in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#settledanalysis\"\n  }, \"Settled Analysis\"), \".\"), mdx(\"h3\", null, \"Settled Analysis\"), mdx(\"p\", null, \"Settled analysis is built into continuous mode: after each interaction (a click,\\na key press), the engine waits for the page to settle and analyzes the newly\\nrevealed content - dropdowns, modals, dynamically loaded sections. It is always\\non during \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \" sessions and needs no configuration; rapid\\ninteractions are coalesced automatically (at most one settled scan per 300 ms).\"), mdx(\"p\", null, \"Two options bound how long each scan waits for the page to settle. The wait\\nholds the driver's command lock, so it can delay your test's next command by up\\nto the timeout - lower it if your app is sensitive to that (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"0\"), \" disables the\\nwait):\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Option\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Type\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Default\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Description\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"settle_wait_timeout_ms\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"int\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"1500\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Maximum time to wait for the page to settle before analyzing\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"settle_wait_poll_ms\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"int\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"50\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"How often the page state is re-checked while waiting\")))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(settle_wait_timeout_ms=500)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"p\", null, \"Settled analysis reacts to your test's \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"interactions\"), \"; scanning driven by the page's own \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"DOM changes\"), \" is tuned separately - see \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#recordingservice\"\n  }, \"Recording Service\"), \".\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Single-page applications.\"), \" SPA flows are covered by the same mechanism:\\nroute changes triggered by your test's interactions are analyzed like any other\\ninteraction, with no extra configuration. Content that appears without any\\ninteraction (for example a timer-driven banner) is not captured automatically -\\ncall \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" once that state is on screen, or interact with the page to\\ntrigger a scan.\"), mdx(\"h3\", null, \"Shadow DOM Support\"), mdx(\"p\", null, \"Shadow DOM is now supported by default. No additional configuration is needed.\"), mdx(\"h3\", null, \"IFrames Support\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \", accessibility analysis includes iframe that exist inside the page.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(include_iframes=False)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"p\", null, \"Visible iframes are analyzed when \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_iframes=True\"), \" (default). Hidden iframes are skipped unless their domain is listed in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_hidden_iframe_domains\"), \" - see \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Include Hidden IFrames Domain\"), \" below.\"), mdx(\"h3\", null, \"Include Hidden IFrames Domain\"), mdx(\"p\", null, \"Configures which hidden iframe domains are included in accessibility analysis.\\nBy default, hidden iframes are excluded for performance and security reasons.\"), mdx(\"p\", null, \"This setting allows you to specify a list of domains to analyze even when the iframe is hidden.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    include_iframes=True,\\n    include_hidden_iframe_domains=[\\\"example.com\\\", \\\"auth.example.com\\\"],\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\")), mdx(\"p\", null, \"Hidden iframes (for example SSO \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"checksession\"), \" frames or zero-size tracking widgets) are \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"skipped by default\"), \". List a domain here to include its hidden iframes in analysis.\"), mdx(\"h3\", null, \"Passed Validations\"), mdx(\"p\", null, \"By default, the Evinced SDK only reports accessibility issues that have failed validation. However, you can also configure the SDK to include \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"passed validations\"), \" in your reports. Passed validations represent accessibility checks that were successfully completed without any issues found.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Why include passed validations?\")), mdx(\"p\", null, \"Including passed validations in your reports provides several benefits:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Comprehensive Coverage\"), \": Get a complete picture of all accessibility checks performed, not just the failures\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Compliance Documentation\"), \": Demonstrate which accessibility standards your application successfully meets\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Trend Analysis\"), \": Track improvements over time by monitoring both failed and passed validation counts\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Quality Assurance\"), \": Verify that accessibility checks are running as expected across your entire application\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Regulatory Reporting\"), \": Provide evidence of accessibility testing coverage for compliance audits\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Report Structure\")), mdx(\"p\", null, \"When passed validations are enabled, your reports will include both:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"failedValidations\"), \": Array of accessibility issues that need to be fixed\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"passedValidations\"), \": Array of accessibility checks that passed successfully\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \" (passed validations are not included)\"), mdx(\"h4\", null, \"Enable Passed Validations\"), mdx(\"p\", null, \"To include passed validations in your reports, set \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_passed_validations\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"True\"), \".\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note.\"), \" Enabling passed validations noticeably increases analysis and reporting overhead - the report carries every check that ran, not only the failures.\"), mdx(\"h4\", null, \"Single Page Analysis with Passed Validations\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver, Report, SaveFileFormat\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(include_passed_validations=True)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\ndriver.get(\\\"https://demo.evinced.com\\\")\\n\\nreport: Report = driver.ev_analyze()\\nfailed = report.get_failed_validations()\\npassed = report.get_passed_validations()\\n\\ndriver.ev_save_file(report, \\\"complete-report.json\\\", SaveFileFormat.JSON)\\n\")), mdx(\"h4\", null, \"Continuous Analysis with Passed Validations\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver, Report\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(include_passed_validations=True)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\\ndriver.ev_start(config)\\ndriver.get(\\\"https://demo.evinced.com\\\")\\ndriver.find_element(\\\"css selector\\\", \\\".dropdown\\\").click()\\n\\nreport: Report = driver.ev_stop()\\nfailed = report.get_failed_validations()\\npassed = report.get_passed_validations()\\n\")), mdx(\"p\", null, \"The option can also be set per call, like any other configuration option:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"report = driver.ev_analyze(EvincedConfig(include_passed_validations=True))\\n\")), mdx(\"h3\", null, \"Proxy\"), mdx(\"p\", null, \"Configures proxy server access settings. Needed to enable outbound communication to the\\nEvinced Platform through a proxy server.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Routing browser traffic\"), \" - configure Selenium's own \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Proxy\"), \" object. This\\naffects the pages the browser loads, not the SDK:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium import webdriver\\nfrom selenium.webdriver.common.proxy import Proxy\\nfrom selenium.webdriver.chrome.options import Options\\nfrom evinced_selenium_sdk import EvincedWebDriver\\n\\nproxy_string = \\\"proxy.example.com:8080\\\"\\n# Or with credentials: proxy_string = \\\"user:password@proxy.example.com:8080\\\"\\n\\nproxy = Proxy()\\nproxy.http_proxy = proxy_string\\nproxy.ssl_proxy = proxy_string\\n\\noptions = Options()\\noptions.proxy = proxy\\ndriver = EvincedWebDriver(webdriver.Chrome(options=options))\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Routing the SDK's own requests\"), \" (authentication and platform upload) - a\\nseparate mechanism: the SDK honors the standard proxy environment variables\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HTTP_PROXY\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HTTPS_PROXY\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"NO_PROXY\"), \", and optional \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PROXY_USERNAME\"), \" /\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PROXY_PASSWORD\"), \". \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"NO_PROXY\"), \" supports wildcard and suffix patterns (e.g.\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*.internal\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".example.com\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"<local>\"), \"), and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"socks5://\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"socks4://\"), \" proxy\\nURLs are supported.\"), mdx(\"h3\", null, \"Network Idle Detection\"), mdx(\"p\", null, \"Network-idle gating waits for network activity to settle before analysis, so the scan runs on a stable page with dynamic content loaded. It is \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"opt-in and requires the WebDriver BiDi transport\"), \" (\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"continuous_transport=\\\"bidi\\\"\"), \" on a driver that advertises \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"webSocketUrl\"), \"); on the interaction (click) transport it is a no-op.\"), mdx(\"p\", null, \"Configure it with the nested \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"network_idle\"), \" option:\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Key\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Type\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Default\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Description\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"enabled\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"bool\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"False\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Enable network-idle gating\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"timeout\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"int\"), \" (ms)\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"300\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Quiet window - idle means 0 in-flight requests for this long\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"maxWait\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"int\"), \" (ms)\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"7500\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Hard cap; proceed anyway after this even if the network never quiets\")))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(\\n    continuous_transport=\\\"bidi\\\",\\n    network_idle={\\\"enabled\\\": True, \\\"timeout\\\": 300, \\\"maxWait\\\": 7500},\\n)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\n\\n# Per-call override for one analysis (e.g. a slower API)\\nreport = driver.ev_analyze(\\n    EvincedConfig(network_idle={\\\"enabled\\\": True, \\\"maxWait\\\": 10000})\\n)\\n\")), mdx(\"p\", null, \"The wait applies before one-shot \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \" and before each continuous-mode scan. A page that never quiets (persistent polling, WebSocket, SSE) proceeds after \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"maxWait\"), \" with a logged warning - it never hangs.\"), mdx(\"h3\", null, \"Global Switch\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \", disables Evinced functionality. Enabled by default, use this setting to disable\\nEvinced accessibility analysis when not needed during test development or when running CI jobs\\nwhere accessibility testing is not intended.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Default:\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \".\"), mdx(\"p\", null, \"When switched off:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_start()\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_save_file()\"), \" will be bypassed.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_stop()\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_analyze()\"), \" will return an empty report.\")), mdx(\"h4\", null, \"Switching Evinced Functionality Off in Configuration\"), mdx(\"p\", null, \"Disable all Evinced analysis at runtime - useful during local development when you want to run functional tests without accessibility overhead, or in CI jobs that skip a11y checks.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import evinced_disable, evinced_enable\\n\\nevinced_disable()\\n\\n# ev_start is skipped; ev_analyze returns an empty Report\\nreport = driver.ev_analyze()\\nassert report.get_failed_validations() == []\\n\\n# Re-enable for the next test\\nevinced_enable()\\ndriver.ev_analyze()  # runs normally again\\n\")), mdx(\"p\", null, \"If you call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced_disable()\"), \" \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"after\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_start()\"), \", you must still call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" to finalize the session - the switch gates new work, not teardown of an active session.\"), mdx(\"h4\", null, \"Switching Evinced Functionality Off in Environment\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Canonical cross-SDK variable\\nexport EV_SWITCH_ON=false\\n\\n# Python-only alias - takes precedence over EV_SWITCH_ON\\nexport EVINCED_DISABLED=true\\n\")), mdx(\"p\", null, \"Note the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"opposite polarity\"), \": disabling is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EV_SWITCH_ON=false\"), \" but \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EVINCED_DISABLED=true\"), \". Setting the wrong value to the wrong variable (for example \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EVINCED_DISABLED=false\"), \" intending to disable) silently does nothing - no error or warning is logged.\"), mdx(\"p\", null, \"The variables are read once, the first time the SDK checks the switch, and cached for the rest of the process - set them before your first use of the SDK. Programmatic \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced_disable()\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"evinced_enable()\"), \" calls take precedence from the moment they run, including over the environment.\"), mdx(\"h3\", null, \"Alerts\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \", Evinced expects alerts to be displayed at any time, after any action, and will wait for them to be closed.\\nWhen \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \", Evinced will not wait for alerts to be closed and will continue the analysis.\"), mdx(\"p\", null, \"Default: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \".\"), mdx(\"p\", null, \"With the default \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"alert_expected=False\"), \", the SDK's background analysis that\\nruns after each interaction can dismiss an alert before your test handles it -\\nthe alert simply disappears. Set \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"alert_expected=True\"), \" when your flow triggers\\nalerts: the SDK then leaves them alone (analysis waits until the alert is\\nclosed) and your test handles them as usual with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"driver.switch_to.alert\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\n\\nconfig = EvincedConfig(alert_expected=True)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\ndriver.ev_start()\\n\")), mdx(\"h2\", null, \"Uploading Reports to Evinced Platform\"), mdx(\"h3\", null, \"Introduction\"), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://platform.evinced.com/web-sdk\"\n  }, \"Evinced Platform\"), \" allows you to seamlessly collect, organize, visualize and\\nmonitor Evinced accessibility reports in one place. In this section, we will\\nguide you through the key functionalities of the upload methods of the\\naccessibility reports from the Evinced SDK to the Evinced Platform, which was\\nintroduced in version 0.1.0. This\\nupload method is fully compatible with the previous versions of the Evinced SDK\\nAPI, and is disabled by default.\"), mdx(\"h3\", null, \"Enable Upload Report to Platform\"), mdx(\"p\", null, \"Check which SDK version you have installed with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"python -m pip show evinced-selenium-sdk\"), \".\"), mdx(\"p\", null, \"Call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_upload_to_platform_config\"), \" \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"before\"), \" creating your \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EvincedWebDriver\"), \", typically in a session-scoped pytest fixture or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"conftest.py\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import set_upload_to_platform_config\\n\\nset_upload_to_platform_config(enableUploadToPlatform=True)\\n\")), mdx(\"p\", null, \"Without this master switch, no upload happens regardless of per-call flags.\"), mdx(\"p\", null, \"Upload is skipped when there are \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"no failed validations\"), \", even if passed validations were collected.\"), mdx(\"h3\", null, \"Automatic Report Upload\"), mdx(\"p\", null, \"Once the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"enableUploadToPlatform\"), \" method is set to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setUploadToPlatformDefault\"), \" is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \" (which is the default),\\nall generated reports will be \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"automatically uploaded\"), \" to the Platform immediately upon calling the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" command.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"How it works:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"enableUploadToPlatform: true\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"setUploadToPlatformDefault\"), \": true (default), upload happens automatically\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"No additional code is needed - just call \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_analyze()\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ev_stop()\"), \" and the report uploads\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Upload occurs synchronously as part of the command execution\")), mdx(\"p\", null, \"If you want to change this behavior and control uploads manually, set the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setUploadToPlatformDefault\"), \" feature flag to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"false\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import set_upload_to_platform_config\\n\\nset_upload_to_platform_config(setUploadToPlatformDefault=False)\\n\")), mdx(\"p\", null, \"If the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setUploadToPlatformDefault\"), \" is disabled, you can still upload\\nselected reports to the platform.\\nFor that, use the following parameter in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" command:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.ev_stop(upload_to_platform=True)\\n\")), mdx(\"p\", null, \"Or, in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze()\"), \" command:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.ev_analyze(upload_to_platform=True)\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"upload_to_platform\"), \" has three states: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"None\"), \" (the default) follows the global upload configuration described above, while \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"True\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"False\"), \" force uploading (or not uploading) that report regardless of it.\"), mdx(\"h3\", null, \"Test Names\"), mdx(\"p\", null, \"To facilitate report management and be able to distinguish between different reports on the Platform, use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"set_test_info\"), \" method to inform the test name and test class.\\nIt\\u2019s recommended to do that in a per-test pytest fixture.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.set_test_info(\\\"test_method_name\\\", \\\"tests/test_module.py\\\")\\n\")), mdx(\"h3\", null, \"Labels and Custom Fields\"), mdx(\"p\", null, \"You can attach labels and custom fields to your report to enhance readability and organization in the platform.\\nLabels help you filter, search, and organize reports on the Evinced Platform.\"), mdx(\"p\", null, \"There are two types of labels:\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Built-in Labels\"), \": Pre-defined labels that can be set using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"add_label\"), \" method. Available built-in labels include:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"testName\"), \" - The name of the test\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"testFile\"), \" - The file path of the test\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"environment\"), \" - The environment where the test runs (e.g., \\\"Development\\\", \\\"Staging\\\", \\\"Production\\\")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"flow\"), \" - The test flow identifier\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"gitBranch\"), \" - The Git branch name\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"gitUserName\"), \" - The Git user name\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"gitVersion\"), \" - The Git commit version\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Custom Labels\"), \": Flexible key-value pairs that can be set using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"custom_label\"), \" method. You can use any custom key-value pairs, including:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Single values: \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"{ productVersion: '1.0.0' }\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Multiple values (arrays): \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"{ browsers: ['Chrome', 'Firefox'] }\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Special label \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"unitId\"), \": Use this to tag tests for relevant units within your organization\")), mdx(\"p\", null, \"See the following code examples of how to set up labels:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.test_run_info.add_label(environment=\\\"production\\\", flow=\\\"standard\\\")\\ndriver.test_run_info.custom_label(\\n    Product_version=\\\"1.2.3\\\",\\n    OS_Type=\\\"Linux\\\",\\n    suite=[\\\"smoke\\\", \\\"regression\\\"],   # a list adds one label per value\\n)\\n\")), mdx(\"p\", null, \"To set labels once for \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"every\"), \" driver created afterwards (e.g. in a session fixture), use the process-global run info:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import get_test_run_info\\n\\nget_test_run_info().custom_label(team=\\\"a11y\\\").add_label(environment=\\\"staging\\\")\\n\")), mdx(\"p\", null, \"The built-in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"gitUserName\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"gitBranch\"), \" / \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"gitVersion\"), \" labels are \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"not collected automatically\"), \" - set them explicitly when you want them, for example from CI environment variables:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"import os\\n\\ndriver.test_run_info.add_label(\\n    gitBranch=os.getenv(\\\"GIT_BRANCH\\\", \\\"\\\"),\\n    gitVersion=os.getenv(\\\"GIT_COMMIT\\\", \\\"\\\"),\\n)\\n\")), mdx(\"h3\", null, \"Per-test Setup and Teardown\"), mdx(\"p\", null, \"Run each test in its own analysis session, so every test uploads its own\\nreport: start the session and set labels before the test, stop the session\\n(which uploads) after it. With pytest, that is an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"autouse\"), \" fixture:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"@pytest.fixture(autouse=True)\\ndef evinced_continuous(driver, request):\\n    driver.set_test_info(request.node.name, str(request.fspath))\\n    driver.ev_start()\\n    yield\\n    driver.ev_stop()\\n\")), mdx(\"h3\", null, \"Putting All of This Together\"), mdx(\"p\", null, \"Here is a complete code snippet of how to perform uploads to the platform on a per-test basis.\"), mdx(\"p\", null, \"pytest \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"conftest.py\"), \" example with platform upload:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"import os\\nimport pytest\\nfrom selenium import webdriver\\nfrom evinced_selenium_sdk import EvincedWebDriver, set_offline_credentials, set_upload_to_platform_config\\n\\n\\n@pytest.fixture(scope=\\\"session\\\", autouse=True)\\ndef evinced_credentials():\\n    set_offline_credentials(\\n        service_id=os.environ[\\\"AUTH_SERVICE_ID\\\"],\\n        token=os.environ[\\\"AUTH_TOKEN\\\"],\\n    )\\n    set_upload_to_platform_config(enableUploadToPlatform=True)\\n\\n\\n@pytest.fixture(scope=\\\"module\\\")\\ndef driver():\\n    d = EvincedWebDriver(webdriver.Chrome())\\n    d.test_run_info.add_label(gitUserName=\\\"git\\\", gitBranch=\\\"main\\\")\\n    d.test_run_info.custom_label(Testing_purpose=\\\"Test platform uploading feature\\\")\\n    yield d\\n    d.quit()\\n\\n\\n@pytest.fixture(autouse=True)\\ndef evinced_session(driver, request):\\n    driver.set_test_info(request.node.name, str(request.fspath))\\n    driver.ev_start()\\n    yield\\n    driver.ev_stop()\\n\\n\\ndef test_demo_home(driver):\\n    driver.get(\\\"https://demo.evinced.com\\\")\\n\")), mdx(\"h3\", null, \"Accessing Upload Information\"), mdx(\"p\", null, \"After a successful upload, retrieve the test ID and platform URL from the driver's run metadata:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"test_id = driver.test_run_info.get_test_id()\\nplatform_url = driver.test_run_info.get_upload_test_url()\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Complete example:\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"report = driver.ev_analyze(upload_to_platform=True)\\nif report.get_failed_validations():\\n    print(f\\\"Upload test ID: {driver.test_run_info.get_test_id()}\\\")\\n    print(f\\\"Platform URL: {driver.test_run_info.get_upload_test_url()}\\\")\\n\")), mdx(\"p\", null, \"Upload runs only when platform upload is enabled, the page is not blank, and there is at least one \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"failed\"), \" validation.\"), mdx(\"h2\", null, \"Tutorials\"), mdx(\"p\", null, \"You can find fully functional \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/GetEvinced/web-sdks-examples-pub\"\n  }, \"example projects\"), \" on our GitHub.\"), mdx(\"h3\", null, \"Generating a comprehensive accessibility report for your application\"), mdx(\"p\", null, \"In this tutorial, we enhance an existing Selenium UI test with the Evinced Selenium Python SDK to check an application for accessibility issues. Prerequisites:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"All prerequisites for the Evinced Selenium Python SDK are met\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"evinced-selenium-sdk\"), \" is installed in your project\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Credentials are configured once in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"conftest.py\"), \", as shown in\\n\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"#authentication\"\n  }, \"Authentication\"), \" - none of the test code below mentions them\")), mdx(\"h4\", null, \"Preface - existing UI test overview\"), mdx(\"p\", null, \"Starting point - a functional UI test:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium import webdriver\\nfrom selenium.webdriver.common.by import By\\n\\n\\ndef test_trvl_filters():\\n    driver = webdriver.Chrome()\\n    try:\\n        driver.get(\\\"https://demo.evinced.com/\\\")\\n        driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(1) > div > div.dropdown.line\\\").click()\\n        driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(2) > div > div.dropdown.line\\\").click()\\n        driver.find_element(By.CSS_SELECTOR, \\\".react-date-picker\\\").click()\\n    finally:\\n        driver.quit()\\n\")), mdx(\"p\", null, \"We use the demo travel site \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://demo.evinced.com/\"\n  }, \"TRVL\"), \" with known accessibility issues.\"), mdx(\"h4\", null, \"Step 1 - Initialize EvincedWebDriver\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"import pytest\\nfrom selenium import webdriver\\nfrom selenium.webdriver.common.by import By\\nfrom evinced_selenium_sdk import EvincedWebDriver, SaveFileFormat\\n\\n\\n@pytest.fixture\\ndef driver():\\n    d = EvincedWebDriver(webdriver.Chrome())\\n    yield d\\n    d.quit()\\n\\n\\ndef test_trvl_filters(driver):\\n    driver.get(\\\"https://demo.evinced.com/\\\")\\n    driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(1) > div > div.dropdown.line\\\").click()\\n    driver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(2) > div > div.dropdown.line\\\").click()\\n    driver.find_element(By.CSS_SELECTOR, \\\".react-date-picker\\\").click()\\n\")), mdx(\"h4\", null, \"Step 2 - Start the Evinced engine\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"@pytest.fixture\\ndef driver():\\n    d = EvincedWebDriver(webdriver.Chrome())\\n    d.ev_start()\\n    yield d\\n    d.quit()\\n\")), mdx(\"h4\", null, \"Step 3 - Stop the engine and save reports\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"@pytest.fixture\\ndef driver(request):\\n    d = EvincedWebDriver(webdriver.Chrome())\\n    d.ev_start()\\n    yield d\\n    report = d.ev_stop()\\n    d.ev_save_file(report, f\\\"{request.node.name}.html\\\", SaveFileFormat.HTML)\\n    d.ev_save_file(report, f\\\"{request.node.name}.json\\\", SaveFileFormat.JSON)\\n    assert len(report.get_failed_validations()) == 0  # optional gating assertion\\n    d.quit()\\n\")), mdx(\"p\", null, \"Run the test with pytest. Additional configuration options are documented in the API section.\"), mdx(\"h3\", null, \"Additional Configuration Examples\"), mdx(\"h4\", null, \"Testing accessibility in a specific state of the application\"), mdx(\"p\", null, \"Open UI state first, then analyze:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium import webdriver\\nfrom selenium.webdriver.common.by import By\\nfrom evinced_selenium_sdk import EvincedWebDriver, SaveFileFormat\\n\\ndriver = EvincedWebDriver(webdriver.Chrome())\\ndriver.get(\\\"https://demo.evinced.com/\\\")\\ndriver.find_element(By.CSS_SELECTOR, \\\"div.filter-container > div:nth-child(1) > div > div.dropdown.line\\\").click()\\nreport = driver.ev_analyze()\\nassert len(report.get_failed_validations()) >= 1\\ndriver.ev_save_file(report, \\\"test-results.html\\\", SaveFileFormat.HTML)\\n\")), mdx(\"h4\", null, \"Running ev_analyze on multiple tabs\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_analyze\"), \" runs on the current window. Switch handles before analyzing a new tab:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium.webdriver.common.by import By\\n\\ndriver.get(\\\"page1.html\\\")\\nreport = driver.ev_analyze()\\nassert len(report.get_failed_validations()) >= 1\\n\\ndriver.find_element(By.ID, \\\"open-tab-link\\\").click()\\nhandles = driver.window_handles\\ndriver.switch_to.window(handles[1])\\n\\nreport = driver.ev_analyze()\\nassert len(report.get_failed_validations()) >= 1\\n\")), mdx(\"h4\", null, \"Running ev_start and ev_stop on multiple tabs\"), mdx(\"p\", null, \"In continuous mode, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"driver.switch_to.window(...)\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"driver.switch_to.new_window(...)\"), \" flushes the current slice and restarts analysis on the destination window. You do \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"not\"), \" need to call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \" before switching - a single \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop()\"), \" at the end can include issues from every window visited during the session:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from selenium.webdriver.common.by import By\\n\\ndriver.get(\\\"page1.html\\\")\\ndriver.ev_start()\\n# interact on page1...\\ndriver.find_element(By.ID, \\\"open-tab-link\\\").click()\\ndriver.switch_to.window(driver.window_handles[1])\\n# interact on the popup tab...\\ndriver.switch_to.window(driver.window_handles[0])  # optional: return to the original window\\nreport = driver.ev_stop()\\n\")), mdx(\"p\", null, \"Closing a popup with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"driver.close()\"), \" keeps the session alive; only closing the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"last\"), \" remaining window tears it down. Issues not yet flushed on the closed window are dropped (the SDK logs a warning) - switch away from the popup or interact on it before closing if you need its final state analyzed.\"), mdx(\"h4\", null, \"Iframes and continuous mode\"), mdx(\"p\", null, \"Enable iframe analysis with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"include_iframes=True\"), \" (default). The engine always runs in the main frame. if your test is inside an iframe when a slice fires, the SDK restores your frame context afterward - you typically do not need to match iframe context before \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ev_stop\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"from evinced_selenium_sdk import EvincedConfig, EvincedWebDriver\\nfrom selenium import webdriver\\nfrom selenium.webdriver.common.by import By\\n\\nconfig = EvincedConfig(include_iframes=True)\\ndriver = EvincedWebDriver(webdriver.Chrome(), config)\\ndriver.get(\\\"page1.html\\\")\\ndriver.switch_to.frame(\\\"iframe1\\\")\\ndriver.ev_start()\\n# interact inside iframe...\\nreport = driver.ev_stop()\\n\")), mdx(\"h3\", null, \"Fail the test if critical issues are found\"), mdx(\"h4\", null, \"Using ev_analyze\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"report = driver.ev_analyze()\\ncritical = [\\n    i for i in report.get_failed_validations()\\n    if (i.get(\\\"severity\\\") or {}).get(\\\"id\\\") == \\\"CRITICAL\\\"\\n]\\nassert not critical, \\\"Critical accessibility issues found\\\"\\n\")), mdx(\"h4\", null, \"Using ev_start / ev_stop\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-python\"\n  }, \"driver.ev_start()\\n# ... test steps ...\\nreport = driver.ev_stop()\\ncritical = [\\n    i for i in report.get_failed_validations()\\n    if (i.get(\\\"severity\\\") or {}).get(\\\"id\\\") == \\\"CRITICAL\\\"\\n]\\nassert not critical, \\\"Critical accessibility issues found\\\"\\n\")), mdx(\"h2\", null, \"Support\"), mdx(\"p\", null, \"Please feel free to reach out to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"mailto:support@evinced.com\"\n  }, \"support@evinced.com\"), \" with any questions.\"), mdx(\"h2\", null, \"FAQ\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Can I configure which validations to run?\"))), mdx(\"p\", null, \"Yes, see the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"#configuration\"\n  }, \"Configuration\"), \" section for details on how to configure Axe validations to your needs.\"), mdx(\"ol\", {\n    \"start\": 2\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Can I run tests with Evinced using cloud-based services like Sauce Labs, Perfecto, or BrowserStack?\"))), mdx(\"p\", null, \"Yes, we have tested the Evinced SDK on many of these types of cloud-based services and expect no issues.\"), mdx(\"p\", null, \"\\xA0\"));\n}\n;\nMDXContent.isMDXComponent = true;","tableOfContents":{"items":[{"url":"#prerequisites","title":"Prerequisites"},{"url":"#get-started","title":"Get started","items":[{"url":"#installation","title":"Installation","items":[{"url":"#adding-evinced-to-an-existing-python-project","title":"Adding Evinced to an existing Python project"},{"url":"#installation-with-a-locally-provided-file","title":"Installation with a locally provided file"},{"url":"#installation-from-a-remote-repository","title":"Installation from a remote repository"}]},{"url":"#ai-skills","title":"AI Skills","items":[{"url":"#installing-the-skills","title":"Installing the skills"},{"url":"#manual-installation","title":"Manual installation"},{"url":"#what-the-skills-do","title":"What the skills do"}]},{"url":"#authentication","title":"Authentication","items":[{"url":"#setting-credentials-an-example","title":"Setting credentials, an example:"}]},{"url":"#your-first-test","title":"Your First Test","items":[{"url":"#sdk-initialization","title":"SDK Initialization"},{"url":"#add-evinced-accessibility-checks-single-run-mode","title":"Add Evinced Accessibility Checks (Single Run Mode)"},{"url":"#add-evinced-accessibility-checks-continuous-mode","title":"Add Evinced Accessibility Checks (Continuous Mode)"}]}]},{"url":"#api","title":"API","items":[{"url":"#evincedwebdriverdriver-config","title":"EvincedWebDriver(driver, config)"},{"url":"#ev_analyzeconfig-upload_to_platformnone","title":"ev_analyze(config, upload_to_platform=None)"},{"url":"#ev_startconfig","title":"ev_start(config)"},{"url":"#ev_stopupload_to_platformnone","title":"ev_stop(upload_to_platform=None)"},{"url":"#ev_save_filereport-destination-file_format","title":"ev_save_file(report, destination, file_format)","items":[{"url":"#save-a-single-report","title":"Save a single report"},{"url":"#save-an-aggregated-run-report","title":"Save an aggregated run report"},{"url":"#savefileformat","title":"SaveFileFormat"}]}]},{"url":"#aggregated-report","title":"Aggregated Report"},{"url":"#configuration","title":"Configuration","items":[{"url":"#engines-configuration","title":"Engines Configuration"},{"url":"#configuration-object","title":"Configuration Object","items":[{"url":"#per-driver-configuration","title":"Per-driver configuration"},{"url":"#per-call-override","title":"Per-call override"}]},{"url":"#root-selector","title":"Root Selector"},{"url":"#axe-configuration","title":"Axe Configuration"},{"url":"#engine-logging","title":"Engine Logging"},{"url":"#reports-screenshots","title":"Reports Screenshots"},{"url":"#toggles","title":"Toggles"},{"url":"#skip-validations","title":"Skip Validations","items":[{"url":"#a-selector-matches-elements-not-subtrees","title":"A selector matches elements, not subtrees"}]},{"url":"#recording-service","title":"Recording Service"},{"url":"#settled-analysis","title":"Settled Analysis"},{"url":"#shadow-dom-support","title":"Shadow DOM Support"},{"url":"#iframes-support","title":"IFrames Support"},{"url":"#include-hidden-iframes-domain","title":"Include Hidden IFrames Domain"},{"url":"#passed-validations","title":"Passed Validations","items":[{"url":"#enable-passed-validations","title":"Enable Passed Validations"},{"url":"#single-page-analysis-with-passed-validations","title":"Single Page Analysis with Passed Validations"},{"url":"#continuous-analysis-with-passed-validations","title":"Continuous Analysis with Passed Validations"}]},{"url":"#proxy","title":"Proxy"},{"url":"#network-idle-detection","title":"Network Idle Detection"},{"url":"#global-switch","title":"Global Switch","items":[{"url":"#switching-evinced-functionality-off-in-configuration","title":"Switching Evinced Functionality Off in Configuration"},{"url":"#switching-evinced-functionality-off-in-environment","title":"Switching Evinced Functionality Off in Environment"}]},{"url":"#alerts","title":"Alerts"}]},{"url":"#uploading-reports-to-evinced-platform","title":"Uploading Reports to Evinced Platform","items":[{"url":"#introduction","title":"Introduction"},{"url":"#enable-upload-report-to-platform","title":"Enable Upload Report to Platform"},{"url":"#automatic-report-upload","title":"Automatic Report Upload"},{"url":"#test-names","title":"Test Names"},{"url":"#labels-and-custom-fields","title":"Labels and Custom Fields"},{"url":"#per-test-setup-and-teardown","title":"Per-test Setup and Teardown"},{"url":"#putting-all-of-this-together","title":"Putting All of This Together"},{"url":"#accessing-upload-information","title":"Accessing Upload Information"}]},{"url":"#tutorials","title":"Tutorials","items":[{"url":"#generating-a-comprehensive-accessibility-report-for-your-application","title":"Generating a comprehensive accessibility report for your application","items":[{"url":"#preface---existing-ui-test-overview","title":"Preface - existing UI test overview"},{"url":"#step-1---initialize-evincedwebdriver","title":"Step 1 - Initialize EvincedWebDriver"},{"url":"#step-2---start-the-evinced-engine","title":"Step 2 - Start the Evinced engine"},{"url":"#step-3---stop-the-engine-and-save-reports","title":"Step 3 - Stop the engine and save reports"}]},{"url":"#additional-configuration-examples","title":"Additional Configuration Examples","items":[{"url":"#testing-accessibility-in-a-specific-state-of-the-application","title":"Testing accessibility in a specific state of the application"},{"url":"#running-ev_analyze-on-multiple-tabs","title":"Running ev_analyze on multiple tabs"},{"url":"#running-ev_start-and-ev_stop-on-multiple-tabs","title":"Running ev_start and ev_stop on multiple tabs"},{"url":"#iframes-and-continuous-mode","title":"Iframes and continuous mode"}]},{"url":"#fail-the-test-if-critical-issues-are-found","title":"Fail the test if critical issues are found","items":[{"url":"#using-ev_analyze","title":"Using ev_analyze"},{"url":"#using-ev_start--ev_stop","title":"Using ev_start / ev_stop"}]}]},{"url":"#support","title":"Support"},{"url":"#faq","title":"FAQ"}]},"parent":{"__typename":"File","relativePath":"sdks-for-web-apps/selenium-python-sdk.md"},"frontmatter":{"metaTitle":"Selenium Python SDK Documentation","metaDescription":null,"index":8}},"allMdx":{"edges":[{"node":{"fields":{"slug":"/tbd","title":"In Writing Process"},"frontmatter":{"metaTitle":"Content in writing process","metaDescription":"","index":0}}},{"node":{"fields":{"slug":"/MCP-Servers","title":"MCP Servers"},"frontmatter":{"metaTitle":"Documentation for Evinced MCP Servers","metaDescription":null,"index":1}}},{"node":{"fields":{"slug":"/","title":"General Introduction"},"frontmatter":{"metaTitle":"Evinced Inc. Documentation for Developers","metaDescription":null,"index":0}}},{"node":{"fields":{"slug":"/MCP-Servers/release-notes","title":"Release Notes of MCP Servers"},"frontmatter":{"metaTitle":"MCP Servers Release Notes","metaDescription":null,"index":8}}},{"node":{"fields":{"slug":"/sdks-for-web-apps","title":"SDKs for Web apps"},"frontmatter":{"metaTitle":"Documentation for SDKs for Web apps","metaDescription":null,"index":1}}},{"node":{"fields":{"slug":"/MCP-Servers/intro","title":"Introduction to Evinced MCP Servers"},"frontmatter":{"metaTitle":"Introduction to Evinced MCP Servers Documentation","metaDescription":null,"index":0}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps","title":"SDKs for Mobile apps"},"frontmatter":{"metaTitle":"Documentation for SDKs for Mobile apps","metaDescription":null,"index":1}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/android-mobile-kit-doc","title":"Android Mobile Kit"},"frontmatter":{"metaTitle":"Android Mobile Kit Documentation","metaDescription":null,"index":7}}},{"node":{"fields":{"slug":"/MCP-Servers/mobile-mcp-server","title":"Mobile MCP Server"},"frontmatter":{"metaTitle":"Evinced Mobile MCP Server Documentation","metaDescription":null,"index":7}}},{"node":{"fields":{"slug":"/MCP-Servers/web-mcp-server","title":"Web MCP Server"},"frontmatter":{"metaTitle":"Evinced Web MCP Server Documentation","metaDescription":null,"index":6}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/mobile-reports","title":"Mobile Reports"},"frontmatter":{"metaTitle":"Mobile Reports","metaDescription":null,"index":1}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/intro","title":"Introduction"},"frontmatter":{"metaTitle":"Introduction to SDKs for Mobile apps","metaDescription":null,"index":0}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes","title":"Release Notes of Mobile SDKs"},"frontmatter":{"metaTitle":"Mobile SDKs Release Notes","metaDescription":null,"index":8}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/appium-sdk-python-doc","title":"Appium Python SDK"},"frontmatter":{"metaTitle":"Appium SDK Python Documentation","metaDescription":null,"index":3}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/intro","title":"Introduction"},"frontmatter":{"metaTitle":"Introduction to SDKs for Web apps","metaDescription":null,"index":0}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/cs-selenium-sdk","title":"Selenium C# SDK"},"frontmatter":{"metaTitle":"Selenium C# SDK Documentation","metaDescription":null,"index":5}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/playwright-java-sdk","title":"Playwright Java SDK"},"frontmatter":{"metaTitle":"Playwright Java SDK Documentation","metaDescription":null,"index":3}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes","title":"Release Notes of Web SDKs"},"frontmatter":{"metaTitle":"Web SDKs Release Notes","metaDescription":null,"index":999}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/cypress-sdk","title":"Cypress JS SDK"},"frontmatter":{"metaTitle":"Cypress JS SDK Documentation","metaDescription":null,"index":2}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/web-reports","title":"Web Reports"},"frontmatter":{"metaTitle":"Web Reports","metaDescription":null,"index":1}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/testcafe-js-sdk","title":"TestCafe JS SDK"},"frontmatter":{"metaTitle":"TestCafe JS SDK Documentation","metaDescription":null,"index":9}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/selenium-js-sdk","title":"Selenium JS SDK"},"frontmatter":{"metaTitle":"Selenium JS SDK Documentation","metaDescription":null,"index":7}}},{"node":{"fields":{"slug":"/MCP-Servers/release-notes/mobile-mcp-server","title":"Mobile MCP Server"},"frontmatter":{"metaTitle":"Mobile MCP Server Release Notes","metaDescription":null,"index":9}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/android-mobile-kit","title":"Android Mobile Kit"},"frontmatter":{"metaTitle":"Android Mobile Kit Release Notes","metaDescription":null,"index":13}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/selenium-python-sdk","title":"Selenium Python SDK"},"frontmatter":{"metaTitle":"Selenium Python SDK Documentation","metaDescription":null,"index":8}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/playwright-js-sdk","title":"Playwright JS SDK"},"frontmatter":{"metaTitle":"Playwright JS SDK Documentation","metaDescription":null,"index":4}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/appium-sdk-python","title":"Appium Python SDK"},"frontmatter":{"metaTitle":"Appium Python SDK Release Notes","metaDescription":null,"index":8}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/appium-sdk-java","title":"Appium Java SDK"},"frontmatter":{"metaTitle":"Appium Java SDK Release Notes","metaDescription":null,"index":7}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/espresso-sdk","title":"Espresso / UIAutomator SDK"},"frontmatter":{"metaTitle":"Espresso / UIAutomator SDK Release Notes","metaDescription":null,"index":10}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/wdio-mobile-sdk","title":"WebdriverIO SDK (Mobile)"},"frontmatter":{"metaTitle":"WebdriverIO SDK (Mobile) Release Notes","metaDescription":null,"index":11}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/release-notes/xcui-sdk","title":"XCUI SDK"},"frontmatter":{"metaTitle":"XCUI SDK Release Notes","metaDescription":null,"index":12}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/playwright-java-sdk","title":"Playwright Java SDK"},"frontmatter":{"metaTitle":"Playwright Java SDK Release Notes","metaDescription":null,"index":51}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/cypress-sdk","title":"Cypress SDK"},"frontmatter":{"metaTitle":"Cypress SDK Release Notes","metaDescription":null,"index":50}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/selenium-cs-sdk","title":"Selenium CS SDK"},"frontmatter":{"metaTitle":"Selenium CS SDK Release Notes","metaDescription":null,"index":53}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/selenium-python-sdk","title":"Selenium Python SDK"},"frontmatter":{"metaTitle":"Selenium Python SDK Release Notes","metaDescription":null,"index":49}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/webdriverio-sdk","title":"WebdriverIO JS SDK"},"frontmatter":{"metaTitle":"WebdriverIO JS SDK Documentation","metaDescription":null,"index":10}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/testcafe-js-sdk","title":"TestCafe JS SDK"},"frontmatter":{"metaTitle":"TestCafe JS SDK Release Notes","metaDescription":null,"index":56}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/selenium-js-sdk","title":"Selenium JS SDK"},"frontmatter":{"metaTitle":"Selenium JS SDK Release Notes","metaDescription":null,"index":55}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/selenium-java-sdk","title":"Selenium Java SDK"},"frontmatter":{"metaTitle":"Selenium Java SDK Release Notes","metaDescription":null,"index":54}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/playwright-js-sdk","title":"Playwright JS SDK"},"frontmatter":{"metaTitle":"Playwright JS SDK Release Notes","metaDescription":null,"index":52}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/unit-tester","title":"Unit Tester"},"frontmatter":{"metaTitle":"Unit Tester Release Notes","metaDescription":null,"index":58}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/release-notes/webdriverio-sdk","title":"WebdriverIO SDK"},"frontmatter":{"metaTitle":"WebdriverIO SDK Release Notes","metaDescription":null,"index":57}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/unit-tester/browser","title":"Unit Tester (browser)"},"frontmatter":{"metaTitle":"Unit Tester (browser) - Documentation","metaDescription":null,"index":11}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/xcui-sdk","title":"XCUI SDK"},"frontmatter":{"metaTitle":"XCUI SDK Documentation","metaDescription":null,"index":6}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/wdio-mobile-sdk","title":"WebdriverIO SDK (Mobile)"},"frontmatter":{"metaTitle":"WebdriverIO Mobile SDK Documentation","metaDescription":null,"index":5}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/selenium-java-sdk","title":"Selenium Java SDK"},"frontmatter":{"metaTitle":"Selenium Java SDK Documentation","metaDescription":null,"index":6}}},{"node":{"fields":{"slug":"/sdks-for-web-apps/unit-tester/main","title":"Unit Tester (Node)"},"frontmatter":{"metaTitle":"Unit Tester (Node) - API Documentation","metaDescription":null,"index":10}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/espresso-sdk","title":"Espresso / UIAutomator SDK"},"frontmatter":{"metaTitle":"Espresso / UIAutomator SDK Documentation","metaDescription":null,"index":4}}},{"node":{"fields":{"slug":"/sdks-for-mobile-apps/appium-sdk-java-doc","title":"Appium Java SDK"},"frontmatter":{"metaTitle":"Appium SDK Java Documentation","metaDescription":null,"index":2}}}]}},"pageContext":{"id":"04ffdc00-1dc2-5e1b-a497-5b5e8fe18c92"}},"staticQueryHashes":["1041749790","1041749790","3130641623","3130641623","3706406642","3706406642","417421954","417421954"]}