[
  {
    "path": ".azure-pipelines/guardian/SDL/.gdnsuppress",
    "content": "{\n  \"hydrated\": false,\n  \"properties\": {\n    \"helpUri\": \"https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/suppressions\",\n    \"hydrationStatus\": \"This file does not contain identifying data. It is safe to check into your repo. To hydrate this file with identifying data, run `guardian hydrate --help` and follow the guidance.\"\n  },\n  \"version\": \"1.0.0\",\n  \"suppressionSets\": {\n    \"default\": {\n      \"name\": \"default\",\n      \"createdDate\": \"2024-02-06 21:00:02Z\",\n      \"lastUpdatedDate\": \"2024-02-06 21:00:02Z\"\n    }\n  },\n  \"results\": {\n    \"bffa73d7410f5963f2538f06124ac5524c076da77867a0a19ccf60e508062dff\": {\n      \"signature\": \"bffa73d7410f5963f2538f06124ac5524c076da77867a0a19ccf60e508062dff\",\n      \"alternativeSignatures\": [],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"964642e3cd0f022d5b63f5d3c467d034df4b1664e58dd132b6cd54c98bdae6a1\": {\n      \"signature\": \"964642e3cd0f022d5b63f5d3c467d034df4b1664e58dd132b6cd54c98bdae6a1\",\n      \"alternativeSignatures\": [\n        \"f2d5560538c833834ca11e62fa6509618ab5454e1e71faf2847cb6fd07f4c4e0\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"5b0f97262e176cd67207fd63a2c74b9984829286e9229d10efc32d6b73130e37\": {\n      \"signature\": \"5b0f97262e176cd67207fd63a2c74b9984829286e9229d10efc32d6b73130e37\",\n      \"alternativeSignatures\": [\n        \"29a18985690880b8caeebc339c7d2afd107510838cdc6561c1f5493478712581\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"636fe8a4848f24231e94dc13a238022f90a2894cd47a483e351e467eeb98de52\": {\n      \"signature\": \"636fe8a4848f24231e94dc13a238022f90a2894cd47a483e351e467eeb98de52\",\n      \"alternativeSignatures\": [\n        \"e20632aa7941af4239fd857f802e05582c841fb9ae84e17c71ca6c7fc713246b\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"67ae7118600b0793ec3f0a58a753888e13ce4badcc15575614ee6aa622e5009c\": {\n      \"signature\": \"67ae7118600b0793ec3f0a58a753888e13ce4badcc15575614ee6aa622e5009c\",\n      \"alternativeSignatures\": [\n        \"d1e68c2c7d9815f47331dd34c31db2634804b45b078a53d00843082747155ac9\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"9b7d0de03b9e0e0b2711e31df7c804528c357bf5aa2d689fb5a5f42750e84077\": {\n      \"signature\": \"9b7d0de03b9e0e0b2711e31df7c804528c357bf5aa2d689fb5a5f42750e84077\",\n      \"alternativeSignatures\": [\n        \"e42bf5a49be2b1b815d1fde98ebf9d463fd2e70be1e8ca661f1210ce5b0c4953\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"06ecbceae8bfd10acf8c35f21af3926d172c7930f24a204cc58b61efc6c4c770\": {\n      \"signature\": \"06ecbceae8bfd10acf8c35f21af3926d172c7930f24a204cc58b61efc6c4c770\",\n      \"alternativeSignatures\": [\n        \"035d6eb1444a809987923a39793fbb1ab9e4462405f38f94bc425c579705a9f2\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"c103671af429c32de81f2dc2e7a92999de88a517d916a8f75c8e37448bb2efe9\": {\n      \"signature\": \"c103671af429c32de81f2dc2e7a92999de88a517d916a8f75c8e37448bb2efe9\",\n      \"alternativeSignatures\": [\n        \"3f904a503c12b62c2922900a2e689632e06272a815448939b1fdd435bcf74388\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    },\n    \"d1196285a4e64cf6f0f7f22a29bf5b33b540137da1a89ed2af0c880d2a8c1d64\": {\n      \"signature\": \"d1196285a4e64cf6f0f7f22a29bf5b33b540137da1a89ed2af0c880d2a8c1d64\",\n      \"alternativeSignatures\": [\n        \"1c24094ca9e68a76a81c747853860e46fd139c9f47f0fdbad9133538e7d064b2\"\n      ],\n      \"memberOf\": [\n        \"default\"\n      ],\n      \"createdDate\": \"2024-02-06 21:00:02Z\"\n    }\n  }\n}\n"
  },
  {
    "path": ".azure-pipelines/publish.yml",
    "content": "pr: none\n\ntrigger:\n  tags:\n    include:\n    - '*'\n\nresources:\n  repositories:\n  - repository: 1esPipelines\n    type: git\n    name: 1ESPipelineTemplates/1ESPipelineTemplates\n    ref: refs/tags/release\n\nextends:\n  template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines\n  parameters:\n    pool:\n      name: DevDivPlaywrightAzurePipelinesUbuntu2204\n      os: linux\n    sdl:\n      sourceAnalysisPool:\n        name: DevDivPlaywrightAzurePipelinesWindows2022\n        # The image must be windows-based due to restrictions of the SDL tools. See: https://aka.ms/AAo6v8e\n        # In the case of a windows build, this can be the same as the above pool image.\n        os: windows\n      suppression:\n        suppressionFile: $(Build.SourcesDirectory)\\.azure-pipelines\\guardian\\SDL\\.gdnsuppress\n    stages:\n    - stage: Stage\n      jobs:\n      - job: Build\n        templateContext:\n          outputs:\n          - output: pipelineArtifact\n            path: $(Build.ArtifactStagingDirectory)/esrp-build\n            artifact: esrp-build\n        steps:\n        - task: UsePythonVersion@0\n          inputs:\n            versionSpec: '3.9'\n          displayName: 'Use Python'\n        - script: |\n            python -m pip install --upgrade pip\n            pip install -r local-requirements.txt\n            pip install -r requirements.txt\n            pip install -e .\n            for wheel in $(python setup.py --list-wheels); do\n              PLAYWRIGHT_TARGET_WHEEL=$wheel python -m build --wheel --outdir $(Build.ArtifactStagingDirectory)/esrp-build\n            done\n          displayName: 'Install & Build'\n      - job: Publish\n        dependsOn: Build\n        templateContext:\n          type: releaseJob\n          isProduction: true\n          inputs:\n          - input: pipelineArtifact\n            artifactName: esrp-build\n            targetPath: $(Build.ArtifactStagingDirectory)/esrp-build\n        steps:\n          - checkout: none\n          - task: EsrpRelease@9\n            inputs:\n              connectedservicename: 'Playwright-ESRP-PME'\n              usemanagedidentity: true\n              keyvaultname: 'playwright-esrp-pme'\n              signcertname: 'ESRP-Release-Sign'\n              clientid: '13434a40-7de4-4c23-81a3-d843dc81c2c5'\n              intent: 'PackageDistribution'\n              contenttype: 'PyPi'\n              # Keeping it commented out as a workaround for:\n              # https://portal.microsofticm.com/imp/v3/incidents/incident/499972482/summary\n              # contentsource: 'folder'\n              folderlocation: '$(Build.ArtifactStagingDirectory)/esrp-build'\n              waitforreleasecompletion: true\n              owners: 'yurys@microsoft.com'\n              approvers: 'yurys@microsoft.com'\n              serviceendpointurl: 'https://api.esrp.microsoft.com'\n              mainpublisher: 'Playwright'\n              domaintenantid: '975f013f-7f24-47e8-a7d3-abc4752bf346'\n            displayName: 'ESRP Release to PIP'\n"
  },
  {
    "path": ".claude/skills/playwright-roll/SKILL.md",
    "content": "---\nname: playwright-roll\ndescription: Roll Playwright Python to a new version\n---\n\nHelp the user roll to a new version of Playwright.\n../../../ROLLING.md contains general instructions and scripts.\n\nStart with updating the version and generating the API to see the state of things.\n\nAfterwards, work through the list of changes that need to be backported.\nYou can find a list of pull requests that might need to be taking into account in the issue titled \"Backport changes\".\nWork through them one-by-one and check off the items that you have handled.\nNot all of them will be relevant, some might have partially been reverted, etc. - so feel free to check with the upstream release branch.\n\nRolling includes:\n- updating client implementation to match changes in the upstream JS implementation (see ../playwright/packages/playwright-core/src/client)\n- adding a couple of new tests to verify new/changed functionality\n\n## Tips & Tricks\n- Project checkouts are in the parent directory (`../`).\n- when updating checkboxes, store the issue content into /tmp and edit it there, then update the issue based on the file\n- use the \"gh\" cli to interact with GitHub\n"
  },
  {
    "path": ".gitattributes",
    "content": "# text files must be lf for golden file tests to work\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "content": "name: Bug Report 🪲\ndescription: Create a bug report to help us improve\ntitle: '[Bug]: '\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        # Please follow these steps first:\n  - type: markdown\n    attributes:\n      value: |\n        ## Troubleshoot\n        If Playwright is not behaving the way you expect, we'd ask you to look at the [documentation](https://playwright.dev/python/docs/intro) and search the issue tracker for evidence supporting your expectation.\n        Please make reasonable efforts to troubleshoot and rule out issues with your code, the configuration, or any 3rd party libraries you might be using.\n        Playwright offers [several debugging tools](https://playwright.dev/python/docs/debug) that you can use to troubleshoot your issues.\n  - type: markdown\n    attributes:\n      value: |\n        ## Ask for help through appropriate channels\n        If you feel unsure about the cause of the problem, consider asking for help on for example [StackOverflow](https://stackoverflow.com/questions/ask) or our [Discord channel](https://aka.ms/playwright/discord) before posting a bug report. The issue tracker is not a help forum.\n  - type: markdown\n    attributes:\n      value: |\n        ## Make a minimal reproduction\n        To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the bug.\n        The simpler you can make it, the more likely we are to successfully verify and fix the bug.\n  - type: markdown\n    attributes:\n      value: |\n        > [!IMPORTANT]\n        > Bug reports without a minimal reproduction will be rejected.\n\n        ---\n  - type: input\n    id: version\n    attributes:\n      label: Version\n      description: |\n        The version of Playwright you are using.\n        Is it the [latest](https://github.com/microsoft/playwright-python/releases)? Test and see if the bug has already been fixed.\n      placeholder: ex. 1.41.1\n    validations:\n      required: true\n  - type: textarea\n    id: reproduction\n    attributes:\n      label: Steps to reproduce\n      description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug.\n      value: |\n        Example steps (replace with your own):\n        1. Clone my repo at https://github.com/<myuser>/example\n        2. pip install -r requirements.txt\n        3. python test.py\n        4. You should see the error come up\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behavior\n      description: A description of what you expect to happen.\n      placeholder: I expect to see X or Y\n    validations:\n      required: true\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: Actual behavior\n      description: |\n        A clear and concise description of the unexpected behavior.\n        Please include any relevant output here, especially any error messages.\n      placeholder: A bug happened!\n    validations:\n      required: true\n  - type: textarea\n    id: context\n    attributes:\n      label: Additional context\n      description: Anything else that might be relevant\n    validations:\n      required: false\n  - type: textarea\n    id: envinfo\n    attributes:\n      label: Environment\n      description: |\n        Please provide information about the environment you are running in.\n      value: |\n        - Operating System: [Ubuntu 22.04]\n        - CPU: [arm64]\n        - Browser: [All, Chromium, Firefox, WebKit]\n        - Python Version: [3.12]\n        - Other info:\n      render: Text\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Join our Discord Server\n    url: https://aka.ms/playwright/discord\n    about: Ask questions and discuss with other community members\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/documentation.yml",
    "content": "name: Documentation 📖\ndescription: Submit a request to add or update documentation\ntitle: '[Docs]: '\nlabels: ['Documentation :book:']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ### Thank you for helping us improve our documentation!\n        Please be sure you are looking at [the Next version of the documentation](https://playwright.dev/python/docs/next/intro) before opening an issue here.\n  - type: textarea\n    id: links\n    attributes:\n      label: Page(s)\n      description: |\n        Links to one or more documentation pages that should be modified.\n        If you are reporting an issue with a specific section of a page, try to link directly to the nearest anchor.\n        If you are suggesting that a new page be created, link to the parent of the proposed page.\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n      description: |\n        Describe the change you are requesting.\n        If the issue pertains to a single function or matcher, be sure to specify the entire call signature.\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.yml",
    "content": "name: Feature Request 🚀\ndescription: Submit a proposal for a new feature\ntitle: '[Feature]: '\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ### Thank you for taking the time to suggest a new feature!\n  - type: textarea\n    id: description\n    attributes:\n      label: '🚀 Feature Request'\n      description: A clear and concise description of what the feature is.\n    validations:\n      required: true\n  - type: textarea\n    id: example\n    attributes:\n      label: Example\n      description: Describe how this feature would be used.\n    validations:\n      required: false\n  - type: textarea\n    id: motivation\n    attributes:\n      label: Motivation\n      description: |\n        Outline your motivation for the proposal. How will it make Playwright better?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.yml",
    "content": "name: 'Questions / Help 💬'\ndescription: If you have questions, please check StackOverflow or Discord\ntitle: '[Please read the message below]'\nlabels: [':speech_balloon: Question']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ## Questions and Help 💬\n\n        This issue tracker is reserved for bug reports and feature requests.\n\n        For anything else, such as questions or getting help, please see:\n\n        - [The Playwright documentation](https://playwright.dev)\n        - [Our Discord server](https://aka.ms/playwright/discord)\n  - type: checkboxes\n    id: no-post\n    attributes:\n      label: |\n        Please do not submit this issue.\n      description: |\n        > [!IMPORTANT]\n        > This issue will be closed.\n      options:\n        - label: I understand\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/regression.yml",
    "content": "name: Report regression\ndescription: Functionality that used to work and does not any more\ntitle: \"[Regression]: \"\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        # Please follow these steps first:\n  - type: markdown\n    attributes:\n      value: |\n        ## Make a minimal reproduction\n        To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the regression.\n        The simpler you can make it, the more likely we are to successfully verify and fix the regression.\n  - type: markdown\n    attributes:\n      value: |\n        > [!IMPORTANT]\n        > Regression reports without a minimal reproduction will be rejected.\n\n        ---\n  - type: input\n    id: goodVersion\n    attributes:\n      label: Last Good Version\n      description: |\n        Last version of Playwright where the feature was working.\n      placeholder: ex. 1.40.1\n    validations:\n      required: true\n  - type: input\n    id: badVersion\n    attributes:\n      label: First Bad Version\n      description: |\n        First version of Playwright where the feature was broken.\n        Is it the [latest](https://github.com/microsoft/playwright-python/releases)? Test and see if the regression has already been fixed.\n      placeholder: ex. 1.41.1\n    validations:\n      required: true\n  - type: textarea\n    id: reproduction\n    attributes:\n      label: Steps to reproduce\n      description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug.\n      value: |\n        Example steps (replace with your own):\n        1. Clone my repo at https://github.com/<myuser>/example\n        2. pip -r requirements.txt\n        3. python test.py\n        4. You should see the error come up\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behavior\n      description: A description of what you expect to happen.\n      placeholder: I expect to see X or Y\n    validations:\n      required: true\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: Actual behavior\n      description: A clear and concise description of the unexpected behavior.\n      placeholder: A bug happened!\n    validations:\n      required: true\n  - type: textarea\n    id: context\n    attributes:\n      label: Additional context\n      description: Anything else that might be relevant\n    validations:\n      required: false\n  - type: textarea\n    id: envinfo\n    attributes:\n      label: Environment\n      description: |\n        Please provide information about the environment you are running in.\n      value: |\n        - Operating System: [Ubuntu 22.04]\n        - CPU: [arm64]\n        - Browser: [All, Chromium, Firefox, WebKit]\n        - Python Version: [3.12]\n        - Other info:\n      render: Text\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"pip\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      actions:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - main\n      - release-*\n  pull_request:\n    branches:\n      - main\n      - release-*\n\nconcurrency:\n  # For pull requests, cancel all currently-running jobs for this workflow\n  # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\njobs:\n  infra:\n    name: Lint\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v5\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n    - name: Install dependencies & browsers\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r local-requirements.txt\n        pip install -r requirements.txt\n        pip install -e .\n        python -m build --wheel\n        python -m playwright install --with-deps\n    - name: Lint\n      run: pre-commit run --show-diff-on-failure --color=always --all-files\n    - name: Generate APIs\n      run: bash scripts/update_api.sh\n    - name: Verify generated API is up to date\n      run: git diff --exit-code\n\n  build:\n    name: Build\n    timeout-minutes: 45\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n        python-version: ['3.9', '3.10']\n        browser: [chromium, firefox, webkit]\n        include:\n        - os: windows-latest\n          python-version: '3.11'\n          browser: chromium\n        - os: macos-latest\n          python-version: '3.11'\n          browser: chromium\n        - os: ubuntu-latest\n          python-version: '3.11'\n          browser: chromium\n        - os: windows-latest\n          python-version: '3.12'\n          browser: chromium\n        - os: macos-latest\n          python-version: '3.12'\n          browser: chromium\n        - os: ubuntu-latest\n          python-version: '3.12'\n          browser: chromium\n        - os: windows-latest\n          python-version: '3.13'\n          browser: chromium\n        - os: macos-latest\n          python-version: '3.13'\n          browser: chromium\n        - os: ubuntu-latest\n          python-version: '3.13'\n          browser: chromium\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v5\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies & browsers\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r local-requirements.txt\n        pip install -r requirements.txt\n        pip install -e .\n        python -m build --wheel\n        python -m playwright install --with-deps ${{ matrix.browser }}\n    - name: Common Tests\n      run: pytest tests/common --browser=${{ matrix.browser }} --timeout 90\n    - name: Test Reference count\n      run: pytest tests/test_reference_count_async.py --browser=${{ matrix.browser }}\n    - name: Test Wheel Installation\n      run: pytest tests/test_installation.py --browser=${{ matrix.browser }}\n    - name: Test Sync API\n      if: matrix.os != 'ubuntu-latest'\n      run: pytest tests/sync --browser=${{ matrix.browser }} --timeout 90\n    - name: Test Sync API\n      if: matrix.os == 'ubuntu-latest'\n      run: xvfb-run pytest tests/sync --browser=${{ matrix.browser }} --timeout 90\n    - name: Test Async API\n      if: matrix.os != 'ubuntu-latest'\n      run: pytest tests/async --browser=${{ matrix.browser }} --timeout 90\n    - name: Test Async API\n      if: matrix.os == 'ubuntu-latest'\n      run: xvfb-run pytest tests/async --browser=${{ matrix.browser }} --timeout 90\n\n  test-stable:\n    name: Stable\n    timeout-minutes: 45\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n        browser-channel: [chrome]\n        include:\n        - os: windows-latest\n          browser-channel: msedge\n        - os: macos-latest\n          browser-channel: msedge\n    runs-on: ${{ matrix.os }}\n    steps:\n    - uses: actions/checkout@v5\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n    - name: Install dependencies & browsers\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r local-requirements.txt\n        pip install -r requirements.txt\n        pip install -e .\n        python -m build --wheel\n        python -m playwright install ${{ matrix.browser-channel }} --with-deps\n    - name: Common Tests\n      run: pytest tests/common --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90\n    - name: Test Sync API\n      if: matrix.os != 'ubuntu-latest'\n      run: pytest tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90\n    - name: Test Sync API\n      if: matrix.os == 'ubuntu-latest'\n      run: xvfb-run pytest tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90\n    - name: Test Async API\n      if: matrix.os != 'ubuntu-latest'\n      run: pytest tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90\n    - name: Test Async API\n      if: matrix.os == 'ubuntu-latest'\n      run: xvfb-run pytest tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90\n\n  build-conda:\n    name: Conda Build\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-22.04, macos-13, windows-2022]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n      - name: Get conda\n        uses: conda-incubator/setup-miniconda@v3\n        with:\n          python-version: 3.9\n          channels: conda-forge\n          miniconda-version: latest\n      - name: Prepare\n        run: conda install conda-build conda-verify\n      - name: Build\n        run: conda build .\n\n  test_examples:\n    name: Examples\n    runs-on: ubuntu-22.04\n    defaults:\n      run:\n        working-directory: examples/todomvc/\n    steps:\n    - uses: actions/checkout@v5\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: '3.10'\n    - name: Install dependencies & browsers\n      run: |\n        pip install -r requirements.txt\n        python -m playwright install --with-deps chromium\n    - name: Common Tests\n      run: pytest\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Upload Python Package\non:\n  release:\n    types: [published]\n  workflow_dispatch:\njobs:\n  deploy-conda:\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            target-platform: linux-x86_64\n          - os: ubuntu-latest\n            target-platform: linux-aarch64\n          - os: windows-latest\n            target-platform: win-64\n          - os: macos-latest-large\n            target-platform: osx-intel\n          - os: macos-latest-xlarge\n            target-platform: osx-arm64\n    runs-on: ${{ matrix.os }}\n    defaults:\n      run:\n        # Required for conda-incubator/setup-miniconda@v3\n        shell: bash -el {0}\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n      - name: Get conda\n        uses: conda-incubator/setup-miniconda@v3\n        with:\n          python-version: 3.9\n          channels: conda-forge\n          miniconda-version: latest\n      - name: Prepare\n        run: conda install anaconda-client conda-build conda-verify\n      - name: Build and Upload\n        env:\n          ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }}\n        run: |\n          conda config --set anaconda_upload yes\n          if [ \"${{ matrix.target-platform }}\" == \"osx-arm64\" ]; then\n            conda build --user microsoft . -m conda_build_config_osx_arm64.yaml\n          elif [ \"${{ matrix.target-platform }}\" == \"linux-aarch64\" ]; then\n            conda build --user microsoft . -m conda_build_config_linux_aarch64.yaml\n          else\n            conda build --user microsoft .\n          fi\n"
  },
  {
    "path": ".github/workflows/publish_docker.yml",
    "content": "name: \"publish release - Docker\"\n\non:\n  workflow_dispatch:\n  release:\n    types: [published]\n\njobs:\n  publish-docker-release:\n    name: \"publish to DockerHub\"\n    runs-on: ubuntu-22.04\n    if: github.repository == 'microsoft/playwright-python'\n    permissions:\n      id-token: write   # This is required for OIDC login (azure/login) to succeed\n      contents: read    # This is required for actions/checkout to succeed\n    environment: Docker\n    steps:\n    - uses: actions/checkout@v5\n    - name: Azure login\n      uses: azure/login@v2\n      with:\n        client-id: ${{ secrets.AZURE_DOCKER_CLIENT_ID }}\n        tenant-id: ${{ secrets.AZURE_DOCKER_TENANT_ID }}\n        subscription-id: ${{ secrets.AZURE_DOCKER_SUBSCRIPTION_ID }}\n    - name: Login to ACR via OIDC\n      run: az acr login --name playwright\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n    - name: Set up Docker QEMU for arm64 docker builds\n      uses: docker/setup-qemu-action@v3\n      with:\n        platforms: arm64\n    - name: Install dependencies & browsers\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r local-requirements.txt\n        pip install -r requirements.txt\n        pip install -e .\n    - run: ./utils/docker/publish_docker.sh stable\n"
  },
  {
    "path": ".github/workflows/test_docker.yml",
    "content": "name: Test Docker\non:\n  push:\n    paths:\n      - '.github/workflows/test_docker.yml'\n      - 'setup.py'\n      - '**/Dockerfile.*'\n    branches:\n      - main\n      - release-*\n  pull_request:\n    paths:\n      - '.github/workflows/test_docker.yml'\n      - 'setup.py'\n      - '**/Dockerfile.*'\n    branches:\n      - main\n      - release-*\njobs:\n  build:\n    timeout-minutes: 120\n    runs-on: ${{ matrix.runs-on }}\n    strategy:\n      fail-fast: false\n      matrix:\n        docker-image-variant:\n          - jammy\n          - noble\n        runs-on:\n          - ubuntu-24.04\n          - ubuntu-24.04-arm\n    steps:\n    - uses: actions/checkout@v5\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r local-requirements.txt\n        pip install -r requirements.txt\n        pip install -e .\n    - name: Build Docker image\n      run: |\n        ARCH=\"${{ matrix.runs-on == 'ubuntu-24.04-arm' && 'arm64' || 'amd64' }}\"\n        bash utils/docker/build.sh --$ARCH ${{ matrix.docker-image-variant }} playwright-python:localbuild-${{ matrix.docker-image-variant }}\n    - name: Test\n      run: |\n        CONTAINER_ID=\"$(docker run --rm -e CI -v $(pwd):/root/playwright --name playwright-docker-test --workdir /root/playwright/ -d -t playwright-python:localbuild-${{ matrix.docker-image-variant }} /bin/bash)\"\n        # Fix permissions for Git inside the container\n        docker exec \"${CONTAINER_ID}\" chown -R root:root /root/playwright\n        docker exec \"${CONTAINER_ID}\" pip install -r local-requirements.txt\n        docker exec \"${CONTAINER_ID}\" pip install -r requirements.txt\n        docker exec \"${CONTAINER_ID}\" pip install -e .\n        docker exec \"${CONTAINER_ID}\" python -m build --wheel\n        docker exec \"${CONTAINER_ID}\" xvfb-run pytest tests/sync/\n        docker exec \"${CONTAINER_ID}\" xvfb-run pytest tests/async/\n"
  },
  {
    "path": ".gitignore",
    "content": "**/__pycache__/\ndriver/\nplaywright/driver/\nplaywright.egg-info/\nbuild/\ndist/\nvenv/\n.idea/\n**/*.pyc\nenv/\nhtmlcov/\n.coverage*\n.DS_Store\n.vscode/\n.eggs\n_repo_version.py\ncoverage.xml\njunit/\nhtmldocs/\nutils/docker/dist/\nPipfile\nPipfile.lock\n.venv/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nrepos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v5.0.0\n    hooks:\n      - id: trailing-whitespace\n      - id: end-of-file-fixer\n        exclude: tests/assets/har-sha1-main-response.txt\n      - id: check-yaml\n      - id: check-toml\n      - id: requirements-txt-fixer\n      - id: check-ast\n      - id: check-builtin-literals\n      - id: check-executables-have-shebangs\n      - id: check-merge-conflict\n  - repo: https://github.com/psf/black\n    rev: 25.1.0\n    hooks:\n      - id: black\n  - repo: https://github.com/pre-commit/mirrors-mypy\n    rev: v1.17.0\n    hooks:\n      - id: mypy\n        additional_dependencies: [types-pyOpenSSL==24.1.0.20240722, types-requests==2.32.4.20250611]\n  - repo: https://github.com/pycqa/flake8\n    rev: 7.3.0\n    hooks:\n      - id: flake8\n  - repo: https://github.com/pycqa/isort\n    rev: 6.0.1\n    hooks:\n      - id: isort\n  - repo: local\n    hooks:\n      - id: pyright\n        name: pyright\n        entry: pyright\n        language: node\n        pass_filenames: false\n        types: [python]\n        additional_dependencies: [\"pyright@1.1.403\"]\n  - repo: local\n    hooks:\n      - id: check-license-header\n        name: Check License Header\n        entry: ./utils/linting/check_file_header.py\n        language: python\n        types: [python]\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## How to Contribute\n\n### Configuring python environment\n\nThe project development requires Python version 3.9+. To set it as default in the environment run the following commands:\n\n```sh\n# You may need to install python 3.9 venv if it's missing, on Ubuntu just run `sudo apt-get install python3.9-venv`\npython3.9 -m venv env\nsource ./env/bin/activate\n```\n\nInstall required dependencies:\n\n```sh\npython -m pip install --upgrade pip\npip install -r local-requirements.txt\n```\n\nBuild and install drivers:\n\n```sh\npip install -e .\npython -m build --wheel\n```\n\nRun tests:\n\n```sh\npytest --browser chromium\n```\n\nChecking for typing errors\n\n```sh\nmypy playwright\n```\n\nFormat the code\n\n```sh\npre-commit install\npre-commit run --all-files\n```\n\nFor more details look at the [CI configuration](./.github/workflows/ci.yml).\n\nCollect coverage\n\n```sh\npytest --browser chromium --cov-report html --cov=playwright\nopen htmlcov/index.html\n```\n\n### Regenerating APIs\n\n```bash\n./scripts/update_api.sh\npre-commit run --all-files\n```\n\n## Contributor License Agreement\n\nThis project welcomes contributions and suggestions. Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\n## Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Portions Copyright (c) Microsoft Corporation.\n   Portions Copyright 2017 Google Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# 🎭 [Playwright](https://playwright.dev) for Python [![PyPI version](https://badge.fury.io/py/playwright.svg)](https://pypi.python.org/pypi/playwright/) [![Anaconda version](https://img.shields.io/conda/v/microsoft/playwright)](https://anaconda.org/Microsoft/playwright) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)\n\nPlaywright is a Python library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) browsers with a single API. Playwright delivers automation that is **ever-green**, **capable**, **reliable** and **fast**. [See how Playwright is better](https://playwright.dev/python).\n\n|          | Linux | macOS | Windows |\n|   :---   | :---: | :---: | :---:   |\n| Chromium <!-- GEN:chromium-version -->145.0.7632.6<!-- GEN:stop --> | ✅ | ✅ | ✅ |\n| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |\n| Firefox <!-- GEN:firefox-version -->146.0.1<!-- GEN:stop --> | ✅ | ✅ | ✅ |\n\n## Documentation\n\n[https://playwright.dev/python/docs/intro](https://playwright.dev/python/docs/intro)\n\n## API Reference\n\n[https://playwright.dev/python/docs/api/class-playwright](https://playwright.dev/python/docs/api/class-playwright)\n\n## Example\n\n```py\nfrom playwright.sync_api import sync_playwright\n\nwith sync_playwright() as p:\n    for browser_type in [p.chromium, p.firefox, p.webkit]:\n        browser = browser_type.launch()\n        page = browser.new_page()\n        page.goto('http://playwright.dev')\n        page.screenshot(path=f'example-{browser_type.name}.png')\n        browser.close()\n```\n\n```py\nimport asyncio\nfrom playwright.async_api import async_playwright\n\nasync def main():\n    async with async_playwright() as p:\n        for browser_type in [p.chromium, p.firefox, p.webkit]:\n            browser = await browser_type.launch()\n            page = await browser.new_page()\n            await page.goto('http://playwright.dev')\n            await page.screenshot(path=f'example-{browser_type.name}.png')\n            await browser.close()\n\nasyncio.run(main())\n```\n\n## Other languages\n\nMore comfortable in another programming language? [Playwright](https://playwright.dev) is also available in\n- [Node.js (JavaScript / TypeScript)](https://playwright.dev/docs/intro),\n- [.NET](https://playwright.dev/dotnet/docs/intro),\n- [Java](https://playwright.dev/java/docs/intro).\n"
  },
  {
    "path": "ROLLING.md",
    "content": "# Rolling Playwright-Python to the latest Playwright driver\n\n* checkout repo: `git clone https://github.com/microsoft/playwright-python`\n* make sure local python is 3.9\n    * create virtual environment, if don't have one: `python -m venv env`\n* activate venv: `source env/bin/activate`\n* install all deps:\n```\npython -m pip install --upgrade pip\npip install -r local-requirements.txt\npre-commit install\npip install -e .\n```\n* change driver version in `setup.py`\n* download new driver: `python -m build --wheel`\n* generate API: `./scripts/update_api.sh`\n* commit changes & send PR\n* wait for bots to pass & merge the PR\n\n\n## Fix typing issues with Playwright ToT\n\n1. `cd playwright`\n1. `API_JSON_MODE=1 node utils/doclint/generateApiJson.js > ../playwright-python/playwright/driver/package/api.json`\n1. `./scripts/update_api.sh`\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).\n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->\n"
  },
  {
    "path": "SUPPORT.md",
    "content": "# Support\n\n## How to file issues and get help\n\nThis project uses GitHub issues to track bugs and feature requests. Please search the [existing issues][gh-issues] before filing new ones to avoid duplicates. For new issues, file your bug or feature request as a new issue using corresponding template.\n\nFor help and questions about using this project, please see the [docs site for Playwright for Python][docs].\n\nJoin our community [Discord Server][discord-server] to connect with other developers using Playwright and ask questions in our 'help-playwright' forum.\n\n## Microsoft Support Policy\n\nSupport for Playwright for Python is limited to the resources listed above.\n\n[gh-issues]: https://github.com/microsoft/playwright-python/issues/\n[docs]: https://playwright.dev/python/\n[discord-server]: https://aka.ms/playwright/discord\n"
  },
  {
    "path": "conda_build_config_linux_aarch64.yaml",
    "content": "target_platform:\n- linux-aarch64\n"
  },
  {
    "path": "conda_build_config_osx_arm64.yaml",
    "content": "target_platform:\n- osx-arm64\n"
  },
  {
    "path": "examples/todomvc/mvctests/__init__.py",
    "content": ""
  },
  {
    "path": "examples/todomvc/mvctests/test_clear_completed_button.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import TODO_ITEMS, create_default_todos\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    create_default_todos(page)\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_display_the_correct_text(page: Page) -> None:\n    page.locator(\".todo-list li .toggle\").first.check()\n    expect(page.locator(\".clear-completed\")).to_have_text(\"Clear completed\")\n\n\ndef test_should_clear_completed_items_when_clicked(page: Page) -> None:\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(1).locator(\".toggle\").check()\n    page.locator(\".clear-completed\").click()\n    expect(todo_items).to_have_count(2)\n    expect(todo_items).to_have_text([TODO_ITEMS[0], TODO_ITEMS[2]])\n\n\ndef test_should_be_hidden_when_there_are_no_items_that_are_completed(\n    page: Page,\n) -> None:\n    page.locator(\".todo-list li .toggle\").first.check()\n    page.locator(\".clear-completed\").click()\n    expect(page.locator(\".clear-completed\")).to_be_hidden()\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_counter.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import TODO_ITEMS, assert_number_of_todos_in_local_storage\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_display_the_current_number_of_todo_items(page: Page) -> None:\n    page.locator(\".new-todo\").fill(TODO_ITEMS[0])\n    page.locator(\".new-todo\").press(\"Enter\")\n    expect(page.locator(\".todo-count\")).to_contain_text(\"1\")\n\n    page.locator(\".new-todo\").fill(TODO_ITEMS[1])\n    page.locator(\".new-todo\").press(\"Enter\")\n    expect(page.locator(\".todo-count\")).to_contain_text(\"2\")\n\n    assert_number_of_todos_in_local_storage(page, 2)\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_editing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import (\n    TODO_ITEMS,\n    assert_number_of_todos_in_local_storage,\n    check_todos_in_local_storage,\n    create_default_todos,\n)\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    create_default_todos(page)\n    assert_number_of_todos_in_local_storage(page, 3)\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_hide_other_controls_when_editing(page: Page) -> None:\n    todo_item = page.locator(\".todo-list li\").nth(1)\n    todo_item.dblclick()\n    expect(todo_item.locator(\".toggle\")).not_to_be_visible()\n    expect(todo_item.locator(\"label\")).not_to_be_visible()\n    assert_number_of_todos_in_local_storage(page, 3)\n\n\ndef test_should_save_edits_on_blur(page: Page) -> None:\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(1).dblclick()\n    todo_items.nth(1).locator(\".edit\").fill(\"buy some sausages\")\n    todo_items.nth(1).locator(\".edit\").dispatch_event(\"blur\")\n\n    expect(todo_items).to_have_text(\n        [\n            TODO_ITEMS[0],\n            \"buy some sausages\",\n            TODO_ITEMS[2],\n        ]\n    )\n    check_todos_in_local_storage(page, \"buy some sausages\")\n\n\ndef test_should_trim_entered_text(page: Page) -> None:\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(1).dblclick()\n    todo_items.nth(1).locator(\".edit\").fill(\"    buy some sausages    \")\n    todo_items.nth(1).locator(\".edit\").press(\"Enter\")\n\n    expect(todo_items).to_have_text(\n        [\n            TODO_ITEMS[0],\n            \"buy some sausages\",\n            TODO_ITEMS[2],\n        ]\n    )\n    check_todos_in_local_storage(page, \"buy some sausages\")\n\n\ndef test_should_remove_the_item_if_an_empty_text_string_was_entered(page: Page) -> None:\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(1).dblclick()\n    todo_items.nth(1).locator(\".edit\").fill(\"\")\n    todo_items.nth(1).locator(\".edit\").press(\"Enter\")\n\n    expect(todo_items).to_have_text(\n        [\n            TODO_ITEMS[0],\n            TODO_ITEMS[2],\n        ]\n    )\n\n\ndef test_should_cancel_edits_on_escape(page: Page) -> None:\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(1).dblclick()\n    todo_items.nth(1).locator(\".edit\").press(\"Escape\")\n    expect(todo_items).to_have_text(TODO_ITEMS)\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_item.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import (\n    TODO_ITEMS,\n    check_number_of_completed_todos_in_local_storage,\n    check_todos_in_local_storage,\n    create_default_todos,\n)\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_allow_me_to_mark_items_as_completed(page: Page) -> None:\n    # Create two items.\n    for item in TODO_ITEMS[:2]:\n        page.locator(\".new-todo\").fill(item)\n        page.locator(\".new-todo\").press(\"Enter\")\n\n    # Check first item.\n    firstTodo = page.locator(\".todo-list li\").nth(0)\n    firstTodo.locator(\".toggle\").check()\n    expect(firstTodo).to_have_class(\"completed\")\n\n    # Check second item.\n    secondTodo = page.locator(\".todo-list li\").nth(1)\n    expect(secondTodo).not_to_have_class(\"completed\")\n    secondTodo.locator(\".toggle\").check()\n\n    # Assert completed class.\n    expect(firstTodo).to_have_class(\"completed\")\n    expect(secondTodo).to_have_class(\"completed\")\n\n\ndef test_should_allow_me_to_un_mark_items_as_completed(page: Page) -> None:\n    # Create two items.\n    for item in TODO_ITEMS[:2]:\n        page.locator(\".new-todo\").fill(item)\n        page.locator(\".new-todo\").press(\"Enter\")\n\n    firstTodo = page.locator(\".todo-list li\").nth(0)\n    secondTodo = page.locator(\".todo-list li\").nth(1)\n    firstTodo.locator(\".toggle\").check()\n    expect(firstTodo).to_have_class(\"completed\")\n    expect(secondTodo).not_to_have_class(\"completed\")\n    check_number_of_completed_todos_in_local_storage(page, 1)\n\n    firstTodo.locator(\".toggle\").uncheck()\n    expect(firstTodo).not_to_have_class(\"completed\")\n    expect(secondTodo).not_to_have_class(\"completed\")\n    check_number_of_completed_todos_in_local_storage(page, 0)\n\n\ndef test_should_allow_me_to_edit_an_item(page: Page) -> None:\n    create_default_todos(page)\n\n    todo_items = page.locator(\".todo-list li\")\n    secondTodo = todo_items.nth(1)\n    secondTodo.dblclick()\n    expect(secondTodo.locator(\".edit\")).to_have_value(TODO_ITEMS[1])\n    secondTodo.locator(\".edit\").fill(\"buy some sausages\")\n    secondTodo.locator(\".edit\").press(\"Enter\")\n\n    # Explicitly assert the new text value.\n    expect(todo_items).to_have_text([TODO_ITEMS[0], \"buy some sausages\", TODO_ITEMS[2]])\n    check_todos_in_local_storage(page, \"buy some sausages\")\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_mark_all_as_completed.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import (\n    assert_number_of_todos_in_local_storage,\n    check_number_of_completed_todos_in_local_storage,\n    create_default_todos,\n)\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_allow_me_to_mark_all_items_as_completed(page: Page) -> None:\n    create_default_todos(page)\n    assert_number_of_todos_in_local_storage(page, 3)\n    # Complete all todos.\n    page.locator(\".toggle-all\").check()\n\n    # Ensure all todos have 'completed' class.\n    expect(page.locator(\".todo-list li\")).to_have_class(\n        [\"completed\", \"completed\", \"completed\"]\n    )\n    check_number_of_completed_todos_in_local_storage(page, 3)\n    assert_number_of_todos_in_local_storage(page, 3)\n\n\ndef test_should_allow_me_to_clear_the_complete_state_of_all_items(page: Page) -> None:\n    create_default_todos(page)\n    assert_number_of_todos_in_local_storage(page, 3)\n    # Check and then immediately uncheck.\n    page.locator(\".toggle-all\").check()\n    page.locator(\".toggle-all\").uncheck()\n\n    # Should be no completed classes.\n    expect(page.locator(\".todo-list li\")).to_have_class([\"\", \"\", \"\"])\n    assert_number_of_todos_in_local_storage(page, 3)\n\n\ndef test_complete_all_checkbox_should_update_state_when_items_are_completed_or_cleared(\n    page: Page,\n) -> None:\n    create_default_todos(page)\n    assert_number_of_todos_in_local_storage(page, 3)\n    toggleAll = page.locator(\".toggle-all\")\n    toggleAll.check()\n    expect(toggleAll).to_be_checked()\n    check_number_of_completed_todos_in_local_storage(page, 3)\n\n    # Uncheck first todo.\n    firstTodo = page.locator(\".todo-list li\").nth(0)\n    firstTodo.locator(\".toggle\").uncheck()\n\n    # Reuse toggleAll locator and make sure its not checked.\n    expect(toggleAll).not_to_be_checked()\n\n    firstTodo.locator(\".toggle\").check()\n    check_number_of_completed_todos_in_local_storage(page, 3)\n\n    # Assert the toggle all is checked again.\n    expect(toggleAll).to_be_checked()\n    assert_number_of_todos_in_local_storage(page, 3)\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_new_todo.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport re\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import (\n    TODO_ITEMS,\n    assert_number_of_todos_in_local_storage,\n    create_default_todos,\n)\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_new_todo_test_should_allow_me_to_add_todo_items(page: Page) -> None:\n    # Create 1st todo.\n    page.locator(\".new-todo\").fill(TODO_ITEMS[0])\n    page.locator(\".new-todo\").press(\"Enter\")\n\n    # Make sure the list only has one todo item.\n    expect(page.locator(\".view label\")).to_have_text([TODO_ITEMS[0]])\n\n    # Create 2nd todo.\n    page.locator(\".new-todo\").fill(TODO_ITEMS[1])\n    page.locator(\".new-todo\").press(\"Enter\")\n\n    # Make sure the list now has two todo items.\n    expect(page.locator(\".view label\")).to_have_text([TODO_ITEMS[0], TODO_ITEMS[1]])\n\n    assert_number_of_todos_in_local_storage(page, 2)\n\n\ndef test_new_todo_test_should_clear_text_input_field_when_an_item_is_added(\n    page: Page,\n) -> None:\n    # Create one todo item.\n    page.locator(\".new-todo\").fill(TODO_ITEMS[0])\n    page.locator(\".new-todo\").press(\"Enter\")\n\n    # Check that input is empty.\n    expect(page.locator(\".new-todo\")).to_be_empty()\n    assert_number_of_todos_in_local_storage(page, 1)\n\n\ndef test_new_todo_test_should_append_new_items_to_the_bottom_of_the_list(\n    page: Page,\n) -> None:\n    # Create 3 items.\n    create_default_todos(page)\n\n    # Check test using different methods.\n    expect(page.locator(\".todo-count\")).to_have_text(\"3 items left\")\n    expect(page.locator(\".todo-count\")).to_contain_text(\"3\")\n    expect(page.locator(\".todo-count\")).to_have_text(re.compile(\"3\"))\n\n    # Check all items in one call.\n    expect(page.locator(\".view label\")).to_have_text(TODO_ITEMS)\n    assert_number_of_todos_in_local_storage(page, 3)\n\n\ndef test_new_todo_should_show_main_and_foter_when_items_added(page: Page) -> None:\n    page.locator(\".new-todo\").fill(TODO_ITEMS[0])\n    page.locator(\".new-todo\").press(\"Enter\")\n\n    expect(page.locator(\".main\")).to_be_visible()\n    expect(page.locator(\".footer\")).to_be_visible()\n    assert_number_of_todos_in_local_storage(page, 1)\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_persistence.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import TODO_ITEMS, check_number_of_completed_todos_in_local_storage\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_persist_its_data(page: Page) -> None:\n    for item in TODO_ITEMS[:2]:\n        page.locator(\".new-todo\").fill(item)\n        page.locator(\".new-todo\").press(\"Enter\")\n\n    todo_items = page.locator(\".todo-list li\")\n    todo_items.nth(0).locator(\".toggle\").check()\n    expect(todo_items).to_have_text([TODO_ITEMS[0], TODO_ITEMS[1]])\n    expect(todo_items).to_have_class([\"completed\", \"\"])\n\n    # Ensure there is 1 completed item.\n    check_number_of_completed_todos_in_local_storage(page, 1)\n\n    # Now reload.\n    page.reload()\n    expect(todo_items).to_have_text([TODO_ITEMS[0], TODO_ITEMS[1]])\n    expect(todo_items).to_have_class([\"completed\", \"\"])\n"
  },
  {
    "path": "examples/todomvc/mvctests/test_routing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Generator\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\n\nfrom .utils import (\n    TODO_ITEMS,\n    check_number_of_completed_todos_in_local_storage,\n    check_todos_in_local_storage,\n    create_default_todos,\n)\n\n\n@pytest.fixture(autouse=True)\ndef run_around_tests(page: Page) -> Generator[None, None, None]:\n    # setup before a test\n    page.goto(\"https://demo.playwright.dev/todomvc\")\n    create_default_todos(page)\n    # make sure the app had a chance to save updated todos in storage\n    # before navigating to a new view, otherwise the items can get lost :(\n    # in some frameworks like Durandal\n    check_todos_in_local_storage(page, TODO_ITEMS[0])\n    # run the actual test\n    yield\n    # run any cleanup code\n\n\ndef test_should_allow_me_to_display_active_item(page: Page) -> None:\n    page.locator(\".todo-list li .toggle\").nth(1).check()\n    check_number_of_completed_todos_in_local_storage(page, 1)\n    page.locator(\".filters >> text=Active\").click()\n    expect(page.locator(\".todo-list li\")).to_have_count(2)\n    expect(page.locator(\".todo-list li\")).to_have_text([TODO_ITEMS[0], TODO_ITEMS[2]])\n\n\ndef test_should_respect_the_back_button(page: Page) -> None:\n    page.locator(\".todo-list li .toggle\").nth(1).check()\n    check_number_of_completed_todos_in_local_storage(page, 1)\n\n    # Showing all items\n    page.locator(\".filters >> text=All\").click()\n    expect(page.locator(\".todo-list li\")).to_have_count(3)\n\n    # Showing active items\n    page.locator(\".filters >> text=Active\").click()\n\n    # Showing completed items\n    page.locator(\".filters >> text=Completed\").click()\n\n    expect(page.locator(\".todo-list li\")).to_have_count(1)\n    page.go_back()\n    expect(page.locator(\".todo-list li\")).to_have_count(2)\n    page.go_back()\n    expect(page.locator(\".todo-list li\")).to_have_count(3)\n\n\ndef test_should_allow_me_to_display_completed_items(page: Page) -> None:\n    page.locator(\".todo-list li .toggle\").nth(1).check()\n    check_number_of_completed_todos_in_local_storage(page, 1)\n    page.locator(\".filters >> text=Completed\").click()\n    expect(page.locator(\".todo-list li\")).to_have_count(1)\n\n\ndef test_should_allow_me_to_display_all_items(page: Page) -> None:\n    page.locator(\".todo-list li .toggle\").nth(1).check()\n    check_number_of_completed_todos_in_local_storage(page, 1)\n    page.locator(\".filters >> text=Active\").click()\n    page.locator(\".filters >> text=Completed\").click()\n    page.locator(\".filters >> text=All\").click()\n    expect(page.locator(\".todo-list li\")).to_have_count(3)\n\n\ndef test_should_highlight_the_current_applied_filter(page: Page) -> None:\n    expect(page.locator(\".filters >> text=All\")).to_have_class(\"selected\")\n    page.locator(\".filters >> text=Active\").click()\n    # Page change - active items.\n    expect(page.locator(\".filters >> text=Active\")).to_have_class(\"selected\")\n    page.locator(\".filters >> text=Completed\").click()\n    # Page change - completed items.\n    expect(page.locator(\".filters >> text=Completed\")).to_have_class(\"selected\")\n"
  },
  {
    "path": "examples/todomvc/mvctests/utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom playwright.sync_api import Page\n\nTODO_ITEMS = [\"buy some cheese\", \"feed the cat\", \"book a doctors appointment\"]\n\n\ndef create_default_todos(page: Page) -> None:\n    for item in TODO_ITEMS:\n        page.locator(\".new-todo\").fill(item)\n        page.locator(\".new-todo\").press(\"Enter\")\n\n\ndef check_number_of_completed_todos_in_local_storage(page: Page, expected: int) -> None:\n    assert (\n        page.evaluate(\n            \"JSON.parse(localStorage['react-todos']).filter(i => i.completed).length\"\n        )\n        == expected\n    )\n\n\ndef assert_number_of_todos_in_local_storage(page: Page, expected: int) -> None:\n    assert len(page.evaluate(\"JSON.parse(localStorage['react-todos'])\")) == expected\n\n\ndef check_todos_in_local_storage(page: Page, title: str) -> None:\n    assert title in page.evaluate(\n        \"JSON.parse(localStorage['react-todos']).map(i => i.title)\"\n    )\n"
  },
  {
    "path": "examples/todomvc/requirements.txt",
    "content": "pytest-playwright\n"
  },
  {
    "path": "local-requirements.txt",
    "content": "autobahn==23.1.2\nblack==25.1.0\nbuild==1.3.0\nflake8==7.2.0\nmypy==1.17.1\nobjgraph==3.6.2\nPillow==11.3.0\npixelmatch==0.3.0\npre-commit==3.5.0\npyOpenSSL==25.1.0\npytest==8.4.1\npytest-asyncio==1.1.0\npytest-cov==6.3.0\npytest-repeat==0.9.4\npytest-rerunfailures==15.1\npytest-timeout==2.4.0\npytest-xdist==3.8.0\nrequests==2.32.5\nservice_identity==24.2.0\ntwisted==25.5.0\ntypes-pyOpenSSL==24.1.0.20240722\ntypes-requests==2.32.4.20250809\n"
  },
  {
    "path": "meta.yaml",
    "content": "package:\n  name: playwright\n  version: \"{{ environ.get('GIT_DESCRIBE_TAG') | replace('v', '') }}\"\n\nsource:\n  path: .\n\nbuild:\n  number: 0\n  script: \"{{ PYTHON }} -m pip install . --no-deps -vv\"\n  binary_relocation: False\n  missing_dso_whitelist: \"*\"\n  entry_points:\n    - playwright = playwright.__main__:main\n\nrequirements:\n  build:\n    - python >=3.9                        # [build_platform != target_platform]\n    - pip                                 # [build_platform != target_platform]\n    - cross-python_{{ target_platform }}  # [build_platform != target_platform]\n  host:\n    - python >=3.9\n    - wheel\n    - pip\n    - curl\n    - setuptools_scm\n  run:\n    - python >=3.9\n    # This should be the same as the dependencies in pyproject.toml\n    - greenlet>=3.1.1,<4.0.0\n    - pyee>=13,<14\n\ntest: # [build_platform == target_platform]\n  files:\n    - scripts/example_sync.py\n    - scripts/example_async.py\n  requires:\n    - pip\n  imports:\n    - playwright\n    - playwright.sync_api\n    - playwright.async_api\n  commands:\n    - playwright --help\n    - playwright install --with-deps\n    - python scripts/example_sync.py\n    - python scripts/example_async.py\n\nabout:\n  home: https://github.com/microsoft/playwright-python\n  license: Apache-2.0\n  license_family: Apache\n  license_file: LICENSE\n  summary: Python version of the Playwright testing and automation library.\n  description: |\n    Playwright is a Python library to automate Chromium,\n    Firefox and WebKit browsers with a single API. Playwright\n    delivers automation that is ever-green, capable, reliable\n    and fast.\n  doc_url: https://playwright.dev/python/docs/intro/\n  dev_url: https://github.com/microsoft/playwright-python\n"
  },
  {
    "path": "playwright/__init__.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nPython package `playwright` is a Python library to automate Chromium,\nFirefox and WebKit with a single API. Playwright is built to enable cross-browser\nweb automation that is ever-green, capable, reliable and fast.\n\"\"\"\n"
  },
  {
    "path": "playwright/__main__.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport subprocess\nimport sys\n\nfrom playwright._impl._driver import compute_driver_executable, get_driver_env\n\n\ndef main() -> None:\n    try:\n        driver_executable, driver_cli = compute_driver_executable()\n        completed_process = subprocess.run(\n            [driver_executable, driver_cli, *sys.argv[1:]], env=get_driver_env()\n        )\n        sys.exit(completed_process.returncode)\n    except KeyboardInterrupt:\n        sys.exit(130)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "playwright/_impl/__init__.py",
    "content": ""
  },
  {
    "path": "playwright/_impl/__pyinstaller/__init__.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom typing import List\n\n\ndef get_hook_dirs() -> List[str]:\n    return [os.path.dirname(__file__)]\n"
  },
  {
    "path": "playwright/_impl/__pyinstaller/hook-playwright.async_api.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom PyInstaller.utils.hooks import collect_data_files  # type: ignore\n\ndatas = collect_data_files(\"playwright\")\n"
  },
  {
    "path": "playwright/_impl/__pyinstaller/hook-playwright.sync_api.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom PyInstaller.utils.hooks import collect_data_files  # type: ignore\n\ndatas = collect_data_files(\"playwright\")\n"
  },
  {
    "path": "playwright/_impl/_api_structures.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Literal, Optional, Sequence, TypedDict, Union\n\n# These are the structures that we like keeping in a JSON form for their potential\n# reuse between SDKs / services. They are public and are a part of the\n# stable API.\n\n# Explicitly mark optional params as such for the documentation\n# If there is at least one optional param, set total=False for better mypy handling.\n\n\nclass Cookie(TypedDict, total=False):\n    name: str\n    value: str\n    domain: str\n    path: str\n    expires: float\n    httpOnly: bool\n    secure: bool\n    sameSite: Literal[\"Lax\", \"None\", \"Strict\"]\n    partitionKey: Optional[str]\n\n\nclass StorageStateCookie(TypedDict, total=False):\n    name: str\n    value: str\n    domain: str\n    path: str\n    expires: float\n    httpOnly: bool\n    secure: bool\n    sameSite: Literal[\"Lax\", \"None\", \"Strict\"]\n\n\n# TODO: We are waiting for PEP705 so SetCookieParam can be readonly and matches Cookie.\nclass SetCookieParam(TypedDict, total=False):\n    name: str\n    value: str\n    url: Optional[str]\n    domain: Optional[str]\n    path: Optional[str]\n    expires: Optional[float]\n    httpOnly: Optional[bool]\n    secure: Optional[bool]\n    sameSite: Optional[Literal[\"Lax\", \"None\", \"Strict\"]]\n    partitionKey: Optional[str]\n\n\nclass FloatRect(TypedDict):\n    x: float\n    y: float\n    width: float\n    height: float\n\n\nclass Geolocation(TypedDict, total=False):\n    latitude: float\n    longitude: float\n    accuracy: Optional[float]\n\n\nclass HttpCredentials(TypedDict, total=False):\n    username: str\n    password: str\n    origin: Optional[str]\n    send: Optional[Literal[\"always\", \"unauthorized\"]]\n\n\nclass LocalStorageEntry(TypedDict):\n    name: str\n    value: str\n\n\nclass OriginState(TypedDict):\n    origin: str\n    localStorage: List[LocalStorageEntry]\n\n\nclass PdfMargins(TypedDict, total=False):\n    top: Optional[Union[str, float]]\n    right: Optional[Union[str, float]]\n    bottom: Optional[Union[str, float]]\n    left: Optional[Union[str, float]]\n\n\nclass Position(TypedDict):\n    x: float\n    y: float\n\n\nclass ProxySettings(TypedDict, total=False):\n    server: str\n    bypass: Optional[str]\n    username: Optional[str]\n    password: Optional[str]\n\n\nclass StorageState(TypedDict, total=False):\n    cookies: List[StorageStateCookie]\n    origins: List[OriginState]\n\n\nclass ClientCertificate(TypedDict, total=False):\n    origin: str\n    certPath: Optional[Union[str, Path]]\n    cert: Optional[bytes]\n    keyPath: Optional[Union[str, Path]]\n    key: Optional[bytes]\n    pfxPath: Optional[Union[str, Path]]\n    pfx: Optional[bytes]\n    passphrase: Optional[str]\n\n\nclass ResourceTiming(TypedDict):\n    startTime: float\n    domainLookupStart: float\n    domainLookupEnd: float\n    connectStart: float\n    secureConnectionStart: float\n    connectEnd: float\n    requestStart: float\n    responseStart: float\n    responseEnd: float\n\n\nclass RequestSizes(TypedDict):\n    requestBodySize: int\n    requestHeadersSize: int\n    responseBodySize: int\n    responseHeadersSize: int\n\n\nclass ViewportSize(TypedDict):\n    width: int\n    height: int\n\n\nclass SourceLocation(TypedDict):\n    url: str\n    lineNumber: int\n    columnNumber: int\n\n\nclass FilePayload(TypedDict):\n    name: str\n    mimeType: str\n    buffer: bytes\n\n\nclass RemoteAddr(TypedDict):\n    ipAddress: str\n    port: int\n\n\nclass SecurityDetails(TypedDict):\n    issuer: Optional[str]\n    protocol: Optional[str]\n    subjectName: Optional[str]\n    validFrom: Optional[float]\n    validTo: Optional[float]\n\n\nclass NameValue(TypedDict):\n    name: str\n    value: str\n\n\nHeadersArray = List[NameValue]\nHeaders = Dict[str, str]\n\n\nclass ServerFilePayload(TypedDict):\n    name: str\n    mimeType: str\n    buffer: str\n\n\nclass FormField(TypedDict, total=False):\n    name: str\n    value: Optional[str]\n    file: Optional[ServerFilePayload]\n\n\nclass ExpectedTextValue(TypedDict, total=False):\n    string: str\n    regexSource: str\n    regexFlags: str\n    matchSubstring: bool\n    normalizeWhiteSpace: bool\n    ignoreCase: Optional[bool]\n\n\nclass FrameExpectOptions(TypedDict, total=False):\n    expressionArg: Any\n    expectedText: Optional[Sequence[ExpectedTextValue]]\n    expectedNumber: Optional[float]\n    expectedValue: Optional[Any]\n    useInnerText: Optional[bool]\n    isNot: bool\n    timeout: Optional[float]\n\n\nclass FrameExpectResult(TypedDict):\n    matches: bool\n    received: Any\n    log: List[str]\n    errorMessage: Optional[str]\n\n\nAriaRole = Literal[\n    \"alert\",\n    \"alertdialog\",\n    \"application\",\n    \"article\",\n    \"banner\",\n    \"blockquote\",\n    \"button\",\n    \"caption\",\n    \"cell\",\n    \"checkbox\",\n    \"code\",\n    \"columnheader\",\n    \"combobox\",\n    \"complementary\",\n    \"contentinfo\",\n    \"definition\",\n    \"deletion\",\n    \"dialog\",\n    \"directory\",\n    \"document\",\n    \"emphasis\",\n    \"feed\",\n    \"figure\",\n    \"form\",\n    \"generic\",\n    \"grid\",\n    \"gridcell\",\n    \"group\",\n    \"heading\",\n    \"img\",\n    \"insertion\",\n    \"link\",\n    \"list\",\n    \"listbox\",\n    \"listitem\",\n    \"log\",\n    \"main\",\n    \"marquee\",\n    \"math\",\n    \"menu\",\n    \"menubar\",\n    \"menuitem\",\n    \"menuitemcheckbox\",\n    \"menuitemradio\",\n    \"meter\",\n    \"navigation\",\n    \"none\",\n    \"note\",\n    \"option\",\n    \"paragraph\",\n    \"presentation\",\n    \"progressbar\",\n    \"radio\",\n    \"radiogroup\",\n    \"region\",\n    \"row\",\n    \"rowgroup\",\n    \"rowheader\",\n    \"scrollbar\",\n    \"search\",\n    \"searchbox\",\n    \"separator\",\n    \"slider\",\n    \"spinbutton\",\n    \"status\",\n    \"strong\",\n    \"subscript\",\n    \"superscript\",\n    \"switch\",\n    \"tab\",\n    \"table\",\n    \"tablist\",\n    \"tabpanel\",\n    \"term\",\n    \"textbox\",\n    \"time\",\n    \"timer\",\n    \"toolbar\",\n    \"tooltip\",\n    \"tree\",\n    \"treegrid\",\n    \"treeitem\",\n]\n\n\nclass TracingGroupLocation(TypedDict):\n    file: str\n    line: Optional[int]\n    column: Optional[int]\n"
  },
  {
    "path": "playwright/_impl/_artifact.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pathlib\nfrom pathlib import Path\nfrom typing import Dict, Optional, Union, cast\n\nfrom playwright._impl._connection import ChannelOwner, from_channel\nfrom playwright._impl._helper import Error, make_dirs_for_file, patch_error_message\nfrom playwright._impl._stream import Stream\n\n\nclass Artifact(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self.absolute_path = initializer[\"absolutePath\"]\n\n    async def path_after_finished(self) -> pathlib.Path:\n        if self._connection.is_remote:\n            raise Error(\n                \"Path is not available when using browser_type.connect(). Use save_as() to save a local copy.\"\n            )\n        path = await self._channel.send(\n            \"pathAfterFinished\",\n            None,\n        )\n        return pathlib.Path(path)\n\n    async def save_as(self, path: Union[str, Path]) -> None:\n        stream = cast(\n            Stream,\n            from_channel(\n                await self._channel.send(\n                    \"saveAsStream\",\n                    None,\n                )\n            ),\n        )\n        make_dirs_for_file(path)\n        await stream.save_as(path)\n\n    async def failure(self) -> Optional[str]:\n        reason = await self._channel.send(\n            \"failure\",\n            None,\n        )\n        if reason is None:\n            return None\n        return patch_error_message(reason)\n\n    async def delete(self) -> None:\n        await self._channel.send(\n            \"delete\",\n            None,\n        )\n\n    async def read_info_buffer(self) -> bytes:\n        stream = cast(\n            Stream,\n            from_channel(\n                await self._channel.send(\n                    \"stream\",\n                    None,\n                )\n            ),\n        )\n        buffer = await stream.read_all()\n        return buffer\n\n    async def cancel(self) -> None:  # pyright: ignore[reportIncompatibleMethodOverride]\n        await self._channel.send(\n            \"cancel\",\n            None,\n        )\n"
  },
  {
    "path": "playwright/_impl/_assertions.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport collections.abc\nfrom typing import Any, List, Optional, Pattern, Sequence, Union\nfrom urllib.parse import urljoin\n\nfrom playwright._impl._api_structures import (\n    AriaRole,\n    ExpectedTextValue,\n    FrameExpectOptions,\n    FrameExpectResult,\n)\nfrom playwright._impl._connection import format_call_log\nfrom playwright._impl._errors import Error\nfrom playwright._impl._fetch import APIResponse\nfrom playwright._impl._helper import is_textual_mime_type\nfrom playwright._impl._locator import Locator\nfrom playwright._impl._page import Page\nfrom playwright._impl._str_utils import escape_regex_flags\n\n\nclass AssertionsBase:\n    def __init__(\n        self,\n        locator: Locator,\n        timeout: float = None,\n        is_not: bool = False,\n        message: Optional[str] = None,\n    ) -> None:\n        self._actual_locator = locator\n        self._loop = locator._loop\n        self._dispatcher_fiber = locator._dispatcher_fiber\n        self._timeout = timeout\n        self._is_not = is_not\n        self._custom_message = message\n\n    async def _call_expect(\n        self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]\n    ) -> FrameExpectResult:\n        raise NotImplementedError(\n            \"_call_expect must be implemented in a derived class.\"\n        )\n\n    async def _expect_impl(\n        self,\n        expression: str,\n        expect_options: FrameExpectOptions,\n        expected: Any,\n        message: str,\n        title: str = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expect_options[\"isNot\"] = self._is_not\n        if expect_options.get(\"timeout\") is None:\n            expect_options[\"timeout\"] = self._timeout or 5_000\n        if expect_options[\"isNot\"]:\n            message = message.replace(\"expected to\", \"expected not to\")\n        if \"useInnerText\" in expect_options and expect_options[\"useInnerText\"] is None:\n            del expect_options[\"useInnerText\"]\n        result = await self._call_expect(expression, expect_options, title)\n        if result[\"matches\"] == self._is_not:\n            actual = result.get(\"received\")\n            if self._custom_message:\n                out_message = self._custom_message\n                if expected is not None:\n                    out_message += f\"\\nExpected value: '{expected or '<None>'}'\"\n            else:\n                out_message = (\n                    f\"{message} '{expected}'\" if expected is not None else f\"{message}\"\n                )\n            error_message = result.get(\"errorMessage\")\n            error_message = f\"\\n{error_message}\" if error_message else \"\"\n            raise AssertionError(\n                f\"{out_message}\\nActual value: {actual}{error_message} {format_call_log(result.get('log'))}\"\n            )\n\n\nclass PageAssertions(AssertionsBase):\n    def __init__(\n        self,\n        page: Page,\n        timeout: float = None,\n        is_not: bool = False,\n        message: Optional[str] = None,\n    ) -> None:\n        super().__init__(page.locator(\":root\"), timeout, is_not, message)\n        self._actual_page = page\n\n    async def _call_expect(\n        self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]\n    ) -> FrameExpectResult:\n        __tracebackhide__ = True\n        return await self._actual_page.main_frame._expect(\n            None, expression, expect_options, title\n        )\n\n    @property\n    def _not(self) -> \"PageAssertions\":\n        return PageAssertions(\n            self._actual_page, self._timeout, not self._is_not, self._custom_message\n        )\n\n    async def to_have_title(\n        self, titleOrRegExp: Union[Pattern[str], str], timeout: float = None\n    ) -> None:\n        __tracebackhide__ = True\n        expected_values = to_expected_text_values(\n            [titleOrRegExp], normalize_white_space=True\n        )\n        await self._expect_impl(\n            \"to.have.title\",\n            FrameExpectOptions(expectedText=expected_values, timeout=timeout),\n            titleOrRegExp,\n            \"Page title expected to be\",\n            'Expect \"to_have_title\"',\n        )\n\n    async def not_to_have_title(\n        self, titleOrRegExp: Union[Pattern[str], str], timeout: float = None\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_title(titleOrRegExp, timeout)\n\n    async def to_have_url(\n        self,\n        urlOrRegExp: Union[str, Pattern[str]],\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        base_url = self._actual_page.context._base_url\n        if isinstance(urlOrRegExp, str) and base_url:\n            urlOrRegExp = urljoin(base_url, urlOrRegExp)\n        expected_text = to_expected_text_values([urlOrRegExp], ignoreCase=ignoreCase)\n        await self._expect_impl(\n            \"to.have.url\",\n            FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n            urlOrRegExp,\n            \"Page URL expected to be\",\n            'Expect \"to_have_url\"',\n        )\n\n    async def not_to_have_url(\n        self,\n        urlOrRegExp: Union[Pattern[str], str],\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_url(urlOrRegExp, timeout, ignoreCase)\n\n\nclass LocatorAssertions(AssertionsBase):\n    def __init__(\n        self,\n        locator: Locator,\n        timeout: float = None,\n        is_not: bool = False,\n        message: Optional[str] = None,\n    ) -> None:\n        super().__init__(locator, timeout, is_not, message)\n        self._actual_locator = locator\n\n    async def _call_expect(\n        self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]\n    ) -> FrameExpectResult:\n        __tracebackhide__ = True\n        return await self._actual_locator._expect(expression, expect_options, title)\n\n    @property\n    def _not(self) -> \"LocatorAssertions\":\n        return LocatorAssertions(\n            self._actual_locator, self._timeout, not self._is_not, self._custom_message\n        )\n\n    async def to_contain_text(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        useInnerText: bool = None,\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if isinstance(expected, collections.abc.Sequence) and not isinstance(\n            expected, str\n        ):\n            expected_text = to_expected_text_values(\n                expected,\n                match_substring=True,\n                normalize_white_space=True,\n                ignoreCase=ignoreCase,\n            )\n            await self._expect_impl(\n                \"to.contain.text.array\",\n                FrameExpectOptions(\n                    expectedText=expected_text,\n                    useInnerText=useInnerText,\n                    timeout=timeout,\n                ),\n                expected,\n                \"Locator expected to contain text\",\n                'Expect \"to_contain_text\"',\n            )\n        else:\n            expected_text = to_expected_text_values(\n                [expected],\n                match_substring=True,\n                normalize_white_space=True,\n                ignoreCase=ignoreCase,\n            )\n            await self._expect_impl(\n                \"to.have.text\",\n                FrameExpectOptions(\n                    expectedText=expected_text,\n                    useInnerText=useInnerText,\n                    timeout=timeout,\n                ),\n                expected,\n                \"Locator expected to contain text\",\n                'Expect \"to_contain_text\"',\n            )\n\n    async def not_to_contain_text(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        useInnerText: bool = None,\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_contain_text(expected, useInnerText, timeout, ignoreCase)\n\n    async def to_have_attribute(\n        self,\n        name: str,\n        value: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_text = to_expected_text_values([value], ignoreCase=ignoreCase)\n        await self._expect_impl(\n            \"to.have.attribute.value\",\n            FrameExpectOptions(\n                expressionArg=name, expectedText=expected_text, timeout=timeout\n            ),\n            value,\n            \"Locator expected to have attribute\",\n            'Expect \"to_have_attribute\"',\n        )\n\n    async def not_to_have_attribute(\n        self,\n        name: str,\n        value: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_attribute(\n            name, value, ignoreCase=ignoreCase, timeout=timeout\n        )\n\n    async def to_have_class(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if isinstance(expected, collections.abc.Sequence) and not isinstance(\n            expected, str\n        ):\n            expected_text = to_expected_text_values(expected)\n            await self._expect_impl(\n                \"to.have.class.array\",\n                FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n                expected,\n                \"Locator expected to have class\",\n                'Expect \"to_have_class\"',\n            )\n        else:\n            expected_text = to_expected_text_values([expected])\n            await self._expect_impl(\n                \"to.have.class\",\n                FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n                expected,\n                \"Locator expected to have class\",\n                'Expect \"to_have_class\"',\n            )\n\n    async def not_to_have_class(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_class(expected, timeout)\n\n    async def to_contain_class(\n        self,\n        expected: Union[\n            Sequence[str],\n            str,\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if isinstance(expected, collections.abc.Sequence) and not isinstance(\n            expected, str\n        ):\n            expected_text = to_expected_text_values(expected)\n            await self._expect_impl(\n                \"to.contain.class.array\",\n                FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n                expected,\n                \"Locator expected to contain class names\",\n                'Expect \"to_contain_class\"',\n            )\n        else:\n            expected_text = to_expected_text_values([expected])\n            await self._expect_impl(\n                \"to.contain.class\",\n                FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n                expected,\n                \"Locator expected to contain class\",\n                'Expect \"to_contain_class\"',\n            )\n\n    async def not_to_contain_class(\n        self,\n        expected: Union[\n            Sequence[str],\n            str,\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_contain_class(expected, timeout)\n\n    async def to_have_count(\n        self,\n        count: int,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.have.count\",\n            FrameExpectOptions(expectedNumber=count, timeout=timeout),\n            count,\n            \"Locator expected to have count\",\n            'Expect \"to_have_count\"',\n        )\n\n    async def not_to_have_count(\n        self,\n        count: int,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_count(count, timeout)\n\n    async def to_have_css(\n        self,\n        name: str,\n        value: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_text = to_expected_text_values([value])\n        await self._expect_impl(\n            \"to.have.css\",\n            FrameExpectOptions(\n                expressionArg=name, expectedText=expected_text, timeout=timeout\n            ),\n            value,\n            \"Locator expected to have CSS\",\n            'Expect \"to_have_css\"',\n        )\n\n    async def not_to_have_css(\n        self,\n        name: str,\n        value: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_css(name, value, timeout)\n\n    async def to_have_id(\n        self,\n        id: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_text = to_expected_text_values([id])\n        await self._expect_impl(\n            \"to.have.id\",\n            FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n            id,\n            \"Locator expected to have ID\",\n            'Expect \"to_have_id\"',\n        )\n\n    async def not_to_have_id(\n        self,\n        id: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_id(id, timeout)\n\n    async def to_have_js_property(\n        self,\n        name: str,\n        value: Any,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.have.property\",\n            FrameExpectOptions(\n                expressionArg=name, expectedValue=value, timeout=timeout\n            ),\n            value,\n            \"Locator expected to have JS Property\",\n            'Expect \"to_have_property\"',\n        )\n\n    async def not_to_have_js_property(\n        self,\n        name: str,\n        value: Any,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_js_property(name, value, timeout)\n\n    async def to_have_value(\n        self,\n        value: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_text = to_expected_text_values([value])\n        await self._expect_impl(\n            \"to.have.value\",\n            FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n            value,\n            \"Locator expected to have Value\",\n            'Expect \"to_have_value\"',\n        )\n\n    async def not_to_have_value(\n        self,\n        value: Union[str, Pattern[str]],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_value(value, timeout)\n\n    async def to_have_values(\n        self,\n        values: Union[\n            Sequence[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]]\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_text = to_expected_text_values(values)\n        await self._expect_impl(\n            \"to.have.values\",\n            FrameExpectOptions(expectedText=expected_text, timeout=timeout),\n            values,\n            \"Locator expected to have Values\",\n            'Expect \"to_have_values\"',\n        )\n\n    async def not_to_have_values(\n        self,\n        values: Union[\n            Sequence[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]]\n        ],\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_values(values, timeout)\n\n    async def to_have_text(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        useInnerText: bool = None,\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if isinstance(expected, collections.abc.Sequence) and not isinstance(\n            expected, str\n        ):\n            expected_text = to_expected_text_values(\n                expected,\n                normalize_white_space=True,\n                ignoreCase=ignoreCase,\n            )\n            await self._expect_impl(\n                \"to.have.text.array\",\n                FrameExpectOptions(\n                    expectedText=expected_text,\n                    useInnerText=useInnerText,\n                    timeout=timeout,\n                ),\n                expected,\n                \"Locator expected to have text\",\n                'Expect \"to_have_text\"',\n            )\n        else:\n            expected_text = to_expected_text_values(\n                [expected], normalize_white_space=True, ignoreCase=ignoreCase\n            )\n            await self._expect_impl(\n                \"to.have.text\",\n                FrameExpectOptions(\n                    expectedText=expected_text,\n                    useInnerText=useInnerText,\n                    timeout=timeout,\n                ),\n                expected,\n                \"Locator expected to have text\",\n                'Expect \"to_have_text\"',\n            )\n\n    async def not_to_have_text(\n        self,\n        expected: Union[\n            Sequence[str],\n            Sequence[Pattern[str]],\n            Sequence[Union[Pattern[str], str]],\n            Pattern[str],\n            str,\n        ],\n        useInnerText: bool = None,\n        timeout: float = None,\n        ignoreCase: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_text(expected, useInnerText, timeout, ignoreCase)\n\n    async def to_be_attached(\n        self,\n        attached: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if attached is None:\n            attached = True\n        attached_string = \"attached\" if attached else \"detached\"\n        await self._expect_impl(\n            (\"to.be.attached\" if attached else \"to.be.detached\"),\n            FrameExpectOptions(timeout=timeout),\n            None,\n            f\"Locator expected to be {attached_string}\",\n            'Expect \"to_be_attached\"',\n        )\n\n    async def to_be_checked(\n        self,\n        timeout: float = None,\n        checked: bool = None,\n        indeterminate: bool = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_value = {}\n        if indeterminate is not None:\n            expected_value[\"indeterminate\"] = indeterminate\n        if checked is not None:\n            expected_value[\"checked\"] = checked\n        checked_string: str\n        if indeterminate:\n            checked_string = \"indeterminate\"\n        else:\n            checked_string = \"unchecked\" if checked is False else \"checked\"\n        await self._expect_impl(\n            \"to.be.checked\",\n            FrameExpectOptions(timeout=timeout, expectedValue=expected_value),\n            None,\n            f\"Locator expected to be {checked_string}\",\n            'Expect \"to_be_checked\"',\n        )\n\n    async def not_to_be_attached(\n        self,\n        attached: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_attached(attached=attached, timeout=timeout)\n\n    async def not_to_be_checked(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_checked(timeout)\n\n    async def to_be_disabled(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.be.disabled\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            \"Locator expected to be disabled\",\n            'Expect \"to_be_disabled\"',\n        )\n\n    async def not_to_be_disabled(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_disabled(timeout)\n\n    async def to_be_editable(\n        self,\n        editable: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if editable is None:\n            editable = True\n        editable_string = \"editable\" if editable else \"readonly\"\n        await self._expect_impl(\n            \"to.be.editable\" if editable else \"to.be.readonly\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            f\"Locator expected to be {editable_string}\",\n            'Expect \"to_be_editable\"',\n        )\n\n    async def not_to_be_editable(\n        self,\n        editable: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_editable(editable, timeout)\n\n    async def to_be_empty(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.be.empty\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            \"Locator expected to be empty\",\n            'Expect \"to_be_empty\"',\n        )\n\n    async def not_to_be_empty(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_empty(timeout)\n\n    async def to_be_enabled(\n        self,\n        enabled: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if enabled is None:\n            enabled = True\n        enabled_string = \"enabled\" if enabled else \"disabled\"\n        await self._expect_impl(\n            \"to.be.enabled\" if enabled else \"to.be.disabled\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            f\"Locator expected to be {enabled_string}\",\n            'Expect \"to_be_enabled\"',\n        )\n\n    async def not_to_be_enabled(\n        self,\n        enabled: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_enabled(enabled, timeout)\n\n    async def to_be_hidden(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.be.hidden\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            \"Locator expected to be hidden\",\n            'Expect \"to_be_hidden\"',\n        )\n\n    async def not_to_be_hidden(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_hidden(timeout)\n\n    async def to_be_visible(\n        self,\n        visible: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        if visible is None:\n            visible = True\n        visible_string = \"visible\" if visible else \"hidden\"\n        await self._expect_impl(\n            \"to.be.visible\" if visible else \"to.be.hidden\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            f\"Locator expected to be {visible_string}\",\n            'Expect \"to_be_visible\"',\n        )\n\n    async def not_to_be_visible(\n        self,\n        visible: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_visible(visible, timeout)\n\n    async def to_be_focused(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.be.focused\",\n            FrameExpectOptions(timeout=timeout),\n            None,\n            \"Locator expected to be focused\",\n            'Expect \"to_be_focused\"',\n        )\n\n    async def not_to_be_focused(\n        self,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_focused(timeout)\n\n    async def to_be_in_viewport(\n        self,\n        ratio: float = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.be.in.viewport\",\n            FrameExpectOptions(timeout=timeout, expectedNumber=ratio),\n            None,\n            \"Locator expected to be in viewport\",\n            'Expect \"to_be_in_viewport\"',\n        )\n\n    async def not_to_be_in_viewport(\n        self, ratio: float = None, timeout: float = None\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_in_viewport(ratio=ratio, timeout=timeout)\n\n    async def to_have_accessible_description(\n        self,\n        description: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_values = to_expected_text_values(\n            [description], ignoreCase=ignoreCase, normalize_white_space=True\n        )\n        await self._expect_impl(\n            \"to.have.accessible.description\",\n            FrameExpectOptions(expectedText=expected_values, timeout=timeout),\n            None,\n            \"Locator expected to have accessible description\",\n            'Expect \"to_have_accessible_description\"',\n        )\n\n    async def not_to_have_accessible_description(\n        self,\n        name: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_accessible_description(name, ignoreCase, timeout)\n\n    async def to_have_accessible_name(\n        self,\n        name: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_values = to_expected_text_values(\n            [name], ignoreCase=ignoreCase, normalize_white_space=True\n        )\n        await self._expect_impl(\n            \"to.have.accessible.name\",\n            FrameExpectOptions(expectedText=expected_values, timeout=timeout),\n            None,\n            \"Locator expected to have accessible name\",\n            'Expect \"to_have_accessible_name\"',\n        )\n\n    async def not_to_have_accessible_name(\n        self,\n        name: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_accessible_name(name, ignoreCase, timeout)\n\n    async def to_have_role(self, role: AriaRole, timeout: float = None) -> None:\n        __tracebackhide__ = True\n        if isinstance(role, Pattern):\n            raise Error('\"role\" argument in to_have_role must be a string')\n        expected_values = to_expected_text_values([role])\n        await self._expect_impl(\n            \"to.have.role\",\n            FrameExpectOptions(expectedText=expected_values, timeout=timeout),\n            None,\n            \"Locator expected to have accessible role\",\n            'Expect \"to_have_role\"',\n        )\n\n    async def to_have_accessible_error_message(\n        self,\n        errorMessage: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        expected_values = to_expected_text_values(\n            [errorMessage], ignoreCase=ignoreCase, normalize_white_space=True\n        )\n        await self._expect_impl(\n            \"to.have.accessible.error.message\",\n            FrameExpectOptions(expectedText=expected_values, timeout=timeout),\n            None,\n            \"Locator expected to have accessible error message\",\n            'Expect \"to_have_accessible_error_message\"',\n        )\n\n    async def not_to_have_accessible_error_message(\n        self,\n        errorMessage: Union[str, Pattern[str]],\n        ignoreCase: bool = None,\n        timeout: float = None,\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_accessible_error_message(\n            errorMessage=errorMessage, ignoreCase=ignoreCase, timeout=timeout\n        )\n\n    async def not_to_have_role(self, role: AriaRole, timeout: float = None) -> None:\n        __tracebackhide__ = True\n        await self._not.to_have_role(role, timeout)\n\n    async def to_match_aria_snapshot(\n        self, expected: str, timeout: float = None\n    ) -> None:\n        __tracebackhide__ = True\n        await self._expect_impl(\n            \"to.match.aria\",\n            FrameExpectOptions(expectedValue=expected, timeout=timeout),\n            expected,\n            \"Locator expected to match Aria snapshot\",\n            'Expect \"to_match_aria_snapshot\"',\n        )\n\n    async def not_to_match_aria_snapshot(\n        self, expected: str, timeout: float = None\n    ) -> None:\n        __tracebackhide__ = True\n        await self._not.to_match_aria_snapshot(expected, timeout)\n\n\nclass APIResponseAssertions:\n    def __init__(\n        self,\n        response: APIResponse,\n        timeout: float = None,\n        is_not: bool = False,\n        message: Optional[str] = None,\n    ) -> None:\n        self._loop = response._loop\n        self._dispatcher_fiber = response._dispatcher_fiber\n        self._timeout = timeout\n        self._is_not = is_not\n        self._actual = response\n        self._custom_message = message\n\n    @property\n    def _not(self) -> \"APIResponseAssertions\":\n        return APIResponseAssertions(\n            self._actual, self._timeout, not self._is_not, self._custom_message\n        )\n\n    async def to_be_ok(\n        self,\n    ) -> None:\n        __tracebackhide__ = True\n        if self._is_not is not self._actual.ok:\n            return\n        message = f\"Response status expected to be within [200..299] range, was '{self._actual.status}'\"\n        if self._is_not:\n            message = message.replace(\"expected to\", \"expected not to\")\n        out_message = self._custom_message or message\n        out_message += format_call_log(await self._actual._fetch_log())\n\n        content_type = self._actual.headers.get(\"content-type\")\n        is_text_encoding = content_type and is_textual_mime_type(content_type)\n        text = await self._actual.text() if is_text_encoding else None\n        if text is not None:\n            out_message += f\"\\n Response Text:\\n{text[:1000]}\"\n\n        raise AssertionError(out_message)\n\n    async def not_to_be_ok(self) -> None:\n        __tracebackhide__ = True\n        await self._not.to_be_ok()\n\n\ndef expected_regex(\n    pattern: Pattern[str],\n    match_substring: bool,\n    normalize_white_space: bool,\n    ignoreCase: Optional[bool] = None,\n) -> ExpectedTextValue:\n    expected = ExpectedTextValue(\n        regexSource=pattern.pattern,\n        regexFlags=escape_regex_flags(pattern),\n        matchSubstring=match_substring,\n        normalizeWhiteSpace=normalize_white_space,\n        ignoreCase=ignoreCase,\n    )\n    if expected[\"ignoreCase\"] is None:\n        del expected[\"ignoreCase\"]\n    return expected\n\n\ndef to_expected_text_values(\n    items: Union[\n        Sequence[Pattern[str]], Sequence[str], Sequence[Union[str, Pattern[str]]]\n    ],\n    match_substring: bool = False,\n    normalize_white_space: bool = False,\n    ignoreCase: Optional[bool] = None,\n) -> Sequence[ExpectedTextValue]:\n    out: List[ExpectedTextValue] = []\n    assert isinstance(items, (list, tuple))\n    for item in items:\n        if isinstance(item, str):\n            o = ExpectedTextValue(\n                string=item,\n                matchSubstring=match_substring,\n                normalizeWhiteSpace=normalize_white_space,\n                ignoreCase=ignoreCase,\n            )\n            if o[\"ignoreCase\"] is None:\n                del o[\"ignoreCase\"]\n            out.append(o)\n        elif isinstance(item, Pattern):\n            out.append(\n                expected_regex(item, match_substring, normalize_white_space, ignoreCase)\n            )\n        else:\n            raise Error(\"value must be a string or regular expression\")\n    return out\n"
  },
  {
    "path": "playwright/_impl/_async_base.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom contextlib import AbstractAsyncContextManager\nfrom types import TracebackType\nfrom typing import Any, Callable, Generic, Optional, Type, TypeVar, Union\n\nfrom playwright._impl._impl_to_api_mapping import ImplToApiMapping, ImplWrapper\n\nmapping = ImplToApiMapping()\n\n\nT = TypeVar(\"T\")\nSelf = TypeVar(\"Self\", bound=\"AsyncContextManager\")\n\n\nclass AsyncEventInfo(Generic[T]):\n    def __init__(self, future: \"asyncio.Future[T]\") -> None:\n        self._future = future\n\n    @property\n    async def value(self) -> T:\n        return mapping.from_maybe_impl(await self._future)\n\n    def _cancel(self) -> None:\n        self._future.cancel()\n\n    def is_done(self) -> bool:\n        return self._future.done()\n\n\nclass AsyncEventContextManager(Generic[T], AbstractAsyncContextManager):\n    def __init__(self, future: \"asyncio.Future[T]\") -> None:\n        self._event = AsyncEventInfo[T](future)\n\n    async def __aenter__(self) -> AsyncEventInfo[T]:\n        return self._event\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        if exc_val:\n            self._event._cancel()\n        else:\n            await self._event.value\n\n\nclass AsyncBase(ImplWrapper):\n    def __init__(self, impl_obj: Any) -> None:\n        super().__init__(impl_obj)\n        self._loop = impl_obj._loop\n\n    def __str__(self) -> str:\n        return self._impl_obj.__str__()\n\n    def _wrap_handler(\n        self, handler: Union[Callable[..., Any], Any]\n    ) -> Callable[..., None]:\n        if callable(handler):\n            return mapping.wrap_handler(handler)\n        return handler\n\n    def on(self, event: Any, f: Any) -> None:\n        \"\"\"Registers the function ``f`` to the event name ``event``.\"\"\"\n        self._impl_obj.on(event, self._wrap_handler(f))\n\n    def once(self, event: Any, f: Any) -> None:\n        \"\"\"The same as ``self.on``, except that the listener is automatically\n        removed after being called.\n        \"\"\"\n        self._impl_obj.once(event, self._wrap_handler(f))\n\n    def remove_listener(self, event: Any, f: Any) -> None:\n        \"\"\"Removes the function ``f`` from ``event``.\"\"\"\n        self._impl_obj.remove_listener(event, self._wrap_handler(f))\n\n\nclass AsyncContextManager(AsyncBase):\n    async def __aenter__(self: Self) -> Self:\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        traceback: Optional[TracebackType],\n    ) -> None:\n        await self.close()\n\n    async def close(self) -> None: ...\n"
  },
  {
    "path": "playwright/_impl/_browser.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import (\n    TYPE_CHECKING,\n    Dict,\n    List,\n    Optional,\n    Pattern,\n    Sequence,\n    Set,\n    Union,\n    cast,\n)\n\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    Geolocation,\n    HttpCredentials,\n    ProxySettings,\n    StorageState,\n    ViewportSize,\n)\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._browser_context import BrowserContext\nfrom playwright._impl._cdp_session import CDPSession\nfrom playwright._impl._connection import ChannelOwner, from_channel\nfrom playwright._impl._errors import is_target_closed_error\nfrom playwright._impl._helper import (\n    ColorScheme,\n    Contrast,\n    ForcedColors,\n    HarContentPolicy,\n    HarMode,\n    ReducedMotion,\n    ServiceWorkersPolicy,\n    locals_to_params,\n    make_dirs_for_file,\n)\nfrom playwright._impl._page import Page\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser_type import BrowserType\n\n\nclass Browser(ChannelOwner):\n    Events = SimpleNamespace(\n        Disconnected=\"disconnected\",\n    )\n\n    def __init__(\n        self, parent: \"BrowserType\", type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._browser_type: Optional[\"BrowserType\"] = None\n        self._is_connected = True\n        self._should_close_connection_on_close = False\n        self._cr_tracing_path: Optional[str] = None\n\n        self._contexts: Set[BrowserContext] = set()\n        self._traces_dir: Optional[str] = None\n        self._channel.on(\n            \"context\",\n            lambda params: self._did_create_context(\n                cast(BrowserContext, from_channel(params[\"context\"]))\n            ),\n        )\n        self._channel.on(\"close\", lambda _: self._on_close())\n        self._close_reason: Optional[str] = None\n\n    def __repr__(self) -> str:\n        return f\"<Browser type={self._browser_type} version={self.version}>\"\n\n    def _connect_to_browser_type(\n        self,\n        browser_type: \"BrowserType\",\n        traces_dir: Optional[str] = None,\n    ) -> None:\n        # Note: when using connect(), `browserType` is different from `this.parent`.\n        # This is why browser type is not wired up in the constructor, and instead this separate method is called later on.\n        self._browser_type = browser_type\n        self._traces_dir = traces_dir\n        for context in self._contexts:\n            self._setup_browser_context(context)\n\n    def _did_create_context(self, context: BrowserContext) -> None:\n        context._browser = self\n        self._contexts.add(context)\n        # Note: when connecting to a browser, initial contexts arrive before `_browserType` is set,\n        # and will be configured later in `ConnectToBrowserType`.\n        if self._browser_type:\n            self._setup_browser_context(context)\n\n    def _setup_browser_context(self, context: BrowserContext) -> None:\n        context._tracing._traces_dir = self._traces_dir\n        assert self._browser_type is not None\n        self._browser_type._playwright.selectors._contexts_for_selectors.add(context)\n\n    def _on_close(self) -> None:\n        self._is_connected = False\n        self.emit(Browser.Events.Disconnected, self)\n\n    @property\n    def contexts(self) -> List[BrowserContext]:\n        return list(self._contexts)\n\n    @property\n    def browser_type(self) -> \"BrowserType\":\n        assert self._browser_type is not None\n        return self._browser_type\n\n    def is_connected(self) -> bool:\n        return self._is_connected\n\n    async def new_context(\n        self,\n        viewport: ViewportSize = None,\n        screen: ViewportSize = None,\n        noViewport: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        javaScriptEnabled: bool = None,\n        bypassCSP: bool = None,\n        userAgent: str = None,\n        locale: str = None,\n        timezoneId: str = None,\n        geolocation: Geolocation = None,\n        permissions: Sequence[str] = None,\n        extraHTTPHeaders: Dict[str, str] = None,\n        offline: bool = None,\n        httpCredentials: HttpCredentials = None,\n        deviceScaleFactor: float = None,\n        isMobile: bool = None,\n        hasTouch: bool = None,\n        colorScheme: ColorScheme = None,\n        reducedMotion: ReducedMotion = None,\n        forcedColors: ForcedColors = None,\n        contrast: Contrast = None,\n        acceptDownloads: bool = None,\n        defaultBrowserType: str = None,\n        proxy: ProxySettings = None,\n        recordHarPath: Union[Path, str] = None,\n        recordHarOmitContent: bool = None,\n        recordVideoDir: Union[Path, str] = None,\n        recordVideoSize: ViewportSize = None,\n        storageState: Union[StorageState, str, Path] = None,\n        baseURL: str = None,\n        strictSelectors: bool = None,\n        serviceWorkers: ServiceWorkersPolicy = None,\n        recordHarUrlFilter: Union[Pattern[str], str] = None,\n        recordHarMode: HarMode = None,\n        recordHarContent: HarContentPolicy = None,\n        clientCertificates: List[ClientCertificate] = None,\n    ) -> BrowserContext:\n        params = locals_to_params(locals())\n        assert self._browser_type is not None\n        await self._browser_type._prepare_browser_context_params(params)\n\n        channel = await self._channel.send(\"newContext\", None, params)\n        context = cast(BrowserContext, from_channel(channel))\n        await context._initialize_har_from_options(\n            record_har_content=recordHarContent,\n            record_har_mode=recordHarMode,\n            record_har_omit_content=recordHarOmitContent,\n            record_har_path=recordHarPath,\n            record_har_url_filter=recordHarUrlFilter,\n        )\n        return context\n\n    async def new_page(\n        self,\n        viewport: ViewportSize = None,\n        screen: ViewportSize = None,\n        noViewport: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        javaScriptEnabled: bool = None,\n        bypassCSP: bool = None,\n        userAgent: str = None,\n        locale: str = None,\n        timezoneId: str = None,\n        geolocation: Geolocation = None,\n        permissions: Sequence[str] = None,\n        extraHTTPHeaders: Dict[str, str] = None,\n        offline: bool = None,\n        httpCredentials: HttpCredentials = None,\n        deviceScaleFactor: float = None,\n        isMobile: bool = None,\n        hasTouch: bool = None,\n        colorScheme: ColorScheme = None,\n        forcedColors: ForcedColors = None,\n        contrast: Contrast = None,\n        reducedMotion: ReducedMotion = None,\n        acceptDownloads: bool = None,\n        defaultBrowserType: str = None,\n        proxy: ProxySettings = None,\n        recordHarPath: Union[Path, str] = None,\n        recordHarOmitContent: bool = None,\n        recordVideoDir: Union[Path, str] = None,\n        recordVideoSize: ViewportSize = None,\n        storageState: Union[StorageState, str, Path] = None,\n        baseURL: str = None,\n        strictSelectors: bool = None,\n        serviceWorkers: ServiceWorkersPolicy = None,\n        recordHarUrlFilter: Union[Pattern[str], str] = None,\n        recordHarMode: HarMode = None,\n        recordHarContent: HarContentPolicy = None,\n        clientCertificates: List[ClientCertificate] = None,\n    ) -> Page:\n        params = locals_to_params(locals())\n\n        async def inner() -> Page:\n            context = await self.new_context(**params)\n            page = await context.new_page()\n            page._owned_context = context\n            context._owner_page = page\n            return page\n\n        return await self._connection.wrap_api_call(inner, title=\"Create page\")\n\n    async def close(self, reason: str = None) -> None:\n        self._close_reason = reason\n        try:\n            if self._should_close_connection_on_close:\n                await self._connection.stop_async()\n            else:\n                await self._channel.send(\"close\", None, {\"reason\": reason})\n        except Exception as e:\n            if not is_target_closed_error(e):\n                raise e\n\n    @property\n    def version(self) -> str:\n        return self._initializer[\"version\"]\n\n    async def new_browser_cdp_session(self) -> CDPSession:\n        return from_channel(await self._channel.send(\"newBrowserCDPSession\", None))\n\n    async def start_tracing(\n        self,\n        page: Page = None,\n        path: Union[str, Path] = None,\n        screenshots: bool = None,\n        categories: Sequence[str] = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        if page:\n            params[\"page\"] = page._channel\n        if path:\n            self._cr_tracing_path = str(path)\n            params[\"path\"] = str(path)\n        await self._channel.send(\"startTracing\", None, params)\n\n    async def stop_tracing(self) -> bytes:\n        artifact = cast(\n            Artifact, from_channel(await self._channel.send(\"stopTracing\", None))\n        )\n        buffer = await artifact.read_info_buffer()\n        await artifact.delete()\n        if self._cr_tracing_path:\n            make_dirs_for_file(self._cr_tracing_path)\n            with open(self._cr_tracing_path, \"wb\") as f:\n                f.write(buffer)\n            self._cr_tracing_path = None\n        return buffer\n"
  },
  {
    "path": "playwright/_impl/_browser_context.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Pattern,\n    Sequence,\n    Set,\n    Union,\n    cast,\n)\n\nfrom playwright._impl._api_structures import (\n    Cookie,\n    Geolocation,\n    SetCookieParam,\n    StorageState,\n)\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._cdp_session import CDPSession\nfrom playwright._impl._clock import Clock\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._console_message import ConsoleMessage\nfrom playwright._impl._dialog import Dialog\nfrom playwright._impl._errors import Error, TargetClosedError\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._fetch import APIRequestContext\nfrom playwright._impl._frame import Frame\nfrom playwright._impl._har_router import HarRouter\nfrom playwright._impl._helper import (\n    HarContentPolicy,\n    HarMode,\n    HarRecordingMetadata,\n    RouteFromHarNotFoundPolicy,\n    RouteHandler,\n    RouteHandlerCallback,\n    TimeoutSettings,\n    URLMatch,\n    WebSocketRouteHandlerCallback,\n    async_readfile,\n    async_writefile,\n    locals_to_params,\n    parse_error,\n    to_impl,\n)\nfrom playwright._impl._network import (\n    Request,\n    Response,\n    Route,\n    WebSocketRoute,\n    WebSocketRouteHandler,\n    serialize_headers,\n)\nfrom playwright._impl._page import BindingCall, Page, Worker\nfrom playwright._impl._str_utils import escape_regex_flags\nfrom playwright._impl._tracing import Tracing\nfrom playwright._impl._waiter import Waiter\nfrom playwright._impl._web_error import WebError\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser import Browser\n\n\nclass BrowserContext(ChannelOwner):\n    Events = SimpleNamespace(\n        # Deprecated in v1.56, never emitted anymore.\n        BackgroundPage=\"backgroundpage\",\n        Close=\"close\",\n        Console=\"console\",\n        Dialog=\"dialog\",\n        Page=\"page\",\n        WebError=\"weberror\",\n        ServiceWorker=\"serviceworker\",\n        Request=\"request\",\n        Response=\"response\",\n        RequestFailed=\"requestfailed\",\n        RequestFinished=\"requestfinished\",\n    )\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        # Browser is null for browser contexts created outside of normal browser, e.g. android or electron.\n        # circular import workaround:\n        self._browser: Optional[\"Browser\"] = None\n        if parent.__class__.__name__ == \"Browser\":\n            self._browser = cast(\"Browser\", parent)\n        self._pages: List[Page] = []\n        self._routes: List[RouteHandler] = []\n        self._web_socket_routes: List[WebSocketRouteHandler] = []\n        self._bindings: Dict[str, Any] = {}\n        self._timeout_settings = TimeoutSettings(None)\n        self._owner_page: Optional[Page] = None\n        self._options: Dict[str, Any] = initializer[\"options\"]\n        self._service_workers: Set[Worker] = set()\n        self._base_url: Optional[str] = self._options.get(\"baseURL\")\n        self._videos_dir: Optional[str] = self._options.get(\"recordVideo\")\n        self._tracing = cast(Tracing, from_channel(initializer[\"tracing\"]))\n        self._har_recorders: Dict[str, HarRecordingMetadata] = {}\n        self._request: APIRequestContext = from_channel(initializer[\"requestContext\"])\n        self._request._timeout_settings = self._timeout_settings\n        self._clock = Clock(self)\n        self._channel.on(\n            \"bindingCall\",\n            lambda params: self._on_binding(from_channel(params[\"binding\"])),\n        )\n        self._channel.on(\"close\", lambda _: self._on_close())\n        self._channel.on(\n            \"page\", lambda params: self._on_page(from_channel(params[\"page\"]))\n        )\n        self._channel.on(\n            \"route\",\n            lambda params: self._loop.create_task(\n                self._on_route(\n                    from_channel(params.get(\"route\")),\n                )\n            ),\n        )\n        self._channel.on(\n            \"webSocketRoute\",\n            lambda params: self._loop.create_task(\n                self._on_web_socket_route(\n                    from_channel(params[\"webSocketRoute\"]),\n                )\n            ),\n        )\n\n        self._channel.on(\n            \"serviceWorker\",\n            lambda params: self._on_service_worker(from_channel(params[\"worker\"])),\n        )\n        self._channel.on(\n            \"console\",\n            lambda event: self._on_console_message(event),\n        )\n\n        self._channel.on(\n            \"dialog\", lambda params: self._on_dialog(from_channel(params[\"dialog\"]))\n        )\n        self._channel.on(\n            \"pageError\",\n            lambda params: self._on_page_error(\n                parse_error(params[\"error\"][\"error\"]),\n                from_nullable_channel(params[\"page\"]),\n            ),\n        )\n        self._channel.on(\n            \"request\",\n            lambda params: self._on_request(\n                from_channel(params[\"request\"]),\n                from_nullable_channel(params.get(\"page\")),\n            ),\n        )\n        self._channel.on(\n            \"response\",\n            lambda params: self._on_response(\n                from_channel(params[\"response\"]),\n                from_nullable_channel(params.get(\"page\")),\n            ),\n        )\n        self._channel.on(\n            \"requestFailed\",\n            lambda params: self._on_request_failed(\n                from_channel(params[\"request\"]),\n                params[\"responseEndTiming\"],\n                params.get(\"failureText\"),\n                from_nullable_channel(params.get(\"page\")),\n            ),\n        )\n        self._channel.on(\n            \"requestFinished\",\n            lambda params: self._on_request_finished(\n                from_channel(params[\"request\"]),\n                from_nullable_channel(params.get(\"response\")),\n                params[\"responseEndTiming\"],\n                from_nullable_channel(params.get(\"page\")),\n            ),\n        )\n        self._closed_future: asyncio.Future = asyncio.Future()\n        self.once(\n            self.Events.Close, lambda context: self._closed_future.set_result(True)\n        )\n        self._close_reason: Optional[str] = None\n        self._har_routers: List[HarRouter] = []\n        self._set_event_to_subscription_mapping(\n            {\n                BrowserContext.Events.Console: \"console\",\n                BrowserContext.Events.Dialog: \"dialog\",\n                BrowserContext.Events.Request: \"request\",\n                BrowserContext.Events.Response: \"response\",\n                BrowserContext.Events.RequestFinished: \"requestFinished\",\n                BrowserContext.Events.RequestFailed: \"requestFailed\",\n            }\n        )\n        self._closing_or_closed = False\n\n    def __repr__(self) -> str:\n        return f\"<BrowserContext browser={self.browser}>\"\n\n    def _on_page(self, page: Page) -> None:\n        self._pages.append(page)\n        self.emit(BrowserContext.Events.Page, page)\n        if page._opener and not page._opener.is_closed():\n            page._opener.emit(Page.Events.Popup, page)\n\n    async def _on_route(self, route: Route) -> None:\n        route._context = self\n        page = route.request._safe_page()\n        route_handlers = self._routes.copy()\n        for route_handler in route_handlers:\n            # If the page or the context was closed we stall all requests right away.\n            if (page and page._close_was_called) or self._closing_or_closed:\n                return\n            if not route_handler.matches(route.request.url):\n                continue\n            if route_handler not in self._routes:\n                continue\n            if route_handler.will_expire:\n                self._routes.remove(route_handler)\n            try:\n                handled = await route_handler.handle(route)\n            finally:\n                if len(self._routes) == 0:\n                    asyncio.create_task(\n                        self._connection.wrap_api_call(\n                            lambda: self._update_interception_patterns(), True\n                        )\n                    )\n            if handled:\n                return\n        try:\n            # If the page is closed or unrouteAll() was called without waiting and interception disabled,\n            # the method will throw an error - silence it.\n            await route._inner_continue(True)\n        except Exception:\n            pass\n\n    async def _on_web_socket_route(self, web_socket_route: WebSocketRoute) -> None:\n        route_handler = next(\n            (\n                route_handler\n                for route_handler in self._web_socket_routes\n                if route_handler.matches(web_socket_route.url)\n            ),\n            None,\n        )\n        if route_handler:\n            await route_handler.handle(web_socket_route)\n        else:\n            web_socket_route.connect_to_server()\n\n    def _on_binding(self, binding_call: BindingCall) -> None:\n        func = self._bindings.get(binding_call._initializer[\"name\"])\n        if func is None:\n            return\n        asyncio.create_task(binding_call.call(func))\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        return self._set_default_navigation_timeout_impl(timeout)\n\n    def _set_default_navigation_timeout_impl(self, timeout: Optional[float]) -> None:\n        self._timeout_settings.set_default_navigation_timeout(timeout)\n\n    def set_default_timeout(self, timeout: float) -> None:\n        return self._set_default_timeout_impl(timeout)\n\n    def _set_default_timeout_impl(self, timeout: Optional[float]) -> None:\n        self._timeout_settings.set_default_timeout(timeout)\n\n    @property\n    def pages(self) -> List[Page]:\n        return self._pages.copy()\n\n    @property\n    def browser(self) -> Optional[\"Browser\"]:\n        return self._browser\n\n    async def _initialize_har_from_options(\n        self,\n        record_har_path: Optional[Union[Path, str]],\n        record_har_content: Optional[HarContentPolicy],\n        record_har_omit_content: Optional[bool],\n        record_har_url_filter: Optional[Union[Pattern[str], str]],\n        record_har_mode: Optional[HarMode],\n    ) -> None:\n        if not record_har_path:\n            return\n        record_har_path = str(record_har_path)\n        default_policy: HarContentPolicy = (\n            \"attach\" if record_har_path.endswith(\".zip\") else \"embed\"\n        )\n        content_policy: HarContentPolicy = record_har_content or (\n            \"omit\" if record_har_omit_content is True else default_policy\n        )\n        await self._record_into_har(\n            har=record_har_path,\n            page=None,\n            url=record_har_url_filter,\n            update_content=content_policy,\n            update_mode=(record_har_mode or \"full\"),\n        )\n\n    async def new_page(self) -> Page:\n        if self._owner_page:\n            raise Error(\"Please use browser.new_context()\")\n        return from_channel(await self._channel.send(\"newPage\", None))\n\n    async def cookies(self, urls: Union[str, Sequence[str]] = None) -> List[Cookie]:\n        if urls is None:\n            urls = []\n        if isinstance(urls, str):\n            urls = [urls]\n        return await self._channel.send(\"cookies\", None, dict(urls=urls))\n\n    async def add_cookies(self, cookies: Sequence[SetCookieParam]) -> None:\n        await self._channel.send(\"addCookies\", None, dict(cookies=cookies))\n\n    async def clear_cookies(\n        self,\n        name: Union[str, Pattern[str]] = None,\n        domain: Union[str, Pattern[str]] = None,\n        path: Union[str, Pattern[str]] = None,\n    ) -> None:\n        await self._channel.send(\n            \"clearCookies\",\n            None,\n            {\n                \"name\": name if isinstance(name, str) else None,\n                \"nameRegexSource\": name.pattern if isinstance(name, Pattern) else None,\n                \"nameRegexFlags\": (\n                    escape_regex_flags(name) if isinstance(name, Pattern) else None\n                ),\n                \"domain\": domain if isinstance(domain, str) else None,\n                \"domainRegexSource\": (\n                    domain.pattern if isinstance(domain, Pattern) else None\n                ),\n                \"domainRegexFlags\": (\n                    escape_regex_flags(domain) if isinstance(domain, Pattern) else None\n                ),\n                \"path\": path if isinstance(path, str) else None,\n                \"pathRegexSource\": path.pattern if isinstance(path, Pattern) else None,\n                \"pathRegexFlags\": (\n                    escape_regex_flags(path) if isinstance(path, Pattern) else None\n                ),\n            },\n        )\n\n    async def grant_permissions(\n        self, permissions: Sequence[str], origin: str = None\n    ) -> None:\n        await self._channel.send(\"grantPermissions\", None, locals_to_params(locals()))\n\n    async def clear_permissions(self) -> None:\n        await self._channel.send(\"clearPermissions\", None)\n\n    async def set_geolocation(self, geolocation: Geolocation = None) -> None:\n        await self._channel.send(\"setGeolocation\", None, locals_to_params(locals()))\n\n    async def set_extra_http_headers(self, headers: Dict[str, str]) -> None:\n        await self._channel.send(\n            \"setExtraHTTPHeaders\", None, dict(headers=serialize_headers(headers))\n        )\n\n    async def set_offline(self, offline: bool) -> None:\n        await self._channel.send(\"setOffline\", None, dict(offline=offline))\n\n    async def add_init_script(\n        self, script: str = None, path: Union[str, Path] = None\n    ) -> None:\n        if path:\n            script = (await async_readfile(path)).decode()\n        if not isinstance(script, str):\n            raise Error(\"Either path or script parameter must be specified\")\n        await self._channel.send(\"addInitScript\", None, dict(source=script))\n\n    async def expose_binding(\n        self, name: str, callback: Callable, handle: bool = None\n    ) -> None:\n        for page in self._pages:\n            if name in page._bindings:\n                raise Error(\n                    f'Function \"{name}\" has been already registered in one of the pages'\n                )\n        if name in self._bindings:\n            raise Error(f'Function \"{name}\" has been already registered')\n        self._bindings[name] = callback\n        await self._channel.send(\n            \"exposeBinding\", None, dict(name=name, needsHandle=handle or False)\n        )\n\n    async def expose_function(self, name: str, callback: Callable) -> None:\n        await self.expose_binding(name, lambda source, *args: callback(*args))\n\n    async def route(\n        self, url: URLMatch, handler: RouteHandlerCallback, times: int = None\n    ) -> None:\n        self._routes.insert(\n            0,\n            RouteHandler(\n                self._base_url,\n                url,\n                handler,\n                True if self._dispatcher_fiber else False,\n                times,\n            ),\n        )\n        await self._update_interception_patterns()\n\n    async def unroute(\n        self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None\n    ) -> None:\n        removed = []\n        remaining = []\n        for route in self._routes:\n            if route.url != url or (handler and route.handler != handler):\n                remaining.append(route)\n            else:\n                removed.append(route)\n        await self._unroute_internal(removed, remaining, \"default\")\n\n    async def _unroute_internal(\n        self,\n        removed: List[RouteHandler],\n        remaining: List[RouteHandler],\n        behavior: Literal[\"default\", \"ignoreErrors\", \"wait\"] = None,\n    ) -> None:\n        self._routes = remaining\n        if behavior is not None and behavior != \"default\":\n            await asyncio.gather(*map(lambda router: router.stop(behavior), removed))  # type: ignore\n        await self._update_interception_patterns()\n\n    async def route_web_socket(\n        self, url: URLMatch, handler: WebSocketRouteHandlerCallback\n    ) -> None:\n        self._web_socket_routes.insert(\n            0,\n            WebSocketRouteHandler(self._base_url, url, handler),\n        )\n        await self._update_web_socket_interception_patterns()\n\n    def _dispose_har_routers(self) -> None:\n        for router in self._har_routers:\n            router.dispose()\n        self._har_routers = []\n\n    async def unroute_all(\n        self, behavior: Literal[\"default\", \"ignoreErrors\", \"wait\"] = None\n    ) -> None:\n        await self._unroute_internal(self._routes, [], behavior)\n        self._dispose_har_routers()\n\n    async def _record_into_har(\n        self,\n        har: Union[Path, str],\n        page: Optional[Page] = None,\n        url: Union[Pattern[str], str] = None,\n        update_content: HarContentPolicy = None,\n        update_mode: HarMode = None,\n    ) -> None:\n        update_content = update_content or \"attach\"\n        params: Dict[str, Any] = {\n            \"options\": {\n                \"zip\": str(har).endswith(\".zip\"),\n                \"content\": update_content,\n                \"urlGlob\": url if isinstance(url, str) else None,\n                \"urlRegexSource\": url.pattern if isinstance(url, Pattern) else None,\n                \"urlRegexFlags\": (\n                    escape_regex_flags(url) if isinstance(url, Pattern) else None\n                ),\n                \"mode\": update_mode or \"minimal\",\n            }\n        }\n        if page:\n            params[\"page\"] = page._channel\n        har_id = await self._channel.send(\"harStart\", None, params)\n        self._har_recorders[har_id] = {\n            \"path\": str(har),\n            \"content\": update_content,\n        }\n\n    async def route_from_har(\n        self,\n        har: Union[Path, str],\n        url: Union[Pattern[str], str] = None,\n        notFound: RouteFromHarNotFoundPolicy = None,\n        update: bool = None,\n        updateContent: Literal[\"attach\", \"embed\"] = None,\n        updateMode: HarMode = None,\n    ) -> None:\n        if update:\n            await self._record_into_har(\n                har=har,\n                page=None,\n                url=url,\n                update_content=updateContent,\n                update_mode=updateMode,\n            )\n            return\n        router = await HarRouter.create(\n            local_utils=self._connection.local_utils,\n            file=str(har),\n            not_found_action=notFound or \"abort\",\n            url_matcher=url,\n        )\n        self._har_routers.append(router)\n        await router.add_context_route(self)\n\n    async def _update_interception_patterns(self) -> None:\n        patterns = RouteHandler.prepare_interception_patterns(self._routes)\n        await self._channel.send(\n            \"setNetworkInterceptionPatterns\", None, {\"patterns\": patterns}\n        )\n\n    async def _update_web_socket_interception_patterns(self) -> None:\n        patterns = WebSocketRouteHandler.prepare_interception_patterns(\n            self._web_socket_routes\n        )\n        await self._channel.send(\n            \"setWebSocketInterceptionPatterns\", None, {\"patterns\": patterns}\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            timeout = self._timeout_settings.timeout()\n        waiter = Waiter(self, f\"browser_context.expect_event({event})\")\n        waiter.reject_on_timeout(\n            timeout, f'Timeout {timeout}ms exceeded while waiting for event \"{event}\"'\n        )\n        if event != BrowserContext.Events.Close:\n            waiter.reject_on_event(\n                self, BrowserContext.Events.Close, lambda: TargetClosedError()\n            )\n        waiter.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(waiter.result())\n\n    def _on_close(self) -> None:\n        self._closing_or_closed = True\n        if self._browser:\n            if self in self._browser._contexts:\n                self._browser._contexts.remove(self)\n            assert self._browser._browser_type is not None\n            if (\n                self\n                in self._browser._browser_type._playwright.selectors._contexts_for_selectors\n            ):\n                self._browser._browser_type._playwright.selectors._contexts_for_selectors.remove(\n                    self\n                )\n\n        self._dispose_har_routers()\n        self._tracing._reset_stack_counter()\n        self.emit(BrowserContext.Events.Close, self)\n\n    async def close(self, reason: str = None) -> None:\n        if self._closing_or_closed:\n            return\n        self._close_reason = reason\n        self._closing_or_closed = True\n\n        await self.request.dispose(reason=reason)\n\n        async def _inner_close() -> None:\n            for har_id, params in self._har_recorders.items():\n                har = cast(\n                    Artifact,\n                    from_channel(\n                        await self._channel.send(\"harExport\", None, {\"harId\": har_id})\n                    ),\n                )\n                # Server side will compress artifact if content is attach or if file is .zip.\n                is_compressed = params.get(\"content\") == \"attach\" or params[\n                    \"path\"\n                ].endswith(\".zip\")\n                need_compressed = params[\"path\"].endswith(\".zip\")\n                if is_compressed and not need_compressed:\n                    tmp_path = params[\"path\"] + \".tmp\"\n                    await har.save_as(tmp_path)\n                    await self._connection.local_utils.har_unzip(\n                        zipFile=tmp_path, harFile=params[\"path\"]\n                    )\n                else:\n                    await har.save_as(params[\"path\"])\n                await har.delete()\n\n        await self._channel._connection.wrap_api_call(_inner_close, True)\n        await self._channel.send(\"close\", None, {\"reason\": reason})\n        await self._closed_future\n\n    async def storage_state(\n        self, path: Union[str, Path] = None, indexedDB: bool = None\n    ) -> StorageState:\n        result = await self._channel.send_return_as_dict(\n            \"storageState\", None, {\"indexedDB\": indexedDB}\n        )\n        if path:\n            await async_writefile(path, json.dumps(result))\n        return result\n\n    def _effective_close_reason(self) -> Optional[str]:\n        if self._close_reason:\n            return self._close_reason\n        if self._browser:\n            return self._browser._close_reason\n        return None\n\n    async def wait_for_event(\n        self, event: str, predicate: Callable = None, timeout: float = None\n    ) -> Any:\n        async with self.expect_event(event, predicate, timeout) as event_info:\n            pass\n        return await event_info\n\n    def expect_console_message(\n        self,\n        predicate: Callable[[ConsoleMessage], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[ConsoleMessage]:\n        return self.expect_event(Page.Events.Console, predicate, timeout)\n\n    def expect_page(\n        self,\n        predicate: Callable[[Page], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Page]:\n        return self.expect_event(BrowserContext.Events.Page, predicate, timeout)\n\n    def _on_service_worker(self, worker: Worker) -> None:\n        worker._context = self\n        self._service_workers.add(worker)\n        self.emit(BrowserContext.Events.ServiceWorker, worker)\n\n    def _on_request_failed(\n        self,\n        request: Request,\n        response_end_timing: float,\n        failure_text: Optional[str],\n        page: Optional[Page],\n    ) -> None:\n        request._failure_text = failure_text\n        request._set_response_end_timing(response_end_timing)\n        self.emit(BrowserContext.Events.RequestFailed, request)\n        if page:\n            page.emit(Page.Events.RequestFailed, request)\n\n    def _on_request_finished(\n        self,\n        request: Request,\n        response: Optional[Response],\n        response_end_timing: float,\n        page: Optional[Page],\n    ) -> None:\n        request._set_response_end_timing(response_end_timing)\n        self.emit(BrowserContext.Events.RequestFinished, request)\n        if page:\n            page.emit(Page.Events.RequestFinished, request)\n        if response:\n            response._finished_future.set_result(True)\n\n    def _on_console_message(self, event: Dict) -> None:\n        message = ConsoleMessage(event, self._loop, self._dispatcher_fiber)\n        worker = message.worker\n        if worker:\n            worker.emit(Worker.Events.Console, message)\n        page = message.page\n        if page:\n            page.emit(Page.Events.Console, message)\n        self.emit(BrowserContext.Events.Console, message)\n\n    def _on_dialog(self, dialog: Dialog) -> None:\n        has_listeners = self.emit(BrowserContext.Events.Dialog, dialog)\n        page = dialog.page\n        if page:\n            has_listeners = page.emit(Page.Events.Dialog, dialog) or has_listeners\n        if not has_listeners:\n            # Although we do similar handling on the server side, we still need this logic\n            # on the client side due to a possible race condition between two async calls:\n            # a) removing \"dialog\" listener subscription (client->server)\n            # b) actual \"dialog\" event (server->client)\n            if dialog.type == \"beforeunload\":\n                asyncio.create_task(dialog.accept())\n            else:\n                asyncio.create_task(dialog.dismiss())\n\n    def _on_page_error(self, error: Error, page: Optional[Page]) -> None:\n        self.emit(\n            BrowserContext.Events.WebError,\n            WebError(self._loop, self._dispatcher_fiber, page, error),\n        )\n        if page:\n            page.emit(Page.Events.PageError, error)\n\n    def _on_request(self, request: Request, page: Optional[Page]) -> None:\n        self.emit(BrowserContext.Events.Request, request)\n        if page:\n            page.emit(Page.Events.Request, request)\n\n    def _on_response(self, response: Response, page: Optional[Page]) -> None:\n        self.emit(BrowserContext.Events.Response, response)\n        if page:\n            page.emit(Page.Events.Response, response)\n\n    @property\n    def background_pages(self) -> List[Page]:\n        return []\n\n    @property\n    def service_workers(self) -> List[Worker]:\n        return list(self._service_workers)\n\n    async def new_cdp_session(self, page: Union[Page, Frame]) -> CDPSession:\n        page = to_impl(page)\n        params = {}\n        if isinstance(page, Page):\n            params[\"page\"] = page._channel\n        elif isinstance(page, Frame):\n            params[\"frame\"] = page._channel\n        else:\n            raise Error(\"page: expected Page or Frame\")\n        return from_channel(await self._channel.send(\"newCDPSession\", None, params))\n\n    @property\n    def tracing(self) -> Tracing:\n        return self._tracing\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        return self._request\n\n    @property\n    def clock(self) -> Clock:\n        return self._clock\n"
  },
  {
    "path": "playwright/_impl/_browser_type.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nimport pathlib\nimport sys\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Dict, List, Optional, Pattern, Sequence, Union, cast\n\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    Geolocation,\n    HttpCredentials,\n    ProxySettings,\n    ViewportSize,\n)\nfrom playwright._impl._browser import Browser\nfrom playwright._impl._browser_context import BrowserContext\nfrom playwright._impl._connection import ChannelOwner, Connection, from_channel\nfrom playwright._impl._errors import Error\nfrom playwright._impl._helper import (\n    PLAYWRIGHT_MAX_DEADLINE,\n    ColorScheme,\n    Contrast,\n    Env,\n    ForcedColors,\n    HarContentPolicy,\n    HarMode,\n    ReducedMotion,\n    ServiceWorkersPolicy,\n    TimeoutSettings,\n    async_readfile,\n    locals_to_params,\n)\nfrom playwright._impl._json_pipe import JsonPipeTransport\nfrom playwright._impl._network import serialize_headers, to_client_certificates_protocol\nfrom playwright._impl._waiter import throw_on_timeout\n\nif TYPE_CHECKING:\n    from playwright._impl._playwright import Playwright\n\n\nclass BrowserType(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._playwright: \"Playwright\"\n\n    def __repr__(self) -> str:\n        return f\"<BrowserType name={self.name} executable_path={self.executable_path}>\"\n\n    @property\n    def name(self) -> str:\n        return self._initializer[\"name\"]\n\n    @property\n    def executable_path(self) -> str:\n        return self._initializer[\"executablePath\"]\n\n    async def launch(\n        self,\n        executablePath: Union[str, Path] = None,\n        channel: str = None,\n        args: Sequence[str] = None,\n        ignoreDefaultArgs: Union[bool, Sequence[str]] = None,\n        handleSIGINT: bool = None,\n        handleSIGTERM: bool = None,\n        handleSIGHUP: bool = None,\n        timeout: float = None,\n        env: Env = None,\n        headless: bool = None,\n        proxy: ProxySettings = None,\n        downloadsPath: Union[str, Path] = None,\n        slowMo: float = None,\n        tracesDir: Union[pathlib.Path, str] = None,\n        chromiumSandbox: bool = None,\n        firefoxUserPrefs: Dict[str, Union[str, float, bool]] = None,\n    ) -> Browser:\n        params = locals_to_params(locals())\n        normalize_launch_params(params)\n        browser = cast(\n            Browser,\n            from_channel(\n                await self._channel.send(\n                    \"launch\", TimeoutSettings.launch_timeout, params\n                )\n            ),\n        )\n        browser._connect_to_browser_type(\n            self, str(tracesDir) if tracesDir is not None else None\n        )\n        return browser\n\n    async def launch_persistent_context(\n        self,\n        userDataDir: Union[str, Path],\n        channel: str = None,\n        executablePath: Union[str, Path] = None,\n        args: Sequence[str] = None,\n        ignoreDefaultArgs: Union[bool, Sequence[str]] = None,\n        handleSIGINT: bool = None,\n        handleSIGTERM: bool = None,\n        handleSIGHUP: bool = None,\n        timeout: float = None,\n        env: Env = None,\n        headless: bool = None,\n        proxy: ProxySettings = None,\n        downloadsPath: Union[str, Path] = None,\n        slowMo: float = None,\n        viewport: ViewportSize = None,\n        screen: ViewportSize = None,\n        noViewport: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        javaScriptEnabled: bool = None,\n        bypassCSP: bool = None,\n        userAgent: str = None,\n        locale: str = None,\n        timezoneId: str = None,\n        geolocation: Geolocation = None,\n        permissions: Sequence[str] = None,\n        extraHTTPHeaders: Dict[str, str] = None,\n        offline: bool = None,\n        httpCredentials: HttpCredentials = None,\n        deviceScaleFactor: float = None,\n        isMobile: bool = None,\n        hasTouch: bool = None,\n        colorScheme: ColorScheme = None,\n        reducedMotion: ReducedMotion = None,\n        forcedColors: ForcedColors = None,\n        contrast: Contrast = None,\n        acceptDownloads: bool = None,\n        tracesDir: Union[pathlib.Path, str] = None,\n        chromiumSandbox: bool = None,\n        firefoxUserPrefs: Dict[str, Union[str, float, bool]] = None,\n        recordHarPath: Union[Path, str] = None,\n        recordHarOmitContent: bool = None,\n        recordVideoDir: Union[Path, str] = None,\n        recordVideoSize: ViewportSize = None,\n        baseURL: str = None,\n        strictSelectors: bool = None,\n        serviceWorkers: ServiceWorkersPolicy = None,\n        recordHarUrlFilter: Union[Pattern[str], str] = None,\n        recordHarMode: HarMode = None,\n        recordHarContent: HarContentPolicy = None,\n        clientCertificates: List[ClientCertificate] = None,\n    ) -> BrowserContext:\n        userDataDir = self._user_data_dir(userDataDir)\n        params = locals_to_params(locals())\n        await self._prepare_browser_context_params(params)\n        normalize_launch_params(params)\n        result = await self._channel.send_return_as_dict(\n            \"launchPersistentContext\", TimeoutSettings.launch_timeout, params\n        )\n        browser = cast(\n            Browser,\n            from_channel(result[\"browser\"]),\n        )\n        browser._connect_to_browser_type(\n            self, str(tracesDir) if tracesDir is not None else None\n        )\n        context = cast(BrowserContext, from_channel(result[\"context\"]))\n        await context._initialize_har_from_options(\n            record_har_content=recordHarContent,\n            record_har_mode=recordHarMode,\n            record_har_omit_content=recordHarOmitContent,\n            record_har_path=recordHarPath,\n            record_har_url_filter=recordHarUrlFilter,\n        )\n        return context\n\n    def _user_data_dir(self, userDataDir: Optional[Union[str, Path]]) -> str:\n        if not userDataDir:\n            return \"\"\n        if not Path(userDataDir).is_absolute():\n            # Can be dropped once we drop Python 3.9 support (10/2025):\n            # https://github.com/python/cpython/issues/82852\n            if sys.platform == \"win32\" and sys.version_info[:2] < (3, 10):\n                return str(pathlib.Path.cwd() / userDataDir)\n            return str(Path(userDataDir).resolve())\n        return str(Path(userDataDir))\n\n    async def connect_over_cdp(\n        self,\n        endpointURL: str,\n        timeout: float = None,\n        slowMo: float = None,\n        headers: Dict[str, str] = None,\n        isLocal: bool = None,\n    ) -> Browser:\n        params = locals_to_params(locals())\n        if params.get(\"headers\"):\n            params[\"headers\"] = serialize_headers(params[\"headers\"])\n        response = await self._channel.send_return_as_dict(\n            \"connectOverCDP\", TimeoutSettings.launch_timeout, params\n        )\n        browser = cast(Browser, from_channel(response[\"browser\"]))\n        browser._connect_to_browser_type(self, None)\n\n        return browser\n\n    async def connect(\n        self,\n        wsEndpoint: str,\n        timeout: float = None,\n        slowMo: float = None,\n        headers: Dict[str, str] = None,\n        exposeNetwork: str = None,\n    ) -> Browser:\n        if slowMo is None:\n            slowMo = 0\n\n        headers = {**(headers if headers else {}), \"x-playwright-browser\": self.name}\n        local_utils = self._connection.local_utils\n        pipe_channel = (\n            await local_utils._channel.send_return_as_dict(\n                \"connect\",\n                None,\n                {\n                    \"wsEndpoint\": wsEndpoint,\n                    \"headers\": headers,\n                    \"slowMo\": slowMo,\n                    \"timeout\": timeout if timeout is not None else 0,\n                    \"exposeNetwork\": exposeNetwork,\n                },\n            )\n        )[\"pipe\"]\n        transport = JsonPipeTransport(self._connection._loop, pipe_channel)\n\n        connection = Connection(\n            self._connection._dispatcher_fiber,\n            self._connection._object_factory,\n            transport,\n            self._connection._loop,\n            local_utils=self._connection.local_utils,\n        )\n        connection.mark_as_remote()\n\n        browser = None\n\n        def handle_transport_close(reason: Optional[str]) -> None:\n            if browser:\n                for context in browser.contexts:\n                    for page in context.pages:\n                        page._on_close()\n                    context._on_close()\n                browser._on_close()\n            connection.cleanup(reason)\n            # TODO: Backport https://github.com/microsoft/playwright/commit/d8d5289e8692c9b1265d23ee66988d1ac5122f33\n            # Give a chance to any API call promises to reject upon page/context closure.\n            # This happens naturally when we receive page.onClose and browser.onClose from the server\n            # in separate tasks. However, upon pipe closure we used to dispatch them all synchronously\n            # here and promises did not have a chance to reject.\n            # The order of rejects vs closure is a part of the API contract and our test runner\n            # relies on it to attribute rejections to the right test.\n\n        transport.once(\"close\", handle_transport_close)\n\n        connection._is_sync = self._connection._is_sync\n        connection._loop.create_task(connection.run())\n        playwright_future = connection.playwright_future\n\n        timeout_future = throw_on_timeout(\n            timeout if timeout is not None else PLAYWRIGHT_MAX_DEADLINE,\n            Error(\"Connection timed out\"),\n        )\n        done, pending = await asyncio.wait(\n            {transport.on_error_future, playwright_future, timeout_future},\n            return_when=asyncio.FIRST_COMPLETED,\n        )\n        if not playwright_future.done():\n            playwright_future.cancel()\n        if not timeout_future.done():\n            timeout_future.cancel()\n        playwright: \"Playwright\" = next(iter(done)).result()\n        playwright._set_selectors(self._playwright.selectors)\n        self._connection._child_ws_connections.append(connection)\n        pre_launched_browser = playwright._initializer.get(\"preLaunchedBrowser\")\n        assert pre_launched_browser\n        browser = cast(Browser, from_channel(pre_launched_browser))\n        browser._should_close_connection_on_close = True\n        browser._connect_to_browser_type(self, None)\n\n        return browser\n\n    async def _prepare_browser_context_params(self, params: Dict) -> None:\n        if params.get(\"noViewport\"):\n            del params[\"noViewport\"]\n            params[\"noDefaultViewport\"] = True\n        if \"defaultBrowserType\" in params:\n            del params[\"defaultBrowserType\"]\n        if \"extraHTTPHeaders\" in params:\n            params[\"extraHTTPHeaders\"] = serialize_headers(params[\"extraHTTPHeaders\"])\n        if \"recordVideoDir\" in params:\n            params[\"recordVideo\"] = {\"dir\": Path(params[\"recordVideoDir\"]).absolute()}\n            if \"recordVideoSize\" in params:\n                params[\"recordVideo\"][\"size\"] = params[\"recordVideoSize\"]\n                del params[\"recordVideoSize\"]\n            del params[\"recordVideoDir\"]\n        if \"storageState\" in params:\n            storageState = params[\"storageState\"]\n            if not isinstance(storageState, dict):\n                params[\"storageState\"] = json.loads(\n                    (await async_readfile(storageState)).decode()\n                )\n        if params.get(\"colorScheme\", None) == \"null\":\n            params[\"colorScheme\"] = \"no-override\"\n        if params.get(\"reducedMotion\", None) == \"null\":\n            params[\"reducedMotion\"] = \"no-override\"\n        if params.get(\"forcedColors\", None) == \"null\":\n            params[\"forcedColors\"] = \"no-override\"\n        if params.get(\"contrast\", None) == \"null\":\n            params[\"contrast\"] = \"no-override\"\n        if \"acceptDownloads\" in params:\n            params[\"acceptDownloads\"] = (\n                \"accept\" if params[\"acceptDownloads\"] else \"deny\"\n            )\n\n        if \"clientCertificates\" in params:\n            params[\"clientCertificates\"] = await to_client_certificates_protocol(\n                params[\"clientCertificates\"]\n            )\n        params[\"selectorEngines\"] = self._playwright.selectors._selector_engines\n        params[\"testIdAttributeName\"] = (\n            self._playwright.selectors._test_id_attribute_name\n        )\n\n        # Remove HAR options\n        params.pop(\"recordHarPath\", None)\n        params.pop(\"recordHarOmitContent\", None)\n        params.pop(\"recordHarUrlFilter\", None)\n        params.pop(\"recordHarMode\", None)\n        params.pop(\"recordHarContent\", None)\n\n\ndef normalize_launch_params(params: Dict) -> None:\n    if \"env\" in params:\n        params[\"env\"] = [\n            {\"name\": name, \"value\": str(value)}\n            for [name, value] in params[\"env\"].items()\n        ]\n    if \"ignoreDefaultArgs\" in params:\n        if params[\"ignoreDefaultArgs\"] is True:\n            params[\"ignoreAllDefaultArgs\"] = True\n            del params[\"ignoreDefaultArgs\"]\n    if \"executablePath\" in params:\n        params[\"executablePath\"] = str(Path(params[\"executablePath\"]))\n    if \"downloadsPath\" in params:\n        params[\"downloadsPath\"] = str(Path(params[\"downloadsPath\"]))\n    if \"tracesDir\" in params:\n        params[\"tracesDir\"] = str(Path(params[\"tracesDir\"]))\n"
  },
  {
    "path": "playwright/_impl/_cdp_session.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Any, Dict\n\nfrom playwright._impl._connection import ChannelOwner\nfrom playwright._impl._helper import locals_to_params\n\n\nclass CDPSession(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._channel.on(\"event\", lambda params: self._on_event(params))\n\n    def _on_event(self, params: Any) -> None:\n        self.emit(params[\"method\"], params.get(\"params\"))\n\n    async def send(self, method: str, params: Dict = None) -> Dict:\n        return await self._channel.send(\"send\", None, locals_to_params(locals()))\n\n    async def detach(self) -> None:\n        await self._channel.send(\n            \"detach\",\n            None,\n        )\n"
  },
  {
    "path": "playwright/_impl/_clock.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nfrom typing import TYPE_CHECKING, Dict, Union\n\nif TYPE_CHECKING:\n    from playwright._impl._browser_context import BrowserContext\n\n\nclass Clock:\n    def __init__(self, browser_context: \"BrowserContext\") -> None:\n        self._browser_context = browser_context\n        self._loop = browser_context._loop\n        self._dispatcher_fiber = browser_context._dispatcher_fiber\n\n    async def install(self, time: Union[float, str, datetime.datetime] = None) -> None:\n        await self._browser_context._channel.send(\n            \"clockInstall\",\n            None,\n            parse_time(time) if time is not None else {},\n        )\n\n    async def fast_forward(\n        self,\n        ticks: Union[int, str],\n    ) -> None:\n        await self._browser_context._channel.send(\n            \"clockFastForward\",\n            None,\n            parse_ticks(ticks),\n        )\n\n    async def pause_at(\n        self,\n        time: Union[float, str, datetime.datetime],\n    ) -> None:\n        await self._browser_context._channel.send(\n            \"clockPauseAt\",\n            None,\n            parse_time(time),\n        )\n\n    async def resume(\n        self,\n    ) -> None:\n        await self._browser_context._channel.send(\"clockResume\", None)\n\n    async def run_for(\n        self,\n        ticks: Union[int, str],\n    ) -> None:\n        await self._browser_context._channel.send(\n            \"clockRunFor\",\n            None,\n            parse_ticks(ticks),\n        )\n\n    async def set_fixed_time(\n        self,\n        time: Union[float, str, datetime.datetime],\n    ) -> None:\n        await self._browser_context._channel.send(\n            \"clockSetFixedTime\",\n            None,\n            parse_time(time),\n        )\n\n    async def set_system_time(\n        self,\n        time: Union[float, str, datetime.datetime],\n    ) -> None:\n        await self._browser_context._channel.send(\n            \"clockSetSystemTime\",\n            None,\n            parse_time(time),\n        )\n\n\ndef parse_time(\n    time: Union[float, str, datetime.datetime],\n) -> Dict[str, Union[int, str]]:\n    if isinstance(time, (float, int)):\n        return {\"timeNumber\": int(time * 1_000)}\n    if isinstance(time, str):\n        return {\"timeString\": time}\n    return {\"timeNumber\": int(time.timestamp() * 1_000)}\n\n\ndef parse_ticks(ticks: Union[int, str]) -> Dict[str, Union[int, str]]:\n    if isinstance(ticks, int):\n        return {\"ticksNumber\": ticks}\n    return {\"ticksString\": ticks}\n"
  },
  {
    "path": "playwright/_impl/_connection.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport collections.abc\nimport contextvars\nimport datetime\nimport inspect\nimport sys\nimport traceback\nfrom pathlib import Path\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    List,\n    Mapping,\n    Optional,\n    TypedDict,\n    Union,\n    cast,\n)\n\nfrom pyee import EventEmitter\nfrom pyee.asyncio import AsyncIOEventEmitter\n\nimport playwright\nimport playwright._impl._impl_to_api_mapping\nfrom playwright._impl._errors import TargetClosedError, rewrite_error\nfrom playwright._impl._greenlets import EventGreenlet\nfrom playwright._impl._helper import Error, ParsedMessagePayload, parse_error\nfrom playwright._impl._transport import Transport\n\nif TYPE_CHECKING:\n    from playwright._impl._local_utils import LocalUtils\n    from playwright._impl._playwright import Playwright\n\nTimeoutCalculator = Optional[Callable[[Optional[float]], float]]\n\n\nclass Channel(AsyncIOEventEmitter):\n    def __init__(self, connection: \"Connection\", object: \"ChannelOwner\") -> None:\n        super().__init__()\n        self._connection = connection\n        self._guid = object._guid\n        self._object = object\n        self.on(\"error\", lambda exc: self._connection._on_event_listener_error(exc))\n\n    async def send(\n        self,\n        method: str,\n        timeout_calculator: TimeoutCalculator,\n        params: Dict = None,\n        is_internal: bool = False,\n        title: str = None,\n    ) -> Any:\n        return await self._connection.wrap_api_call(\n            lambda: self._inner_send(method, timeout_calculator, params, False),\n            is_internal,\n            title,\n        )\n\n    async def send_return_as_dict(\n        self,\n        method: str,\n        timeout_calculator: TimeoutCalculator,\n        params: Dict = None,\n        is_internal: bool = False,\n        title: str = None,\n    ) -> Any:\n        return await self._connection.wrap_api_call(\n            lambda: self._inner_send(method, timeout_calculator, params, True),\n            is_internal,\n            title,\n        )\n\n    def send_no_reply(\n        self,\n        method: str,\n        timeout_calculator: TimeoutCalculator,\n        params: Dict = None,\n        is_internal: bool = False,\n        title: str = None,\n    ) -> None:\n        # No reply messages are used to e.g. waitForEventInfo(after).\n        self._connection.wrap_api_call_sync(\n            lambda: self._connection._send_message_to_server(\n                self._object,\n                method,\n                _augment_params(params, timeout_calculator),\n                True,\n            ),\n            is_internal,\n            title,\n        )\n\n    async def _inner_send(\n        self,\n        method: str,\n        timeout_calculator: TimeoutCalculator,\n        params: Optional[Dict],\n        return_as_dict: bool,\n    ) -> Any:\n        if self._connection._error:\n            error = self._connection._error\n            self._connection._error = None\n            raise error\n        callback = self._connection._send_message_to_server(\n            self._object, method, _augment_params(params, timeout_calculator)\n        )\n        done, _ = await asyncio.wait(\n            {\n                self._connection._transport.on_error_future,\n                callback.future,\n            },\n            return_when=asyncio.FIRST_COMPLETED,\n        )\n        if not callback.future.done():\n            callback.future.cancel()\n        result = next(iter(done)).result()\n        # Protocol now has named return values, assume result is one level deeper unless\n        # there is explicit ambiguity.\n        if not result:\n            return None\n        assert isinstance(result, dict)\n        if return_as_dict:\n            return result\n        if len(result) == 0:\n            return None\n        assert len(result) == 1\n        key = next(iter(result))\n        return result[key]\n\n\nclass ChannelOwner(AsyncIOEventEmitter):\n    def __init__(\n        self,\n        parent: Union[\"ChannelOwner\", \"Connection\"],\n        type: str,\n        guid: str,\n        initializer: Dict,\n    ) -> None:\n        super().__init__(loop=parent._loop)\n        self._loop: asyncio.AbstractEventLoop = parent._loop\n        self._dispatcher_fiber: Any = parent._dispatcher_fiber\n        self._type = type\n        self._guid: str = guid\n        self._connection: Connection = (\n            parent._connection if isinstance(parent, ChannelOwner) else parent\n        )\n        self._parent: Optional[ChannelOwner] = (\n            parent if isinstance(parent, ChannelOwner) else None\n        )\n        self._objects: Dict[str, \"ChannelOwner\"] = {}\n        self._channel: Channel = Channel(self._connection, self)\n        self._initializer = initializer\n        self._was_collected = False\n\n        self._connection._objects[guid] = self\n        if self._parent:\n            self._parent._objects[guid] = self\n\n        self._event_to_subscription_mapping: Dict[str, str] = {}\n\n    def _dispose(self, reason: Optional[str]) -> None:\n        # Clean up from parent and connection.\n        if self._parent:\n            del self._parent._objects[self._guid]\n        del self._connection._objects[self._guid]\n        self._was_collected = reason == \"gc\"\n\n        # Dispose all children.\n        for object in list(self._objects.values()):\n            object._dispose(reason)\n        self._objects.clear()\n\n    def _adopt(self, child: \"ChannelOwner\") -> None:\n        del cast(\"ChannelOwner\", child._parent)._objects[child._guid]\n        self._objects[child._guid] = child\n        child._parent = self\n\n    def _set_event_to_subscription_mapping(self, mapping: Dict[str, str]) -> None:\n        self._event_to_subscription_mapping = mapping\n\n    def _update_subscription(self, event: str, enabled: bool) -> None:\n        protocol_event = self._event_to_subscription_mapping.get(event)\n        if protocol_event:\n            self._connection.wrap_api_call_sync(\n                lambda: self._channel.send_no_reply(\n                    \"updateSubscription\",\n                    None,\n                    {\"event\": protocol_event, \"enabled\": enabled},\n                ),\n                True,\n            )\n\n    def _add_event_handler(self, event: str, k: Any, v: Any) -> None:\n        if not self.listeners(event):\n            self._update_subscription(event, True)\n        super()._add_event_handler(event, k, v)\n\n    def remove_listener(self, event: str, f: Any) -> None:\n        super().remove_listener(event, f)\n        if not self.listeners(event):\n            self._update_subscription(event, False)\n\n\nclass ProtocolCallback:\n    def __init__(self, loop: asyncio.AbstractEventLoop, no_reply: bool = False) -> None:\n        self.stack_trace: traceback.StackSummary\n        self.no_reply = no_reply\n        self.future = loop.create_future()\n        if no_reply:\n            self.future.set_result(None)\n        # The outer task can get cancelled by the user, this forwards the cancellation to the inner task.\n        current_task = asyncio.current_task()\n\n        def cb(task: asyncio.Task) -> None:\n            if current_task:\n                current_task.remove_done_callback(cb)\n            if task.cancelled():\n                self.future.cancel()\n\n        if current_task:\n            current_task.add_done_callback(cb)\n            self.future.add_done_callback(\n                lambda _: (\n                    current_task.remove_done_callback(cb) if current_task else None\n                )\n            )\n\n\nclass RootChannelOwner(ChannelOwner):\n    def __init__(self, connection: \"Connection\") -> None:\n        super().__init__(connection, \"Root\", \"\", {})\n\n    async def initialize(self) -> \"Playwright\":\n        return from_channel(\n            await self._channel.send(\n                \"initialize\",\n                None,\n                {\n                    \"sdkLanguage\": \"python\",\n                },\n            )\n        )\n\n\nclass Connection(EventEmitter):\n    def __init__(\n        self,\n        dispatcher_fiber: Any,\n        object_factory: Callable[[ChannelOwner, str, str, Dict], ChannelOwner],\n        transport: Transport,\n        loop: asyncio.AbstractEventLoop,\n        local_utils: Optional[\"LocalUtils\"] = None,\n    ) -> None:\n        super().__init__()\n        self._dispatcher_fiber = dispatcher_fiber\n        self._transport = transport\n        self._transport.on_message = lambda msg: self.dispatch(msg)\n        self._waiting_for_object: Dict[str, Callable[[ChannelOwner], None]] = {}\n        self._last_id = 0\n        self._objects: Dict[str, ChannelOwner] = {}\n        self._callbacks: Dict[int, ProtocolCallback] = {}\n        self._object_factory = object_factory\n        self._is_sync = False\n        self._child_ws_connections: List[\"Connection\"] = []\n        self._loop = loop\n        self.playwright_future: asyncio.Future[\"Playwright\"] = loop.create_future()\n        self._error: Optional[BaseException] = None\n        self.is_remote = False\n        self._init_task: Optional[asyncio.Task] = None\n        self._api_zone: contextvars.ContextVar[Optional[ParsedStackTrace]] = (\n            contextvars.ContextVar(\"ApiZone\", default=None)\n        )\n        self._local_utils: Optional[\"LocalUtils\"] = local_utils\n        self._tracing_count = 0\n        self._closed_error: Optional[Exception] = None\n\n    @property\n    def local_utils(self) -> \"LocalUtils\":\n        assert self._local_utils\n        return self._local_utils\n\n    def mark_as_remote(self) -> None:\n        self.is_remote = True\n\n    async def run_as_sync(self) -> None:\n        self._is_sync = True\n        await self.run()\n\n    async def run(self) -> None:\n        self._loop = asyncio.get_running_loop()\n        self._root_object = RootChannelOwner(self)\n\n        async def init() -> None:\n            self.playwright_future.set_result(await self._root_object.initialize())\n\n        await self._transport.connect()\n        self._init_task = self._loop.create_task(init())\n        await self._transport.run()\n\n    def stop_sync(self) -> None:\n        self._transport.request_stop()\n        self._dispatcher_fiber.switch()\n        self._loop.run_until_complete(self._transport.wait_until_stopped())\n        self.cleanup()\n\n    async def stop_async(self) -> None:\n        self._transport.request_stop()\n        await self._transport.wait_until_stopped()\n        self.cleanup()\n\n    def cleanup(self, cause: str = None) -> None:\n        self._closed_error = TargetClosedError(cause) if cause else TargetClosedError()\n        if self._init_task and not self._init_task.done():\n            self._init_task.cancel()\n        for ws_connection in self._child_ws_connections:\n            ws_connection._transport.dispose()\n        for callback in self._callbacks.values():\n            # To prevent 'Future exception was never retrieved' we ignore all callbacks that are no_reply.\n            if callback.no_reply:\n                continue\n            if callback.future.cancelled():\n                continue\n            callback.future.set_exception(self._closed_error)\n        self._callbacks.clear()\n        self.emit(\"close\")\n\n    def call_on_object_with_known_name(\n        self, guid: str, callback: Callable[[ChannelOwner], None]\n    ) -> None:\n        self._waiting_for_object[guid] = callback\n\n    def set_is_tracing(self, is_tracing: bool) -> None:\n        if is_tracing:\n            self._tracing_count += 1\n        else:\n            self._tracing_count -= 1\n\n    def _send_message_to_server(\n        self, object: ChannelOwner, method: str, params: Dict, no_reply: bool = False\n    ) -> ProtocolCallback:\n        if self._closed_error:\n            raise self._closed_error\n        if object._was_collected:\n            raise Error(\n                \"The object has been collected to prevent unbounded heap growth.\"\n            )\n        self._last_id += 1\n        id = self._last_id\n        callback = ProtocolCallback(self._loop, no_reply=no_reply)\n        task = asyncio.current_task(self._loop)\n        callback.stack_trace = cast(\n            traceback.StackSummary,\n            getattr(task, \"__pw_stack_trace__\", traceback.extract_stack(limit=10)),\n        )\n        callback.no_reply = no_reply\n        stack_trace_information = cast(ParsedStackTrace, self._api_zone.get())\n        frames = stack_trace_information.get(\"frames\", [])\n        location = (\n            {\n                \"file\": frames[0][\"file\"],\n                \"line\": frames[0][\"line\"],\n                \"column\": frames[0][\"column\"],\n            }\n            if frames\n            else None\n        )\n        metadata = {\n            \"wallTime\": int(datetime.datetime.now().timestamp() * 1000),\n            \"apiName\": stack_trace_information[\"apiName\"],\n            \"internal\": not stack_trace_information[\"apiName\"],\n        }\n        if location:\n            metadata[\"location\"] = location  # type: ignore\n        title = stack_trace_information[\"title\"]\n        if title:\n            metadata[\"title\"] = title\n        message = {\n            \"id\": id,\n            \"guid\": object._guid,\n            \"method\": method,\n            \"params\": self._replace_channels_with_guids(params),\n            \"metadata\": metadata,\n        }\n        if self._tracing_count > 0 and frames and object._guid != \"localUtils\":\n            self.local_utils.add_stack_to_tracing_no_reply(id, frames)\n\n        self._callbacks[id] = callback\n        self._transport.send(message)\n\n        return callback\n\n    def dispatch(self, msg: ParsedMessagePayload) -> None:\n        if self._closed_error:\n            return\n        id = msg.get(\"id\")\n        if id:\n            callback = self._callbacks.pop(id)\n            if callback.future.cancelled():\n                return\n            # No reply messages are used to e.g. waitForEventInfo(after) which returns exceptions on page close.\n            # To prevent 'Future exception was never retrieved' we just ignore such messages.\n            if callback.no_reply:\n                return\n            error = msg.get(\"error\")\n            if error and not msg.get(\"result\"):\n                parsed_error = parse_error(\n                    error[\"error\"], format_call_log(msg.get(\"log\"))  # type: ignore\n                )\n                parsed_error._stack = \"\".join(callback.stack_trace.format())\n                callback.future.set_exception(parsed_error)\n            else:\n                result = self._replace_guids_with_channels(msg.get(\"result\"))\n                callback.future.set_result(result)\n            return\n\n        guid = msg[\"guid\"]\n        method = msg[\"method\"]\n        params = msg.get(\"params\")\n        if method == \"__create__\":\n            assert params\n            parent = self._objects[guid]\n            self._create_remote_object(\n                parent, params[\"type\"], params[\"guid\"], params[\"initializer\"]\n            )\n            return\n\n        object = self._objects.get(guid)\n        if not object:\n            raise Exception(f'Cannot find object to \"{method}\": {guid}')\n\n        if method == \"__adopt__\":\n            child_guid = cast(Dict[str, str], params)[\"guid\"]\n            child = self._objects.get(child_guid)\n            if not child:\n                raise Exception(f\"Unknown new child: {child_guid}\")\n            object._adopt(child)\n            return\n\n        if method == \"__dispose__\":\n            assert isinstance(params, dict)\n            self._objects[guid]._dispose(cast(Optional[str], params.get(\"reason\")))\n            return\n        object = self._objects[guid]\n        should_replace_guids_with_channels = \"jsonPipe@\" not in guid\n        try:\n            if self._is_sync:\n                for listener in object._channel.listeners(method):\n                    # Event handlers like route/locatorHandlerTriggered require us to perform async work.\n                    # In order to report their potential errors to the user, we need to catch it and store it in the connection\n                    def _done_callback(future: asyncio.Future) -> None:\n                        exc = future.exception()\n                        if exc:\n                            self._on_event_listener_error(exc)\n\n                    def _listener_with_error_handler_attached(params: Any) -> None:\n                        potential_future = listener(params)\n                        if asyncio.isfuture(potential_future):\n                            potential_future.add_done_callback(_done_callback)\n\n                    # Each event handler is a potentilly blocking context, create a fiber for each\n                    # and switch to them in order, until they block inside and pass control to each\n                    # other and then eventually back to dispatcher as listener functions return.\n                    g = EventGreenlet(_listener_with_error_handler_attached)\n                    if should_replace_guids_with_channels:\n                        g.switch(self._replace_guids_with_channels(params))\n                    else:\n                        g.switch(params)\n            else:\n                if should_replace_guids_with_channels:\n                    object._channel.emit(\n                        method, self._replace_guids_with_channels(params)\n                    )\n                else:\n                    object._channel.emit(method, params)\n        except BaseException as exc:\n            self._on_event_listener_error(exc)\n\n    def _on_event_listener_error(self, exc: BaseException) -> None:\n        print(\"Error occurred in event listener\", file=sys.stderr)\n        traceback.print_exception(type(exc), exc, exc.__traceback__, file=sys.stderr)\n        # Save the error to throw at the next API call. This \"replicates\" unhandled rejection in Node.js.\n        self._error = exc\n\n    def _create_remote_object(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> ChannelOwner:\n        initializer = self._replace_guids_with_channels(initializer)\n        result = self._object_factory(parent, type, guid, initializer)\n        if guid in self._waiting_for_object:\n            self._waiting_for_object.pop(guid)(result)\n        return result\n\n    def _replace_channels_with_guids(\n        self,\n        payload: Any,\n    ) -> Any:\n        if payload is None:\n            return payload\n        if isinstance(payload, Path):\n            return str(payload)\n        if isinstance(payload, collections.abc.Sequence) and not isinstance(\n            payload, str\n        ):\n            return list(map(self._replace_channels_with_guids, payload))\n        if isinstance(payload, Channel):\n            return dict(guid=payload._guid)\n        if isinstance(payload, dict):\n            result = {}\n            for key, value in payload.items():\n                result[key] = self._replace_channels_with_guids(value)\n            return result\n        return payload\n\n    def _replace_guids_with_channels(self, payload: Any) -> Any:\n        if payload is None:\n            return payload\n        if isinstance(payload, list):\n            return list(map(self._replace_guids_with_channels, payload))\n        if isinstance(payload, dict):\n            if payload.get(\"guid\") in self._objects:\n                return self._objects[payload[\"guid\"]]._channel\n            result = {}\n            for key, value in payload.items():\n                result[key] = self._replace_guids_with_channels(value)\n            return result\n        return payload\n\n    async def wrap_api_call(\n        self, cb: Callable[[], Any], is_internal: bool = False, title: str = None\n    ) -> Any:\n        if self._api_zone.get():\n            return await cb()\n        task = asyncio.current_task(self._loop)\n        st: List[inspect.FrameInfo] = getattr(\n            task, \"__pw_stack__\", None\n        ) or inspect.stack(0)\n\n        parsed_st = _extract_stack_trace_information_from_stack(st, is_internal, title)\n        self._api_zone.set(parsed_st)\n        try:\n            return await cb()\n        except Exception as error:\n            raise rewrite_error(error, f\"{parsed_st['apiName']}: {error}\") from None\n        finally:\n            self._api_zone.set(None)\n\n    def wrap_api_call_sync(\n        self, cb: Callable[[], Any], is_internal: bool = False, title: str = None\n    ) -> Any:\n        if self._api_zone.get():\n            return cb()\n        task = asyncio.current_task(self._loop)\n        st: List[inspect.FrameInfo] = getattr(\n            task, \"__pw_stack__\", None\n        ) or inspect.stack(0)\n        parsed_st = _extract_stack_trace_information_from_stack(st, is_internal, title)\n        self._api_zone.set(parsed_st)\n        try:\n            return cb()\n        except Exception as error:\n            raise rewrite_error(error, f\"{parsed_st['apiName']}: {error}\") from None\n        finally:\n            self._api_zone.set(None)\n\n\ndef from_channel(channel: Channel) -> Any:\n    return channel._object\n\n\ndef from_nullable_channel(channel: Optional[Channel]) -> Optional[Any]:\n    return channel._object if channel else None\n\n\nclass StackFrame(TypedDict):\n    file: str\n    line: int\n    column: int\n    function: Optional[str]\n\n\nclass ParsedStackTrace(TypedDict):\n    frames: List[StackFrame]\n    apiName: Optional[str]\n    title: Optional[str]\n\n\ndef _extract_stack_trace_information_from_stack(\n    st: List[inspect.FrameInfo], is_internal: bool, title: str = None\n) -> ParsedStackTrace:\n    playwright_module_path = str(Path(playwright.__file__).parents[0])\n    last_internal_api_name = \"\"\n    api_name = \"\"\n    parsed_frames: List[StackFrame] = []\n    for frame in st:\n        # Sync and Async implementations can have event handlers. When these are sync, they\n        # get evaluated in the context of the event loop, so they contain the stack trace of when\n        # the message was received. _impl_to_api_mapping is glue between the user-code and internal\n        # code to translate impl classes to api classes. We want to ignore these frames.\n        if playwright._impl._impl_to_api_mapping.__file__ == frame.filename:\n            continue\n        is_playwright_internal = frame.filename.startswith(playwright_module_path)\n\n        method_name = \"\"\n        if \"self\" in frame[0].f_locals:\n            method_name = frame[0].f_locals[\"self\"].__class__.__name__ + \".\"\n        method_name += frame[0].f_code.co_name\n\n        if not is_playwright_internal:\n            parsed_frames.append(\n                {\n                    \"file\": frame.filename,\n                    \"line\": frame.lineno,\n                    \"column\": 0,\n                    \"function\": method_name,\n                }\n            )\n        if is_playwright_internal:\n            last_internal_api_name = method_name\n        elif last_internal_api_name:\n            api_name = last_internal_api_name\n            last_internal_api_name = \"\"\n    if not api_name:\n        api_name = last_internal_api_name\n\n    return {\n        \"frames\": parsed_frames,\n        \"apiName\": \"\" if is_internal else api_name,\n        \"title\": title,\n    }\n\n\ndef _augment_params(\n    params: Optional[Dict],\n    timeout_calculator: Optional[Callable[[Optional[float]], float]],\n) -> Dict:\n    if params is None:\n        params = {}\n    if timeout_calculator:\n        params[\"timeout\"] = timeout_calculator(params.get(\"timeout\"))\n    return _filter_none(params)\n\n\ndef _filter_none(d: Mapping) -> Dict:\n    result = {}\n    for k, v in d.items():\n        if v is None:\n            continue\n        result[k] = _filter_none(v) if isinstance(v, dict) else v\n    return result\n\n\ndef format_call_log(log: Optional[List[str]]) -> str:\n    if not log:\n        return \"\"\n    if len(list(filter(lambda x: x.strip(), log))) == 0:\n        return \"\"\n    return \"\\nCall log:\\n\" + \"\\n\".join(log) + \"\\n\"\n"
  },
  {
    "path": "playwright/_impl/_console_message.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom asyncio import AbstractEventLoop\nfrom typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union\n\nfrom playwright._impl._api_structures import SourceLocation\nfrom playwright._impl._connection import from_channel, from_nullable_channel\nfrom playwright._impl._js_handle import JSHandle\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._page import Page\n    from playwright._impl._worker import Worker\n\n\nclass ConsoleMessage:\n    def __init__(\n        self, event: Dict, loop: AbstractEventLoop, dispatcher_fiber: Any\n    ) -> None:\n        self._event = event\n        self._loop = loop\n        self._dispatcher_fiber = dispatcher_fiber\n        self._page: Optional[\"Page\"] = from_nullable_channel(event.get(\"page\"))\n        self._worker: Optional[\"Worker\"] = from_nullable_channel(event.get(\"worker\"))\n\n    def __repr__(self) -> str:\n        return f\"<ConsoleMessage type={self.type} text={self.text}>\"\n\n    def __str__(self) -> str:\n        return self.text\n\n    @property\n    def type(self) -> Union[\n        Literal[\"assert\"],\n        Literal[\"clear\"],\n        Literal[\"count\"],\n        Literal[\"debug\"],\n        Literal[\"dir\"],\n        Literal[\"dirxml\"],\n        Literal[\"endGroup\"],\n        Literal[\"error\"],\n        Literal[\"info\"],\n        Literal[\"log\"],\n        Literal[\"profile\"],\n        Literal[\"profileEnd\"],\n        Literal[\"startGroup\"],\n        Literal[\"startGroupCollapsed\"],\n        Literal[\"table\"],\n        Literal[\"time\"],\n        Literal[\"timeEnd\"],\n        Literal[\"trace\"],\n        Literal[\"warning\"],\n    ]:\n        return self._event[\"type\"]\n\n    @property\n    def text(self) -> str:\n        return self._event[\"text\"]\n\n    @property\n    def args(self) -> List[JSHandle]:\n        return list(map(from_channel, self._event[\"args\"]))\n\n    @property\n    def location(self) -> SourceLocation:\n        return self._event[\"location\"]\n\n    @property\n    def page(self) -> Optional[\"Page\"]:\n        return self._page\n\n    @property\n    def worker(self) -> Optional[\"Worker\"]:\n        return self._worker\n"
  },
  {
    "path": "playwright/_impl/_dialog.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import TYPE_CHECKING, Dict, Optional\n\nfrom playwright._impl._connection import ChannelOwner, from_nullable_channel\nfrom playwright._impl._helper import locals_to_params\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._page import Page\n\n\nclass Dialog(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._page: Optional[\"Page\"] = from_nullable_channel(initializer.get(\"page\"))\n\n    def __repr__(self) -> str:\n        return f\"<Dialog type={self.type} message={self.message} default_value={self.default_value}>\"\n\n    @property\n    def type(self) -> str:\n        return self._initializer[\"type\"]\n\n    @property\n    def message(self) -> str:\n        return self._initializer[\"message\"]\n\n    @property\n    def default_value(self) -> str:\n        return self._initializer[\"defaultValue\"]\n\n    @property\n    def page(self) -> Optional[\"Page\"]:\n        return self._page\n\n    async def accept(self, promptText: str = None) -> None:\n        await self._channel.send(\"accept\", None, locals_to_params(locals()))\n\n    async def dismiss(self) -> None:\n        await self._channel.send(\n            \"dismiss\",\n            None,\n        )\n"
  },
  {
    "path": "playwright/_impl/_download.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pathlib\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Optional, Union\n\nfrom playwright._impl._artifact import Artifact\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._page import Page\n\n\nclass Download:\n    def __init__(\n        self, page: \"Page\", url: str, suggested_filename: str, artifact: Artifact\n    ) -> None:\n        self._page = page\n        self._loop = page._loop\n        self._dispatcher_fiber = page._dispatcher_fiber\n        self._url = url\n        self._suggested_filename = suggested_filename\n        self._artifact = artifact\n\n    def __repr__(self) -> str:\n        return f\"<Download url={self.url!r} suggested_filename={self.suggested_filename!r}>\"\n\n    @property\n    def page(self) -> \"Page\":\n        return self._page\n\n    @property\n    def url(self) -> str:\n        return self._url\n\n    @property\n    def suggested_filename(self) -> str:\n        return self._suggested_filename\n\n    async def delete(self) -> None:\n        await self._artifact.delete()\n\n    async def failure(self) -> Optional[str]:\n        return await self._artifact.failure()\n\n    async def path(self) -> pathlib.Path:\n        return await self._artifact.path_after_finished()\n\n    async def save_as(self, path: Union[str, Path]) -> None:\n        await self._artifact.save_as(path)\n\n    async def cancel(self) -> None:\n        return await self._artifact.cancel()\n"
  },
  {
    "path": "playwright/_impl/_driver.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nimport os\nimport sys\nfrom pathlib import Path\nfrom typing import Tuple\n\nimport playwright\nfrom playwright._repo_version import version\n\n\ndef compute_driver_executable() -> Tuple[str, str]:\n    driver_path = Path(inspect.getfile(playwright)).parent / \"driver\"\n    cli_path = str(driver_path / \"package\" / \"cli.js\")\n    if sys.platform == \"win32\":\n        return (\n            os.getenv(\"PLAYWRIGHT_NODEJS_PATH\", str(driver_path / \"node.exe\")),\n            cli_path,\n        )\n    return (os.getenv(\"PLAYWRIGHT_NODEJS_PATH\", str(driver_path / \"node\")), cli_path)\n\n\ndef get_driver_env() -> dict:\n    env = os.environ.copy()\n    env[\"PW_LANG_NAME\"] = \"python\"\n    env[\"PW_LANG_NAME_VERSION\"] = f\"{sys.version_info.major}.{sys.version_info.minor}\"\n    env[\"PW_CLI_DISPLAY_VERSION\"] = version\n    return env\n"
  },
  {
    "path": "playwright/_impl/_element_handle.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport mimetypes\nfrom pathlib import Path\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Sequence,\n    Union,\n    cast,\n)\n\nfrom playwright._impl._api_structures import FilePayload, FloatRect, Position\nfrom playwright._impl._connection import ChannelOwner, from_nullable_channel\nfrom playwright._impl._helper import (\n    Error,\n    KeyboardModifier,\n    MouseButton,\n    async_writefile,\n    locals_to_params,\n    make_dirs_for_file,\n)\nfrom playwright._impl._js_handle import (\n    JSHandle,\n    Serializable,\n    parse_result,\n    serialize_argument,\n)\nfrom playwright._impl._set_input_files_helpers import convert_input_files\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._frame import Frame\n    from playwright._impl._locator import Locator\n\n\nclass ElementHandle(JSHandle):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._frame = cast(\"Frame\", parent)\n\n    async def _createSelectorForTest(self, name: str) -> Optional[str]:\n        return await self._channel.send(\n            \"createSelectorForTest\", self._frame._timeout, dict(name=name)\n        )\n\n    def as_element(self) -> Optional[\"ElementHandle\"]:\n        return self\n\n    async def owner_frame(self) -> Optional[\"Frame\"]:\n        return from_nullable_channel(await self._channel.send(\"ownerFrame\", None))\n\n    async def content_frame(self) -> Optional[\"Frame\"]:\n        return from_nullable_channel(await self._channel.send(\"contentFrame\", None))\n\n    async def get_attribute(self, name: str) -> Optional[str]:\n        return await self._channel.send(\"getAttribute\", None, dict(name=name))\n\n    async def text_content(self) -> Optional[str]:\n        return await self._channel.send(\"textContent\", None)\n\n    async def inner_text(self) -> str:\n        return await self._channel.send(\"innerText\", None)\n\n    async def inner_html(self) -> str:\n        return await self._channel.send(\"innerHTML\", None)\n\n    async def is_checked(self) -> bool:\n        return await self._channel.send(\"isChecked\", None)\n\n    async def is_disabled(self) -> bool:\n        return await self._channel.send(\"isDisabled\", None)\n\n    async def is_editable(self) -> bool:\n        return await self._channel.send(\"isEditable\", None)\n\n    async def is_enabled(self) -> bool:\n        return await self._channel.send(\"isEnabled\", None)\n\n    async def is_hidden(self) -> bool:\n        return await self._channel.send(\"isHidden\", None)\n\n    async def is_visible(self) -> bool:\n        return await self._channel.send(\"isVisible\", None)\n\n    async def dispatch_event(self, type: str, eventInit: Dict = None) -> None:\n        await self._channel.send(\n            \"dispatchEvent\",\n            None,\n            dict(type=type, eventInit=serialize_argument(eventInit)),\n        )\n\n    async def scroll_into_view_if_needed(self, timeout: float = None) -> None:\n        await self._channel.send(\n            \"scrollIntoViewIfNeeded\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def hover(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"hover\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def click(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        await self._channel.send(\n            \"click\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def dblclick(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        await self._channel.send(\n            \"dblclick\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def select_option(\n        self,\n        value: Union[str, Sequence[str]] = None,\n        index: Union[int, Sequence[int]] = None,\n        label: Union[str, Sequence[str]] = None,\n        element: Union[\"ElementHandle\", Sequence[\"ElementHandle\"]] = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n    ) -> List[str]:\n        params = locals_to_params(\n            dict(\n                timeout=timeout,\n                force=force,\n                **convert_select_option_values(value, index, label, element),\n            )\n        )\n        return await self._channel.send(\"selectOption\", self._frame._timeout, params)\n\n    async def tap(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"tap\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def fill(\n        self,\n        value: str,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"fill\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def select_text(self, force: bool = None, timeout: float = None) -> None:\n        await self._channel.send(\n            \"selectText\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def input_value(self, timeout: float = None) -> str:\n        return await self._channel.send(\n            \"inputValue\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def set_input_files(\n        self,\n        files: Union[\n            str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]\n        ],\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        frame = await self.owner_frame()\n        if not frame:\n            raise Error(\"Cannot set input files to detached element\")\n        converted = await convert_input_files(files, frame.page.context)\n        await self._channel.send(\n            \"setInputFiles\",\n            self._frame._timeout,\n            {\n                \"timeout\": timeout,\n                **converted,\n            },\n        )\n\n    async def focus(self) -> None:\n        await self._channel.send(\"focus\", None)\n\n    async def type(\n        self,\n        text: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"type\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def press(\n        self,\n        key: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"press\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def set_checked(\n        self,\n        checked: bool,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        if checked:\n            await self.check(\n                position=position,\n                timeout=timeout,\n                force=force,\n                trial=trial,\n            )\n        else:\n            await self.uncheck(\n                position=position,\n                timeout=timeout,\n                force=force,\n                trial=trial,\n            )\n\n    async def check(\n        self,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"check\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def uncheck(\n        self,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"uncheck\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def bounding_box(self) -> Optional[FloatRect]:\n        return await self._channel.send(\"boundingBox\", None)\n\n    async def screenshot(\n        self,\n        timeout: float = None,\n        type: Literal[\"jpeg\", \"png\"] = None,\n        path: Union[str, Path] = None,\n        quality: int = None,\n        omitBackground: bool = None,\n        animations: Literal[\"allow\", \"disabled\"] = None,\n        caret: Literal[\"hide\", \"initial\"] = None,\n        scale: Literal[\"css\", \"device\"] = None,\n        mask: Sequence[\"Locator\"] = None,\n        maskColor: str = None,\n        style: str = None,\n    ) -> bytes:\n        params = locals_to_params(locals())\n        if \"path\" in params:\n            if \"type\" not in params:\n                params[\"type\"] = determine_screenshot_type(params[\"path\"])\n            del params[\"path\"]\n        if \"mask\" in params:\n            params[\"mask\"] = list(\n                map(\n                    lambda locator: (\n                        {\n                            \"frame\": locator._frame._channel,\n                            \"selector\": locator._selector,\n                        }\n                    ),\n                    params[\"mask\"],\n                )\n            )\n        encoded_binary = await self._channel.send(\n            \"screenshot\", self._frame._timeout, params\n        )\n        decoded_binary = base64.b64decode(encoded_binary)\n        if path:\n            make_dirs_for_file(path)\n            await async_writefile(path, decoded_binary)\n        return decoded_binary\n\n    async def query_selector(self, selector: str) -> Optional[\"ElementHandle\"]:\n        return from_nullable_channel(\n            await self._channel.send(\"querySelector\", None, dict(selector=selector))\n        )\n\n    async def query_selector_all(self, selector: str) -> List[\"ElementHandle\"]:\n        return list(\n            map(\n                cast(Callable[[Any], Any], from_nullable_channel),\n                await self._channel.send(\n                    \"querySelectorAll\", None, dict(selector=selector)\n                ),\n            )\n        )\n\n    async def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n    ) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evalOnSelector\",\n                None,\n                dict(\n                    selector=selector,\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def eval_on_selector_all(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n    ) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evalOnSelectorAll\",\n                None,\n                dict(\n                    selector=selector,\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def wait_for_element_state(\n        self,\n        state: Literal[\n            \"disabled\", \"editable\", \"enabled\", \"hidden\", \"stable\", \"visible\"\n        ],\n        timeout: float = None,\n    ) -> None:\n        await self._channel.send(\n            \"waitForElementState\", self._frame._timeout, locals_to_params(locals())\n        )\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        state: Literal[\"attached\", \"detached\", \"hidden\", \"visible\"] = None,\n        timeout: float = None,\n        strict: bool = None,\n    ) -> Optional[\"ElementHandle\"]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"waitForSelector\", self._frame._timeout, locals_to_params(locals())\n            )\n        )\n\n\ndef convert_select_option_values(\n    value: Union[str, Sequence[str]] = None,\n    index: Union[int, Sequence[int]] = None,\n    label: Union[str, Sequence[str]] = None,\n    element: Union[\"ElementHandle\", Sequence[\"ElementHandle\"]] = None,\n) -> Any:\n    if value is None and index is None and label is None and element is None:\n        return {}\n\n    options: Any = None\n    elements: Any = None\n    if value is not None:\n        if isinstance(value, str):\n            value = [value]\n        options = (options or []) + list(map(lambda e: dict(valueOrLabel=e), value))\n    if index is not None:\n        if isinstance(index, int):\n            index = [index]\n        options = (options or []) + list(map(lambda e: dict(index=e), index))\n    if label is not None:\n        if isinstance(label, str):\n            label = [label]\n        options = (options or []) + list(map(lambda e: dict(label=e), label))\n    if element:\n        if isinstance(element, ElementHandle):\n            element = [element]\n        elements = list(map(lambda e: e._channel, element))\n\n    return dict(options=options, elements=elements)\n\n\ndef determine_screenshot_type(path: Union[str, Path]) -> Literal[\"jpeg\", \"png\"]:\n    mime_type, _ = mimetypes.guess_type(path)\n    if mime_type == \"image/png\":\n        return \"png\"\n    if mime_type == \"image/jpeg\":\n        return \"jpeg\"\n    raise Error(f'Unsupported screenshot mime type for path \"{path}\": {mime_type}')\n"
  },
  {
    "path": "playwright/_impl/_errors.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# These are types that we use in the API. They are public and are a part of the\n# stable API.\n\n\nfrom typing import Optional\n\n\ndef is_target_closed_error(error: Exception) -> bool:\n    return isinstance(error, TargetClosedError)\n\n\nclass Error(Exception):\n    def __init__(self, message: str) -> None:\n        self._message = message\n        self._name: Optional[str] = None\n        self._stack: Optional[str] = None\n        super().__init__(message)\n\n    @property\n    def message(self) -> str:\n        return self._message\n\n    @property\n    def name(self) -> Optional[str]:\n        return self._name\n\n    @property\n    def stack(self) -> Optional[str]:\n        return self._stack\n\n\nclass TimeoutError(Error):\n    pass\n\n\nclass TargetClosedError(Error):\n    def __init__(self, message: str = None) -> None:\n        super().__init__(message or \"Target page, context or browser has been closed\")\n\n\ndef rewrite_error(error: Exception, message: str) -> Exception:\n    rewritten_exc = type(error)(message)\n    if isinstance(rewritten_exc, Error) and isinstance(error, Error):\n        rewritten_exc._name = error.name\n        rewritten_exc._stack = error.stack\n    return rewritten_exc\n"
  },
  {
    "path": "playwright/_impl/_event_context_manager.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Any, Generic, TypeVar\n\nT = TypeVar(\"T\")\n\n\nclass EventContextManagerImpl(Generic[T]):\n    def __init__(self, future: asyncio.Future) -> None:\n        self._future: asyncio.Future = future\n\n    @property\n    def future(self) -> asyncio.Future:\n        return self._future\n\n    async def __aenter__(self) -> asyncio.Future:\n        return self._future\n\n    async def __aexit__(self, *args: Any) -> None:\n        await self._future\n"
  },
  {
    "path": "playwright/_impl/_fetch.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport json\nimport pathlib\nimport typing\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional, Union, cast\n\nimport playwright._impl._network as network\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    FilePayload,\n    FormField,\n    Headers,\n    HttpCredentials,\n    ProxySettings,\n    ServerFilePayload,\n    StorageState,\n)\nfrom playwright._impl._connection import ChannelOwner, from_channel\nfrom playwright._impl._errors import is_target_closed_error\nfrom playwright._impl._helper import (\n    Error,\n    NameValue,\n    TargetClosedError,\n    TimeoutSettings,\n    async_readfile,\n    async_writefile,\n    is_file_payload,\n    locals_to_params,\n    object_to_array,\n    to_impl,\n)\nfrom playwright._impl._network import serialize_headers, to_client_certificates_protocol\nfrom playwright._impl._tracing import Tracing\n\nif typing.TYPE_CHECKING:\n    from playwright._impl._playwright import Playwright\n\n\nFormType = Dict[str, Union[bool, float, str]]\nDataType = Union[Any, bytes, str]\nMultipartType = Dict[str, Union[bytes, bool, float, str, FilePayload]]\nParamsType = Union[Dict[str, Union[bool, float, str]], str]\n\n\nclass APIRequest:\n    def __init__(self, playwright: \"Playwright\") -> None:\n        self.playwright = playwright\n        self._loop = playwright._loop\n        self._dispatcher_fiber = playwright._connection._dispatcher_fiber\n\n    async def new_context(\n        self,\n        baseURL: str = None,\n        extraHTTPHeaders: Dict[str, str] = None,\n        httpCredentials: HttpCredentials = None,\n        ignoreHTTPSErrors: bool = None,\n        proxy: ProxySettings = None,\n        userAgent: str = None,\n        timeout: float = None,\n        storageState: Union[StorageState, str, Path] = None,\n        clientCertificates: List[ClientCertificate] = None,\n        failOnStatusCode: bool = None,\n        maxRedirects: int = None,\n    ) -> \"APIRequestContext\":\n        params = locals_to_params(locals())\n        if \"storageState\" in params:\n            storage_state = params[\"storageState\"]\n            if not isinstance(storage_state, dict) and storage_state:\n                params[\"storageState\"] = json.loads(\n                    (await async_readfile(storage_state)).decode()\n                )\n        if \"extraHTTPHeaders\" in params:\n            params[\"extraHTTPHeaders\"] = serialize_headers(params[\"extraHTTPHeaders\"])\n        params[\"clientCertificates\"] = await to_client_certificates_protocol(\n            params.get(\"clientCertificates\")\n        )\n        context = cast(\n            APIRequestContext,\n            from_channel(\n                await self.playwright._channel.send(\"newRequest\", None, params)\n            ),\n        )\n        context._timeout_settings.set_default_timeout(timeout)\n        return context\n\n\nclass APIRequestContext(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._tracing: Tracing = from_channel(initializer[\"tracing\"])\n        self._close_reason: Optional[str] = None\n        self._timeout_settings = TimeoutSettings(None)\n\n    async def dispose(self, reason: str = None) -> None:\n        self._close_reason = reason\n        try:\n            await self._channel.send(\"dispose\", None, {\"reason\": reason})\n        except Error as e:\n            if is_target_closed_error(e):\n                return\n            raise e\n        self._tracing._reset_stack_counter()\n\n    async def delete(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: MultipartType = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"DELETE\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def head(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: MultipartType = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"HEAD\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def get(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: MultipartType = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"GET\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def patch(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"PATCH\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def put(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"PUT\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def post(\n        self,\n        url: str,\n        params: ParamsType = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        return await self.fetch(\n            url,\n            method=\"POST\",\n            params=params,\n            headers=headers,\n            data=data,\n            form=form,\n            multipart=multipart,\n            timeout=timeout,\n            failOnStatusCode=failOnStatusCode,\n            ignoreHTTPSErrors=ignoreHTTPSErrors,\n            maxRedirects=maxRedirects,\n            maxRetries=maxRetries,\n        )\n\n    async def fetch(\n        self,\n        urlOrRequest: Union[str, network.Request],\n        params: ParamsType = None,\n        method: str = None,\n        headers: Headers = None,\n        data: DataType = None,\n        form: FormType = None,\n        multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        url = urlOrRequest if isinstance(urlOrRequest, str) else None\n        request = (\n            cast(network.Request, to_impl(urlOrRequest))\n            if isinstance(to_impl(urlOrRequest), network.Request)\n            else None\n        )\n        assert request or isinstance(\n            urlOrRequest, str\n        ), \"First argument must be either URL string or Request\"\n        return await self._inner_fetch(\n            request,\n            url,\n            method,\n            headers,\n            data,\n            params,\n            form,\n            multipart,\n            timeout,\n            failOnStatusCode,\n            ignoreHTTPSErrors,\n            maxRedirects,\n            maxRetries,\n        )\n\n    async def _inner_fetch(\n        self,\n        request: Optional[network.Request],\n        url: Optional[str],\n        method: str = None,\n        headers: Headers = None,\n        data: DataType = None,\n        params: ParamsType = None,\n        form: FormType = None,\n        multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,\n        timeout: float = None,\n        failOnStatusCode: bool = None,\n        ignoreHTTPSErrors: bool = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n    ) -> \"APIResponse\":\n        if self._close_reason:\n            raise TargetClosedError(self._close_reason)\n        assert (\n            (1 if data else 0) + (1 if form else 0) + (1 if multipart else 0)\n        ) <= 1, \"Only one of 'data', 'form' or 'multipart' can be specified\"\n        assert (\n            maxRedirects is None or maxRedirects >= 0\n        ), \"'max_redirects' must be greater than or equal to '0'\"\n        assert (\n            maxRetries is None or maxRetries >= 0\n        ), \"'max_retries' must be greater than or equal to '0'\"\n        url = url or (request.url if request else url)\n        method = method or (request.method if request else \"GET\")\n        # Cannot call allHeaders() here as the request may be paused inside route handler.\n        headers_obj = headers or (request.headers if request else None)\n        serialized_headers = serialize_headers(headers_obj) if headers_obj else None\n        json_data: Any = None\n        form_data: Optional[List[NameValue]] = None\n        multipart_data: Optional[List[FormField]] = None\n        post_data_buffer: Optional[bytes] = None\n        if data is not None:\n            if isinstance(data, str):\n                if is_json_content_type(serialized_headers):\n                    json_data = data if is_json_parsable(data) else json.dumps(data)\n                else:\n                    post_data_buffer = data.encode()\n            elif isinstance(data, bytes):\n                post_data_buffer = data\n            elif isinstance(data, (dict, list, int, bool)):\n                json_data = json.dumps(data)\n            else:\n                raise Error(f\"Unsupported 'data' type: {type(data)}\")\n        elif form:\n            form_data = object_to_array(form)\n        elif multipart:\n            multipart_data = []\n            # Convert file-like values to ServerFilePayload structs.\n            for name, value in multipart.items():\n                if is_file_payload(value):\n                    payload = cast(FilePayload, value)\n                    assert isinstance(\n                        payload[\"buffer\"], bytes\n                    ), f\"Unexpected buffer type of 'data.{name}'\"\n                    multipart_data.append(\n                        FormField(name=name, file=file_payload_to_json(payload))\n                    )\n                elif isinstance(value, str):\n                    multipart_data.append(FormField(name=name, value=value))\n        if (\n            post_data_buffer is None\n            and json_data is None\n            and form_data is None\n            and multipart_data is None\n        ):\n            post_data_buffer = request.post_data_buffer if request else None\n        post_data = (\n            base64.b64encode(post_data_buffer).decode() if post_data_buffer else None\n        )\n\n        response = await self._channel.send(\n            \"fetch\",\n            self._timeout_settings.timeout,\n            {\n                \"url\": url,\n                \"timeout\": timeout,\n                \"params\": object_to_array(params) if isinstance(params, dict) else None,\n                \"encodedParams\": params if isinstance(params, str) else None,\n                \"method\": method,\n                \"headers\": serialized_headers,\n                \"postData\": post_data,\n                \"jsonData\": json_data,\n                \"formData\": form_data,\n                \"multipartData\": multipart_data,\n                \"failOnStatusCode\": failOnStatusCode,\n                \"ignoreHTTPSErrors\": ignoreHTTPSErrors,\n                \"maxRedirects\": maxRedirects,\n                \"maxRetries\": maxRetries,\n            },\n        )\n        return APIResponse(self, response)\n\n    async def storage_state(\n        self,\n        path: Union[pathlib.Path, str] = None,\n        indexedDB: bool = None,\n    ) -> StorageState:\n        result = await self._channel.send_return_as_dict(\n            \"storageState\", None, {\"indexedDB\": indexedDB}\n        )\n        if path:\n            await async_writefile(path, json.dumps(result))\n        return result\n\n\ndef file_payload_to_json(payload: FilePayload) -> ServerFilePayload:\n    return ServerFilePayload(\n        name=payload[\"name\"],\n        mimeType=payload[\"mimeType\"],\n        buffer=base64.b64encode(payload[\"buffer\"]).decode(),\n    )\n\n\nclass APIResponse:\n    def __init__(self, context: APIRequestContext, initializer: Dict) -> None:\n        self._loop = context._loop\n        self._dispatcher_fiber = context._connection._dispatcher_fiber\n        self._request = context\n        self._initializer = initializer\n        self._headers = network.RawHeaders(initializer[\"headers\"])\n\n    def __repr__(self) -> str:\n        return f\"<APIResponse url={self.url!r} status={self.status!r} status_text={self.status_text!r}>\"\n\n    @property\n    def ok(self) -> bool:\n        return self.status >= 200 and self.status <= 299\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def status(self) -> int:\n        return self._initializer[\"status\"]\n\n    @property\n    def status_text(self) -> str:\n        return self._initializer[\"statusText\"]\n\n    @property\n    def headers(self) -> Headers:\n        return self._headers.headers()\n\n    @property\n    def headers_array(self) -> network.HeadersArray:\n        return self._headers.headers_array()\n\n    async def body(self) -> bytes:\n        try:\n            result = await self._request._connection.wrap_api_call(\n                lambda: self._request._channel.send_return_as_dict(\n                    \"fetchResponseBody\",\n                    None,\n                    {\n                        \"fetchUid\": self._fetch_uid,\n                    },\n                ),\n                True,\n            )\n            if result is None:\n                raise Error(\"Response has been disposed\")\n            return base64.b64decode(result[\"binary\"])\n        except Error as exc:\n            if is_target_closed_error(exc):\n                raise Error(\"Response has been disposed\")\n            raise exc\n\n    async def text(self) -> str:\n        content = await self.body()\n        return content.decode()\n\n    async def json(self) -> Any:\n        content = await self.text()\n        return json.loads(content)\n\n    async def dispose(self) -> None:\n        await self._request._channel.send(\n            \"disposeAPIResponse\",\n            None,\n            {\n                \"fetchUid\": self._fetch_uid,\n            },\n        )\n\n    @property\n    def _fetch_uid(self) -> str:\n        return self._initializer[\"fetchUid\"]\n\n    async def _fetch_log(self) -> List[str]:\n        return await self._request._channel.send(\n            \"fetchLog\",\n            None,\n            {\n                \"fetchUid\": self._fetch_uid,\n            },\n        )\n\n\ndef is_json_content_type(headers: network.HeadersArray = None) -> bool:\n    if not headers:\n        return False\n    for header in headers:\n        if header[\"name\"] == \"Content-Type\":\n            return header[\"value\"].startswith(\"application/json\")\n    return False\n\n\ndef is_json_parsable(value: Any) -> bool:\n    if not isinstance(value, str):\n        return False\n    try:\n        json.loads(value)\n        return True\n    except json.JSONDecodeError:\n        return False\n"
  },
  {
    "path": "playwright/_impl/_file_chooser.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Sequence, Union\n\nfrom playwright._impl._api_structures import FilePayload\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._element_handle import ElementHandle\n    from playwright._impl._page import Page\n\n\nclass FileChooser:\n    def __init__(\n        self, page: \"Page\", element_handle: \"ElementHandle\", is_multiple: bool\n    ) -> None:\n        self._page = page\n        self._loop = page._loop\n        self._dispatcher_fiber = page._dispatcher_fiber\n        self._element_handle = element_handle\n        self._is_multiple = is_multiple\n\n    def __repr__(self) -> str:\n        return f\"<FileChooser page={self._page} element={self._element_handle}>\"\n\n    @property\n    def page(self) -> \"Page\":\n        return self._page\n\n    @property\n    def element(self) -> \"ElementHandle\":\n        return self._element_handle\n\n    def is_multiple(self) -> bool:\n        return self._is_multiple\n\n    async def set_files(\n        self,\n        files: Union[\n            str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]\n        ],\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self._element_handle.set_input_files(files, timeout, noWaitAfter)\n"
  },
  {
    "path": "playwright/_impl/_frame.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom pathlib import Path\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Pattern,\n    Sequence,\n    Set,\n    Union,\n    cast,\n)\n\nfrom pyee import EventEmitter\n\nfrom playwright._impl._api_structures import (\n    AriaRole,\n    FilePayload,\n    FrameExpectOptions,\n    FrameExpectResult,\n    Position,\n)\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._element_handle import ElementHandle, convert_select_option_values\nfrom playwright._impl._errors import Error\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._helper import (\n    DocumentLoadState,\n    FrameNavigatedEvent,\n    KeyboardModifier,\n    MouseButton,\n    TimeoutSettings,\n    URLMatch,\n    async_readfile,\n    locals_to_params,\n    monotonic_time,\n    url_matches,\n)\nfrom playwright._impl._js_handle import (\n    JSHandle,\n    Serializable,\n    add_source_url_to_script,\n    parse_result,\n    parse_value,\n    serialize_argument,\n)\nfrom playwright._impl._locator import (\n    FrameLocator,\n    Locator,\n    get_by_alt_text_selector,\n    get_by_label_selector,\n    get_by_placeholder_selector,\n    get_by_role_selector,\n    get_by_test_id_selector,\n    get_by_text_selector,\n    get_by_title_selector,\n    test_id_attribute_name,\n)\nfrom playwright._impl._network import Response\nfrom playwright._impl._set_input_files_helpers import convert_input_files\nfrom playwright._impl._waiter import Waiter\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._page import Page\n\n\nclass Frame(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._parent_frame = from_nullable_channel(initializer.get(\"parentFrame\"))\n        if self._parent_frame:\n            self._parent_frame._child_frames.append(self)\n        self._name = initializer[\"name\"]\n        self._url = initializer[\"url\"]\n        self._detached = False\n        self._child_frames: List[Frame] = []\n        self._page: Optional[Page] = None\n        self._load_states: Set[str] = set(initializer[\"loadStates\"])\n        self._event_emitter = EventEmitter()\n        self._channel.on(\n            \"loadstate\",\n            lambda params: self._on_load_state(params.get(\"add\"), params.get(\"remove\")),\n        )\n        self._channel.on(\n            \"navigated\",\n            lambda params: self._on_frame_navigated(params),\n        )\n\n    def __repr__(self) -> str:\n        return f\"<Frame name={self.name} url={self.url!r}>\"\n\n    def _on_load_state(\n        self, add: DocumentLoadState = None, remove: DocumentLoadState = None\n    ) -> None:\n        if add:\n            self._load_states.add(add)\n            self._event_emitter.emit(\"loadstate\", add)\n        elif remove and remove in self._load_states:\n            self._load_states.remove(remove)\n        if not self._parent_frame and add == \"load\" and self._page:\n            self._page.emit(\"load\", self._page)\n        if not self._parent_frame and add == \"domcontentloaded\" and self._page:\n            self._page.emit(\"domcontentloaded\", self._page)\n\n    def _on_frame_navigated(self, event: FrameNavigatedEvent) -> None:\n        self._url = event[\"url\"]\n        self._name = event[\"name\"]\n        self._event_emitter.emit(\"navigated\", event)\n        if \"error\" not in event and self._page:\n            self._page.emit(\"framenavigated\", self)\n\n    async def _query_count(self, selector: str) -> int:\n        return await self._channel.send(\"queryCount\", None, {\"selector\": selector})\n\n    @property\n    def page(self) -> \"Page\":\n        assert self._page\n        return self._page\n\n    async def goto(\n        self,\n        url: str,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n        referer: str = None,\n    ) -> Optional[Response]:\n        return cast(\n            Optional[Response],\n            from_nullable_channel(\n                await self._channel.send(\n                    \"goto\", self._navigation_timeout, locals_to_params(locals())\n                )\n            ),\n        )\n\n    def _setup_navigation_waiter(self, wait_name: str, timeout: float = None) -> Waiter:\n        assert self._page\n        waiter = Waiter(self._page, f\"frame.{wait_name}\")\n        waiter.reject_on_event(\n            self._page,\n            \"close\",\n            lambda: cast(\"Page\", self._page)._close_error_with_reason(),\n        )\n        waiter.reject_on_event(\n            self._page, \"crash\", Error(\"Navigation failed because page crashed!\")\n        )\n        waiter.reject_on_event(\n            self._page,\n            \"framedetached\",\n            Error(\"Navigating frame was detached!\"),\n            lambda frame: frame == self,\n        )\n        timeout = self._page._timeout_settings.navigation_timeout(timeout)\n        waiter.reject_on_timeout(timeout, f\"Timeout {timeout}ms exceeded.\")\n        return waiter\n\n    async def _expect(\n        self,\n        selector: Optional[str],\n        expression: str,\n        options: FrameExpectOptions,\n        title: str = None,\n    ) -> FrameExpectResult:\n        if \"expectedValue\" in options:\n            options[\"expectedValue\"] = serialize_argument(options[\"expectedValue\"])\n        result = await self._channel.send_return_as_dict(\n            \"expect\",\n            self._timeout,\n            {\n                \"selector\": selector,\n                \"expression\": expression,\n                **options,\n            },\n            title=title,\n        )\n        if result.get(\"received\"):\n            result[\"received\"] = parse_value(result[\"received\"])\n        return result\n\n    def expect_navigation(\n        self,\n        url: URLMatch = None,\n        waitUntil: DocumentLoadState = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Response]:\n        assert self._page\n        if not waitUntil:\n            waitUntil = \"load\"\n\n        if timeout is None:\n            timeout = self._page._timeout_settings.navigation_timeout()\n        deadline = monotonic_time() + timeout\n        waiter = self._setup_navigation_waiter(\"expect_navigation\", timeout)\n\n        to_url = f' to \"{url}\"' if url else \"\"\n        waiter.log(f\"waiting for navigation{to_url} until '{waitUntil}'\")\n\n        def predicate(event: Any) -> bool:\n            # Any failed navigation results in a rejection.\n            if event.get(\"error\"):\n                return True\n            waiter.log(f'  navigated to \"{event[\"url\"]}\"')\n            return url_matches(\n                cast(\"Page\", self._page)._browser_context._base_url,\n                event[\"url\"],\n                url,\n            )\n\n        waiter.wait_for_event(\n            self._event_emitter,\n            \"navigated\",\n            predicate=predicate,\n        )\n\n        async def continuation() -> Optional[Response]:\n            event = await waiter.result()\n            if \"error\" in event:\n                raise Error(event[\"error\"])\n            if waitUntil not in self._load_states:\n                t = deadline - monotonic_time()\n                if t > 0:\n                    await self._wait_for_load_state_impl(state=waitUntil, timeout=t)\n            if \"newDocument\" in event and \"request\" in event[\"newDocument\"]:\n                request = from_channel(event[\"newDocument\"][\"request\"])\n                return await request.response()\n            return None\n\n        return EventContextManagerImpl(asyncio.create_task(continuation()))\n\n    async def wait_for_url(\n        self,\n        url: URLMatch,\n        waitUntil: DocumentLoadState = None,\n        timeout: float = None,\n    ) -> None:\n        assert self._page\n        if url_matches(self._page._browser_context._base_url, self.url, url):\n            await self._wait_for_load_state_impl(state=waitUntil, timeout=timeout)\n            return\n        async with self.expect_navigation(\n            url=url, waitUntil=waitUntil, timeout=timeout\n        ):\n            pass\n\n    async def wait_for_load_state(\n        self,\n        state: Literal[\"domcontentloaded\", \"load\", \"networkidle\"] = None,\n        timeout: float = None,\n    ) -> None:\n        return await self._wait_for_load_state_impl(state, timeout)\n\n    async def _wait_for_load_state_impl(\n        self, state: DocumentLoadState = None, timeout: float = None\n    ) -> None:\n        if not state:\n            state = \"load\"\n        if state not in (\"load\", \"domcontentloaded\", \"networkidle\", \"commit\"):\n            raise Error(\n                \"state: expected one of (load|domcontentloaded|networkidle|commit)\"\n            )\n        waiter = self._setup_navigation_waiter(\"wait_for_load_state\", timeout)\n\n        if state in self._load_states:\n            waiter.log(f'  not waiting, \"{state}\" event already fired')\n            # TODO: align with upstream\n            waiter._fulfill(None)\n        else:\n\n            def handle_load_state_event(actual_state: str) -> bool:\n                waiter.log(f'\"{actual_state}\" event fired')\n                return actual_state == state\n\n            waiter.wait_for_event(\n                self._event_emitter,\n                \"loadstate\",\n                handle_load_state_event,\n            )\n        await waiter.result()\n\n    def _timeout(self, timeout: Optional[float]) -> float:\n        timeout_settings = (\n            self._page._timeout_settings if self._page else TimeoutSettings(None)\n        )\n        return timeout_settings.timeout(timeout)\n\n    def _navigation_timeout(self, timeout: Optional[float]) -> float:\n        timeout_settings = (\n            self._page._timeout_settings if self._page else TimeoutSettings(None)\n        )\n        return timeout_settings.navigation_timeout(timeout)\n\n    async def frame_element(self) -> ElementHandle:\n        return from_channel(await self._channel.send(\"frameElement\", None))\n\n    async def evaluate(self, expression: str, arg: Serializable = None) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evaluateExpression\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: Serializable = None\n    ) -> JSHandle:\n        return from_channel(\n            await self._channel.send(\n                \"evaluateExpressionHandle\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def query_selector(\n        self, selector: str, strict: bool = None\n    ) -> Optional[ElementHandle]:\n        return from_nullable_channel(\n            await self._channel.send(\"querySelector\", None, locals_to_params(locals()))\n        )\n\n    async def query_selector_all(self, selector: str) -> List[ElementHandle]:\n        return list(\n            map(\n                from_channel,\n                await self._channel.send(\n                    \"querySelectorAll\", None, dict(selector=selector)\n                ),\n            )\n        )\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        strict: bool = None,\n        timeout: float = None,\n        state: Literal[\"attached\", \"detached\", \"hidden\", \"visible\"] = None,\n    ) -> Optional[ElementHandle]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"waitForSelector\", self._timeout, locals_to_params(locals())\n            )\n        )\n\n    async def is_checked(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._channel.send(\n            \"isChecked\", self._timeout, locals_to_params(locals())\n        )\n\n    async def is_disabled(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._channel.send(\n            \"isDisabled\", self._timeout, locals_to_params(locals())\n        )\n\n    async def is_editable(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._channel.send(\n            \"isEditable\", self._timeout, locals_to_params(locals())\n        )\n\n    async def is_enabled(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._channel.send(\n            \"isEnabled\", self._timeout, locals_to_params(locals())\n        )\n\n    async def is_hidden(self, selector: str, strict: bool = None) -> bool:\n        return await self._channel.send(\n            \"isHidden\", self._timeout, locals_to_params(locals())\n        )\n\n    async def is_visible(self, selector: str, strict: bool = None) -> bool:\n        return await self._channel.send(\n            \"isVisible\", self._timeout, locals_to_params(locals())\n        )\n\n    async def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        eventInit: Dict = None,\n        strict: bool = None,\n        timeout: float = None,\n    ) -> None:\n        await self._channel.send(\n            \"dispatchEvent\",\n            self._timeout,\n            locals_to_params(\n                dict(\n                    selector=selector,\n                    type=type,\n                    eventInit=serialize_argument(eventInit),\n                    strict=strict,\n                    timeout=timeout,\n                ),\n            ),\n        )\n\n    async def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n        strict: bool = None,\n    ) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evalOnSelector\",\n                None,\n                locals_to_params(\n                    dict(\n                        selector=selector,\n                        expression=expression,\n                        arg=serialize_argument(arg),\n                        strict=strict,\n                    )\n                ),\n            )\n        )\n\n    async def eval_on_selector_all(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n    ) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evalOnSelectorAll\",\n                None,\n                dict(\n                    selector=selector,\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def content(self) -> str:\n        return await self._channel.send(\"content\", None)\n\n    async def set_content(\n        self,\n        html: str,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n    ) -> None:\n        await self._channel.send(\n            \"setContent\", self._navigation_timeout, locals_to_params(locals())\n        )\n\n    @property\n    def name(self) -> str:\n        return self._name or \"\"\n\n    @property\n    def url(self) -> str:\n        return self._url or \"\"\n\n    @property\n    def parent_frame(self) -> Optional[\"Frame\"]:\n        return self._parent_frame\n\n    @property\n    def child_frames(self) -> List[\"Frame\"]:\n        return self._child_frames.copy()\n\n    def is_detached(self) -> bool:\n        return self._detached\n\n    async def add_script_tag(\n        self,\n        url: str = None,\n        path: Union[str, Path] = None,\n        content: str = None,\n        type: str = None,\n    ) -> ElementHandle:\n        params = locals_to_params(locals())\n        if path:\n            params[\"content\"] = add_source_url_to_script(\n                (await async_readfile(path)).decode(), path\n            )\n            del params[\"path\"]\n        return from_channel(await self._channel.send(\"addScriptTag\", None, params))\n\n    async def add_style_tag(\n        self, url: str = None, path: Union[str, Path] = None, content: str = None\n    ) -> ElementHandle:\n        params = locals_to_params(locals())\n        if path:\n            params[\"content\"] = (\n                (await async_readfile(path)).decode()\n                + \"\\n/*# sourceURL=\"\n                + str(Path(path))\n                + \"*/\"\n            )\n            del params[\"path\"]\n        return from_channel(await self._channel.send(\"addStyleTag\", None, params))\n\n    async def click(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._click(**locals_to_params(locals()))\n\n    async def _click(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        await self._channel.send(\"click\", self._timeout, locals_to_params(locals()))\n\n    async def dblclick(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\n            \"dblclick\", self._timeout, locals_to_params(locals()), title=\"Double click\"\n        )\n\n    async def tap(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\"tap\", self._timeout, locals_to_params(locals()))\n\n    async def fill(\n        self,\n        selector: str,\n        value: str,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        force: bool = None,\n    ) -> None:\n        await self._fill(**locals_to_params(locals()))\n\n    async def _fill(\n        self,\n        selector: str,\n        value: str,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        force: bool = None,\n        title: str = None,\n    ) -> None:\n        await self._channel.send(\"fill\", self._timeout, locals_to_params(locals()))\n\n    def locator(\n        self,\n        selector: str,\n        hasText: Union[str, Pattern[str]] = None,\n        hasNotText: Union[str, Pattern[str]] = None,\n        has: Locator = None,\n        hasNot: Locator = None,\n    ) -> Locator:\n        return Locator(\n            self,\n            selector,\n            has_text=hasText,\n            has_not_text=hasNotText,\n            has=has,\n            has_not=hasNot,\n        )\n\n    def get_by_alt_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_alt_text_selector(text, exact=exact))\n\n    def get_by_label(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_label_selector(text, exact=exact))\n\n    def get_by_placeholder(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_placeholder_selector(text, exact=exact))\n\n    def get_by_role(\n        self,\n        role: AriaRole,\n        checked: bool = None,\n        disabled: bool = None,\n        expanded: bool = None,\n        includeHidden: bool = None,\n        level: int = None,\n        name: Union[str, Pattern[str]] = None,\n        pressed: bool = None,\n        selected: bool = None,\n        exact: bool = None,\n    ) -> \"Locator\":\n        return self.locator(\n            get_by_role_selector(\n                role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=includeHidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> \"Locator\":\n        return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))\n\n    def get_by_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_text_selector(text, exact=exact))\n\n    def get_by_title(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_title_selector(text, exact=exact))\n\n    def frame_locator(self, selector: str) -> FrameLocator:\n        return FrameLocator(self, selector)\n\n    async def focus(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> None:\n        await self._channel.send(\"focus\", self._timeout, locals_to_params(locals()))\n\n    async def text_content(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> Optional[str]:\n        return await self._channel.send(\n            \"textContent\", self._timeout, locals_to_params(locals())\n        )\n\n    async def inner_text(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> str:\n        return await self._channel.send(\n            \"innerText\", self._timeout, locals_to_params(locals())\n        )\n\n    async def inner_html(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> str:\n        return await self._channel.send(\n            \"innerHTML\", self._timeout, locals_to_params(locals())\n        )\n\n    async def get_attribute(\n        self, selector: str, name: str, strict: bool = None, timeout: float = None\n    ) -> Optional[str]:\n        return await self._channel.send(\n            \"getAttribute\", self._timeout, locals_to_params(locals())\n        )\n\n    async def hover(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\"hover\", self._timeout, locals_to_params(locals()))\n\n    async def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        sourcePosition: Position = None,\n        targetPosition: Position = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        timeout: float = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        await self._channel.send(\n            \"dragAndDrop\", self._timeout, locals_to_params(locals())\n        )\n\n    async def select_option(\n        self,\n        selector: str,\n        value: Union[str, Sequence[str]] = None,\n        index: Union[int, Sequence[int]] = None,\n        label: Union[str, Sequence[str]] = None,\n        element: Union[\"ElementHandle\", Sequence[\"ElementHandle\"]] = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        force: bool = None,\n    ) -> List[str]:\n        params = locals_to_params(\n            dict(\n                selector=selector,\n                timeout=timeout,\n                strict=strict,\n                force=force,\n                **convert_select_option_values(value, index, label, element),\n            )\n        )\n        return await self._channel.send(\"selectOption\", self._timeout, params)\n\n    async def input_value(\n        self,\n        selector: str,\n        strict: bool = None,\n        timeout: float = None,\n    ) -> str:\n        return await self._channel.send(\n            \"inputValue\", self._timeout, locals_to_params(locals())\n        )\n\n    async def set_input_files(\n        self,\n        selector: str,\n        files: Union[\n            str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]\n        ],\n        strict: bool = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        converted = await convert_input_files(files, self.page.context)\n        await self._channel.send(\n            \"setInputFiles\",\n            self._timeout,\n            {\n                \"selector\": selector,\n                \"strict\": strict,\n                \"timeout\": self._timeout(timeout),\n                **converted,\n            },\n        )\n\n    async def type(\n        self,\n        selector: str,\n        text: str,\n        delay: float = None,\n        strict: bool = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self._channel.send(\"type\", self._timeout, locals_to_params(locals()))\n\n    async def press(\n        self,\n        selector: str,\n        key: str,\n        delay: float = None,\n        strict: bool = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self._channel.send(\"press\", self._timeout, locals_to_params(locals()))\n\n    async def check(\n        self,\n        selector: str,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\"check\", self._timeout, locals_to_params(locals()))\n\n    async def uncheck(\n        self,\n        selector: str,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        await self._channel.send(\"uncheck\", self._timeout, locals_to_params(locals()))\n\n    async def wait_for_timeout(self, timeout: float) -> None:\n        await self._channel.send(\"waitForTimeout\", None, {\"waitTimeout\": timeout})\n\n    async def wait_for_function(\n        self,\n        expression: str,\n        arg: Serializable = None,\n        timeout: float = None,\n        polling: Union[float, Literal[\"raf\"]] = None,\n    ) -> JSHandle:\n        if isinstance(polling, str) and polling != \"raf\":\n            raise Error(f\"Unknown polling option: {polling}\")\n        params = locals_to_params(locals())\n        params[\"arg\"] = serialize_argument(arg)\n        if polling is not None and polling != \"raf\":\n            params[\"pollingInterval\"] = polling\n        return from_channel(\n            await self._channel.send(\"waitForFunction\", self._timeout, params)\n        )\n\n    async def title(self) -> str:\n        return await self._channel.send(\"title\", None)\n\n    async def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        if checked:\n            await self.check(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n        else:\n            await self.uncheck(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n\n    async def _highlight(self, selector: str) -> None:\n        await self._channel.send(\"highlight\", None, {\"selector\": selector})\n"
  },
  {
    "path": "playwright/_impl/_glob.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping\nescaped_chars = {\"$\", \"^\", \"+\", \".\", \"*\", \"(\", \")\", \"|\", \"\\\\\", \"?\", \"{\", \"}\", \"[\", \"]\"}\n\n\ndef glob_to_regex_pattern(glob: str) -> str:\n    tokens = [\"^\"]\n    in_group = False\n\n    i = 0\n    while i < len(glob):\n        c = glob[i]\n        if c == \"\\\\\" and i + 1 < len(glob):\n            char = glob[i + 1]\n            tokens.append(\"\\\\\" + char if char in escaped_chars else char)\n            i += 1\n        elif c == \"*\":\n            char_before = glob[i - 1] if i > 0 else None\n            star_count = 1\n            while i + 1 < len(glob) and glob[i + 1] == \"*\":\n                star_count += 1\n                i += 1\n            if star_count > 1:\n                char_after = glob[i + 1] if i + 1 < len(glob) else None\n                if char_after == \"/\":\n                    if char_before == \"/\":\n                        tokens.append(\"((.+/)|)\")\n                    else:\n                        tokens.append(\"(.*/)\")\n                    i += 1\n                else:\n                    tokens.append(\"(.*)\")\n            else:\n                tokens.append(\"([^/]*)\")\n        else:\n            if c == \"{\":\n                in_group = True\n                tokens.append(\"(\")\n            elif c == \"}\":\n                in_group = False\n                tokens.append(\")\")\n            elif c == \",\":\n                if in_group:\n                    tokens.append(\"|\")\n                else:\n                    tokens.append(\"\\\\\" + c)\n            else:\n                tokens.append(\"\\\\\" + c if c in escaped_chars else c)\n        i += 1\n\n    tokens.append(\"$\")\n    return \"\".join(tokens)\n"
  },
  {
    "path": "playwright/_impl/_greenlets.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport os\nfrom typing import Tuple\n\nimport greenlet\n\n\ndef _greenlet_trace_callback(\n    event: str, args: Tuple[greenlet.greenlet, greenlet.greenlet]\n) -> None:\n    if event in (\"switch\", \"throw\"):\n        origin, target = args\n        print(f\"Transfer from {origin} to {target} with {event}\")\n\n\nif os.environ.get(\"INTERNAL_PW_GREENLET_DEBUG\"):\n    greenlet.settrace(_greenlet_trace_callback)\n\n\nclass MainGreenlet(greenlet.greenlet):\n    def __str__(self) -> str:\n        return \"<MainGreenlet>\"\n\n\nclass RouteGreenlet(greenlet.greenlet):\n    def __str__(self) -> str:\n        return \"<RouteGreenlet>\"\n\n\nclass LocatorHandlerGreenlet(greenlet.greenlet):\n    def __str__(self) -> str:\n        return \"<LocatorHandlerGreenlet>\"\n\n\nclass EventGreenlet(greenlet.greenlet):\n    def __str__(self) -> str:\n        return \"<EventGreenlet>\"\n"
  },
  {
    "path": "playwright/_impl/_har_router.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\nimport base64\nfrom typing import TYPE_CHECKING, Optional, cast\n\nfrom playwright._impl._api_structures import HeadersArray\nfrom playwright._impl._helper import (\n    HarLookupResult,\n    RouteFromHarNotFoundPolicy,\n    URLMatch,\n)\nfrom playwright._impl._local_utils import LocalUtils\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser_context import BrowserContext\n    from playwright._impl._network import Route\n    from playwright._impl._page import Page\n\n\nclass HarRouter:\n    def __init__(\n        self,\n        local_utils: LocalUtils,\n        har_id: str,\n        not_found_action: RouteFromHarNotFoundPolicy,\n        url_matcher: Optional[URLMatch] = None,\n    ) -> None:\n        self._local_utils: LocalUtils = local_utils\n        self._har_id: str = har_id\n        self._not_found_action: RouteFromHarNotFoundPolicy = not_found_action\n        self._options_url_match: Optional[URLMatch] = url_matcher\n\n    @staticmethod\n    async def create(\n        local_utils: LocalUtils,\n        file: str,\n        not_found_action: RouteFromHarNotFoundPolicy,\n        url_matcher: Optional[URLMatch] = None,\n    ) -> \"HarRouter\":\n        har_id = await local_utils._channel.send(\"harOpen\", None, {\"file\": file})\n        return HarRouter(\n            local_utils=local_utils,\n            har_id=har_id,\n            not_found_action=not_found_action,\n            url_matcher=url_matcher,\n        )\n\n    async def _handle(self, route: \"Route\") -> None:\n        request = route.request\n        response: HarLookupResult = await self._local_utils.har_lookup(\n            harId=self._har_id,\n            url=request.url,\n            method=request.method,\n            headers=await request.headers_array(),\n            postData=request.post_data_buffer,\n            isNavigationRequest=request.is_navigation_request(),\n        )\n        action = response[\"action\"]\n        if action == \"redirect\":\n            redirect_url = response[\"redirectURL\"]\n            assert redirect_url\n            await route._redirected_navigation_request(redirect_url)\n            return\n\n        if action == \"fulfill\":\n            # If the response status is -1, the request was canceled or stalled, so we just stall it here.\n            # See https://github.com/microsoft/playwright/issues/29311.\n            # TODO: it'd be better to abort such requests, but then we likely need to respect the timing,\n            # because the request might have been stalled for a long time until the very end of the\n            # test when HAR was recorded but we'd abort it immediately.\n            if response.get(\"status\") == -1:\n                return\n            body = response[\"body\"]\n            assert body is not None\n            await route.fulfill(\n                status=response.get(\"status\"),\n                headers={\n                    v[\"name\"]: v[\"value\"]\n                    for v in cast(HeadersArray, response.get(\"headers\", []))\n                },\n                body=base64.b64decode(body),\n            )\n            return\n\n        if action == \"error\":\n            pass\n        # Report the error, but fall through to the default handler.\n\n        if self._not_found_action == \"abort\":\n            await route.abort()\n            return\n\n        await route.fallback()\n\n    async def add_context_route(self, context: \"BrowserContext\") -> None:\n        await context.route(\n            url=self._options_url_match or \"**/*\",\n            handler=lambda route, _: asyncio.create_task(self._handle(route)),\n        )\n\n    async def add_page_route(self, page: \"Page\") -> None:\n        await page.route(\n            url=self._options_url_match or \"**/*\",\n            handler=lambda route, _: asyncio.create_task(self._handle(route)),\n        )\n\n    def dispose(self) -> None:\n        asyncio.create_task(\n            self._local_utils._channel.send(\"harClose\", None, {\"harId\": self._har_id})\n        )\n"
  },
  {
    "path": "playwright/_impl/_helper.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\nimport math\nimport os\nimport re\nimport time\nimport traceback\nfrom pathlib import Path\nfrom types import TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Pattern,\n    Set,\n    Tuple,\n    TypedDict,\n    TypeVar,\n    Union,\n    cast,\n)\nfrom urllib.parse import ParseResult, urljoin, urlparse, urlunparse\n\nfrom playwright._impl._api_structures import NameValue\nfrom playwright._impl._errors import (\n    Error,\n    TargetClosedError,\n    TimeoutError,\n    is_target_closed_error,\n    rewrite_error,\n)\nfrom playwright._impl._glob import glob_to_regex_pattern\nfrom playwright._impl._greenlets import RouteGreenlet\nfrom playwright._impl._str_utils import escape_regex_flags\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._api_structures import HeadersArray\n    from playwright._impl._network import Request, Response, Route, WebSocketRoute\n\nURLMatch = Union[str, Pattern[str], Callable[[str], bool]]\nURLMatchRequest = Union[str, Pattern[str], Callable[[\"Request\"], bool]]\nURLMatchResponse = Union[str, Pattern[str], Callable[[\"Response\"], bool]]\nRouteHandlerCallback = Union[\n    Callable[[\"Route\"], Any], Callable[[\"Route\", \"Request\"], Any]\n]\nWebSocketRouteHandlerCallback = Callable[[\"WebSocketRoute\"], Any]\n\nColorScheme = Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\nForcedColors = Literal[\"active\", \"none\", \"null\"]\nContrast = Literal[\"more\", \"no-preference\", \"null\"]\nReducedMotion = Literal[\"no-preference\", \"null\", \"reduce\"]\nDocumentLoadState = Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\nKeyboardModifier = Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]\nMouseButton = Literal[\"left\", \"middle\", \"right\"]\nServiceWorkersPolicy = Literal[\"allow\", \"block\"]\nHarMode = Literal[\"full\", \"minimal\"]\nHarContentPolicy = Literal[\"attach\", \"embed\", \"omit\"]\nRouteFromHarNotFoundPolicy = Literal[\"abort\", \"fallback\"]\n\n\nclass ErrorPayload(TypedDict, total=False):\n    message: str\n    name: str\n    stack: str\n    value: Optional[Any]\n\n\nclass HarRecordingMetadata(TypedDict, total=False):\n    path: str\n    content: Optional[HarContentPolicy]\n\n\ndef prepare_record_har_options(params: Dict) -> Dict[str, Any]:\n    out_params: Dict[str, Any] = {\"path\": str(params[\"recordHarPath\"])}\n    if \"recordHarUrlFilter\" in params:\n        opt = params[\"recordHarUrlFilter\"]\n        if isinstance(opt, str):\n            out_params[\"urlGlob\"] = opt\n        if isinstance(opt, Pattern):\n            out_params[\"urlRegexSource\"] = opt.pattern\n            out_params[\"urlRegexFlags\"] = escape_regex_flags(opt)\n        del params[\"recordHarUrlFilter\"]\n    if \"recordHarMode\" in params:\n        out_params[\"mode\"] = params[\"recordHarMode\"]\n        del params[\"recordHarMode\"]\n\n    new_content_api = None\n    old_content_api = None\n    if \"recordHarContent\" in params:\n        new_content_api = params[\"recordHarContent\"]\n        del params[\"recordHarContent\"]\n    if \"recordHarOmitContent\" in params:\n        old_content_api = params[\"recordHarOmitContent\"]\n        del params[\"recordHarOmitContent\"]\n    content = new_content_api or (\"omit\" if old_content_api else None)\n    if content:\n        out_params[\"content\"] = content\n\n    return out_params\n\n\nclass ParsedMessageParams(TypedDict):\n    type: str\n    guid: str\n    initializer: Dict\n\n\nclass ParsedMessagePayload(TypedDict, total=False):\n    id: int\n    guid: str\n    method: str\n    params: ParsedMessageParams\n    result: Any\n    error: ErrorPayload\n\n\nclass Document(TypedDict):\n    request: Optional[Any]\n\n\nclass FrameNavigatedEvent(TypedDict):\n    url: str\n    name: str\n    newDocument: Optional[Document]\n    error: Optional[str]\n\n\nEnv = Dict[str, Union[str, float, bool]]\n\n\ndef url_matches(\n    base_url: Optional[str],\n    url_string: str,\n    match: Optional[URLMatch],\n    websocket_url: bool = None,\n) -> bool:\n    if not match:\n        return True\n    if isinstance(match, str):\n        match = re.compile(\n            resolve_glob_to_regex_pattern(base_url, match, websocket_url)\n        )\n    if isinstance(match, Pattern):\n        return bool(match.search(url_string))\n    return match(url_string)\n\n\ndef resolve_glob_to_regex_pattern(\n    base_url: Optional[str], glob: str, websocket_url: bool = None\n) -> str:\n    if websocket_url:\n        base_url = to_websocket_base_url(base_url)\n    glob = resolve_glob_base(base_url, glob)\n    return glob_to_regex_pattern(glob)\n\n\ndef to_websocket_base_url(base_url: Optional[str]) -> Optional[str]:\n    if base_url is not None and re.match(r\"^https?://\", base_url):\n        base_url = re.sub(r\"^http\", \"ws\", base_url)\n    return base_url\n\n\ndef resolve_glob_base(base_url: Optional[str], match: str) -> str:\n    if match[0] == \"*\":\n        return match\n\n    token_map: Dict[str, str] = {}\n\n    def map_token(original: str, replacement: str) -> str:\n        if len(original) == 0:\n            return \"\"\n        token_map[replacement] = original\n        return replacement\n\n    # Escaped `\\\\?` behaves the same as `?` in our glob patterns.\n    match = match.replace(r\"\\\\?\", \"?\")\n    # Special case about: URLs as they are not relative to base_url\n    if (\n        match.startswith(\"about:\")\n        or match.startswith(\"data:\")\n        or match.startswith(\"chrome:\")\n        or match.startswith(\"edge:\")\n        or match.startswith(\"file:\")\n    ):\n        # about: and data: URLs are not relative to base_url, so we return them as is.\n        return match\n    # Glob symbols may be escaped in the URL and some of them such as ? affect resolution,\n    # so we replace them with safe components first.\n    processed_parts = []\n    for index, token in enumerate(match.split(\"/\")):\n        if token in (\".\", \"..\", \"\"):\n            processed_parts.append(token)\n            continue\n        # Handle special case of http*://, note that the new schema has to be\n        # a web schema so that slashes are properly inserted after domain.\n        if index == 0 and token.endswith(\":\"):\n            # Replace any pattern with http:\n            if \"*\" in token or \"{\" in token:\n                processed_parts.append(map_token(token, \"http:\"))\n            else:\n                # Preserve explicit schema as is as it may affect trailing slashes after domain.\n                processed_parts.append(token)\n            continue\n        question_index = token.find(\"?\")\n        if question_index == -1:\n            processed_parts.append(map_token(token, f\"$_{index}_$\"))\n        else:\n            new_prefix = map_token(token[:question_index], f\"$_{index}_$\")\n            new_suffix = map_token(token[question_index:], f\"?$_{index}_$\")\n            processed_parts.append(new_prefix + new_suffix)\n\n    relative_path = \"/\".join(processed_parts)\n    resolved, case_insensitive_part = resolve_base_url(base_url, relative_path)\n\n    for token, original in token_map.items():\n        normalize = case_insensitive_part and token in case_insensitive_part\n        resolved = resolved.replace(\n            token, original.lower() if normalize else original, 1\n        )\n\n    return resolved\n\n\ndef resolve_base_url(\n    base_url: Optional[str], given_url: str\n) -> Tuple[str, Optional[str]]:\n    try:\n        url = nodelike_urlparse(\n            urljoin(base_url if base_url is not None else \"\", given_url)\n        )\n        resolved = urlunparse(url)\n        # Schema and domain are case-insensitive.\n        hostname_port = (\n            url.hostname or \"\"\n        )  # can't use parsed.netloc because it includes userinfo (username:password)\n        if url.port:\n            hostname_port += f\":{url.port}\"\n        case_insensitive_prefix = f\"{url.scheme}://{hostname_port}\"\n        return resolved, case_insensitive_prefix\n    except Exception:\n        return given_url, None\n\n\ndef nodelike_urlparse(url: str) -> ParseResult:\n    parsed = urlparse(url, allow_fragments=True)\n\n    # https://url.spec.whatwg.org/#special-scheme\n    is_special_url = parsed.scheme in [\"http\", \"https\", \"ws\", \"wss\", \"ftp\", \"file\"]\n    if is_special_url:\n        # special urls have a list path, list paths are serialized as follows: https://url.spec.whatwg.org/#url-path-serializer\n        # urllib diverges, so we patch it here\n        if parsed.path == \"\":\n            parsed = parsed._replace(path=\"/\")\n\n    return parsed\n\n\nclass HarLookupResult(TypedDict, total=False):\n    action: Literal[\"error\", \"redirect\", \"fulfill\", \"noentry\"]\n    message: Optional[str]\n    redirectURL: Optional[str]\n    status: Optional[int]\n    headers: Optional[\"HeadersArray\"]\n    body: Optional[str]\n\n\nDEFAULT_PLAYWRIGHT_TIMEOUT_IN_MILLISECONDS = 30000\nDEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT_IN_MILLISECONDS = 180000\nPLAYWRIGHT_MAX_DEADLINE = 2147483647  # 2^31-1\n\n\nclass TimeoutSettings:\n\n    @staticmethod\n    def launch_timeout(timeout: Optional[float] = None) -> float:\n        return (\n            timeout\n            if timeout is not None\n            else DEFAULT_PLAYWRIGHT_LAUNCH_TIMEOUT_IN_MILLISECONDS\n        )\n\n    def __init__(self, parent: Optional[\"TimeoutSettings\"]) -> None:\n        self._parent = parent\n        self._default_timeout: Optional[float] = None\n        self._default_navigation_timeout: Optional[float] = None\n\n    def set_default_timeout(self, timeout: Optional[float]) -> None:\n        self._default_timeout = timeout\n\n    def timeout(self, timeout: float = None) -> float:\n        if timeout is not None:\n            return timeout\n        if self._default_timeout is not None:\n            return self._default_timeout\n        if self._parent:\n            return self._parent.timeout()\n        return DEFAULT_PLAYWRIGHT_TIMEOUT_IN_MILLISECONDS\n\n    def set_default_navigation_timeout(\n        self, navigation_timeout: Optional[float]\n    ) -> None:\n        self._default_navigation_timeout = navigation_timeout\n\n    def default_navigation_timeout(self) -> Optional[float]:\n        return self._default_navigation_timeout\n\n    def default_timeout(self) -> Optional[float]:\n        return self._default_timeout\n\n    def navigation_timeout(self, timeout: float = None) -> float:\n        if timeout is not None:\n            return timeout\n        if self._default_navigation_timeout is not None:\n            return self._default_navigation_timeout\n        if self._default_timeout is not None:\n            return self._default_timeout\n        if self._parent:\n            return self._parent.navigation_timeout()\n        return DEFAULT_PLAYWRIGHT_TIMEOUT_IN_MILLISECONDS\n\n\ndef serialize_error(ex: Exception, tb: Optional[TracebackType]) -> ErrorPayload:\n    return ErrorPayload(\n        message=str(ex), name=\"Error\", stack=\"\".join(traceback.format_tb(tb))\n    )\n\n\ndef parse_error(error: ErrorPayload, log: Optional[str] = None) -> Error:\n    base_error_class = Error\n    if error.get(\"name\") == \"TimeoutError\":\n        base_error_class = TimeoutError\n    if error.get(\"name\") == \"TargetClosedError\":\n        base_error_class = TargetClosedError\n    if not log:\n        log = \"\"\n    exc = base_error_class(patch_error_message(error[\"message\"]) + log)\n    exc._name = error[\"name\"]\n    exc._stack = error[\"stack\"]\n    return exc\n\n\ndef patch_error_message(message: str) -> str:\n    match = re.match(r\"(\\w+)(: expected .*)\", message)\n    if match:\n        message = to_snake_case(match.group(1)) + match.group(2)\n    message = message.replace(\n        \"Pass { acceptDownloads: true }\", \"Pass 'accept_downloads=True'\"\n    )\n    return message\n\n\ndef locals_to_params(args: Dict) -> Dict:\n    copy = {}\n    for key in args:\n        if key == \"self\":\n            continue\n        if args[key] is not None:\n            copy[key] = (\n                args[key]\n                if not isinstance(args[key], Dict)\n                else locals_to_params(args[key])\n            )\n    return copy\n\n\ndef monotonic_time() -> int:\n    return math.floor(time.monotonic() * 1000)\n\n\nclass RouteHandlerInvocation:\n    complete: \"asyncio.Future\"\n    route: \"Route\"\n\n    def __init__(self, complete: \"asyncio.Future\", route: \"Route\") -> None:\n        self.complete = complete\n        self.route = route\n\n\nclass RouteHandler:\n    def __init__(\n        self,\n        base_url: Optional[str],\n        url: URLMatch,\n        handler: RouteHandlerCallback,\n        is_sync: bool,\n        times: Optional[int] = None,\n    ):\n        self._base_url = base_url\n        self.url = url\n        self.handler = handler\n        self._times = times if times else math.inf\n        self._handled_count = 0\n        self._is_sync = is_sync\n        self._ignore_exception = False\n        self._active_invocations: Set[RouteHandlerInvocation] = set()\n\n    def matches(self, request_url: str) -> bool:\n        return url_matches(self._base_url, request_url, self.url)\n\n    async def handle(self, route: \"Route\") -> bool:\n        handler_invocation = RouteHandlerInvocation(\n            asyncio.get_running_loop().create_future(), route\n        )\n        self._active_invocations.add(handler_invocation)\n        try:\n            return await self._handle_internal(route)\n        except Exception as e:\n            # If the handler was stopped (without waiting for completion), we ignore all exceptions.\n            if self._ignore_exception:\n                return False\n            if is_target_closed_error(e):\n                # We are failing in the handler because the target has closed.\n                # Give user a hint!\n                optional_async_prefix = \"await \" if not self._is_sync else \"\"\n                raise rewrite_error(\n                    e,\n                    f\"\\\"{str(e)}\\\" while running route callback.\\nConsider awaiting `{optional_async_prefix}page.unroute_all(behavior='ignoreErrors')`\\nbefore the end of the test to ignore remaining routes in flight.\",\n                )\n            raise e\n        finally:\n            handler_invocation.complete.set_result(None)\n            self._active_invocations.remove(handler_invocation)\n\n    async def _handle_internal(self, route: \"Route\") -> bool:\n        handled_future = route._start_handling()\n\n        self._handled_count += 1\n        if self._is_sync:\n            handler_finished_future = route._loop.create_future()\n\n            def _handler() -> None:\n                try:\n                    self.handler(route, route.request)  # type: ignore\n                    handler_finished_future.set_result(None)\n                except Exception as e:\n                    handler_finished_future.set_exception(e)\n\n            # As with event handlers, each route handler is a potentially blocking context\n            # so it needs a fiber.\n            g = RouteGreenlet(_handler)\n            g.switch()\n            await handler_finished_future\n        else:\n            coro_or_future = self.handler(route, route.request)  # type: ignore\n            if coro_or_future:\n                # separate task so that we get a proper stack trace for exceptions / tracing api_name extraction\n                await asyncio.ensure_future(coro_or_future)\n        return await handled_future\n\n    async def stop(self, behavior: Literal[\"ignoreErrors\", \"wait\"]) -> None:\n        # When a handler is manually unrouted or its page/context is closed we either\n        # - wait for the current handler invocations to finish\n        # - or do not wait, if the user opted out of it, but swallow all exceptions\n        #   that happen after the unroute/close.\n        if behavior == \"ignoreErrors\":\n            self._ignore_exception = True\n        else:\n            tasks = []\n            for activation in self._active_invocations:\n                if not activation.route._did_throw:\n                    tasks.append(activation.complete)\n            await asyncio.gather(*tasks)\n\n    @property\n    def will_expire(self) -> bool:\n        return self._handled_count + 1 >= self._times\n\n    @staticmethod\n    def prepare_interception_patterns(\n        handlers: List[\"RouteHandler\"],\n    ) -> List[Dict[str, str]]:\n        patterns = []\n        all = False\n        for handler in handlers:\n            if isinstance(handler.url, str):\n                patterns.append({\"glob\": handler.url})\n            elif isinstance(handler.url, re.Pattern):\n                patterns.append(\n                    {\n                        \"regexSource\": handler.url.pattern,\n                        \"regexFlags\": escape_regex_flags(handler.url),\n                    }\n                )\n            else:\n                all = True\n        if all:\n            return [{\"glob\": \"**/*\"}]\n        return patterns\n\n\nto_snake_case_regex = re.compile(\"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))\")\n\n\ndef to_snake_case(name: str) -> str:\n    return to_snake_case_regex.sub(r\"_\\1\", name).lower()\n\n\ndef make_dirs_for_file(path: Union[Path, str]) -> None:\n    if not os.path.isabs(path):\n        path = Path.cwd() / path\n    os.makedirs(os.path.dirname(path), exist_ok=True)\n\n\nasync def async_writefile(file: Union[str, Path], data: Union[str, bytes]) -> None:\n    def inner() -> None:\n        with open(file, \"w\" if isinstance(data, str) else \"wb\") as fh:\n            fh.write(data)\n\n    loop = asyncio.get_running_loop()\n    await loop.run_in_executor(None, inner)\n\n\nasync def async_readfile(file: Union[str, Path]) -> bytes:\n    def inner() -> bytes:\n        with open(file, \"rb\") as fh:\n            return fh.read()\n\n    loop = asyncio.get_running_loop()\n    return await loop.run_in_executor(None, inner)\n\n\nT = TypeVar(\"T\")\n\n\ndef to_impl(obj: T) -> T:\n    if hasattr(obj, \"_impl_obj\"):\n        return cast(Any, obj)._impl_obj\n    return obj\n\n\ndef object_to_array(obj: Optional[Dict]) -> Optional[List[NameValue]]:\n    if not obj:\n        return None\n    result = []\n    for key, value in obj.items():\n        result.append(NameValue(name=key, value=str(value)))\n    return result\n\n\ndef is_file_payload(value: Optional[Any]) -> bool:\n    return (\n        isinstance(value, dict)\n        and \"name\" in value\n        and \"mimeType\" in value\n        and \"buffer\" in value\n    )\n\n\nTEXTUAL_MIME_TYPE = re.compile(\n    r\"^(text\\/.*?|application\\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\\/svg(\\+xml)?|application\\/.*?(\\+json|\\+xml))(;\\s*charset=.*)?$\"\n)\n\n\ndef is_textual_mime_type(mime_type: str) -> bool:\n    return bool(TEXTUAL_MIME_TYPE.match(mime_type))\n"
  },
  {
    "path": "playwright/_impl/_impl_to_api_mapping.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nfrom typing import Any, Callable, Dict, List, Optional, Sequence, Union\n\nfrom playwright._impl._errors import Error\nfrom playwright._impl._map import Map\n\nAPI_ATTR = \"_pw_api_instance_\"\nIMPL_ATTR = \"_pw_impl_instance_\"\n\n\nclass ImplWrapper:\n    def __init__(self, impl_obj: Any) -> None:\n        self._impl_obj = impl_obj\n\n    def __repr__(self) -> str:\n        return self._impl_obj.__repr__()\n\n\nclass ImplToApiMapping:\n    def __init__(self) -> None:\n        self._mapping: Dict[type, type] = {}\n\n    def register(self, impl_class: type, api_class: type) -> None:\n        self._mapping[impl_class] = api_class\n\n    def from_maybe_impl(\n        self, obj: Any, visited: Optional[Map[Any, Union[List, Dict]]] = None\n    ) -> Any:\n        # Python does share default arguments between calls, so we need to\n        # create a new map if it is not provided.\n        if not visited:\n            visited = Map()\n        if not obj:\n            return obj\n        if isinstance(obj, dict):\n            if obj in visited:\n                return visited[obj]\n            o: Dict = {}\n            visited[obj] = o\n            for name, value in obj.items():\n                o[name] = self.from_maybe_impl(value, visited)\n            return o\n        if isinstance(obj, list):\n            if obj in visited:\n                return visited[obj]\n            a: List = []\n            visited[obj] = a\n            for item in obj:\n                a.append(self.from_maybe_impl(item, visited))\n            return a\n        api_class = self._mapping.get(type(obj))\n        if api_class:\n            api_instance = getattr(obj, API_ATTR, None)\n            if not api_instance:\n                api_instance = api_class(obj)\n                setattr(obj, API_ATTR, api_instance)\n            return api_instance\n        else:\n            return obj\n\n    def from_impl(self, obj: Any) -> Any:\n        assert obj\n        result = self.from_maybe_impl(obj)\n        assert result\n        return result\n\n    def from_impl_nullable(self, obj: Any = None) -> Optional[Any]:\n        return self.from_impl(obj) if obj else None\n\n    def from_impl_list(self, items: Sequence[Any]) -> List[Any]:\n        return list(map(lambda a: self.from_impl(a), items))\n\n    def from_impl_dict(self, map: Dict[str, Any]) -> Dict[str, Any]:\n        return {name: self.from_impl(value) for name, value in map.items()}\n\n    def to_impl(\n        self, obj: Any, visited: Optional[Map[Any, Union[List, Dict]]] = None\n    ) -> Any:\n        if visited is None:\n            visited = Map()\n        try:\n            if not obj:\n                return obj\n            if isinstance(obj, dict):\n                if obj in visited:\n                    return visited[obj]\n                o: Dict = {}\n                visited[obj] = o\n                for name, value in obj.items():\n                    o[name] = self.to_impl(value, visited)\n                return o\n            if isinstance(obj, list):\n                if obj in visited:\n                    return visited[obj]\n                a: List = []\n                visited[obj] = a\n                for item in obj:\n                    a.append(self.to_impl(item, visited))\n                return a\n            if isinstance(obj, ImplWrapper):\n                return obj._impl_obj\n            return obj\n        except RecursionError:\n            raise Error(\"Maximum argument depth exceeded\")\n\n    def wrap_handler(self, handler: Callable[..., Any]) -> Callable[..., None]:\n        def wrapper_func(*args: Any) -> Any:\n            arg_count = len(inspect.signature(handler).parameters)\n            return handler(\n                *list(map(lambda a: self.from_maybe_impl(a), args))[:arg_count]\n            )\n\n        if inspect.ismethod(handler):\n            wrapper = getattr(handler.__self__, IMPL_ATTR + handler.__name__, None)\n            if not wrapper:\n                wrapper = wrapper_func\n                setattr(\n                    handler.__self__,\n                    IMPL_ATTR + handler.__name__,\n                    wrapper,\n                )\n            return wrapper\n\n        wrapper = getattr(handler, IMPL_ATTR, None)\n        if not wrapper:\n            wrapper = wrapper_func\n            setattr(handler, IMPL_ATTR, wrapper)\n        return wrapper\n"
  },
  {
    "path": "playwright/_impl/_input.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright._impl._connection import Channel\nfrom playwright._impl._helper import MouseButton, locals_to_params\n\n\nclass Keyboard:\n    def __init__(self, channel: Channel) -> None:\n        self._channel = channel\n        self._loop = channel._connection._loop\n        self._dispatcher_fiber = channel._connection._dispatcher_fiber\n\n    async def down(self, key: str) -> None:\n        await self._channel.send(\"keyboardDown\", None, locals_to_params(locals()))\n\n    async def up(self, key: str) -> None:\n        await self._channel.send(\"keyboardUp\", None, locals_to_params(locals()))\n\n    async def insert_text(self, text: str) -> None:\n        await self._channel.send(\"keyboardInsertText\", None, locals_to_params(locals()))\n\n    async def type(self, text: str, delay: float = None) -> None:\n        await self._channel.send(\"keyboardType\", None, locals_to_params(locals()))\n\n    async def press(self, key: str, delay: float = None) -> None:\n        await self._channel.send(\"keyboardPress\", None, locals_to_params(locals()))\n\n\nclass Mouse:\n    def __init__(self, channel: Channel) -> None:\n        self._channel = channel\n        self._loop = channel._connection._loop\n        self._dispatcher_fiber = channel._connection._dispatcher_fiber\n\n    async def move(self, x: float, y: float, steps: int = None) -> None:\n        await self._channel.send(\"mouseMove\", None, locals_to_params(locals()))\n\n    async def down(\n        self,\n        button: MouseButton = None,\n        clickCount: int = None,\n    ) -> None:\n        await self._channel.send(\"mouseDown\", None, locals_to_params(locals()))\n\n    async def up(\n        self,\n        button: MouseButton = None,\n        clickCount: int = None,\n    ) -> None:\n        await self._channel.send(\"mouseUp\", None, locals_to_params(locals()))\n\n    async def _click(\n        self,\n        x: float,\n        y: float,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        title: str = None,\n    ) -> None:\n        await self._channel.send(\n            \"mouseClick\", None, locals_to_params(locals()), title=title\n        )\n\n    async def click(\n        self,\n        x: float,\n        y: float,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n    ) -> None:\n        params = locals()\n        del params[\"self\"]\n        await self._click(**params)\n\n    async def dblclick(\n        self,\n        x: float,\n        y: float,\n        delay: float = None,\n        button: MouseButton = None,\n    ) -> None:\n        await self._click(\n            x, y, delay=delay, button=button, clickCount=2, title=\"Double click\"\n        )\n\n    async def wheel(self, deltaX: float, deltaY: float) -> None:\n        await self._channel.send(\"mouseWheel\", None, locals_to_params(locals()))\n\n\nclass Touchscreen:\n    def __init__(self, channel: Channel) -> None:\n        self._channel = channel\n        self._loop = channel._connection._loop\n        self._dispatcher_fiber = channel._connection._dispatcher_fiber\n\n    async def tap(self, x: float, y: float) -> None:\n        await self._channel.send(\"touchscreenTap\", None, locals_to_params(locals()))\n"
  },
  {
    "path": "playwright/_impl/_js_handle.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport collections.abc\nimport datetime\nimport math\nimport struct\nimport traceback\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Any, Dict, List, Optional, Union\nfrom urllib.parse import ParseResult, urlparse, urlunparse\n\nfrom playwright._impl._connection import Channel, ChannelOwner, from_channel\nfrom playwright._impl._errors import Error, is_target_closed_error\nfrom playwright._impl._map import Map\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._element_handle import ElementHandle\n\n\nSerializable = Any\n\n\nclass VisitorInfo:\n    visited: Map[Any, int]\n    last_id: int\n\n    def __init__(self) -> None:\n        self.visited = Map()\n        self.last_id = 0\n\n    def visit(self, obj: Any) -> int:\n        assert obj not in self.visited\n        self.last_id += 1\n        self.visited[obj] = self.last_id\n        return self.last_id\n\n\nclass JSHandle(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._preview = self._initializer[\"preview\"]\n        self._channel.on(\n            \"previewUpdated\", lambda params: self._on_preview_updated(params[\"preview\"])\n        )\n\n    def __repr__(self) -> str:\n        return f\"<JSHandle preview={self._preview}>\"\n\n    def __str__(self) -> str:\n        return self._preview\n\n    def _on_preview_updated(self, preview: str) -> None:\n        self._preview = preview\n\n    async def evaluate(self, expression: str, arg: Serializable = None) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evaluateExpression\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: Serializable = None\n    ) -> \"JSHandle\":\n        return from_channel(\n            await self._channel.send(\n                \"evaluateExpressionHandle\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def get_property(self, propertyName: str) -> \"JSHandle\":\n        return from_channel(\n            await self._channel.send(\"getProperty\", None, dict(name=propertyName))\n        )\n\n    async def get_properties(self) -> Dict[str, \"JSHandle\"]:\n        return {\n            prop[\"name\"]: from_channel(prop[\"value\"])\n            for prop in await self._channel.send(\n                \"getPropertyList\",\n                None,\n            )\n        }\n\n    def as_element(self) -> Optional[\"ElementHandle\"]:\n        return None\n\n    async def dispose(self) -> None:\n        try:\n            await self._channel.send(\n                \"dispose\",\n                None,\n            )\n        except Exception as e:\n            if not is_target_closed_error(e):\n                raise e\n\n    async def json_value(self) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"jsonValue\",\n                None,\n            )\n        )\n\n\ndef serialize_value(\n    value: Any, handles: List[Channel], visitor_info: Optional[VisitorInfo] = None\n) -> Any:\n    if visitor_info is None:\n        visitor_info = VisitorInfo()\n    if isinstance(value, JSHandle):\n        h = len(handles)\n        handles.append(value._channel)\n        return dict(h=h)\n    if value is None:\n        return dict(v=\"null\")\n    if isinstance(value, float):\n        if value == float(\"inf\"):\n            return dict(v=\"Infinity\")\n        if value == float(\"-inf\"):\n            return dict(v=\"-Infinity\")\n        if value == float(\"-0\"):\n            return dict(v=\"-0\")\n        if math.isnan(value):\n            return dict(v=\"NaN\")\n    if isinstance(value, datetime.datetime):\n        # Node.js Date objects are always in UTC.\n        return {\n            \"d\": datetime.datetime.strftime(\n                value.astimezone(datetime.timezone.utc), \"%Y-%m-%dT%H:%M:%S.%fZ\"\n            )\n        }\n    if isinstance(value, Exception):\n        return {\n            \"e\": {\n                \"m\": str(value),\n                \"n\": (\n                    (value.name or \"\")\n                    if isinstance(value, Error)\n                    else value.__class__.__name__\n                ),\n                \"s\": (\n                    (value.stack or \"\")\n                    if isinstance(value, Error)\n                    else \"\".join(\n                        traceback.format_exception(type(value), value=value, tb=None)\n                    )\n                ),\n            }\n        }\n    if isinstance(value, bool):\n        return {\"b\": value}\n    if isinstance(value, (int, float)):\n        return {\"n\": value}\n    if isinstance(value, str):\n        return {\"s\": value}\n    if isinstance(value, ParseResult):\n        return {\"u\": urlunparse(value)}\n\n    if value in visitor_info.visited:\n        return dict(ref=visitor_info.visited[value])\n\n    if isinstance(value, collections.abc.Sequence) and not isinstance(value, str):\n        id = visitor_info.visit(value)\n        a = []\n        for e in value:\n            a.append(serialize_value(e, handles, visitor_info))\n        return dict(a=a, id=id)\n\n    if isinstance(value, dict):\n        id = visitor_info.visit(value)\n        o = []\n        for name in value:\n            o.append(\n                {\"k\": name, \"v\": serialize_value(value[name], handles, visitor_info)}\n            )\n        return dict(o=o, id=id)\n    return dict(v=\"undefined\")\n\n\ndef serialize_argument(arg: Serializable = None) -> Any:\n    handles: List[Channel] = []\n    value = serialize_value(arg, handles)\n    return dict(value=value, handles=handles)\n\n\ndef parse_value(value: Any, refs: Optional[Dict[int, Any]] = None) -> Any:\n    if refs is None:\n        refs = {}\n    if value is None:\n        return None\n    if isinstance(value, dict):\n        if \"ref\" in value:\n            return refs[value[\"ref\"]]\n\n        if \"v\" in value:\n            v = value[\"v\"]\n            if v == \"Infinity\":\n                return float(\"inf\")\n            if v == \"-Infinity\":\n                return float(\"-inf\")\n            if v == \"-0\":\n                return float(\"-0\")\n            if v == \"NaN\":\n                return float(\"nan\")\n            if v == \"undefined\":\n                return None\n            if v == \"null\":\n                return None\n            return v\n\n        if \"u\" in value:\n            return urlparse(value[\"u\"])\n\n        if \"bi\" in value:\n            return int(value[\"bi\"])\n\n        if \"e\" in value:\n            error = Error(value[\"e\"][\"m\"])\n            error._name = value[\"e\"][\"n\"]\n            error._stack = value[\"e\"][\"s\"]\n            return error\n\n        if \"a\" in value:\n            a: List = []\n            refs[value[\"id\"]] = a\n            for e in value[\"a\"]:\n                a.append(parse_value(e, refs))\n            return a\n\n        if \"d\" in value:\n            # Node.js Date objects are always in UTC.\n            return datetime.datetime.strptime(\n                value[\"d\"], \"%Y-%m-%dT%H:%M:%S.%fZ\"\n            ).replace(tzinfo=datetime.timezone.utc)\n\n        if \"o\" in value:\n            o: Dict = {}\n            refs[value[\"id\"]] = o\n            for e in value[\"o\"]:\n                o[e[\"k\"]] = parse_value(e[\"v\"], refs)\n            return o\n\n        if \"n\" in value:\n            return value[\"n\"]\n\n        if \"s\" in value:\n            return value[\"s\"]\n\n        if \"b\" in value:\n            return value[\"b\"]\n\n        if \"ta\" in value:\n            encoded_bytes = value[\"ta\"][\"b\"]\n            decoded_bytes = base64.b64decode(encoded_bytes)\n            array_type = value[\"ta\"][\"k\"]\n            if array_type == \"i8\":\n                word_size = 1\n                fmt = \"b\"\n            elif array_type == \"ui8\" or array_type == \"ui8c\":\n                word_size = 1\n                fmt = \"B\"\n            elif array_type == \"i16\":\n                word_size = 2\n                fmt = \"h\"\n            elif array_type == \"ui16\":\n                word_size = 2\n                fmt = \"H\"\n            elif array_type == \"i32\":\n                word_size = 4\n                fmt = \"i\"\n            elif array_type == \"ui32\":\n                word_size = 4\n                fmt = \"I\"\n            elif array_type == \"f32\":\n                word_size = 4\n                fmt = \"f\"\n            elif array_type == \"f64\":\n                word_size = 8\n                fmt = \"d\"\n            elif array_type == \"bi64\":\n                word_size = 8\n                fmt = \"q\"\n            elif array_type == \"bui64\":\n                word_size = 8\n                fmt = \"Q\"\n            else:\n                raise ValueError(f\"Unsupported array type: {array_type}\")\n\n            byte_len = len(decoded_bytes)\n            if byte_len % word_size != 0:\n                raise ValueError(\n                    f\"Decoded bytes length {byte_len} is not a multiple of word size {word_size}\"\n                )\n\n            if byte_len == 0:\n                return []\n            array_len = byte_len // word_size\n            # \"<\" denotes little-endian\n            format_string = f\"<{array_len}{fmt}\"\n            return list(struct.unpack(format_string, decoded_bytes))\n    return value\n\n\ndef parse_result(result: Any) -> Any:\n    return parse_value(result)\n\n\ndef add_source_url_to_script(source: str, path: Union[str, Path]) -> str:\n    return source + \"\\n//# sourceURL=\" + str(path).replace(\"\\n\", \"\")\n"
  },
  {
    "path": "playwright/_impl/_json_pipe.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Dict, Optional, cast\n\nfrom pyee.asyncio import AsyncIOEventEmitter\n\nfrom playwright._impl._connection import Channel\nfrom playwright._impl._errors import TargetClosedError\nfrom playwright._impl._helper import Error, ParsedMessagePayload\nfrom playwright._impl._transport import Transport\n\n\nclass JsonPipeTransport(AsyncIOEventEmitter, Transport):\n    def __init__(\n        self,\n        loop: asyncio.AbstractEventLoop,\n        pipe_channel: Channel,\n    ) -> None:\n        super().__init__(loop)\n        Transport.__init__(self, loop)\n        self._stop_requested = False\n        self._pipe_channel = pipe_channel\n\n    def request_stop(self) -> None:\n        self._stop_requested = True\n        self._pipe_channel.send_no_reply(\"close\", None, {})\n\n    def dispose(self) -> None:\n        self.on_error_future.cancel()\n        self._stopped_future.cancel()\n\n    async def wait_until_stopped(self) -> None:\n        await self._stopped_future\n\n    async def connect(self) -> None:\n        self._stopped_future: asyncio.Future = asyncio.Future()\n\n        def handle_message(message: Dict) -> None:\n            if self._stop_requested:\n                return\n            self.on_message(cast(ParsedMessagePayload, message))\n\n        def handle_closed(reason: Optional[str]) -> None:\n            self.emit(\"close\", reason)\n            if reason:\n                self.on_error_future.set_exception(TargetClosedError(reason))\n            self._stopped_future.set_result(None)\n\n        self._pipe_channel.on(\n            \"message\",\n            lambda params: handle_message(params[\"message\"]),\n        )\n        self._pipe_channel.on(\n            \"closed\",\n            lambda params: handle_closed(params.get(\"reason\")),\n        )\n\n    async def run(self) -> None:\n        await self._stopped_future\n\n    def send(self, message: Dict) -> None:\n        if self._stop_requested:\n            raise Error(\"Playwright connection closed\")\n        self._pipe_channel.send_no_reply(\"send\", None, {\"message\": message})\n"
  },
  {
    "path": "playwright/_impl/_local_utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nfrom typing import Dict, List, Optional, cast\n\nfrom playwright._impl._api_structures import HeadersArray\nfrom playwright._impl._connection import ChannelOwner, StackFrame\nfrom playwright._impl._helper import HarLookupResult, locals_to_params\n\n\nclass LocalUtils(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self.devices = {\n            device[\"name\"]: parse_device_descriptor(device[\"descriptor\"])\n            for device in initializer[\"deviceDescriptors\"]\n        }\n\n    async def zip(self, params: Dict) -> None:\n        await self._channel.send(\"zip\", None, params)\n\n    async def har_open(self, file: str) -> None:\n        params = locals_to_params(locals())\n        await self._channel.send(\"harOpen\", None, params)\n\n    async def har_lookup(\n        self,\n        harId: str,\n        url: str,\n        method: str,\n        headers: HeadersArray,\n        isNavigationRequest: bool,\n        postData: Optional[bytes] = None,\n    ) -> HarLookupResult:\n        params = locals_to_params(locals())\n        if \"postData\" in params:\n            params[\"postData\"] = base64.b64encode(params[\"postData\"]).decode()\n        return cast(\n            HarLookupResult,\n            await self._channel.send_return_as_dict(\"harLookup\", None, params),\n        )\n\n    async def har_close(self, harId: str) -> None:\n        params = locals_to_params(locals())\n        await self._channel.send(\"harClose\", None, params)\n\n    async def har_unzip(self, zipFile: str, harFile: str) -> None:\n        params = locals_to_params(locals())\n        await self._channel.send(\"harUnzip\", None, params)\n\n    async def tracing_started(self, tracesDir: Optional[str], traceName: str) -> str:\n        params = locals_to_params(locals())\n        return await self._channel.send(\"tracingStarted\", None, params)\n\n    async def trace_discarded(self, stacks_id: str) -> None:\n        return await self._channel.send(\"traceDiscarded\", None, {\"stacksId\": stacks_id})\n\n    def add_stack_to_tracing_no_reply(self, id: int, frames: List[StackFrame]) -> None:\n        self._channel.send_no_reply(\n            \"addStackToTracingNoReply\",\n            None,\n            {\n                \"callData\": {\n                    \"stack\": frames,\n                    \"id\": id,\n                }\n            },\n        )\n\n\ndef parse_device_descriptor(dict: Dict) -> Dict:\n    return {\n        \"user_agent\": dict[\"userAgent\"],\n        \"viewport\": dict[\"viewport\"],\n        \"device_scale_factor\": dict[\"deviceScaleFactor\"],\n        \"is_mobile\": dict[\"isMobile\"],\n        \"has_touch\": dict[\"hasTouch\"],\n        \"default_browser_type\": dict[\"defaultBrowserType\"],\n    }\n"
  },
  {
    "path": "playwright/_impl/_locator.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport pathlib\nimport re\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Awaitable,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Pattern,\n    Sequence,\n    Tuple,\n    TypeVar,\n    Union,\n)\n\nfrom playwright._impl._api_structures import (\n    AriaRole,\n    FilePayload,\n    FloatRect,\n    FrameExpectOptions,\n    FrameExpectResult,\n    Position,\n)\nfrom playwright._impl._element_handle import ElementHandle\nfrom playwright._impl._helper import (\n    Error,\n    KeyboardModifier,\n    MouseButton,\n    locals_to_params,\n    monotonic_time,\n    to_impl,\n)\nfrom playwright._impl._js_handle import Serializable\nfrom playwright._impl._str_utils import (\n    escape_for_attribute_selector,\n    escape_for_text_selector,\n)\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._frame import Frame\n    from playwright._impl._js_handle import JSHandle\n    from playwright._impl._page import Page\n\nT = TypeVar(\"T\")\n\n\nclass Locator:\n    def __init__(\n        self,\n        frame: \"Frame\",\n        selector: str,\n        has_text: Union[str, Pattern[str]] = None,\n        has_not_text: Union[str, Pattern[str]] = None,\n        has: \"Locator\" = None,\n        has_not: \"Locator\" = None,\n        visible: bool = None,\n    ) -> None:\n        self._frame = frame\n        self._selector = selector\n        self._loop = frame._loop\n        self._dispatcher_fiber = frame._connection._dispatcher_fiber\n\n        if has_text:\n            self._selector += f\" >> internal:has-text={escape_for_text_selector(has_text, exact=False)}\"\n\n        if has:\n            if has._frame != frame:\n                raise Error('Inner \"has\" locator must belong to the same frame.')\n            self._selector += \" >> internal:has=\" + json.dumps(\n                has._selector, ensure_ascii=False\n            )\n\n        if has_not_text:\n            self._selector += f\" >> internal:has-not-text={escape_for_text_selector(has_not_text, exact=False)}\"\n\n        if has_not:\n            locator = has_not\n            if locator._frame != frame:\n                raise Error('Inner \"has_not\" locator must belong to the same frame.')\n            self._selector += \" >> internal:has-not=\" + json.dumps(locator._selector)\n\n        if visible is not None:\n            self._selector += f\" >> visible={bool_to_js_bool(visible)}\"\n\n    def __repr__(self) -> str:\n        return f\"<Locator frame={self._frame!r} selector={self._selector!r}>\"\n\n    async def _with_element(\n        self,\n        task: Callable[[ElementHandle, float], Awaitable[T]],\n        timeout: float = None,\n    ) -> T:\n        timeout = self._frame._timeout(timeout)\n        deadline = (monotonic_time() + timeout) if timeout else 0\n        handle = await self.element_handle(timeout=timeout)\n        if not handle:\n            raise Error(f\"Could not resolve {self._selector} to DOM Element\")\n        try:\n            return await task(\n                handle,\n                (deadline - monotonic_time()) if deadline else 0,\n            )\n        finally:\n            await handle.dispose()\n\n    def _equals(self, locator: \"Locator\") -> bool:\n        return self._frame == locator._frame and self._selector == locator._selector\n\n    @property\n    def page(self) -> \"Page\":\n        return self._frame.page\n\n    async def bounding_box(self, timeout: float = None) -> Optional[FloatRect]:\n        return await self._with_element(\n            lambda h, _: h.bounding_box(),\n            timeout,\n        )\n\n    async def check(\n        self,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.check(self._selector, strict=True, **params)\n\n    async def click(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame._click(self._selector, strict=True, **params)\n\n    async def dblclick(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.dblclick(self._selector, strict=True, **params)\n\n    async def dispatch_event(\n        self,\n        type: str,\n        eventInit: Dict = None,\n        timeout: float = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.dispatch_event(self._selector, strict=True, **params)\n\n    async def evaluate(\n        self, expression: str, arg: Serializable = None, timeout: float = None\n    ) -> Any:\n        return await self._with_element(\n            lambda h, _: h.evaluate(expression, arg),\n            timeout,\n        )\n\n    async def evaluate_all(self, expression: str, arg: Serializable = None) -> Any:\n        params = locals_to_params(locals())\n        return await self._frame.eval_on_selector_all(self._selector, **params)\n\n    async def evaluate_handle(\n        self, expression: str, arg: Serializable = None, timeout: float = None\n    ) -> \"JSHandle\":\n        return await self._with_element(\n            lambda h, _: h.evaluate_handle(expression, arg), timeout\n        )\n\n    async def fill(\n        self,\n        value: str,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.fill(self._selector, strict=True, **params)\n\n    async def clear(\n        self,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        await self._frame._fill(self._selector, value=\"\", title=\"Clear\", **params)\n\n    def locator(\n        self,\n        selectorOrLocator: Union[str, \"Locator\"],\n        hasText: Union[str, Pattern[str]] = None,\n        hasNotText: Union[str, Pattern[str]] = None,\n        has: \"Locator\" = None,\n        hasNot: \"Locator\" = None,\n    ) -> \"Locator\":\n        if isinstance(selectorOrLocator, str):\n            return Locator(\n                self._frame,\n                f\"{self._selector} >> {selectorOrLocator}\",\n                has_text=hasText,\n                has_not_text=hasNotText,\n                has_not=hasNot,\n                has=has,\n            )\n        selectorOrLocator = to_impl(selectorOrLocator)\n        if selectorOrLocator._frame != self._frame:\n            raise Error(\"Locators must belong to the same frame.\")\n        return Locator(\n            self._frame,\n            f\"{self._selector} >> internal:chain={json.dumps(selectorOrLocator._selector)}\",\n            has_text=hasText,\n            has_not_text=hasNotText,\n            has_not=hasNot,\n            has=has,\n        )\n\n    def get_by_alt_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_alt_text_selector(text, exact=exact))\n\n    def get_by_label(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_label_selector(text, exact=exact))\n\n    def get_by_placeholder(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_placeholder_selector(text, exact=exact))\n\n    def get_by_role(\n        self,\n        role: AriaRole,\n        checked: bool = None,\n        disabled: bool = None,\n        expanded: bool = None,\n        includeHidden: bool = None,\n        level: int = None,\n        name: Union[str, Pattern[str]] = None,\n        pressed: bool = None,\n        selected: bool = None,\n        exact: bool = None,\n    ) -> \"Locator\":\n        return self.locator(\n            get_by_role_selector(\n                role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=includeHidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> \"Locator\":\n        return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))\n\n    def get_by_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_text_selector(text, exact=exact))\n\n    def get_by_title(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_title_selector(text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        return FrameLocator(self._frame, self._selector + \" >> \" + selector)\n\n    async def element_handle(\n        self,\n        timeout: float = None,\n    ) -> ElementHandle:\n        params = locals_to_params(locals())\n        handle = await self._frame.wait_for_selector(\n            self._selector, strict=True, state=\"attached\", **params\n        )\n        assert handle\n        return handle\n\n    async def element_handles(self) -> List[ElementHandle]:\n        return await self._frame.query_selector_all(self._selector)\n\n    @property\n    def first(self) -> \"Locator\":\n        return Locator(self._frame, f\"{self._selector} >> nth=0\")\n\n    @property\n    def last(self) -> \"Locator\":\n        return Locator(self._frame, f\"{self._selector} >> nth=-1\")\n\n    def nth(self, index: int) -> \"Locator\":\n        return Locator(self._frame, f\"{self._selector} >> nth={index}\")\n\n    @property\n    def content_frame(self) -> \"FrameLocator\":\n        return FrameLocator(self._frame, self._selector)\n\n    def describe(self, description: str) -> \"Locator\":\n        return Locator(\n            self._frame,\n            f\"{self._selector} >> internal:describe={json.dumps(description)}\",\n        )\n\n    @property\n    def description(self) -> Optional[str]:\n        try:\n            match = re.search(\n                r' >> internal:describe=(\"(?:[^\"\\\\]|\\\\.)*\")$', self._selector\n            )\n            if match:\n                description = json.loads(match.group(1))\n                if isinstance(description, str):\n                    return description\n        except (json.JSONDecodeError, ValueError):\n            pass\n        return None\n\n    def filter(\n        self,\n        hasText: Union[str, Pattern[str]] = None,\n        hasNotText: Union[str, Pattern[str]] = None,\n        has: \"Locator\" = None,\n        hasNot: \"Locator\" = None,\n        visible: bool = None,\n    ) -> \"Locator\":\n        return Locator(\n            self._frame,\n            self._selector,\n            has_text=hasText,\n            has_not_text=hasNotText,\n            has=has,\n            has_not=hasNot,\n            visible=visible,\n        )\n\n    def or_(self, locator: \"Locator\") -> \"Locator\":\n        if locator._frame != self._frame:\n            raise Error(\"Locators must belong to the same frame.\")\n        return Locator(\n            self._frame,\n            self._selector + \" >> internal:or=\" + json.dumps(locator._selector),\n        )\n\n    def and_(self, locator: \"Locator\") -> \"Locator\":\n        if locator._frame != self._frame:\n            raise Error(\"Locators must belong to the same frame.\")\n        return Locator(\n            self._frame,\n            self._selector + \" >> internal:and=\" + json.dumps(locator._selector),\n        )\n\n    async def focus(self, timeout: float = None) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.focus(self._selector, strict=True, **params)\n\n    async def blur(self, timeout: float = None) -> None:\n        await self._frame._channel.send(\n            \"blur\",\n            self._frame._timeout,\n            {\n                \"selector\": self._selector,\n                \"strict\": True,\n                **locals_to_params(locals()),\n            },\n        )\n\n    async def all(\n        self,\n    ) -> List[\"Locator\"]:\n        result = []\n        for index in range(await self.count()):\n            result.append(self.nth(index))\n        return result\n\n    async def count(\n        self,\n    ) -> int:\n        return await self._frame._query_count(self._selector)\n\n    async def drag_to(\n        self,\n        target: \"Locator\",\n        force: bool = None,\n        noWaitAfter: bool = None,\n        timeout: float = None,\n        trial: bool = None,\n        sourcePosition: Position = None,\n        targetPosition: Position = None,\n        steps: int = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        del params[\"target\"]\n        return await self._frame.drag_and_drop(\n            self._selector, target._selector, strict=True, **params\n        )\n\n    async def get_attribute(self, name: str, timeout: float = None) -> Optional[str]:\n        params = locals_to_params(locals())\n        return await self._frame.get_attribute(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def hover(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n        trial: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.hover(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def inner_html(self, timeout: float = None) -> str:\n        params = locals_to_params(locals())\n        return await self._frame.inner_html(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def inner_text(self, timeout: float = None) -> str:\n        params = locals_to_params(locals())\n        return await self._frame.inner_text(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def input_value(self, timeout: float = None) -> str:\n        params = locals_to_params(locals())\n        return await self._frame.input_value(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def is_checked(self, timeout: float = None) -> bool:\n        params = locals_to_params(locals())\n        return await self._frame.is_checked(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def is_disabled(self, timeout: float = None) -> bool:\n        params = locals_to_params(locals())\n        return await self._frame.is_disabled(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def is_editable(self, timeout: float = None) -> bool:\n        params = locals_to_params(locals())\n        return await self._frame.is_editable(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def is_enabled(self, timeout: float = None) -> bool:\n        params = locals_to_params(locals())\n        return await self._frame.is_enabled(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def is_hidden(self, timeout: float = None) -> bool:\n        # timeout is deprecated and does nothing\n        return await self._frame.is_hidden(\n            self._selector,\n            strict=True,\n        )\n\n    async def is_visible(self, timeout: float = None) -> bool:\n        # timeout is deprecated and does nothing\n        return await self._frame.is_visible(\n            self._selector,\n            strict=True,\n        )\n\n    async def press(\n        self,\n        key: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.press(self._selector, strict=True, **params)\n\n    async def screenshot(\n        self,\n        timeout: float = None,\n        type: Literal[\"jpeg\", \"png\"] = None,\n        path: Union[str, pathlib.Path] = None,\n        quality: int = None,\n        omitBackground: bool = None,\n        animations: Literal[\"allow\", \"disabled\"] = None,\n        caret: Literal[\"hide\", \"initial\"] = None,\n        scale: Literal[\"css\", \"device\"] = None,\n        mask: Sequence[\"Locator\"] = None,\n        maskColor: str = None,\n        style: str = None,\n    ) -> bytes:\n        params = locals_to_params(locals())\n        return await self._with_element(\n            lambda h, timeout: h.screenshot(\n                **{**params, \"timeout\": timeout},\n            ),\n        )\n\n    async def aria_snapshot(self, timeout: float = None) -> str:\n        return await self._frame._channel.send(\n            \"ariaSnapshot\",\n            self._frame._timeout,\n            {\n                \"selector\": self._selector,\n                **locals_to_params(locals()),\n            },\n        )\n\n    async def scroll_into_view_if_needed(\n        self,\n        timeout: float = None,\n    ) -> None:\n        return await self._with_element(\n            lambda h, timeout: h.scroll_into_view_if_needed(timeout=timeout),\n            timeout,\n        )\n\n    async def select_option(\n        self,\n        value: Union[str, Sequence[str]] = None,\n        index: Union[int, Sequence[int]] = None,\n        label: Union[str, Sequence[str]] = None,\n        element: Union[\"ElementHandle\", Sequence[\"ElementHandle\"]] = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n    ) -> List[str]:\n        params = locals_to_params(locals())\n        return await self._frame.select_option(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def select_text(self, force: bool = None, timeout: float = None) -> None:\n        params = locals_to_params(locals())\n        return await self._with_element(\n            lambda h, timeout: h.select_text(**{**params, \"timeout\": timeout}),\n            timeout,\n        )\n\n    async def set_input_files(\n        self,\n        files: Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            Sequence[Union[str, pathlib.Path]],\n            Sequence[FilePayload],\n        ],\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.set_input_files(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def tap(\n        self,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.tap(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def text_content(self, timeout: float = None) -> Optional[str]:\n        params = locals_to_params(locals())\n        return await self._frame.text_content(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def type(\n        self,\n        text: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.type(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def press_sequentially(\n        self,\n        text: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        await self.type(text, delay=delay, timeout=timeout)\n\n    async def uncheck(\n        self,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        return await self._frame.uncheck(\n            self._selector,\n            strict=True,\n            **params,\n        )\n\n    async def all_inner_texts(\n        self,\n    ) -> List[str]:\n        return await self._frame.eval_on_selector_all(\n            self._selector, \"ee => ee.map(e => e.innerText)\"\n        )\n\n    async def all_text_contents(\n        self,\n    ) -> List[str]:\n        return await self._frame.eval_on_selector_all(\n            self._selector, \"ee => ee.map(e => e.textContent || '')\"\n        )\n\n    async def wait_for(\n        self,\n        timeout: float = None,\n        state: Literal[\"attached\", \"detached\", \"hidden\", \"visible\"] = None,\n    ) -> None:\n        await self._frame.wait_for_selector(\n            self._selector, strict=True, timeout=timeout, state=state\n        )\n\n    async def set_checked(\n        self,\n        checked: bool,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n    ) -> None:\n        if checked:\n            await self.check(\n                position=position,\n                timeout=timeout,\n                force=force,\n                trial=trial,\n            )\n        else:\n            await self.uncheck(\n                position=position,\n                timeout=timeout,\n                force=force,\n                trial=trial,\n            )\n\n    async def _expect(\n        self,\n        expression: str,\n        options: FrameExpectOptions,\n        title: str = None,\n    ) -> FrameExpectResult:\n        return await self._frame._expect(self._selector, expression, options, title)\n\n    async def highlight(self) -> None:\n        await self._frame._highlight(self._selector)\n\n\nclass FrameLocator:\n    def __init__(self, frame: \"Frame\", frame_selector: str) -> None:\n        self._frame = frame\n        self._loop = frame._loop\n        self._dispatcher_fiber = frame._connection._dispatcher_fiber\n        self._frame_selector = frame_selector\n\n    def locator(\n        self,\n        selectorOrLocator: Union[\"Locator\", str],\n        hasText: Union[str, Pattern[str]] = None,\n        hasNotText: Union[str, Pattern[str]] = None,\n        has: Locator = None,\n        hasNot: Locator = None,\n    ) -> Locator:\n        if isinstance(selectorOrLocator, str):\n            return Locator(\n                self._frame,\n                f\"{self._frame_selector} >> internal:control=enter-frame >> {selectorOrLocator}\",\n                has_text=hasText,\n                has_not_text=hasNotText,\n                has=has,\n                has_not=hasNot,\n            )\n        selectorOrLocator = to_impl(selectorOrLocator)\n        if selectorOrLocator._frame != self._frame:\n            raise ValueError(\"Locators must belong to the same frame.\")\n        return Locator(\n            self._frame,\n            f\"{self._frame_selector} >> internal:control=enter-frame >> {selectorOrLocator._selector}\",\n            has_text=hasText,\n            has_not_text=hasNotText,\n            has=has,\n            has_not=hasNot,\n        )\n\n    def get_by_alt_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_alt_text_selector(text, exact=exact))\n\n    def get_by_label(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_label_selector(text, exact=exact))\n\n    def get_by_placeholder(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_placeholder_selector(text, exact=exact))\n\n    def get_by_role(\n        self,\n        role: AriaRole,\n        checked: bool = None,\n        disabled: bool = None,\n        expanded: bool = None,\n        includeHidden: bool = None,\n        level: int = None,\n        name: Union[str, Pattern[str]] = None,\n        pressed: bool = None,\n        selected: bool = None,\n        exact: bool = None,\n    ) -> \"Locator\":\n        return self.locator(\n            get_by_role_selector(\n                role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=includeHidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> \"Locator\":\n        return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))\n\n    def get_by_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_text_selector(text, exact=exact))\n\n    def get_by_title(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self.locator(get_by_title_selector(text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        return FrameLocator(\n            self._frame,\n            f\"{self._frame_selector} >> internal:control=enter-frame >> {selector}\",\n        )\n\n    @property\n    def first(self) -> \"FrameLocator\":\n        return FrameLocator(self._frame, f\"{self._frame_selector} >> nth=0\")\n\n    @property\n    def last(self) -> \"FrameLocator\":\n        return FrameLocator(self._frame, f\"{self._frame_selector} >> nth=-1\")\n\n    @property\n    def owner(self) -> \"Locator\":\n        return Locator(self._frame, self._frame_selector)\n\n    def nth(self, index: int) -> \"FrameLocator\":\n        return FrameLocator(self._frame, f\"{self._frame_selector} >> nth={index}\")\n\n    def __repr__(self) -> str:\n        return f\"<FrameLocator frame={self._frame!r} selector={self._frame_selector!r}>\"\n\n\n_test_id_attribute_name: str = \"data-testid\"\n\n\ndef test_id_attribute_name() -> str:\n    return _test_id_attribute_name\n\n\ndef set_test_id_attribute_name(attribute_name: str) -> None:\n    global _test_id_attribute_name\n    _test_id_attribute_name = attribute_name\n\n\ndef get_by_test_id_selector(\n    test_id_attribute_name: str, test_id: Union[str, Pattern[str]]\n) -> str:\n    return f\"internal:testid=[{test_id_attribute_name}={escape_for_attribute_selector(test_id, True)}]\"\n\n\ndef get_by_attribute_text_selector(\n    attr_name: str, text: Union[str, Pattern[str]], exact: bool = None\n) -> str:\n    return f\"internal:attr=[{attr_name}={escape_for_attribute_selector(text, exact=exact)}]\"\n\n\ndef get_by_label_selector(text: Union[str, Pattern[str]], exact: bool = None) -> str:\n    return \"internal:label=\" + escape_for_text_selector(text, exact=exact)\n\n\ndef get_by_alt_text_selector(text: Union[str, Pattern[str]], exact: bool = None) -> str:\n    return get_by_attribute_text_selector(\"alt\", text, exact=exact)\n\n\ndef get_by_title_selector(text: Union[str, Pattern[str]], exact: bool = None) -> str:\n    return get_by_attribute_text_selector(\"title\", text, exact=exact)\n\n\ndef get_by_placeholder_selector(\n    text: Union[str, Pattern[str]], exact: bool = None\n) -> str:\n    return get_by_attribute_text_selector(\"placeholder\", text, exact=exact)\n\n\ndef get_by_text_selector(text: Union[str, Pattern[str]], exact: bool = None) -> str:\n    return \"internal:text=\" + escape_for_text_selector(text, exact=exact)\n\n\ndef bool_to_js_bool(value: bool) -> str:\n    return \"true\" if value else \"false\"\n\n\ndef get_by_role_selector(\n    role: AriaRole,\n    checked: bool = None,\n    disabled: bool = None,\n    expanded: bool = None,\n    includeHidden: bool = None,\n    level: int = None,\n    name: Union[str, Pattern[str]] = None,\n    pressed: bool = None,\n    selected: bool = None,\n    exact: bool = None,\n) -> str:\n    props: List[Tuple[str, str]] = []\n    if checked is not None:\n        props.append((\"checked\", bool_to_js_bool(checked)))\n    if disabled is not None:\n        props.append((\"disabled\", bool_to_js_bool(disabled)))\n    if selected is not None:\n        props.append((\"selected\", bool_to_js_bool(selected)))\n    if expanded is not None:\n        props.append((\"expanded\", bool_to_js_bool(expanded)))\n    if includeHidden is not None:\n        props.append((\"include-hidden\", bool_to_js_bool(includeHidden)))\n    if level is not None:\n        props.append((\"level\", str(level)))\n    if name is not None:\n        props.append(\n            (\n                \"name\",\n                escape_for_attribute_selector(name, exact=exact),\n            )\n        )\n    if pressed is not None:\n        props.append((\"pressed\", bool_to_js_bool(pressed)))\n    props_str = \"\".join([f\"[{t[0]}={t[1]}]\" for t in props])\n    return f\"internal:role={role}{props_str}\"\n"
  },
  {
    "path": "playwright/_impl/_map.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Dict, Generic, Tuple, TypeVar\n\nK = TypeVar(\"K\")\nV = TypeVar(\"V\")\n\n\nclass Map(Generic[K, V]):\n    def __init__(self) -> None:\n        self._entries: Dict[int, Tuple[K, V]] = {}\n\n    def __contains__(self, item: K) -> bool:\n        return id(item) in self._entries\n\n    def __setitem__(self, idx: K, value: V) -> None:\n        self._entries[id(idx)] = (idx, value)\n\n    def __getitem__(self, obj: K) -> V:\n        return self._entries[id(obj)][1]\n"
  },
  {
    "path": "playwright/_impl/_network.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nimport inspect\nimport json\nimport json as json_utils\nimport mimetypes\nimport re\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Coroutine,\n    Dict,\n    List,\n    Optional,\n    TypedDict,\n    Union,\n    cast,\n)\nfrom urllib import parse\n\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    Headers,\n    HeadersArray,\n    RemoteAddr,\n    RequestSizes,\n    ResourceTiming,\n    SecurityDetails,\n)\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._errors import Error\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._helper import (\n    URLMatch,\n    WebSocketRouteHandlerCallback,\n    async_readfile,\n    locals_to_params,\n    url_matches,\n)\nfrom playwright._impl._str_utils import escape_regex_flags\nfrom playwright._impl._waiter import Waiter\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser_context import BrowserContext\n    from playwright._impl._fetch import APIResponse\n    from playwright._impl._frame import Frame\n    from playwright._impl._page import Page, Worker\n\n\nclass FallbackOverrideParameters(TypedDict, total=False):\n    url: Optional[str]\n    method: Optional[str]\n    headers: Optional[Dict[str, str]]\n    postData: Optional[Union[str, bytes]]\n\n\nclass SerializedFallbackOverrides:\n    def __init__(self) -> None:\n        self.url: Optional[str] = None\n        self.method: Optional[str] = None\n        self.headers: Optional[Dict[str, str]] = None\n        self.post_data_buffer: Optional[bytes] = None\n\n\ndef serialize_headers(headers: Dict[str, str]) -> HeadersArray:\n    return [\n        {\"name\": name, \"value\": value}\n        for name, value in headers.items()\n        if value is not None\n    ]\n\n\nasync def to_client_certificates_protocol(\n    clientCertificates: Optional[List[ClientCertificate]],\n) -> Optional[List[Dict[str, str]]]:\n    if not clientCertificates:\n        return None\n    out = []\n    for clientCertificate in clientCertificates:\n        out_record = {\n            \"origin\": clientCertificate[\"origin\"],\n        }\n        if passphrase := clientCertificate.get(\"passphrase\"):\n            out_record[\"passphrase\"] = passphrase\n        if pfx := clientCertificate.get(\"pfx\"):\n            out_record[\"pfx\"] = base64.b64encode(pfx).decode()\n        if pfx_path := clientCertificate.get(\"pfxPath\"):\n            out_record[\"pfx\"] = base64.b64encode(\n                await async_readfile(pfx_path)\n            ).decode()\n        if cert := clientCertificate.get(\"cert\"):\n            out_record[\"cert\"] = base64.b64encode(cert).decode()\n        if cert_path := clientCertificate.get(\"certPath\"):\n            out_record[\"cert\"] = base64.b64encode(\n                await async_readfile(cert_path)\n            ).decode()\n        if key := clientCertificate.get(\"key\"):\n            out_record[\"key\"] = base64.b64encode(key).decode()\n        if key_path := clientCertificate.get(\"keyPath\"):\n            out_record[\"key\"] = base64.b64encode(\n                await async_readfile(key_path)\n            ).decode()\n        out.append(out_record)\n    return out\n\n\nclass Request(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._redirected_from: Optional[\"Request\"] = from_nullable_channel(\n            initializer.get(\"redirectedFrom\")\n        )\n        self._redirected_to: Optional[\"Request\"] = None\n        if self._redirected_from:\n            self._redirected_from._redirected_to = self\n        self._failure_text: Optional[str] = None\n        self._timing: ResourceTiming = {\n            \"startTime\": 0,\n            \"domainLookupStart\": -1,\n            \"domainLookupEnd\": -1,\n            \"connectStart\": -1,\n            \"secureConnectionStart\": -1,\n            \"connectEnd\": -1,\n            \"requestStart\": -1,\n            \"responseStart\": -1,\n            \"responseEnd\": -1,\n        }\n        self._provisional_headers = RawHeaders(self._initializer[\"headers\"])\n        self._all_headers_future: Optional[asyncio.Future[RawHeaders]] = None\n        self._fallback_overrides: SerializedFallbackOverrides = (\n            SerializedFallbackOverrides()\n        )\n\n    def __repr__(self) -> str:\n        return f\"<Request url={self.url!r} method={self.method!r}>\"\n\n    def _apply_fallback_overrides(self, overrides: FallbackOverrideParameters) -> None:\n        self._fallback_overrides.url = overrides.get(\n            \"url\", self._fallback_overrides.url\n        )\n        self._fallback_overrides.method = overrides.get(\n            \"method\", self._fallback_overrides.method\n        )\n        self._fallback_overrides.headers = overrides.get(\n            \"headers\", self._fallback_overrides.headers\n        )\n        post_data = overrides.get(\"postData\")\n        if isinstance(post_data, str):\n            self._fallback_overrides.post_data_buffer = post_data.encode()\n        elif isinstance(post_data, bytes):\n            self._fallback_overrides.post_data_buffer = post_data\n        elif post_data is not None:\n            self._fallback_overrides.post_data_buffer = json.dumps(post_data).encode()\n\n    @property\n    def url(self) -> str:\n        return cast(str, self._fallback_overrides.url or self._initializer[\"url\"])\n\n    @property\n    def resource_type(self) -> str:\n        return self._initializer[\"resourceType\"]\n\n    @property\n    def service_worker(self) -> Optional[\"Worker\"]:\n        return cast(\n            Optional[\"Worker\"],\n            from_nullable_channel(self._initializer.get(\"serviceWorker\")),\n        )\n\n    @property\n    def method(self) -> str:\n        return cast(str, self._fallback_overrides.method or self._initializer[\"method\"])\n\n    async def sizes(self) -> RequestSizes:\n        response = await self.response()\n        if not response:\n            raise Error(\"Unable to fetch sizes for failed request\")\n        return await response._channel.send(\n            \"sizes\",\n            None,\n        )\n\n    @property\n    def post_data(self) -> Optional[str]:\n        data = self._fallback_overrides.post_data_buffer\n        if data:\n            return data.decode()\n        base64_post_data = self._initializer.get(\"postData\")\n        if base64_post_data is not None:\n            return base64.b64decode(base64_post_data).decode()\n        return None\n\n    @property\n    def post_data_json(self) -> Optional[Any]:\n        post_data = self.post_data\n        if not post_data:\n            return None\n        content_type = self.headers[\"content-type\"]\n        if \"application/x-www-form-urlencoded\" in content_type:\n            return dict(parse.parse_qsl(post_data))\n        try:\n            return json.loads(post_data)\n        except Exception:\n            raise Error(f\"POST data is not a valid JSON object: {post_data}\")\n\n    @property\n    def post_data_buffer(self) -> Optional[bytes]:\n        if self._fallback_overrides.post_data_buffer:\n            return self._fallback_overrides.post_data_buffer\n        if self._initializer.get(\"postData\"):\n            return base64.b64decode(self._initializer[\"postData\"])\n        return None\n\n    async def response(self) -> Optional[\"Response\"]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"response\",\n                None,\n            )\n        )\n\n    @property\n    def frame(self) -> \"Frame\":\n        if not self._initializer.get(\"frame\"):\n            raise Error(\"Service Worker requests do not have an associated frame.\")\n        frame = cast(\"Frame\", from_channel(self._initializer[\"frame\"]))\n        if not frame._page:\n            raise Error(\n                \"\\n\".join(\n                    [\n                        \"Frame for this navigation request is not available, because the request\",\n                        \"was issued before the frame is created. You can check whether the request\",\n                        \"is a navigation request by calling isNavigationRequest() method.\",\n                    ]\n                )\n            )\n        return frame\n\n    def is_navigation_request(self) -> bool:\n        return self._initializer[\"isNavigationRequest\"]\n\n    @property\n    def redirected_from(self) -> Optional[\"Request\"]:\n        return self._redirected_from\n\n    @property\n    def redirected_to(self) -> Optional[\"Request\"]:\n        return self._redirected_to\n\n    @property\n    def failure(self) -> Optional[str]:\n        return self._failure_text\n\n    @property\n    def timing(self) -> ResourceTiming:\n        return self._timing\n\n    def _set_response_end_timing(self, response_end_timing: float) -> None:\n        self._timing[\"responseEnd\"] = response_end_timing\n        if self._timing[\"responseStart\"] == -1:\n            self._timing[\"responseStart\"] = response_end_timing\n\n    @property\n    def headers(self) -> Headers:\n        override = self._fallback_overrides.headers\n        if override:\n            return RawHeaders._from_headers_dict_lossy(override).headers()\n        return self._provisional_headers.headers()\n\n    async def all_headers(self) -> Headers:\n        return (await self._actual_headers()).headers()\n\n    async def headers_array(self) -> HeadersArray:\n        return (await self._actual_headers()).headers_array()\n\n    async def header_value(self, name: str) -> Optional[str]:\n        return (await self._actual_headers()).get(name)\n\n    async def _actual_headers(self) -> \"RawHeaders\":\n        override = self._fallback_overrides.headers\n        if override:\n            return RawHeaders(serialize_headers(override))\n        if not self._all_headers_future:\n            self._all_headers_future = asyncio.Future()\n            headers = await self._channel.send(\n                \"rawRequestHeaders\", None, is_internal=True\n            )\n            self._all_headers_future.set_result(RawHeaders(headers))\n        return await self._all_headers_future\n\n    def _target_closed_future(self) -> asyncio.Future:\n        frame = cast(\n            Optional[\"Frame\"], from_nullable_channel(self._initializer.get(\"frame\"))\n        )\n        if not frame:\n            return asyncio.Future()\n        page = frame._page\n        if not page:\n            return asyncio.Future()\n        return page._closed_or_crashed_future\n\n    def _safe_page(self) -> \"Optional[Page]\":\n        frame = from_nullable_channel(self._initializer.get(\"frame\"))\n        if not frame:\n            return None\n        return cast(\"Frame\", frame)._page\n\n\nclass Route(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._handling_future: Optional[asyncio.Future[\"bool\"]] = None\n        self._context: \"BrowserContext\" = cast(\"BrowserContext\", None)\n        self._did_throw = False\n\n    def _start_handling(self) -> \"asyncio.Future[bool]\":\n        self._handling_future = asyncio.Future()\n        return self._handling_future\n\n    def _report_handled(self, done: bool) -> None:\n        chain = self._handling_future\n        assert chain\n        self._handling_future = None\n        chain.set_result(done)\n\n    def _check_not_handled(self) -> None:\n        if not self._handling_future:\n            raise Error(\"Route is already handled!\")\n\n    def __repr__(self) -> str:\n        return f\"<Route request={self.request}>\"\n\n    @property\n    def request(self) -> Request:\n        return from_channel(self._initializer[\"request\"])\n\n    async def abort(self, errorCode: str = None) -> None:\n        await self._handle_route(\n            lambda: self._race_with_page_close(\n                self._channel.send(\n                    \"abort\",\n                    None,\n                    {\n                        \"errorCode\": errorCode,\n                    },\n                )\n            )\n        )\n\n    async def fulfill(\n        self,\n        status: int = None,\n        headers: Dict[str, str] = None,\n        body: Union[str, bytes] = None,\n        json: Any = None,\n        path: Union[str, Path] = None,\n        contentType: str = None,\n        response: \"APIResponse\" = None,\n    ) -> None:\n        await self._handle_route(\n            lambda: self._inner_fulfill(\n                status, headers, body, json, path, contentType, response\n            )\n        )\n\n    async def _inner_fulfill(\n        self,\n        status: int = None,\n        headers: Dict[str, str] = None,\n        body: Union[str, bytes] = None,\n        json: Any = None,\n        path: Union[str, Path] = None,\n        contentType: str = None,\n        response: \"APIResponse\" = None,\n    ) -> None:\n        params = locals_to_params(locals())\n\n        if json is not None:\n            if body is not None:\n                raise Error(\"Can specify either body or json parameters\")\n            body = json_utils.dumps(json)\n\n        if response:\n            del params[\"response\"]\n            params[\"status\"] = (\n                params[\"status\"] if params.get(\"status\") else response.status\n            )\n            params[\"headers\"] = (\n                params[\"headers\"] if params.get(\"headers\") else response.headers\n            )\n            from playwright._impl._fetch import APIResponse\n\n            if body is None and path is None and isinstance(response, APIResponse):\n                if response._request._connection is self._connection:\n                    params[\"fetchResponseUid\"] = response._fetch_uid\n                else:\n                    body = await response.body()\n\n        length = 0\n        if isinstance(body, str):\n            params[\"body\"] = body\n            params[\"isBase64\"] = False\n            length = len(body.encode())\n        elif isinstance(body, bytes):\n            params[\"body\"] = base64.b64encode(body).decode()\n            params[\"isBase64\"] = True\n            length = len(body)\n        elif path:\n            del params[\"path\"]\n            file_content = Path(path).read_bytes()\n            params[\"body\"] = base64.b64encode(file_content).decode()\n            params[\"isBase64\"] = True\n            length = len(file_content)\n\n        headers = {k.lower(): str(v) for k, v in params.get(\"headers\", {}).items()}\n        if params.get(\"contentType\"):\n            headers[\"content-type\"] = params[\"contentType\"]\n        elif json:\n            headers[\"content-type\"] = \"application/json\"\n        elif path:\n            headers[\"content-type\"] = (\n                mimetypes.guess_type(str(Path(path)))[0] or \"application/octet-stream\"\n            )\n        if length and \"content-length\" not in headers:\n            headers[\"content-length\"] = str(length)\n        params[\"headers\"] = serialize_headers(headers)\n\n        await self._race_with_page_close(self._channel.send(\"fulfill\", None, params))\n\n    async def _handle_route(self, callback: Callable) -> None:\n        self._check_not_handled()\n        try:\n            await callback()\n            self._report_handled(True)\n        except Exception as e:\n            self._did_throw = True\n            raise e\n\n    async def fetch(\n        self,\n        url: str = None,\n        method: str = None,\n        headers: Dict[str, str] = None,\n        postData: Union[Any, str, bytes] = None,\n        maxRedirects: int = None,\n        maxRetries: int = None,\n        timeout: float = None,\n    ) -> \"APIResponse\":\n        return await self._connection.wrap_api_call(\n            lambda: self._context.request._inner_fetch(\n                self.request,\n                url,\n                method,\n                headers,\n                postData,\n                maxRedirects=maxRedirects,\n                maxRetries=maxRetries,\n                timeout=timeout,\n            )\n        )\n\n    async def fallback(\n        self,\n        url: str = None,\n        method: str = None,\n        headers: Dict[str, str] = None,\n        postData: Union[Any, str, bytes] = None,\n    ) -> None:\n        overrides = cast(FallbackOverrideParameters, locals_to_params(locals()))\n        self._check_not_handled()\n        self.request._apply_fallback_overrides(overrides)\n        self._report_handled(False)\n\n    async def continue_(\n        self,\n        url: str = None,\n        method: str = None,\n        headers: Dict[str, str] = None,\n        postData: Union[Any, str, bytes] = None,\n    ) -> None:\n        overrides = cast(FallbackOverrideParameters, locals_to_params(locals()))\n\n        async def _inner() -> None:\n            self.request._apply_fallback_overrides(overrides)\n            await self._inner_continue(False)\n\n        return await self._handle_route(_inner)\n\n    async def _inner_continue(self, is_fallback: bool = False) -> None:\n        options = self.request._fallback_overrides\n        await self._race_with_page_close(\n            self._channel.send(\n                \"continue\",\n                None,\n                {\n                    \"url\": options.url,\n                    \"method\": options.method,\n                    \"headers\": (\n                        serialize_headers(options.headers) if options.headers else None\n                    ),\n                    \"postData\": (\n                        base64.b64encode(options.post_data_buffer).decode()\n                        if options.post_data_buffer is not None\n                        else None\n                    ),\n                    \"isFallback\": is_fallback,\n                },\n            )\n        )\n\n    async def _redirected_navigation_request(self, url: str) -> None:\n        await self._handle_route(\n            lambda: self._race_with_page_close(\n                self._channel.send(\"redirectNavigationRequest\", None, {\"url\": url})\n            )\n        )\n\n    async def _race_with_page_close(self, future: Coroutine) -> None:\n        fut = asyncio.create_task(future)\n        # Rewrite the user's stack to the new task which runs in the background.\n        setattr(\n            fut,\n            \"__pw_stack__\",\n            getattr(asyncio.current_task(self._loop), \"__pw_stack__\", inspect.stack(0)),\n        )\n        target_closed_future = self.request._target_closed_future()\n        await asyncio.wait(\n            [fut, target_closed_future],\n            return_when=asyncio.FIRST_COMPLETED,\n        )\n        if fut.done() and fut.exception():\n            raise cast(BaseException, fut.exception())\n        if target_closed_future.done():\n            await asyncio.gather(fut, return_exceptions=True)\n\n\ndef _create_task_and_ignore_exception(\n    loop: asyncio.AbstractEventLoop, coro: Coroutine\n) -> None:\n    async def _ignore_exception() -> None:\n        try:\n            await coro\n        except Exception:\n            pass\n\n    loop.create_task(_ignore_exception())\n\n\nclass ServerWebSocketRoute:\n    def __init__(self, ws: \"WebSocketRoute\"):\n        self._ws = ws\n\n    def on_message(self, handler: Callable[[Union[str, bytes]], Any]) -> None:\n        self._ws._on_server_message = handler\n\n    def on_close(self, handler: Callable[[Optional[int], Optional[str]], Any]) -> None:\n        self._ws._on_server_close = handler\n\n    def connect_to_server(self) -> None:\n        raise NotImplementedError(\n            \"connectToServer must be called on the page-side WebSocketRoute\"\n        )\n\n    @property\n    def url(self) -> str:\n        return self._ws._initializer[\"url\"]\n\n    def close(self, code: int = None, reason: str = None) -> None:\n        _create_task_and_ignore_exception(\n            self._ws._loop,\n            self._ws._channel.send(\n                \"closeServer\",\n                None,\n                {\n                    \"code\": code,\n                    \"reason\": reason,\n                    \"wasClean\": True,\n                },\n            ),\n        )\n\n    def send(self, message: Union[str, bytes]) -> None:\n        if isinstance(message, str):\n            _create_task_and_ignore_exception(\n                self._ws._loop,\n                self._ws._channel.send(\n                    \"sendToServer\", None, {\"message\": message, \"isBase64\": False}\n                ),\n            )\n        else:\n            _create_task_and_ignore_exception(\n                self._ws._loop,\n                self._ws._channel.send(\n                    \"sendToServer\",\n                    None,\n                    {\"message\": base64.b64encode(message).decode(), \"isBase64\": True},\n                ),\n            )\n\n\nclass WebSocketRoute(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._on_page_message: Optional[Callable[[Union[str, bytes]], Any]] = None\n        self._on_page_close: Optional[Callable[[Optional[int], Optional[str]], Any]] = (\n            None\n        )\n        self._on_server_message: Optional[Callable[[Union[str, bytes]], Any]] = None\n        self._on_server_close: Optional[\n            Callable[[Optional[int], Optional[str]], Any]\n        ] = None\n        self._server = ServerWebSocketRoute(self)\n        self._connected = False\n\n        self._channel.on(\"messageFromPage\", self._channel_message_from_page)\n        self._channel.on(\"messageFromServer\", self._channel_message_from_server)\n        self._channel.on(\"closePage\", self._channel_close_page)\n        self._channel.on(\"closeServer\", self._channel_close_server)\n\n    def _channel_message_from_page(self, event: Dict) -> None:\n        if self._on_page_message:\n            self._on_page_message(\n                base64.b64decode(event[\"message\"])\n                if event[\"isBase64\"]\n                else event[\"message\"]\n            )\n        elif self._connected:\n            _create_task_and_ignore_exception(\n                self._loop, self._channel.send(\"sendToServer\", None, event)\n            )\n\n    def _channel_message_from_server(self, event: Dict) -> None:\n        if self._on_server_message:\n            self._on_server_message(\n                base64.b64decode(event[\"message\"])\n                if event[\"isBase64\"]\n                else event[\"message\"]\n            )\n        else:\n            _create_task_and_ignore_exception(\n                self._loop, self._channel.send(\"sendToPage\", None, event)\n            )\n\n    def _channel_close_page(self, event: Dict) -> None:\n        if self._on_page_close:\n            self._on_page_close(event[\"code\"], event[\"reason\"])\n        else:\n            _create_task_and_ignore_exception(\n                self._loop, self._channel.send(\"closeServer\", None, event)\n            )\n\n    def _channel_close_server(self, event: Dict) -> None:\n        if self._on_server_close:\n            self._on_server_close(event[\"code\"], event[\"reason\"])\n        else:\n            _create_task_and_ignore_exception(\n                self._loop, self._channel.send(\"closePage\", None, event)\n            )\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    async def close(self, code: int = None, reason: str = None) -> None:\n        try:\n            await self._channel.send(\n                \"closePage\", None, {\"code\": code, \"reason\": reason, \"wasClean\": True}\n            )\n        except Exception:\n            pass\n\n    def connect_to_server(self) -> \"WebSocketRoute\":\n        if self._connected:\n            raise Error(\"Already connected to the server\")\n        self._connected = True\n        asyncio.create_task(\n            self._channel.send(\n                \"connect\",\n                None,\n            )\n        )\n        return cast(\"WebSocketRoute\", self._server)\n\n    def send(self, message: Union[str, bytes]) -> None:\n        if isinstance(message, str):\n            _create_task_and_ignore_exception(\n                self._loop,\n                self._channel.send(\n                    \"sendToPage\", None, {\"message\": message, \"isBase64\": False}\n                ),\n            )\n        else:\n            _create_task_and_ignore_exception(\n                self._loop,\n                self._channel.send(\n                    \"sendToPage\",\n                    None,\n                    {\n                        \"message\": base64.b64encode(message).decode(),\n                        \"isBase64\": True,\n                    },\n                ),\n            )\n\n    def on_message(self, handler: Callable[[Union[str, bytes]], Any]) -> None:\n        self._on_page_message = handler\n\n    def on_close(self, handler: Callable[[Optional[int], Optional[str]], Any]) -> None:\n        self._on_page_close = handler\n\n    async def _after_handle(self) -> None:\n        if self._connected:\n            return\n        # Ensure that websocket is \"open\" and can send messages without an actual server connection.\n        try:\n            await self._channel.send(\n                \"ensureOpened\",\n                None,\n            )\n        except Exception:\n            pass\n\n\nclass WebSocketRouteHandler:\n    def __init__(\n        self,\n        base_url: Optional[str],\n        url: URLMatch,\n        handler: WebSocketRouteHandlerCallback,\n    ):\n        self._base_url = base_url\n        self.url = url\n        self.handler = handler\n\n    @staticmethod\n    def prepare_interception_patterns(\n        handlers: List[\"WebSocketRouteHandler\"],\n    ) -> List[dict]:\n        patterns = []\n        all_urls = False\n        for handler in handlers:\n            if isinstance(handler.url, str):\n                patterns.append({\"glob\": handler.url})\n            elif isinstance(handler.url, re.Pattern):\n                patterns.append(\n                    {\n                        \"regexSource\": handler.url.pattern,\n                        \"regexFlags\": escape_regex_flags(handler.url),\n                    }\n                )\n            else:\n                all_urls = True\n\n        if all_urls:\n            return [{\"glob\": \"**/*\"}]\n        return patterns\n\n    def matches(self, ws_url: str) -> bool:\n        return url_matches(self._base_url, ws_url, self.url, True)\n\n    async def handle(self, websocket_route: \"WebSocketRoute\") -> None:\n        coro_or_future = self.handler(websocket_route)\n        if asyncio.iscoroutine(coro_or_future):\n            await coro_or_future\n        await websocket_route._after_handle()\n\n\nclass Response(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._request: Request = from_channel(self._initializer[\"request\"])\n        timing = self._initializer[\"timing\"]\n        self._request._timing[\"startTime\"] = timing[\"startTime\"]\n        self._request._timing[\"domainLookupStart\"] = timing[\"domainLookupStart\"]\n        self._request._timing[\"domainLookupEnd\"] = timing[\"domainLookupEnd\"]\n        self._request._timing[\"connectStart\"] = timing[\"connectStart\"]\n        self._request._timing[\"secureConnectionStart\"] = timing[\"secureConnectionStart\"]\n        self._request._timing[\"connectEnd\"] = timing[\"connectEnd\"]\n        self._request._timing[\"requestStart\"] = timing[\"requestStart\"]\n        self._request._timing[\"responseStart\"] = timing[\"responseStart\"]\n        self._provisional_headers = RawHeaders(\n            cast(HeadersArray, self._initializer[\"headers\"])\n        )\n        self._raw_headers_future: Optional[asyncio.Future[RawHeaders]] = None\n        self._finished_future: asyncio.Future[bool] = asyncio.Future()\n\n    def __repr__(self) -> str:\n        return f\"<Response url={self.url!r} request={self.request}>\"\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def ok(self) -> bool:\n        # Status 0 is for file:// URLs\n        return self._initializer[\"status\"] == 0 or (\n            self._initializer[\"status\"] >= 200 and self._initializer[\"status\"] <= 299\n        )\n\n    @property\n    def status(self) -> int:\n        return self._initializer[\"status\"]\n\n    @property\n    def status_text(self) -> str:\n        return self._initializer[\"statusText\"]\n\n    @property\n    def headers(self) -> Headers:\n        return self._provisional_headers.headers()\n\n    @property\n    def from_service_worker(self) -> bool:\n        return self._initializer[\"fromServiceWorker\"]\n\n    async def all_headers(self) -> Headers:\n        return (await self._actual_headers()).headers()\n\n    async def headers_array(self) -> HeadersArray:\n        return (await self._actual_headers()).headers_array()\n\n    async def header_value(self, name: str) -> Optional[str]:\n        return (await self._actual_headers()).get(name)\n\n    async def header_values(self, name: str) -> List[str]:\n        return (await self._actual_headers()).get_all(name)\n\n    async def _actual_headers(self) -> \"RawHeaders\":\n        if not self._raw_headers_future:\n            self._raw_headers_future = asyncio.Future()\n            headers = cast(\n                HeadersArray,\n                await self._channel.send(\n                    \"rawResponseHeaders\",\n                    None,\n                ),\n            )\n            self._raw_headers_future.set_result(RawHeaders(headers))\n        return await self._raw_headers_future\n\n    async def server_addr(self) -> Optional[RemoteAddr]:\n        return await self._channel.send(\n            \"serverAddr\",\n            None,\n        )\n\n    async def security_details(self) -> Optional[SecurityDetails]:\n        return await self._channel.send(\n            \"securityDetails\",\n            None,\n        )\n\n    async def finished(self) -> None:\n        async def on_finished() -> None:\n            await self._request._target_closed_future()\n            raise Error(\"Target closed\")\n\n        on_finished_task = asyncio.create_task(on_finished())\n        await asyncio.wait(\n            cast(\n                List[Union[asyncio.Task, asyncio.Future]],\n                [self._finished_future, on_finished_task],\n            ),\n            return_when=asyncio.FIRST_COMPLETED,\n        )\n        if on_finished_task.done():\n            await on_finished_task\n\n    async def body(self) -> bytes:\n        binary = await self._channel.send(\n            \"body\",\n            None,\n        )\n        return base64.b64decode(binary)\n\n    async def text(self) -> str:\n        content = await self.body()\n        return content.decode()\n\n    async def json(self) -> Any:\n        return json.loads(await self.text())\n\n    @property\n    def request(self) -> Request:\n        return self._request\n\n    @property\n    def frame(self) -> \"Frame\":\n        return self._request.frame\n\n\nclass WebSocket(ChannelOwner):\n    Events = SimpleNamespace(\n        Close=\"close\",\n        FrameReceived=\"framereceived\",\n        FrameSent=\"framesent\",\n        Error=\"socketerror\",\n    )\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._is_closed = False\n        self._page = cast(\"Page\", parent)\n        self._channel.on(\n            \"frameSent\",\n            lambda params: self._on_frame_sent(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"frameReceived\",\n            lambda params: self._on_frame_received(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"socketError\",\n            lambda params: self.emit(WebSocket.Events.Error, params[\"error\"]),\n        )\n        self._channel.on(\"close\", lambda params: self._on_close())\n\n    def __repr__(self) -> str:\n        return f\"<WebSocket url={self.url!r}>\"\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            timeout = cast(Any, self._parent)._timeout_settings.timeout()\n        waiter = Waiter(self, f\"web_socket.expect_event({event})\")\n        waiter.reject_on_timeout(\n            cast(float, timeout),\n            f'Timeout {timeout}ms exceeded while waiting for event \"{event}\"',\n        )\n        if event != WebSocket.Events.Close:\n            waiter.reject_on_event(self, WebSocket.Events.Close, Error(\"Socket closed\"))\n        if event != WebSocket.Events.Error:\n            waiter.reject_on_event(self, WebSocket.Events.Error, Error(\"Socket error\"))\n        waiter.reject_on_event(\n            self._page, \"close\", lambda: self._page._close_error_with_reason()\n        )\n        waiter.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(waiter.result())\n\n    async def wait_for_event(\n        self, event: str, predicate: Callable = None, timeout: float = None\n    ) -> Any:\n        async with self.expect_event(event, predicate, timeout) as event_info:\n            pass\n        return await event_info\n\n    def _on_frame_sent(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameSent, base64.b64decode(data))\n        elif opcode == 1:\n            self.emit(WebSocket.Events.FrameSent, data)\n\n    def _on_frame_received(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameReceived, base64.b64decode(data))\n        elif opcode == 1:\n            self.emit(WebSocket.Events.FrameReceived, data)\n\n    def is_closed(self) -> bool:\n        return self._is_closed\n\n    def _on_close(self) -> None:\n        self._is_closed = True\n        self.emit(WebSocket.Events.Close, self)\n\n\nclass RawHeaders:\n    def __init__(self, headers: HeadersArray) -> None:\n        self._headers_array = headers\n        self._headers_map: Dict[str, Dict[str, bool]] = defaultdict(dict)\n        for header in headers:\n            self._headers_map[header[\"name\"].lower()][header[\"value\"]] = True\n\n    @staticmethod\n    def _from_headers_dict_lossy(headers: Dict[str, str]) -> \"RawHeaders\":\n        return RawHeaders(serialize_headers(headers))\n\n    def get(self, name: str) -> Optional[str]:\n        values = self.get_all(name)\n        if not values:\n            return None\n        separator = \"\\n\" if name.lower() == \"set-cookie\" else \", \"\n        return separator.join(values)\n\n    def get_all(self, name: str) -> List[str]:\n        return list(self._headers_map[name.lower()].keys())\n\n    def headers(self) -> Dict[str, str]:\n        result = {}\n        for name in self._headers_map.keys():\n            result[name] = cast(str, self.get(name))\n        return result\n\n    def headers_array(self) -> HeadersArray:\n        return self._headers_array\n"
  },
  {
    "path": "playwright/_impl/_object_factory.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict, cast\n\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._browser import Browser\nfrom playwright._impl._browser_context import BrowserContext\nfrom playwright._impl._browser_type import BrowserType\nfrom playwright._impl._cdp_session import CDPSession\nfrom playwright._impl._connection import ChannelOwner\nfrom playwright._impl._dialog import Dialog\nfrom playwright._impl._element_handle import ElementHandle\nfrom playwright._impl._fetch import APIRequestContext\nfrom playwright._impl._frame import Frame\nfrom playwright._impl._js_handle import JSHandle\nfrom playwright._impl._local_utils import LocalUtils\nfrom playwright._impl._network import (\n    Request,\n    Response,\n    Route,\n    WebSocket,\n    WebSocketRoute,\n)\nfrom playwright._impl._page import BindingCall, Page, Worker\nfrom playwright._impl._playwright import Playwright\nfrom playwright._impl._stream import Stream\nfrom playwright._impl._tracing import Tracing\nfrom playwright._impl._writable_stream import WritableStream\n\n\nclass DummyObject(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n\ndef create_remote_object(\n    parent: ChannelOwner, type: str, guid: str, initializer: Dict\n) -> ChannelOwner:\n    if type == \"Artifact\":\n        return Artifact(parent, type, guid, initializer)\n    if type == \"APIRequestContext\":\n        return APIRequestContext(parent, type, guid, initializer)\n    if type == \"BindingCall\":\n        return BindingCall(parent, type, guid, initializer)\n    if type == \"Browser\":\n        return Browser(cast(BrowserType, parent), type, guid, initializer)\n    if type == \"BrowserType\":\n        return BrowserType(parent, type, guid, initializer)\n    if type == \"BrowserContext\":\n        return BrowserContext(parent, type, guid, initializer)\n    if type == \"CDPSession\":\n        return CDPSession(parent, type, guid, initializer)\n    if type == \"Dialog\":\n        return Dialog(parent, type, guid, initializer)\n    if type == \"ElementHandle\":\n        return ElementHandle(parent, type, guid, initializer)\n    if type == \"Frame\":\n        return Frame(parent, type, guid, initializer)\n    if type == \"JSHandle\":\n        return JSHandle(parent, type, guid, initializer)\n    if type == \"LocalUtils\":\n        local_utils = LocalUtils(parent, type, guid, initializer)\n        if not local_utils._connection._local_utils:\n            local_utils._connection._local_utils = local_utils\n        return local_utils\n    if type == \"Page\":\n        return Page(parent, type, guid, initializer)\n    if type == \"Playwright\":\n        return Playwright(parent, type, guid, initializer)\n    if type == \"Request\":\n        return Request(parent, type, guid, initializer)\n    if type == \"Response\":\n        return Response(parent, type, guid, initializer)\n    if type == \"Route\":\n        return Route(parent, type, guid, initializer)\n    if type == \"Stream\":\n        return Stream(parent, type, guid, initializer)\n    if type == \"Tracing\":\n        return Tracing(parent, type, guid, initializer)\n    if type == \"WebSocket\":\n        return WebSocket(parent, type, guid, initializer)\n    if type == \"WebSocketRoute\":\n        return WebSocketRoute(parent, type, guid, initializer)\n    if type == \"Worker\":\n        return Worker(parent, type, guid, initializer)\n    if type == \"WritableStream\":\n        return WritableStream(parent, type, guid, initializer)\n    return DummyObject(parent, type, guid, initializer)\n"
  },
  {
    "path": "playwright/_impl/_page.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nimport inspect\nimport re\nimport sys\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Pattern,\n    Sequence,\n    Union,\n    cast,\n)\n\nfrom playwright._impl._api_structures import (\n    AriaRole,\n    FilePayload,\n    FloatRect,\n    PdfMargins,\n    Position,\n    ViewportSize,\n)\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._clock import Clock\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._console_message import ConsoleMessage\nfrom playwright._impl._download import Download\nfrom playwright._impl._element_handle import ElementHandle, determine_screenshot_type\nfrom playwright._impl._errors import Error, TargetClosedError, is_target_closed_error\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._file_chooser import FileChooser\nfrom playwright._impl._frame import Frame\nfrom playwright._impl._greenlets import LocatorHandlerGreenlet\nfrom playwright._impl._har_router import HarRouter\nfrom playwright._impl._helper import (\n    ColorScheme,\n    Contrast,\n    DocumentLoadState,\n    ForcedColors,\n    HarMode,\n    KeyboardModifier,\n    MouseButton,\n    ReducedMotion,\n    RouteFromHarNotFoundPolicy,\n    RouteHandler,\n    RouteHandlerCallback,\n    TimeoutSettings,\n    URLMatch,\n    URLMatchRequest,\n    URLMatchResponse,\n    WebSocketRouteHandlerCallback,\n    async_readfile,\n    async_writefile,\n    locals_to_params,\n    make_dirs_for_file,\n    parse_error,\n    serialize_error,\n    url_matches,\n)\nfrom playwright._impl._input import Keyboard, Mouse, Touchscreen\nfrom playwright._impl._js_handle import (\n    JSHandle,\n    Serializable,\n    add_source_url_to_script,\n    parse_result,\n    serialize_argument,\n)\nfrom playwright._impl._network import (\n    Request,\n    Response,\n    Route,\n    WebSocketRoute,\n    WebSocketRouteHandler,\n    serialize_headers,\n)\nfrom playwright._impl._video import Video\nfrom playwright._impl._waiter import Waiter\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser_context import BrowserContext\n    from playwright._impl._fetch import APIRequestContext\n    from playwright._impl._locator import FrameLocator, Locator\n    from playwright._impl._network import WebSocket\n\n\nclass LocatorHandler:\n    locator: \"Locator\"\n    handler: Union[Callable[[\"Locator\"], Any], Callable[..., Any]]\n    times: Union[int, None]\n\n    def __init__(\n        self, locator: \"Locator\", handler: Callable[..., Any], times: Union[int, None]\n    ) -> None:\n        self.locator = locator\n        self._handler = handler\n        self.times = times\n\n    def __call__(self) -> Any:\n        arg_count = len(inspect.signature(self._handler).parameters)\n        if arg_count == 0:\n            return self._handler()\n        return self._handler(self.locator)\n\n\nclass Page(ChannelOwner):\n    Events = SimpleNamespace(\n        Close=\"close\",\n        Crash=\"crash\",\n        Console=\"console\",\n        Dialog=\"dialog\",\n        Download=\"download\",\n        FileChooser=\"filechooser\",\n        DOMContentLoaded=\"domcontentloaded\",\n        PageError=\"pageerror\",\n        Request=\"request\",\n        Response=\"response\",\n        RequestFailed=\"requestfailed\",\n        RequestFinished=\"requestfinished\",\n        FrameAttached=\"frameattached\",\n        FrameDetached=\"framedetached\",\n        FrameNavigated=\"framenavigated\",\n        Load=\"load\",\n        Popup=\"popup\",\n        WebSocket=\"websocket\",\n        Worker=\"worker\",\n    )\n    keyboard: Keyboard\n    mouse: Mouse\n    touchscreen: Touchscreen\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._browser_context = cast(\"BrowserContext\", parent)\n        self.keyboard = Keyboard(self._channel)\n        self.mouse = Mouse(self._channel)\n        self.touchscreen = Touchscreen(self._channel)\n\n        self._main_frame: Frame = from_channel(initializer[\"mainFrame\"])\n        self._main_frame._page = self\n        self._frames = [self._main_frame]\n        self._viewport_size: Optional[ViewportSize] = initializer.get(\"viewportSize\")\n        self._is_closed = False\n        self._workers: List[\"Worker\"] = []\n        self._bindings: Dict[str, Any] = {}\n        self._routes: List[RouteHandler] = []\n        self._web_socket_routes: List[WebSocketRouteHandler] = []\n        self._owned_context: Optional[\"BrowserContext\"] = None\n        self._timeout_settings: TimeoutSettings = TimeoutSettings(\n            self._browser_context._timeout_settings\n        )\n        self._video: Optional[Video] = None\n        self._opener = cast(\"Page\", from_nullable_channel(initializer.get(\"opener\")))\n        self._close_reason: Optional[str] = None\n        self._close_was_called = False\n        self._har_routers: List[HarRouter] = []\n        self._locator_handlers: Dict[str, LocatorHandler] = {}\n\n        self._channel.on(\n            \"bindingCall\",\n            lambda params: self._on_binding(from_channel(params[\"binding\"])),\n        )\n        self._channel.on(\"close\", lambda _: self._on_close())\n        self._channel.on(\"crash\", lambda _: self._on_crash())\n        self._channel.on(\"download\", lambda params: self._on_download(params))\n        self._channel.on(\n            \"fileChooser\",\n            lambda params: self.emit(\n                Page.Events.FileChooser,\n                FileChooser(\n                    self, from_channel(params[\"element\"]), params[\"isMultiple\"]\n                ),\n            ),\n        )\n        self._channel.on(\n            \"frameAttached\",\n            lambda params: self._on_frame_attached(from_channel(params[\"frame\"])),\n        )\n        self._channel.on(\n            \"frameDetached\",\n            lambda params: self._on_frame_detached(from_channel(params[\"frame\"])),\n        )\n        self._channel.on(\n            \"locatorHandlerTriggered\",\n            lambda params: self._loop.create_task(\n                self._on_locator_handler_triggered(params[\"uid\"])\n            ),\n        )\n        self._channel.on(\n            \"route\",\n            lambda params: self._loop.create_task(\n                self._on_route(from_channel(params[\"route\"]))\n            ),\n        )\n        self._channel.on(\n            \"webSocketRoute\",\n            lambda params: self._loop.create_task(\n                self._on_web_socket_route(from_channel(params[\"webSocketRoute\"]))\n            ),\n        )\n        self._channel.on(\"video\", lambda params: self._on_video(params))\n        self._channel.on(\"viewportSizeChanged\", self._on_viewport_size_changed)\n        self._channel.on(\n            \"webSocket\",\n            lambda params: self.emit(\n                Page.Events.WebSocket, from_channel(params[\"webSocket\"])\n            ),\n        )\n        self._channel.on(\n            \"worker\", lambda params: self._on_worker(from_channel(params[\"worker\"]))\n        )\n        self._closed_or_crashed_future: asyncio.Future = asyncio.Future()\n        self.on(\n            Page.Events.Close,\n            lambda _: (\n                self._closed_or_crashed_future.set_result(\n                    self._close_error_with_reason()\n                )\n                if not self._closed_or_crashed_future.done()\n                else None\n            ),\n        )\n        self.on(\n            Page.Events.Crash,\n            lambda _: (\n                self._closed_or_crashed_future.set_result(TargetClosedError())\n                if not self._closed_or_crashed_future.done()\n                else None\n            ),\n        )\n\n        self._set_event_to_subscription_mapping(\n            {\n                Page.Events.Console: \"console\",\n                Page.Events.Dialog: \"dialog\",\n                Page.Events.Request: \"request\",\n                Page.Events.Response: \"response\",\n                Page.Events.RequestFinished: \"requestFinished\",\n                Page.Events.RequestFailed: \"requestFailed\",\n                Page.Events.FileChooser: \"fileChooser\",\n            }\n        )\n\n    def __repr__(self) -> str:\n        return f\"<Page url={self.url!r}>\"\n\n    def _on_frame_attached(self, frame: Frame) -> None:\n        frame._page = self\n        self._frames.append(frame)\n        self.emit(Page.Events.FrameAttached, frame)\n\n    def _on_frame_detached(self, frame: Frame) -> None:\n        self._frames.remove(frame)\n        frame._detached = True\n        self.emit(Page.Events.FrameDetached, frame)\n\n    async def _on_route(self, route: Route) -> None:\n        route._context = self.context\n        route_handlers = self._routes.copy()\n        for route_handler in route_handlers:\n            # If the page was closed we stall all requests right away.\n            if self._close_was_called or self.context._closing_or_closed:\n                return\n            if not route_handler.matches(route.request.url):\n                continue\n            if route_handler not in self._routes:\n                continue\n            if route_handler.will_expire:\n                self._routes.remove(route_handler)\n            try:\n                handled = await route_handler.handle(route)\n            finally:\n                if len(self._routes) == 0:\n\n                    async def _update_interceptor_patterns_ignore_exceptions() -> None:\n                        try:\n                            await self._update_interception_patterns()\n                        except Error:\n                            pass\n\n                    asyncio.create_task(\n                        self._connection.wrap_api_call(\n                            _update_interceptor_patterns_ignore_exceptions, True\n                        )\n                    )\n            if handled:\n                return\n        await self._browser_context._on_route(route)\n\n    async def _on_web_socket_route(self, web_socket_route: WebSocketRoute) -> None:\n        route_handler = next(\n            (\n                route_handler\n                for route_handler in self._web_socket_routes\n                if route_handler.matches(web_socket_route.url)\n            ),\n            None,\n        )\n        if route_handler:\n            await route_handler.handle(web_socket_route)\n        else:\n            await self._browser_context._on_web_socket_route(web_socket_route)\n\n    def _on_binding(self, binding_call: \"BindingCall\") -> None:\n        func = self._bindings.get(binding_call._initializer[\"name\"])\n        if func:\n            asyncio.create_task(binding_call.call(func))\n        self._browser_context._on_binding(binding_call)\n\n    def _on_worker(self, worker: \"Worker\") -> None:\n        self._workers.append(worker)\n        worker._page = self\n        self.emit(Page.Events.Worker, worker)\n\n    def _on_close(self) -> None:\n        self._is_closed = True\n        if self in self._browser_context._pages:\n            self._browser_context._pages.remove(self)\n        self._dispose_har_routers()\n        self.emit(Page.Events.Close, self)\n\n    def _on_crash(self) -> None:\n        self.emit(Page.Events.Crash, self)\n\n    def _on_download(self, params: Any) -> None:\n        url = params[\"url\"]\n        suggested_filename = params[\"suggestedFilename\"]\n        artifact = cast(Artifact, from_channel(params[\"artifact\"]))\n        self.emit(\n            Page.Events.Download, Download(self, url, suggested_filename, artifact)\n        )\n\n    def _on_video(self, params: Any) -> None:\n        artifact = from_channel(params[\"artifact\"])\n        self._force_video()._artifact_ready(artifact)\n\n    def _on_viewport_size_changed(self, params: Any) -> None:\n        self._viewport_size = params[\"viewportSize\"]\n\n    @property\n    def context(self) -> \"BrowserContext\":\n        return self._browser_context\n\n    @property\n    def clock(self) -> Clock:\n        return self._browser_context.clock\n\n    async def opener(self) -> Optional[\"Page\"]:\n        if self._opener and self._opener.is_closed():\n            return None\n        return self._opener\n\n    @property\n    def main_frame(self) -> Frame:\n        return self._main_frame\n\n    def frame(self, name: str = None, url: URLMatch = None) -> Optional[Frame]:\n        for frame in self._frames:\n            if name and frame.name == name:\n                return frame\n            if url and url_matches(self._browser_context._base_url, frame.url, url):\n                return frame\n\n        return None\n\n    @property\n    def frames(self) -> List[Frame]:\n        return self._frames.copy()\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        self._timeout_settings.set_default_navigation_timeout(timeout)\n\n    def set_default_timeout(self, timeout: float) -> None:\n        self._timeout_settings.set_default_timeout(timeout)\n\n    async def query_selector(\n        self,\n        selector: str,\n        strict: bool = None,\n    ) -> Optional[ElementHandle]:\n        return await self._main_frame.query_selector(selector, strict)\n\n    async def query_selector_all(self, selector: str) -> List[ElementHandle]:\n        return await self._main_frame.query_selector_all(selector)\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        timeout: float = None,\n        state: Literal[\"attached\", \"detached\", \"hidden\", \"visible\"] = None,\n        strict: bool = None,\n    ) -> Optional[ElementHandle]:\n        return await self._main_frame.wait_for_selector(**locals_to_params(locals()))\n\n    async def is_checked(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._main_frame.is_checked(**locals_to_params(locals()))\n\n    async def is_disabled(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._main_frame.is_disabled(**locals_to_params(locals()))\n\n    async def is_editable(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._main_frame.is_editable(**locals_to_params(locals()))\n\n    async def is_enabled(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        return await self._main_frame.is_enabled(**locals_to_params(locals()))\n\n    async def is_hidden(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        # timeout is deprecated and does nothing\n        return await self._main_frame.is_hidden(selector=selector, strict=strict)\n\n    async def is_visible(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> bool:\n        # timeout is deprecated and does nothing\n        return await self._main_frame.is_visible(selector=selector, strict=strict)\n\n    async def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        eventInit: Dict = None,\n        timeout: float = None,\n        strict: bool = None,\n    ) -> None:\n        return await self._main_frame.dispatch_event(**locals_to_params(locals()))\n\n    async def evaluate(self, expression: str, arg: Serializable = None) -> Any:\n        return await self._main_frame.evaluate(expression, arg)\n\n    async def evaluate_handle(\n        self, expression: str, arg: Serializable = None\n    ) -> JSHandle:\n        return await self._main_frame.evaluate_handle(expression, arg)\n\n    async def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n        strict: bool = None,\n    ) -> Any:\n        return await self._main_frame.eval_on_selector(\n            selector, expression, arg, strict\n        )\n\n    async def eval_on_selector_all(\n        self,\n        selector: str,\n        expression: str,\n        arg: Serializable = None,\n    ) -> Any:\n        return await self._main_frame.eval_on_selector_all(selector, expression, arg)\n\n    async def add_script_tag(\n        self,\n        url: str = None,\n        path: Union[str, Path] = None,\n        content: str = None,\n        type: str = None,\n    ) -> ElementHandle:\n        return await self._main_frame.add_script_tag(**locals_to_params(locals()))\n\n    async def add_style_tag(\n        self, url: str = None, path: Union[str, Path] = None, content: str = None\n    ) -> ElementHandle:\n        return await self._main_frame.add_style_tag(**locals_to_params(locals()))\n\n    async def expose_function(self, name: str, callback: Callable) -> None:\n        await self.expose_binding(name, lambda source, *args: callback(*args))\n\n    async def expose_binding(\n        self, name: str, callback: Callable, handle: bool = None\n    ) -> None:\n        if name in self._bindings:\n            raise Error(f'Function \"{name}\" has been already registered')\n        if name in self._browser_context._bindings:\n            raise Error(\n                f'Function \"{name}\" has been already registered in the browser context'\n            )\n        self._bindings[name] = callback\n        await self._channel.send(\n            \"exposeBinding\",\n            None,\n            dict(name=name, needsHandle=handle or False),\n        )\n\n    async def set_extra_http_headers(self, headers: Dict[str, str]) -> None:\n        await self._channel.send(\n            \"setExtraHTTPHeaders\",\n            None,\n            dict(headers=serialize_headers(headers)),\n        )\n\n    @property\n    def url(self) -> str:\n        return self._main_frame.url\n\n    async def content(self) -> str:\n        return await self._main_frame.content()\n\n    async def set_content(\n        self,\n        html: str,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n    ) -> None:\n        return await self._main_frame.set_content(**locals_to_params(locals()))\n\n    async def goto(\n        self,\n        url: str,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n        referer: str = None,\n    ) -> Optional[Response]:\n        return await self._main_frame.goto(**locals_to_params(locals()))\n\n    async def reload(\n        self,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n    ) -> Optional[Response]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"reload\",\n                self._timeout_settings.navigation_timeout,\n                locals_to_params(locals()),\n            )\n        )\n\n    async def wait_for_load_state(\n        self,\n        state: Literal[\"domcontentloaded\", \"load\", \"networkidle\"] = None,\n        timeout: float = None,\n    ) -> None:\n        return await self._main_frame.wait_for_load_state(**locals_to_params(locals()))\n\n    async def wait_for_url(\n        self,\n        url: URLMatch,\n        waitUntil: DocumentLoadState = None,\n        timeout: float = None,\n    ) -> None:\n        return await self._main_frame.wait_for_url(**locals_to_params(locals()))\n\n    async def wait_for_event(\n        self, event: str, predicate: Callable = None, timeout: float = None\n    ) -> Any:\n        async with self.expect_event(event, predicate, timeout) as event_info:\n            pass\n        return await event_info\n\n    async def go_back(\n        self,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n    ) -> Optional[Response]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"goBack\",\n                self._timeout_settings.navigation_timeout,\n                locals_to_params(locals()),\n            )\n        )\n\n    async def go_forward(\n        self,\n        timeout: float = None,\n        waitUntil: DocumentLoadState = None,\n    ) -> Optional[Response]:\n        return from_nullable_channel(\n            await self._channel.send(\n                \"goForward\",\n                self._timeout_settings.navigation_timeout,\n                locals_to_params(locals()),\n            )\n        )\n\n    async def request_gc(self) -> None:\n        await self._channel.send(\"requestGC\", None)\n\n    async def emulate_media(\n        self,\n        media: Literal[\"null\", \"print\", \"screen\"] = None,\n        colorScheme: ColorScheme = None,\n        reducedMotion: ReducedMotion = None,\n        forcedColors: ForcedColors = None,\n        contrast: Contrast = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        if \"media\" in params:\n            params[\"media\"] = \"no-override\" if params[\"media\"] == \"null\" else media\n        if \"colorScheme\" in params:\n            params[\"colorScheme\"] = (\n                \"no-override\" if params[\"colorScheme\"] == \"null\" else colorScheme\n            )\n        if \"reducedMotion\" in params:\n            params[\"reducedMotion\"] = (\n                \"no-override\" if params[\"reducedMotion\"] == \"null\" else reducedMotion\n            )\n        if \"forcedColors\" in params:\n            params[\"forcedColors\"] = (\n                \"no-override\" if params[\"forcedColors\"] == \"null\" else forcedColors\n            )\n        if \"contrast\" in params:\n            params[\"contrast\"] = (\n                \"no-override\" if params[\"contrast\"] == \"null\" else contrast\n            )\n        await self._channel.send(\"emulateMedia\", None, params)\n\n    async def set_viewport_size(self, viewportSize: ViewportSize) -> None:\n        self._viewport_size = viewportSize\n        await self._channel.send(\n            \"setViewportSize\",\n            None,\n            locals_to_params(locals()),\n        )\n\n    @property\n    def viewport_size(self) -> Optional[ViewportSize]:\n        return self._viewport_size\n\n    async def bring_to_front(self) -> None:\n        await self._channel.send(\"bringToFront\", None)\n\n    async def add_init_script(\n        self, script: str = None, path: Union[str, Path] = None\n    ) -> None:\n        if path:\n            script = add_source_url_to_script(\n                (await async_readfile(path)).decode(), path\n            )\n        if not isinstance(script, str):\n            raise Error(\"Either path or script parameter must be specified\")\n        await self._channel.send(\"addInitScript\", None, dict(source=script))\n\n    async def route(\n        self, url: URLMatch, handler: RouteHandlerCallback, times: int = None\n    ) -> None:\n        self._routes.insert(\n            0,\n            RouteHandler(\n                self._browser_context._base_url,\n                url,\n                handler,\n                True if self._dispatcher_fiber else False,\n                times,\n            ),\n        )\n        await self._update_interception_patterns()\n\n    async def unroute(\n        self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None\n    ) -> None:\n        removed = []\n        remaining = []\n        for route in self._routes:\n            if route.url != url or (handler and route.handler != handler):\n                remaining.append(route)\n            else:\n                removed.append(route)\n        await self._unroute_internal(removed, remaining, \"default\")\n\n    async def _unroute_internal(\n        self,\n        removed: List[RouteHandler],\n        remaining: List[RouteHandler],\n        behavior: Literal[\"default\", \"ignoreErrors\", \"wait\"] = None,\n    ) -> None:\n        self._routes = remaining\n        if behavior is not None and behavior != \"default\":\n            await asyncio.gather(\n                *map(\n                    lambda route: route.stop(behavior),  # type: ignore\n                    removed,\n                )\n            )\n        await self._update_interception_patterns()\n\n    async def route_web_socket(\n        self, url: URLMatch, handler: WebSocketRouteHandlerCallback\n    ) -> None:\n        self._web_socket_routes.insert(\n            0,\n            WebSocketRouteHandler(self._browser_context._base_url, url, handler),\n        )\n        await self._update_web_socket_interception_patterns()\n\n    def _dispose_har_routers(self) -> None:\n        for router in self._har_routers:\n            router.dispose()\n        self._har_routers = []\n\n    async def unroute_all(\n        self, behavior: Literal[\"default\", \"ignoreErrors\", \"wait\"] = None\n    ) -> None:\n        await self._unroute_internal(self._routes, [], behavior)\n        self._dispose_har_routers()\n\n    async def route_from_har(\n        self,\n        har: Union[Path, str],\n        url: Union[Pattern[str], str] = None,\n        notFound: RouteFromHarNotFoundPolicy = None,\n        update: bool = None,\n        updateContent: Literal[\"attach\", \"embed\"] = None,\n        updateMode: HarMode = None,\n    ) -> None:\n        if update:\n            await self._browser_context._record_into_har(\n                har=har,\n                page=self,\n                url=url,\n                update_content=updateContent,\n                update_mode=updateMode,\n            )\n            return\n        router = await HarRouter.create(\n            local_utils=self._connection.local_utils,\n            file=str(har),\n            not_found_action=notFound or \"abort\",\n            url_matcher=url,\n        )\n        self._har_routers.append(router)\n        await router.add_page_route(self)\n\n    async def _update_interception_patterns(self) -> None:\n        patterns = RouteHandler.prepare_interception_patterns(self._routes)\n        await self._channel.send(\n            \"setNetworkInterceptionPatterns\",\n            None,\n            {\"patterns\": patterns},\n        )\n\n    async def _update_web_socket_interception_patterns(self) -> None:\n        patterns = WebSocketRouteHandler.prepare_interception_patterns(\n            self._web_socket_routes\n        )\n        await self._channel.send(\n            \"setWebSocketInterceptionPatterns\",\n            None,\n            {\"patterns\": patterns},\n        )\n\n    async def screenshot(\n        self,\n        timeout: float = None,\n        type: Literal[\"jpeg\", \"png\"] = None,\n        path: Union[str, Path] = None,\n        quality: int = None,\n        omitBackground: bool = None,\n        fullPage: bool = None,\n        clip: FloatRect = None,\n        animations: Literal[\"allow\", \"disabled\"] = None,\n        caret: Literal[\"hide\", \"initial\"] = None,\n        scale: Literal[\"css\", \"device\"] = None,\n        mask: Sequence[\"Locator\"] = None,\n        maskColor: str = None,\n        style: str = None,\n    ) -> bytes:\n        params = locals_to_params(locals())\n        if \"path\" in params:\n            if \"type\" not in params:\n                params[\"type\"] = determine_screenshot_type(params[\"path\"])\n            del params[\"path\"]\n        if \"mask\" in params:\n            params[\"mask\"] = list(\n                map(\n                    lambda locator: (\n                        {\n                            \"frame\": locator._frame._channel,\n                            \"selector\": locator._selector,\n                        }\n                    ),\n                    params[\"mask\"],\n                )\n            )\n        encoded_binary = await self._channel.send(\n            \"screenshot\", self._timeout_settings.timeout, params\n        )\n        decoded_binary = base64.b64decode(encoded_binary)\n        if path:\n            make_dirs_for_file(path)\n            await async_writefile(path, decoded_binary)\n        return decoded_binary\n\n    async def title(self) -> str:\n        return await self._main_frame.title()\n\n    async def close(self, runBeforeUnload: bool = None, reason: str = None) -> None:\n        self._close_reason = reason\n        self._close_was_called = True\n        try:\n            await self._channel.send(\"close\", None, locals_to_params(locals()))\n            if self._owned_context:\n                await self._owned_context.close()\n        except Exception as e:\n            if not is_target_closed_error(e) and not runBeforeUnload:\n                raise e\n\n    def is_closed(self) -> bool:\n        return self._is_closed\n\n    async def click(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        clickCount: int = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        trial: bool = None,\n        strict: bool = None,\n    ) -> None:\n        return await self._main_frame._click(**locals_to_params(locals()))\n\n    async def dblclick(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        delay: float = None,\n        button: MouseButton = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        return await self._main_frame.dblclick(**locals_to_params(locals()))\n\n    async def tap(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        return await self._main_frame.tap(**locals_to_params(locals()))\n\n    async def fill(\n        self,\n        selector: str,\n        value: str,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        force: bool = None,\n    ) -> None:\n        return await self._main_frame.fill(**locals_to_params(locals()))\n\n    def locator(\n        self,\n        selector: str,\n        hasText: Union[str, Pattern[str]] = None,\n        hasNotText: Union[str, Pattern[str]] = None,\n        has: \"Locator\" = None,\n        hasNot: \"Locator\" = None,\n    ) -> \"Locator\":\n        return self._main_frame.locator(\n            selector,\n            hasText=hasText,\n            hasNotText=hasNotText,\n            has=has,\n            hasNot=hasNot,\n        )\n\n    def get_by_alt_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self._main_frame.get_by_alt_text(text, exact=exact)\n\n    def get_by_label(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self._main_frame.get_by_label(text, exact=exact)\n\n    def get_by_placeholder(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self._main_frame.get_by_placeholder(text, exact=exact)\n\n    def get_by_role(\n        self,\n        role: AriaRole,\n        checked: bool = None,\n        disabled: bool = None,\n        expanded: bool = None,\n        includeHidden: bool = None,\n        level: int = None,\n        name: Union[str, Pattern[str]] = None,\n        pressed: bool = None,\n        selected: bool = None,\n        exact: bool = None,\n    ) -> \"Locator\":\n        return self._main_frame.get_by_role(\n            role,\n            checked=checked,\n            disabled=disabled,\n            expanded=expanded,\n            includeHidden=includeHidden,\n            level=level,\n            name=name,\n            pressed=pressed,\n            selected=selected,\n            exact=exact,\n        )\n\n    def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> \"Locator\":\n        return self._main_frame.get_by_test_id(testId)\n\n    def get_by_text(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self._main_frame.get_by_text(text, exact=exact)\n\n    def get_by_title(\n        self, text: Union[str, Pattern[str]], exact: bool = None\n    ) -> \"Locator\":\n        return self._main_frame.get_by_title(text, exact=exact)\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        return self.main_frame.frame_locator(selector)\n\n    async def focus(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> None:\n        return await self._main_frame.focus(**locals_to_params(locals()))\n\n    async def text_content(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> Optional[str]:\n        return await self._main_frame.text_content(**locals_to_params(locals()))\n\n    async def inner_text(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> str:\n        return await self._main_frame.inner_text(**locals_to_params(locals()))\n\n    async def inner_html(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> str:\n        return await self._main_frame.inner_html(**locals_to_params(locals()))\n\n    async def get_attribute(\n        self, selector: str, name: str, strict: bool = None, timeout: float = None\n    ) -> Optional[str]:\n        return await self._main_frame.get_attribute(**locals_to_params(locals()))\n\n    async def hover(\n        self,\n        selector: str,\n        modifiers: Sequence[KeyboardModifier] = None,\n        position: Position = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        return await self._main_frame.hover(**locals_to_params(locals()))\n\n    async def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        sourcePosition: Position = None,\n        targetPosition: Position = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        timeout: float = None,\n        strict: bool = None,\n        trial: bool = None,\n        steps: int = None,\n    ) -> None:\n        return await self._main_frame.drag_and_drop(**locals_to_params(locals()))\n\n    async def select_option(\n        self,\n        selector: str,\n        value: Union[str, Sequence[str]] = None,\n        index: Union[int, Sequence[int]] = None,\n        label: Union[str, Sequence[str]] = None,\n        element: Union[\"ElementHandle\", Sequence[\"ElementHandle\"]] = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        force: bool = None,\n        strict: bool = None,\n    ) -> List[str]:\n        params = locals_to_params(locals())\n        return await self._main_frame.select_option(**params)\n\n    async def input_value(\n        self, selector: str, strict: bool = None, timeout: float = None\n    ) -> str:\n        params = locals_to_params(locals())\n        return await self._main_frame.input_value(**params)\n\n    async def set_input_files(\n        self,\n        selector: str,\n        files: Union[\n            str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]\n        ],\n        timeout: float = None,\n        strict: bool = None,\n        noWaitAfter: bool = None,\n    ) -> None:\n        return await self._main_frame.set_input_files(**locals_to_params(locals()))\n\n    async def type(\n        self,\n        selector: str,\n        text: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n    ) -> None:\n        return await self._main_frame.type(**locals_to_params(locals()))\n\n    async def press(\n        self,\n        selector: str,\n        key: str,\n        delay: float = None,\n        timeout: float = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n    ) -> None:\n        return await self._main_frame.press(**locals_to_params(locals()))\n\n    async def check(\n        self,\n        selector: str,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        return await self._main_frame.check(**locals_to_params(locals()))\n\n    async def uncheck(\n        self,\n        selector: str,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        return await self._main_frame.uncheck(**locals_to_params(locals()))\n\n    async def wait_for_timeout(self, timeout: float) -> None:\n        await self._main_frame.wait_for_timeout(timeout)\n\n    async def wait_for_function(\n        self,\n        expression: str,\n        arg: Serializable = None,\n        timeout: float = None,\n        polling: Union[float, Literal[\"raf\"]] = None,\n    ) -> JSHandle:\n        return await self._main_frame.wait_for_function(**locals_to_params(locals()))\n\n    @property\n    def workers(self) -> List[\"Worker\"]:\n        return self._workers.copy()\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        return self.context.request\n\n    async def pause(self) -> None:\n        default_navigation_timeout = (\n            self._browser_context._timeout_settings.default_navigation_timeout()\n        )\n        default_timeout = self._browser_context._timeout_settings.default_timeout()\n        self._browser_context.set_default_navigation_timeout(0)\n        self._browser_context.set_default_timeout(0)\n        try:\n            await asyncio.wait(\n                [\n                    asyncio.create_task(\n                        self._browser_context._channel.send(\"pause\", None)\n                    ),\n                    self._closed_or_crashed_future,\n                ],\n                return_when=asyncio.FIRST_COMPLETED,\n            )\n        finally:\n            self._browser_context._set_default_navigation_timeout_impl(\n                default_navigation_timeout\n            )\n            self._browser_context._set_default_timeout_impl(default_timeout)\n\n    async def pdf(\n        self,\n        scale: float = None,\n        displayHeaderFooter: bool = None,\n        headerTemplate: str = None,\n        footerTemplate: str = None,\n        printBackground: bool = None,\n        landscape: bool = None,\n        pageRanges: str = None,\n        format: str = None,\n        width: Union[str, float] = None,\n        height: Union[str, float] = None,\n        preferCSSPageSize: bool = None,\n        margin: PdfMargins = None,\n        path: Union[str, Path] = None,\n        outline: bool = None,\n        tagged: bool = None,\n    ) -> bytes:\n        params = locals_to_params(locals())\n        if \"path\" in params:\n            del params[\"path\"]\n        encoded_binary = await self._channel.send(\"pdf\", None, params)\n        decoded_binary = base64.b64decode(encoded_binary)\n        if path:\n            make_dirs_for_file(path)\n            await async_writefile(path, decoded_binary)\n        return decoded_binary\n\n    def _force_video(self) -> Video:\n        if not self._video:\n            self._video = Video(self)\n        return self._video\n\n    @property\n    def video(\n        self,\n    ) -> Optional[Video]:\n        # Note: we are creating Video object lazily, because we do not know\n        # BrowserContextOptions when constructing the page - it is assigned\n        # too late during launchPersistentContext.\n        if not self._browser_context._videos_dir:\n            return None\n        return self._force_video()\n\n    def _close_error_with_reason(self) -> TargetClosedError:\n        return TargetClosedError(\n            self._close_reason or self._browser_context._effective_close_reason()\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        return self._expect_event(\n            event, predicate, timeout, f'waiting for event \"{event}\"'\n        )\n\n    def _expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n        log_line: str = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            timeout = self._timeout_settings.timeout()\n        waiter = Waiter(self, f\"page.expect_event({event})\")\n        waiter.reject_on_timeout(\n            timeout, f'Timeout {timeout}ms exceeded while waiting for event \"{event}\"'\n        )\n        if log_line:\n            waiter.log(log_line)\n        if event != Page.Events.Crash:\n            waiter.reject_on_event(self, Page.Events.Crash, Error(\"Page crashed\"))\n        if event != Page.Events.Close:\n            waiter.reject_on_event(\n                self, Page.Events.Close, lambda: self._close_error_with_reason()\n            )\n        waiter.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(waiter.result())\n\n    def expect_console_message(\n        self,\n        predicate: Callable[[ConsoleMessage], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[ConsoleMessage]:\n        return self.expect_event(Page.Events.Console, predicate, timeout)\n\n    def expect_download(\n        self,\n        predicate: Callable[[Download], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Download]:\n        return self.expect_event(Page.Events.Download, predicate, timeout)\n\n    def expect_file_chooser(\n        self,\n        predicate: Callable[[FileChooser], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[FileChooser]:\n        return self.expect_event(Page.Events.FileChooser, predicate, timeout)\n\n    def expect_navigation(\n        self,\n        url: URLMatch = None,\n        waitUntil: DocumentLoadState = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Response]:\n        return self.main_frame.expect_navigation(url, waitUntil, timeout)\n\n    def expect_popup(\n        self,\n        predicate: Callable[[\"Page\"], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[\"Page\"]:\n        return self.expect_event(Page.Events.Popup, predicate, timeout)\n\n    def expect_request(\n        self,\n        urlOrPredicate: URLMatchRequest,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Request]:\n        def my_predicate(request: Request) -> bool:\n            if not callable(urlOrPredicate):\n                return url_matches(\n                    self._browser_context._base_url,\n                    request.url,\n                    urlOrPredicate,\n                )\n            return urlOrPredicate(request)\n\n        trimmed_url = trim_url(urlOrPredicate)\n        log_line = f\"waiting for request {trimmed_url}\" if trimmed_url else None\n        return self._expect_event(\n            Page.Events.Request,\n            predicate=my_predicate,\n            timeout=timeout,\n            log_line=log_line,\n        )\n\n    def expect_request_finished(\n        self,\n        predicate: Callable[[\"Request\"], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Request]:\n        return self.expect_event(\n            Page.Events.RequestFinished, predicate=predicate, timeout=timeout\n        )\n\n    def expect_response(\n        self,\n        urlOrPredicate: URLMatchResponse,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[Response]:\n        def my_predicate(request: Response) -> bool:\n            if not callable(urlOrPredicate):\n                return url_matches(\n                    self._browser_context._base_url,\n                    request.url,\n                    urlOrPredicate,\n                )\n            return urlOrPredicate(request)\n\n        trimmed_url = trim_url(urlOrPredicate)\n        log_line = f\"waiting for response {trimmed_url}\" if trimmed_url else None\n        return self._expect_event(\n            Page.Events.Response,\n            predicate=my_predicate,\n            timeout=timeout,\n            log_line=log_line,\n        )\n\n    def expect_websocket(\n        self,\n        predicate: Callable[[\"WebSocket\"], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[\"WebSocket\"]:\n        return self.expect_event(\"websocket\", predicate, timeout)\n\n    def expect_worker(\n        self,\n        predicate: Callable[[\"Worker\"], bool] = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl[\"Worker\"]:\n        return self.expect_event(\"worker\", predicate, timeout)\n\n    async def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        position: Position = None,\n        timeout: float = None,\n        force: bool = None,\n        noWaitAfter: bool = None,\n        strict: bool = None,\n        trial: bool = None,\n    ) -> None:\n        if checked:\n            await self.check(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n        else:\n            await self.uncheck(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n\n    async def add_locator_handler(\n        self,\n        locator: \"Locator\",\n        handler: Union[Callable[[\"Locator\"], Any], Callable[[], Any]],\n        noWaitAfter: bool = None,\n        times: int = None,\n    ) -> None:\n        if locator._frame != self._main_frame:\n            raise Error(\"Locator must belong to the main frame of this page\")\n        if times == 0:\n            return\n        uid = await self._channel.send(\n            \"registerLocatorHandler\",\n            None,\n            {\n                \"selector\": locator._selector,\n                \"noWaitAfter\": noWaitAfter,\n            },\n        )\n        self._locator_handlers[uid] = LocatorHandler(\n            handler=handler, times=times, locator=locator\n        )\n\n    async def _on_locator_handler_triggered(self, uid: str) -> None:\n        remove = False\n        try:\n            handler = self._locator_handlers.get(uid)\n            if handler and handler.times != 0:\n                if handler.times is not None:\n                    handler.times -= 1\n                if self._dispatcher_fiber:\n                    handler_finished_future = self._loop.create_future()\n\n                    def _handler() -> None:\n                        try:\n                            handler()\n                            handler_finished_future.set_result(None)\n                        except Exception as e:\n                            handler_finished_future.set_exception(e)\n\n                    g = LocatorHandlerGreenlet(_handler)\n                    g.switch()\n                    await handler_finished_future\n                else:\n                    coro_or_future = handler()\n                    if coro_or_future:\n                        await coro_or_future\n                remove = handler.times == 0\n        finally:\n            if remove:\n                del self._locator_handlers[uid]\n            try:\n                await self._connection.wrap_api_call(\n                    lambda: self._channel.send(\n                        \"resolveLocatorHandlerNoReply\",\n                        None,\n                        {\"uid\": uid, \"remove\": remove},\n                    ),\n                    is_internal=True,\n                )\n            except Error:\n                pass\n\n    async def remove_locator_handler(self, locator: \"Locator\") -> None:\n        for uid, data in self._locator_handlers.copy().items():\n            if data.locator._equals(locator):\n                del self._locator_handlers[uid]\n                self._channel.send_no_reply(\n                    \"unregisterLocatorHandler\",\n                    None,\n                    {\"uid\": uid},\n                )\n\n    async def requests(self) -> List[Request]:\n        request_objects = await self._channel.send(\"requests\", None)\n        return [from_channel(r) for r in request_objects]\n\n    async def console_messages(self) -> List[ConsoleMessage]:\n        message_dicts = await self._channel.send(\"consoleMessages\", None)\n        return [\n            ConsoleMessage(\n                {**event, \"page\": self._channel}, self._loop, self._dispatcher_fiber\n            )\n            for event in message_dicts\n        ]\n\n    async def page_errors(self) -> List[Error]:\n        error_objects = await self._channel.send(\"pageErrors\", None)\n        return [parse_error(error[\"error\"]) for error in error_objects]\n\n\nclass Worker(ChannelOwner):\n    Events = SimpleNamespace(Close=\"close\", Console=\"console\")\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._set_event_to_subscription_mapping({Worker.Events.Console: \"console\"})\n        self._channel.on(\"close\", lambda _: self._on_close())\n        self._page: Optional[Page] = None\n        self._context: Optional[\"BrowserContext\"] = None\n\n    def __repr__(self) -> str:\n        return f\"<Worker url={self.url!r}>\"\n\n    def _on_close(self) -> None:\n        if self._page:\n            self._page._workers.remove(self)\n        if self._context:\n            self._context._service_workers.remove(self)\n        self.emit(Worker.Events.Close, self)\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    async def evaluate(self, expression: str, arg: Serializable = None) -> Any:\n        return parse_result(\n            await self._channel.send(\n                \"evaluateExpression\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: Serializable = None\n    ) -> JSHandle:\n        return from_channel(\n            await self._channel.send(\n                \"evaluateExpressionHandle\",\n                None,\n                dict(\n                    expression=expression,\n                    arg=serialize_argument(arg),\n                ),\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            if self._page:\n                timeout = self._page._timeout_settings.timeout()\n            elif self._context:\n                timeout = self._context._timeout_settings.timeout()\n            else:\n                timeout = 30000\n        waiter = Waiter(self, f\"worker.expect_event({event})\")\n        waiter.reject_on_timeout(\n            cast(float, timeout),\n            f'Timeout {timeout}ms exceeded while waiting for event \"{event}\"',\n        )\n        if event != Worker.Events.Close:\n            waiter.reject_on_event(\n                self, Worker.Events.Close, lambda: TargetClosedError()\n            )\n        waiter.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(waiter.result())\n\n\nclass BindingCall(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n    async def call(self, func: Callable) -> None:\n        try:\n            frame = from_channel(self._initializer[\"frame\"])\n            source = dict(context=frame._page.context, page=frame._page, frame=frame)\n            if self._initializer.get(\"handle\"):\n                result = func(source, from_channel(self._initializer[\"handle\"]))\n            else:\n                func_args = list(map(parse_result, self._initializer[\"args\"]))\n                result = func(source, *func_args)\n            if inspect.iscoroutine(result):\n                result = await result\n            await self._channel.send(\n                \"resolve\", None, dict(result=serialize_argument(result))\n            )\n        except Exception as e:\n            tb = sys.exc_info()[2]\n            asyncio.create_task(\n                self._channel.send(\n                    \"reject\", None, dict(error=dict(error=serialize_error(e, tb)))\n                )\n            )\n\n\ndef trim_url(param: Union[URLMatchRequest, URLMatchResponse]) -> Optional[str]:\n    if isinstance(param, re.Pattern):\n        return trim_end(param.pattern)\n    if isinstance(param, str):\n        return trim_end(param)\n    return None\n\n\ndef trim_end(s: str) -> str:\n    if len(s) > 50:\n        return s[:50] + \"\\u2026\"\n    return s\n"
  },
  {
    "path": "playwright/_impl/_path_utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nfrom pathlib import Path\nfrom types import FrameType\nfrom typing import cast\n\n\ndef get_file_dirname() -> Path:\n    \"\"\"Returns the callee (`__file__`) directory name\"\"\"\n    frame = cast(FrameType, inspect.currentframe()).f_back\n    module = inspect.getmodule(frame)\n    assert module\n    assert module.__file__\n    return Path(module.__file__).parent.absolute()\n"
  },
  {
    "path": "playwright/_impl/_playwright.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nfrom playwright._impl._browser_type import BrowserType\nfrom playwright._impl._connection import ChannelOwner, from_channel\nfrom playwright._impl._fetch import APIRequest\nfrom playwright._impl._selectors import Selectors\n\n\nclass Playwright(ChannelOwner):\n    devices: Dict\n    selectors: Selectors\n    chromium: BrowserType\n    firefox: BrowserType\n    webkit: BrowserType\n    request: APIRequest\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self.request = APIRequest(self)\n        self.chromium = from_channel(initializer[\"chromium\"])\n        self.chromium._playwright = self\n        self.firefox = from_channel(initializer[\"firefox\"])\n        self.firefox._playwright = self\n        self.webkit = from_channel(initializer[\"webkit\"])\n        self.webkit._playwright = self\n\n        self.selectors = Selectors(self._loop, self._dispatcher_fiber)\n\n        self.devices = self._connection.local_utils.devices\n\n    def __getitem__(self, value: str) -> \"BrowserType\":\n        if value == \"chromium\":\n            return self.chromium\n        elif value == \"firefox\":\n            return self.firefox\n        elif value == \"webkit\":\n            return self.webkit\n        raise ValueError(\"Invalid browser \" + value)\n\n    def _set_selectors(self, selectors: Selectors) -> None:\n        self.selectors = selectors\n\n    async def stop(self) -> None:\n        pass\n"
  },
  {
    "path": "playwright/_impl/_selectors.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional, Set, Union\n\nfrom playwright._impl._browser_context import BrowserContext\nfrom playwright._impl._errors import Error\nfrom playwright._impl._helper import async_readfile\nfrom playwright._impl._locator import set_test_id_attribute_name\n\n\nclass Selectors:\n    def __init__(self, loop: asyncio.AbstractEventLoop, dispatcher_fiber: Any) -> None:\n        self._loop = loop\n        self._contexts_for_selectors: Set[BrowserContext] = set()\n        self._selector_engines: List[Dict] = []\n        self._dispatcher_fiber = dispatcher_fiber\n        self._test_id_attribute_name: Optional[str] = None\n\n    async def register(\n        self,\n        name: str,\n        script: str = None,\n        path: Union[str, Path] = None,\n        contentScript: bool = None,\n    ) -> None:\n        if any(engine for engine in self._selector_engines if engine[\"name\"] == name):\n            raise Error(\n                f'Selectors.register: \"{name}\" selector engine has been already registered'\n            )\n        if not script and not path:\n            raise Error(\"Either source or path should be specified\")\n        if path:\n            script = (await async_readfile(path)).decode()\n        engine: Dict[str, Any] = dict(name=name, source=script)\n        if contentScript:\n            engine[\"contentScript\"] = contentScript\n        for context in self._contexts_for_selectors:\n            await context._channel.send(\n                \"registerSelectorEngine\",\n                None,\n                {\"selectorEngine\": engine},\n            )\n        self._selector_engines.append(engine)\n\n    def set_test_id_attribute(self, attributeName: str) -> None:\n        set_test_id_attribute_name(attributeName)\n        self._test_id_attribute_name = attributeName\n        for context in self._contexts_for_selectors:\n            context._channel.send_no_reply(\n                \"setTestIdAttributeName\",\n                None,\n                {\"testIdAttributeName\": attributeName},\n            )\n"
  },
  {
    "path": "playwright/_impl/_set_input_files_helpers.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport base64\nimport collections.abc\nimport os\nimport stat\nfrom pathlib import Path\nfrom typing import (\n    TYPE_CHECKING,\n    Dict,\n    List,\n    Optional,\n    Sequence,\n    Tuple,\n    TypedDict,\n    Union,\n    cast,\n)\n\nfrom playwright._impl._connection import Channel, from_channel\nfrom playwright._impl._helper import Error\nfrom playwright._impl._writable_stream import WritableStream\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._browser_context import BrowserContext\n\nfrom playwright._impl._api_structures import FilePayload\n\nSIZE_LIMIT_IN_BYTES = 50 * 1024 * 1024\n\n\nclass InputFilesList(TypedDict, total=False):\n    streams: Optional[List[Channel]]\n    directoryStream: Optional[Channel]\n    localDirectory: Optional[str]\n    localPaths: Optional[List[str]]\n    payloads: Optional[List[Dict[str, Union[str, bytes]]]]\n\n\ndef _list_files(directory: str) -> List[str]:\n    files = []\n    for root, _, filenames in os.walk(directory):\n        for filename in filenames:\n            files.append(os.path.join(root, filename))\n    return files\n\n\nasync def convert_input_files(\n    files: Union[\n        str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]\n    ],\n    context: \"BrowserContext\",\n) -> InputFilesList:\n    items = (\n        files\n        if isinstance(files, collections.abc.Sequence) and not isinstance(files, str)\n        else [files]\n    )\n\n    if any([isinstance(item, (str, Path)) for item in items]):\n        if not all([isinstance(item, (str, Path)) for item in items]):\n            raise Error(\"File paths cannot be mixed with buffers\")\n\n        (local_paths, local_directory) = resolve_paths_and_directory_for_input_files(\n            cast(Sequence[Union[str, Path]], items)\n        )\n\n        if context._channel._connection.is_remote:\n            files_to_stream = cast(\n                List[str],\n                (_list_files(local_directory) if local_directory else local_paths),\n            )\n            streams = []\n            result = await context._connection.wrap_api_call(\n                lambda: context._channel.send_return_as_dict(\n                    \"createTempFiles\",\n                    None,\n                    {\n                        \"rootDirName\": (\n                            os.path.basename(local_directory)\n                            if local_directory\n                            else None\n                        ),\n                        \"items\": list(\n                            map(\n                                lambda file: dict(\n                                    name=(\n                                        os.path.relpath(file, local_directory)\n                                        if local_directory\n                                        else os.path.basename(file)\n                                    ),\n                                    lastModifiedMs=int(os.path.getmtime(file) * 1000),\n                                ),\n                                files_to_stream,\n                            )\n                        ),\n                    },\n                )\n            )\n            for i, file in enumerate(result[\"writableStreams\"]):\n                stream: WritableStream = from_channel(file)\n                await stream.copy(files_to_stream[i])\n                streams.append(stream._channel)\n            return InputFilesList(\n                streams=None if local_directory else streams,\n                directoryStream=result.get(\"rootDir\"),\n            )\n        return InputFilesList(localPaths=local_paths, localDirectory=local_directory)\n\n    file_payload_exceeds_size_limit = (\n        sum([len(f.get(\"buffer\", \"\")) for f in items if not isinstance(f, (str, Path))])\n        > SIZE_LIMIT_IN_BYTES\n    )\n    if file_payload_exceeds_size_limit:\n        raise Error(\n            \"Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.\"\n        )\n\n    return InputFilesList(\n        payloads=[\n            {\n                \"name\": item[\"name\"],\n                \"mimeType\": item[\"mimeType\"],\n                \"buffer\": base64.b64encode(item[\"buffer\"]).decode(),\n            }\n            for item in cast(List[FilePayload], items)\n        ]\n    )\n\n\ndef resolve_paths_and_directory_for_input_files(\n    items: Sequence[Union[str, Path]],\n) -> Tuple[Optional[List[str]], Optional[str]]:\n    local_paths: Optional[List[str]] = None\n    local_directory: Optional[str] = None\n    for item in items:\n        item_stat = os.stat(item)  # Raises FileNotFoundError if doesn't exist\n        if stat.S_ISDIR(item_stat.st_mode):\n            if local_directory:\n                raise Error(\"Multiple directories are not supported\")\n            local_directory = str(Path(item).resolve())\n        else:\n            local_paths = local_paths or []\n            local_paths.append(str(Path(item).resolve()))\n    if local_paths and local_directory:\n        raise Error(\"File paths must be all files or a single directory\")\n    return (local_paths, local_directory)\n"
  },
  {
    "path": "playwright/_impl/_str_utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport re\nfrom typing import Pattern, Union\n\n\ndef escape_regex_flags(pattern: Pattern) -> str:\n    flags = \"\"\n    if pattern.flags != 0:\n        flags = \"\"\n    if (pattern.flags & int(re.IGNORECASE)) != 0:\n        flags += \"i\"\n    if (pattern.flags & int(re.DOTALL)) != 0:\n        flags += \"s\"\n    if (pattern.flags & int(re.MULTILINE)) != 0:\n        flags += \"m\"\n    assert (\n        pattern.flags\n        & ~(int(re.MULTILINE) | int(re.IGNORECASE) | int(re.DOTALL) | int(re.UNICODE))\n        == 0\n    ), \"Unexpected re.Pattern flag, only MULTILINE, IGNORECASE and DOTALL are supported.\"\n    return flags\n\n\ndef escape_for_regex(text: str) -> str:\n    return re.sub(r\"[.*+?^>${}()|[\\]\\\\]\", \"\\\\$&\", text)\n\n\ndef escape_regex_for_selector(text: Pattern) -> str:\n    # Even number of backslashes followed by the quote -> insert a backslash.\n    return (\n        \"/\"\n        + re.sub(r'(^|[^\\\\])(\\\\\\\\)*([\"\\'`])', r\"\\1\\2\\\\\\3\", text.pattern).replace(\n            \">>\", \"\\\\>\\\\>\"\n        )\n        + \"/\"\n        + escape_regex_flags(text)\n    )\n\n\ndef escape_for_text_selector(\n    text: Union[str, Pattern[str]], exact: bool = None, case_sensitive: bool = None\n) -> str:\n    if isinstance(text, Pattern):\n        return escape_regex_for_selector(text)\n    return json.dumps(text) + (\"s\" if exact else \"i\")\n\n\ndef escape_for_attribute_selector(\n    value: Union[str, Pattern], exact: bool = None\n) -> str:\n    if isinstance(value, Pattern):\n        return escape_regex_for_selector(value)\n    # TODO: this should actually be\n    #   cssEscape(value).replace(/\\\\ /g, ' ')\n    # However, our attribute selectors do not conform to CSS parsing spec,\n    # so we escape them differently.\n    return (\n        '\"'\n        + value.replace(\"\\\\\", \"\\\\\\\\\").replace('\"', '\\\\\"')\n        + '\"'\n        + (\"s\" if exact else \"i\")\n    )\n"
  },
  {
    "path": "playwright/_impl/_stream.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nfrom pathlib import Path\nfrom typing import Dict, Union\n\nfrom playwright._impl._connection import ChannelOwner\n\n\nclass Stream(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n    async def save_as(self, path: Union[str, Path]) -> None:\n        file = await self._loop.run_in_executor(None, lambda: open(path, \"wb\"))\n        while True:\n            binary = await self._channel.send(\"read\", None, {\"size\": 1024 * 1024})\n            if not binary:\n                break\n            await self._loop.run_in_executor(\n                None, lambda: file.write(base64.b64decode(binary))\n            )\n        await self._loop.run_in_executor(None, lambda: file.close())\n\n    async def read_all(self) -> bytes:\n        binary = b\"\"\n        while True:\n            chunk = await self._channel.send(\"read\", None, {\"size\": 1024 * 1024})\n            if not chunk:\n                break\n            binary += base64.b64decode(chunk)\n        return binary\n"
  },
  {
    "path": "playwright/_impl/_sync_base.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport inspect\nimport traceback\nfrom contextlib import AbstractContextManager\nfrom types import TracebackType\nfrom typing import (\n    Any,\n    Callable,\n    Coroutine,\n    Generator,\n    Generic,\n    Optional,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n)\n\nimport greenlet\n\nfrom playwright._impl._helper import Error\nfrom playwright._impl._impl_to_api_mapping import ImplToApiMapping, ImplWrapper\n\nmapping = ImplToApiMapping()\n\n\nT = TypeVar(\"T\")\nSelf = TypeVar(\"Self\", bound=\"SyncContextManager\")\n\n\nclass EventInfo(Generic[T]):\n    def __init__(self, sync_base: \"SyncBase\", future: \"asyncio.Future[T]\") -> None:\n        self._sync_base = sync_base\n        self._future = future\n        g_self = greenlet.getcurrent()\n        self._future.add_done_callback(lambda _: g_self.switch())\n\n    @property\n    def value(self) -> T:\n        while not self._future.done():\n            self._sync_base._dispatcher_fiber.switch()\n        asyncio._set_running_loop(self._sync_base._loop)\n        exception = self._future.exception()\n        if exception:\n            raise exception\n        return cast(T, mapping.from_maybe_impl(self._future.result()))\n\n    def _cancel(self) -> None:\n        self._future.cancel()\n\n    def is_done(self) -> bool:\n        return self._future.done()\n\n\nclass EventContextManager(Generic[T], AbstractContextManager):\n    def __init__(self, sync_base: \"SyncBase\", future: \"asyncio.Future[T]\") -> None:\n        self._event = EventInfo[T](sync_base, future)\n\n    def __enter__(self) -> EventInfo[T]:\n        return self._event\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        if exc_val:\n            self._event._cancel()\n        else:\n            self._event.value\n\n\nclass SyncBase(ImplWrapper):\n    def __init__(self, impl_obj: Any) -> None:\n        super().__init__(impl_obj)\n        self._loop: asyncio.AbstractEventLoop = impl_obj._loop\n        self._dispatcher_fiber = impl_obj._dispatcher_fiber\n\n    def __str__(self) -> str:\n        return self._impl_obj.__str__()\n\n    def _sync(\n        self,\n        coro: Union[Coroutine[Any, Any, Any], Generator[Any, Any, Any]],\n    ) -> Any:\n        __tracebackhide__ = True\n        if self._loop.is_closed():\n            coro.close()\n            raise Error(\"Event loop is closed! Is Playwright already stopped?\")\n\n        g_self = greenlet.getcurrent()\n        task: asyncio.tasks.Task[Any] = self._loop.create_task(coro)\n        setattr(task, \"__pw_stack__\", inspect.stack(0))\n        setattr(task, \"__pw_stack_trace__\", traceback.extract_stack(limit=10))\n\n        task.add_done_callback(lambda _: g_self.switch())\n        while not task.done():\n            self._dispatcher_fiber.switch()\n        asyncio._set_running_loop(self._loop)\n        return task.result()\n\n    def _wrap_handler(\n        self, handler: Union[Callable[..., Any], Any]\n    ) -> Callable[..., None]:\n        if callable(handler):\n            return mapping.wrap_handler(handler)\n        return handler\n\n    def on(self, event: Any, f: Any) -> None:\n        \"\"\"Registers the function ``f`` to the event name ``event``.\"\"\"\n        self._impl_obj.on(event, self._wrap_handler(f))\n\n    def once(self, event: Any, f: Any) -> None:\n        \"\"\"The same as ``self.on``, except that the listener is automatically\n        removed after being called.\n        \"\"\"\n        self._impl_obj.once(event, self._wrap_handler(f))\n\n    def remove_listener(self, event: Any, f: Any) -> None:\n        \"\"\"Removes the function ``f`` from ``event``.\"\"\"\n        self._impl_obj.remove_listener(event, self._wrap_handler(f))\n\n\nclass SyncContextManager(SyncBase):\n    def __enter__(self: Self) -> Self:\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        _traceback: Optional[TracebackType],\n    ) -> None:\n        self.close()\n\n    def close(self) -> None: ...\n"
  },
  {
    "path": "playwright/_impl/_tracing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pathlib\nfrom typing import Dict, Optional, Union, cast\n\nfrom playwright._impl._api_structures import TracingGroupLocation\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._connection import ChannelOwner, from_nullable_channel\nfrom playwright._impl._helper import locals_to_params\n\n\nclass Tracing(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._include_sources: bool = False\n        self._stacks_id: Optional[str] = None\n        self._is_tracing: bool = False\n        self._traces_dir: Optional[str] = None\n\n    async def start(\n        self,\n        name: str = None,\n        title: str = None,\n        snapshots: bool = None,\n        screenshots: bool = None,\n        sources: bool = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        self._include_sources = bool(sources)\n\n        await self._channel.send(\"tracingStart\", None, params)\n        trace_name = await self._channel.send(\n            \"tracingStartChunk\", None, {\"title\": title, \"name\": name}\n        )\n        await self._start_collecting_stacks(trace_name)\n\n    async def start_chunk(self, title: str = None, name: str = None) -> None:\n        params = locals_to_params(locals())\n        trace_name = await self._channel.send(\"tracingStartChunk\", None, params)\n        await self._start_collecting_stacks(trace_name)\n\n    async def _start_collecting_stacks(self, trace_name: str) -> None:\n        if not self._is_tracing:\n            self._is_tracing = True\n            self._connection.set_is_tracing(True)\n        self._stacks_id = await self._connection.local_utils.tracing_started(\n            self._traces_dir, trace_name\n        )\n\n    async def stop_chunk(self, path: Union[pathlib.Path, str] = None) -> None:\n        await self._do_stop_chunk(path)\n\n    async def stop(self, path: Union[pathlib.Path, str] = None) -> None:\n        await self._do_stop_chunk(path)\n        await self._channel.send(\n            \"tracingStop\",\n            None,\n        )\n\n    async def _do_stop_chunk(self, file_path: Union[pathlib.Path, str] = None) -> None:\n        self._reset_stack_counter()\n\n        if not file_path:\n            # Not interested in any artifacts\n            await self._channel.send(\"tracingStopChunk\", None, {\"mode\": \"discard\"})\n            if self._stacks_id:\n                await self._connection.local_utils.trace_discarded(self._stacks_id)\n            return\n\n        is_local = not self._connection.is_remote\n\n        if is_local:\n            result = await self._channel.send_return_as_dict(\n                \"tracingStopChunk\", None, {\"mode\": \"entries\"}\n            )\n            await self._connection.local_utils.zip(\n                {\n                    \"zipFile\": str(file_path),\n                    \"entries\": result[\"entries\"],\n                    \"stacksId\": self._stacks_id,\n                    \"mode\": \"write\",\n                    \"includeSources\": self._include_sources,\n                }\n            )\n            return\n\n        result = await self._channel.send_return_as_dict(\n            \"tracingStopChunk\",\n            None,\n            {\n                \"mode\": \"archive\",\n            },\n        )\n\n        artifact = cast(\n            Optional[Artifact],\n            from_nullable_channel(result.get(\"artifact\")),\n        )\n\n        # The artifact may be missing if the browser closed while stopping tracing.\n        if not artifact:\n            if self._stacks_id:\n                await self._connection.local_utils.trace_discarded(self._stacks_id)\n            return\n\n        # Save trace to the final local file.\n        await artifact.save_as(file_path)\n        await artifact.delete()\n\n        await self._connection.local_utils.zip(\n            {\n                \"zipFile\": str(file_path),\n                \"entries\": [],\n                \"stacksId\": self._stacks_id,\n                \"mode\": \"append\",\n                \"includeSources\": self._include_sources,\n            }\n        )\n\n    def _reset_stack_counter(self) -> None:\n        if self._is_tracing:\n            self._is_tracing = False\n            self._connection.set_is_tracing(False)\n\n    async def group(self, name: str, location: TracingGroupLocation = None) -> None:\n        await self._channel.send(\"tracingGroup\", None, locals_to_params(locals()))\n\n    async def group_end(self) -> None:\n        await self._channel.send(\n            \"tracingGroupEnd\",\n            None,\n        )\n"
  },
  {
    "path": "playwright/_impl/_transport.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport io\nimport json\nimport os\nimport subprocess\nimport sys\nfrom abc import ABC, abstractmethod\nfrom typing import Callable, Dict, Optional, Union\n\nfrom playwright._impl._driver import compute_driver_executable, get_driver_env\nfrom playwright._impl._helper import ParsedMessagePayload\n\n\n# Sourced from: https://github.com/pytest-dev/pytest/blob/da01ee0a4bb0af780167ecd228ab3ad249511302/src/_pytest/faulthandler.py#L69-L77\ndef _get_stderr_fileno() -> Optional[int]:\n    try:\n        # when using pythonw, sys.stderr is None.\n        # when Pyinstaller is used, there is no closed attribute because Pyinstaller monkey-patches it with a NullWriter class\n        if sys.stderr is None or not hasattr(sys.stderr, \"closed\"):\n            return None\n        if sys.stderr.closed:\n            return None\n\n        return sys.stderr.fileno()\n    except (NotImplementedError, AttributeError, io.UnsupportedOperation):\n        # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.\n        # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors\n        # This is potentially dangerous, but the best we can do.\n        if not hasattr(sys, \"__stderr__\") or not sys.__stderr__:\n            return None\n        return sys.__stderr__.fileno()\n\n\nclass Transport(ABC):\n    def __init__(self, loop: asyncio.AbstractEventLoop) -> None:\n        self._loop = loop\n        self.on_message: Callable[[ParsedMessagePayload], None] = lambda _: None\n        self.on_error_future: asyncio.Future = loop.create_future()\n\n    @abstractmethod\n    def request_stop(self) -> None:\n        pass\n\n    def dispose(self) -> None:\n        pass\n\n    @abstractmethod\n    async def wait_until_stopped(self) -> None:\n        pass\n\n    @abstractmethod\n    async def connect(self) -> None:\n        pass\n\n    @abstractmethod\n    async def run(self) -> None:\n        pass\n\n    @abstractmethod\n    def send(self, message: Dict) -> None:\n        pass\n\n    def serialize_message(self, message: Dict) -> bytes:\n        msg = json.dumps(message)\n        if \"DEBUGP\" in os.environ:  # pragma: no cover\n            print(\"\\x1b[32mSEND>\\x1b[0m\", json.dumps(message, indent=2))\n        return msg.encode()\n\n    def deserialize_message(self, data: Union[str, bytes]) -> ParsedMessagePayload:\n        obj = json.loads(data)\n\n        if \"DEBUGP\" in os.environ:  # pragma: no cover\n            print(\"\\x1b[33mRECV>\\x1b[0m\", json.dumps(obj, indent=2))\n        return obj\n\n\nclass PipeTransport(Transport):\n    def __init__(self, loop: asyncio.AbstractEventLoop) -> None:\n        super().__init__(loop)\n        self._stopped = False\n\n    def request_stop(self) -> None:\n        assert self._output\n        self._stopped = True\n        self._output.close()\n\n    async def wait_until_stopped(self) -> None:\n        await self._stopped_future\n\n    async def connect(self) -> None:\n        self._stopped_future: asyncio.Future = asyncio.Future()\n\n        try:\n            # For pyinstaller and Nuitka\n            env = get_driver_env()\n            if getattr(sys, \"frozen\", False) or globals().get(\"__compiled__\"):\n                env.setdefault(\"PLAYWRIGHT_BROWSERS_PATH\", \"0\")\n\n            startupinfo = None\n            if sys.platform == \"win32\":\n                startupinfo = subprocess.STARTUPINFO()\n                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW\n                startupinfo.wShowWindow = subprocess.SW_HIDE\n\n            executable_path, entrypoint_path = compute_driver_executable()\n            self._proc = await asyncio.create_subprocess_exec(\n                executable_path,\n                entrypoint_path,\n                \"run-driver\",\n                stdin=asyncio.subprocess.PIPE,\n                stdout=asyncio.subprocess.PIPE,\n                stderr=_get_stderr_fileno(),\n                limit=32768,\n                env=env,\n                startupinfo=startupinfo,\n            )\n        except Exception as exc:\n            self.on_error_future.set_exception(exc)\n            raise exc\n\n        self._output = self._proc.stdin\n\n    async def run(self) -> None:\n        assert self._proc.stdout\n        assert self._proc.stdin\n        while not self._stopped:\n            try:\n                buffer = await self._proc.stdout.readexactly(4)\n                if self._stopped:\n                    break\n                length = int.from_bytes(buffer, byteorder=\"little\", signed=False)\n                buffer = bytes(0)\n                while length:\n                    to_read = min(length, 32768)\n                    data = await self._proc.stdout.readexactly(to_read)\n                    if self._stopped:\n                        break\n                    length -= to_read\n                    if len(buffer):\n                        buffer = buffer + data\n                    else:\n                        buffer = data\n                if self._stopped:\n                    break\n\n                obj = self.deserialize_message(buffer)\n                self.on_message(obj)\n            except asyncio.IncompleteReadError:\n                if not self._stopped:\n                    self.on_error_future.set_exception(\n                        Exception(\"Connection closed while reading from the driver\")\n                    )\n                break\n            await asyncio.sleep(0)\n\n        await self._proc.communicate()\n        self._stopped_future.set_result(None)\n\n    def send(self, message: Dict) -> None:\n        assert self._output\n        data = self.serialize_message(message)\n        self._output.write(\n            len(data).to_bytes(4, byteorder=\"little\", signed=False) + data\n        )\n"
  },
  {
    "path": "playwright/_impl/_video.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pathlib\nfrom typing import TYPE_CHECKING, Union\n\nfrom playwright._impl._artifact import Artifact\nfrom playwright._impl._helper import Error\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._page import Page\n\n\nclass Video:\n    def __init__(self, page: \"Page\") -> None:\n        self._loop = page._loop\n        self._dispatcher_fiber = page._dispatcher_fiber\n        self._page = page\n        self._artifact_future = page._loop.create_future()\n        if page.is_closed():\n            self._page_closed()\n        else:\n            page.on(\"close\", lambda page: self._page_closed())\n\n    def __repr__(self) -> str:\n        return f\"<Video page={self._page}>\"\n\n    def _page_closed(self) -> None:\n        if not self._artifact_future.done():\n            self._artifact_future.set_exception(Error(\"Page closed\"))\n\n    def _artifact_ready(self, artifact: Artifact) -> None:\n        if not self._artifact_future.done():\n            self._artifact_future.set_result(artifact)\n\n    async def path(self) -> pathlib.Path:\n        if self._page._connection.is_remote:\n            raise Error(\n                \"Path is not available when using browserType.connect(). Use save_as() to save a local copy.\"\n            )\n        artifact = await self._artifact_future\n        if not artifact:\n            raise Error(\"Page did not produce any video frames\")\n        return artifact.absolute_path\n\n    async def save_as(self, path: Union[str, pathlib.Path]) -> None:\n        if self._page._connection._is_sync and not self._page._is_closed:\n            raise Error(\n                \"Page is not yet closed. Close the page prior to calling save_as\"\n            )\n        artifact = await self._artifact_future\n        if not artifact:\n            raise Error(\"Page did not produce any video frames\")\n        await artifact.save_as(path)\n\n    async def delete(self) -> None:\n        artifact = await self._artifact_future\n        if not artifact:\n            raise Error(\"Page did not produce any video frames\")\n        await artifact.delete()\n"
  },
  {
    "path": "playwright/_impl/_waiter.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport math\nimport uuid\nfrom asyncio.tasks import Task\nfrom typing import Any, Callable, List, Tuple, Union\n\nfrom pyee import EventEmitter\n\nfrom playwright._impl._connection import ChannelOwner\nfrom playwright._impl._errors import Error, TimeoutError\n\n\nclass Waiter:\n    def __init__(self, channel_owner: ChannelOwner, event: str) -> None:\n        self._result: asyncio.Future = asyncio.Future()\n        self._wait_id = uuid.uuid4().hex\n        self._loop = channel_owner._loop\n        self._pending_tasks: List[Task] = []\n        self._channel = channel_owner._channel\n        self._registered_listeners: List[Tuple[EventEmitter, str, Callable]] = []\n        self._logs: List[str] = []\n        self._wait_for_event_info_before(self._wait_id, event)\n\n    def _wait_for_event_info_before(self, wait_id: str, event: str) -> None:\n        self._channel.send_no_reply(\n            \"waitForEventInfo\",\n            None,\n            {\n                \"info\": {\n                    \"waitId\": wait_id,\n                    \"phase\": \"before\",\n                    \"event\": event,\n                }\n            },\n        )\n\n    def _wait_for_event_info_after(self, wait_id: str, error: Exception = None) -> None:\n        self._channel._connection.wrap_api_call_sync(\n            lambda: self._channel.send_no_reply(\n                \"waitForEventInfo\",\n                None,\n                {\n                    \"info\": {\n                        \"waitId\": wait_id,\n                        \"phase\": \"after\",\n                        **({\"error\": str(error)} if error else {}),\n                    },\n                },\n            ),\n            True,\n        )\n\n    def reject_on_event(\n        self,\n        emitter: EventEmitter,\n        event: str,\n        error: Union[Error, Callable[..., Error]],\n        predicate: Callable = None,\n    ) -> None:\n        def listener(event_data: Any = None) -> None:\n            if not predicate or predicate(event_data):\n                self._reject(error() if callable(error) else error)\n\n        emitter.on(event, listener)\n        self._registered_listeners.append((emitter, event, listener))\n\n    def reject_on_timeout(self, timeout: float, message: str) -> None:\n        if timeout == 0:\n            return\n\n        async def reject() -> None:\n            await asyncio.sleep(timeout / 1000)\n            self._reject(TimeoutError(message))\n\n        self._pending_tasks.append(self._loop.create_task(reject()))\n\n    def _cleanup(self) -> None:\n        for task in self._pending_tasks:\n            if not task.done():\n                task.cancel()\n        for listener in self._registered_listeners:\n            listener[0].remove_listener(listener[1], listener[2])\n\n    def _fulfill(self, result: Any) -> None:\n        self._cleanup()\n        if not self._result.done():\n            self._result.set_result(result)\n        self._wait_for_event_info_after(self._wait_id)\n\n    def _reject(self, exception: Exception) -> None:\n        self._cleanup()\n        if exception:\n            base_class = TimeoutError if isinstance(exception, TimeoutError) else Error\n            exception = base_class(str(exception) + format_log_recording(self._logs))\n        if not self._result.done():\n            self._result.set_exception(exception)\n        self._wait_for_event_info_after(self._wait_id, exception)\n\n    def wait_for_event(\n        self,\n        emitter: EventEmitter,\n        event: str,\n        predicate: Callable = None,\n    ) -> None:\n        def listener(event_data: Any = None) -> None:\n            if not predicate or predicate(event_data):\n                self._fulfill(event_data)\n\n        emitter.on(event, listener)\n        self._registered_listeners.append((emitter, event, listener))\n\n    def result(self) -> asyncio.Future:\n        return self._result\n\n    def log(self, message: str) -> None:\n        self._logs.append(message)\n        try:\n            self._channel._connection.wrap_api_call_sync(\n                lambda: self._channel.send_no_reply(\n                    \"waitForEventInfo\",\n                    None,\n                    {\n                        \"info\": {\n                            \"waitId\": self._wait_id,\n                            \"phase\": \"log\",\n                            \"message\": message,\n                        },\n                    },\n                ),\n                True,\n            )\n        except Exception:\n            pass\n\n\ndef throw_on_timeout(timeout: float, exception: Exception) -> asyncio.Task:\n    async def throw() -> None:\n        await asyncio.sleep(timeout / 1000)\n        raise exception\n\n    return asyncio.create_task(throw())\n\n\ndef format_log_recording(log: List[str]) -> str:\n    if not log:\n        return \"\"\n    header = \" logs \"\n    header_length = 60\n    left_length = math.floor((header_length - len(header)) / 2)\n    right_length = header_length - len(header) - left_length\n    new_line = \"\\n\"\n    return f\"{new_line}{'=' * left_length}{header}{'=' * right_length}{new_line}{new_line.join(log)}{new_line}{'=' * header_length}\"\n"
  },
  {
    "path": "playwright/_impl/_web_error.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom asyncio import AbstractEventLoop\nfrom typing import Any, Optional\n\nfrom playwright._impl._helper import Error\nfrom playwright._impl._page import Page\n\n\nclass WebError:\n    def __init__(\n        self,\n        loop: AbstractEventLoop,\n        dispatcher_fiber: Any,\n        page: Optional[Page],\n        error: Error,\n    ) -> None:\n        self._loop = loop\n        self._dispatcher_fiber = dispatcher_fiber\n        self._page = page\n        self._error = error\n\n    @property\n    def page(self) -> Optional[Page]:\n        return self._page\n\n    @property\n    def error(self) -> Error:\n        return self._error\n"
  },
  {
    "path": "playwright/_impl/_writable_stream.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport os\nfrom pathlib import Path\nfrom typing import Dict, Union\n\nfrom playwright._impl._connection import ChannelOwner\n\n# COPY_BUFSIZE is taken from shutil.py in the standard library\n_WINDOWS = os.name == \"nt\"\nCOPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024\n\n\nclass WritableStream(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n    async def copy(self, path: Union[str, Path]) -> None:\n        with open(path, \"rb\") as f:\n            while True:\n                data = f.read(COPY_BUFSIZE)\n                if not data:\n                    break\n                await self._channel.send(\n                    \"write\", None, {\"binary\": base64.b64encode(data).decode()}\n                )\n        await self._channel.send(\"close\", None)\n"
  },
  {
    "path": "playwright/async_api/__init__.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nPython package `playwright` is a Python library to automate Chromium,\nFirefox and WebKit with a single API. Playwright is built to enable cross-browser\nweb automation that is ever-green, capable, reliable and fast.\n\"\"\"\n\nfrom typing import Any, Optional, Union, overload\n\nimport playwright._impl._api_structures\nimport playwright._impl._errors\nimport playwright.async_api._generated\nfrom playwright._impl._assertions import (\n    APIResponseAssertions as APIResponseAssertionsImpl,\n)\nfrom playwright._impl._assertions import LocatorAssertions as LocatorAssertionsImpl\nfrom playwright._impl._assertions import PageAssertions as PageAssertionsImpl\nfrom playwright.async_api._context_manager import PlaywrightContextManager\nfrom playwright.async_api._generated import (\n    APIRequest,\n    APIRequestContext,\n    APIResponse,\n    APIResponseAssertions,\n    Browser,\n    BrowserContext,\n    BrowserType,\n    CDPSession,\n    ConsoleMessage,\n    Dialog,\n    Download,\n    ElementHandle,\n    FileChooser,\n    Frame,\n    FrameLocator,\n    JSHandle,\n    Keyboard,\n    Locator,\n    LocatorAssertions,\n    Mouse,\n    Page,\n    PageAssertions,\n    Playwright,\n    Request,\n    Response,\n    Route,\n    Selectors,\n    Touchscreen,\n    Video,\n    WebError,\n    WebSocket,\n    WebSocketRoute,\n    Worker,\n)\n\nChromiumBrowserContext = BrowserContext\n\nCookie = playwright._impl._api_structures.Cookie\nFilePayload = playwright._impl._api_structures.FilePayload\nFloatRect = playwright._impl._api_structures.FloatRect\nGeolocation = playwright._impl._api_structures.Geolocation\nHttpCredentials = playwright._impl._api_structures.HttpCredentials\nPdfMargins = playwright._impl._api_structures.PdfMargins\nPosition = playwright._impl._api_structures.Position\nProxySettings = playwright._impl._api_structures.ProxySettings\nResourceTiming = playwright._impl._api_structures.ResourceTiming\nSourceLocation = playwright._impl._api_structures.SourceLocation\nStorageState = playwright._impl._api_structures.StorageState\nStorageStateCookie = playwright._impl._api_structures.StorageStateCookie\nViewportSize = playwright._impl._api_structures.ViewportSize\n\nError = playwright._impl._errors.Error\nTimeoutError = playwright._impl._errors.TimeoutError\n\n\ndef async_playwright() -> PlaywrightContextManager:\n    return PlaywrightContextManager()\n\n\nclass Expect:\n    _unset: Any = object()\n\n    def __init__(self) -> None:\n        self._timeout: Optional[float] = None\n\n    def set_options(self, timeout: Optional[float] = _unset) -> None:\n        \"\"\"\n        This method sets global `expect()` options.\n\n        Args:\n            timeout (float): Timeout value in milliseconds. Default to 5000 milliseconds.\n\n        Returns:\n            None\n        \"\"\"\n        if timeout is not self._unset:\n            self._timeout = timeout\n\n    @overload\n    def __call__(\n        self, actual: Page, message: Optional[str] = None\n    ) -> PageAssertions: ...\n\n    @overload\n    def __call__(\n        self, actual: Locator, message: Optional[str] = None\n    ) -> LocatorAssertions: ...\n\n    @overload\n    def __call__(\n        self, actual: APIResponse, message: Optional[str] = None\n    ) -> APIResponseAssertions: ...\n\n    def __call__(\n        self, actual: Union[Page, Locator, APIResponse], message: Optional[str] = None\n    ) -> Union[PageAssertions, LocatorAssertions, APIResponseAssertions]:\n        if isinstance(actual, Page):\n            return PageAssertions(\n                PageAssertionsImpl(actual._impl_obj, self._timeout, message=message)\n            )\n        elif isinstance(actual, Locator):\n            return LocatorAssertions(\n                LocatorAssertionsImpl(actual._impl_obj, self._timeout, message=message)\n            )\n        elif isinstance(actual, APIResponse):\n            return APIResponseAssertions(\n                APIResponseAssertionsImpl(\n                    actual._impl_obj, self._timeout, message=message\n                )\n            )\n        raise ValueError(f\"Unsupported type: {type(actual)}\")\n\n\nexpect = Expect()\n\n\n__all__ = [\n    \"expect\",\n    \"async_playwright\",\n    \"APIRequest\",\n    \"APIRequestContext\",\n    \"APIResponse\",\n    \"Browser\",\n    \"BrowserContext\",\n    \"BrowserType\",\n    \"CDPSession\",\n    \"ChromiumBrowserContext\",\n    \"ConsoleMessage\",\n    \"Cookie\",\n    \"Dialog\",\n    \"Download\",\n    \"ElementHandle\",\n    \"Error\",\n    \"FileChooser\",\n    \"FilePayload\",\n    \"FloatRect\",\n    \"Frame\",\n    \"FrameLocator\",\n    \"Geolocation\",\n    \"HttpCredentials\",\n    \"JSHandle\",\n    \"Keyboard\",\n    \"Locator\",\n    \"Mouse\",\n    \"Page\",\n    \"PdfMargins\",\n    \"Position\",\n    \"Playwright\",\n    \"ProxySettings\",\n    \"Request\",\n    \"ResourceTiming\",\n    \"Response\",\n    \"Route\",\n    \"Selectors\",\n    \"SourceLocation\",\n    \"StorageState\",\n    \"StorageStateCookie\",\n    \"TimeoutError\",\n    \"Touchscreen\",\n    \"Video\",\n    \"ViewportSize\",\n    \"WebError\",\n    \"WebSocket\",\n    \"WebSocketRoute\",\n    \"Worker\",\n]\n"
  },
  {
    "path": "playwright/async_api/_context_manager.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Any\n\nfrom playwright._impl._connection import Connection\nfrom playwright._impl._object_factory import create_remote_object\nfrom playwright._impl._transport import PipeTransport\nfrom playwright.async_api._generated import Playwright as AsyncPlaywright\n\n\nclass PlaywrightContextManager:\n    def __init__(self) -> None:\n        self._connection: Connection\n        self._exit_was_called = False\n\n    async def __aenter__(self) -> AsyncPlaywright:\n        loop = asyncio.get_running_loop()\n        self._connection = Connection(\n            None,\n            create_remote_object,\n            PipeTransport(loop),\n            loop,\n        )\n        loop.create_task(self._connection.run())\n        playwright_future = self._connection.playwright_future\n\n        done, _ = await asyncio.wait(\n            {self._connection._transport.on_error_future, playwright_future},\n            return_when=asyncio.FIRST_COMPLETED,\n        )\n        if not playwright_future.done():\n            playwright_future.cancel()\n        playwright = AsyncPlaywright(next(iter(done)).result())\n        playwright.stop = self.__aexit__  # type: ignore\n        return playwright\n\n    async def start(self) -> AsyncPlaywright:\n        return await self.__aenter__()\n\n    async def __aexit__(self, *args: Any) -> None:\n        if self._exit_was_called:\n            return\n        self._exit_was_called = True\n        await self._connection.stop_async()\n"
  },
  {
    "path": "playwright/async_api/_generated.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport datetime\nimport pathlib\nimport typing\nfrom typing import Literal\n\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    Cookie,\n    FilePayload,\n    FloatRect,\n    Geolocation,\n    HttpCredentials,\n    NameValue,\n    PdfMargins,\n    Position,\n    ProxySettings,\n    RemoteAddr,\n    RequestSizes,\n    ResourceTiming,\n    SecurityDetails,\n    SetCookieParam,\n    SourceLocation,\n    StorageState,\n    TracingGroupLocation,\n    ViewportSize,\n)\nfrom playwright._impl._assertions import (\n    APIResponseAssertions as APIResponseAssertionsImpl,\n)\nfrom playwright._impl._assertions import LocatorAssertions as LocatorAssertionsImpl\nfrom playwright._impl._assertions import PageAssertions as PageAssertionsImpl\nfrom playwright._impl._async_base import (\n    AsyncBase,\n    AsyncContextManager,\n    AsyncEventContextManager,\n    mapping,\n)\nfrom playwright._impl._browser import Browser as BrowserImpl\nfrom playwright._impl._browser_context import BrowserContext as BrowserContextImpl\nfrom playwright._impl._browser_type import BrowserType as BrowserTypeImpl\nfrom playwright._impl._cdp_session import CDPSession as CDPSessionImpl\nfrom playwright._impl._clock import Clock as ClockImpl\nfrom playwright._impl._console_message import ConsoleMessage as ConsoleMessageImpl\nfrom playwright._impl._dialog import Dialog as DialogImpl\nfrom playwright._impl._download import Download as DownloadImpl\nfrom playwright._impl._element_handle import ElementHandle as ElementHandleImpl\nfrom playwright._impl._errors import Error\nfrom playwright._impl._fetch import APIRequest as APIRequestImpl\nfrom playwright._impl._fetch import APIRequestContext as APIRequestContextImpl\nfrom playwright._impl._fetch import APIResponse as APIResponseImpl\nfrom playwright._impl._file_chooser import FileChooser as FileChooserImpl\nfrom playwright._impl._frame import Frame as FrameImpl\nfrom playwright._impl._input import Keyboard as KeyboardImpl\nfrom playwright._impl._input import Mouse as MouseImpl\nfrom playwright._impl._input import Touchscreen as TouchscreenImpl\nfrom playwright._impl._js_handle import JSHandle as JSHandleImpl\nfrom playwright._impl._locator import FrameLocator as FrameLocatorImpl\nfrom playwright._impl._locator import Locator as LocatorImpl\nfrom playwright._impl._network import Request as RequestImpl\nfrom playwright._impl._network import Response as ResponseImpl\nfrom playwright._impl._network import Route as RouteImpl\nfrom playwright._impl._network import WebSocket as WebSocketImpl\nfrom playwright._impl._network import WebSocketRoute as WebSocketRouteImpl\nfrom playwright._impl._page import Page as PageImpl\nfrom playwright._impl._page import Worker as WorkerImpl\nfrom playwright._impl._playwright import Playwright as PlaywrightImpl\nfrom playwright._impl._selectors import Selectors as SelectorsImpl\nfrom playwright._impl._tracing import Tracing as TracingImpl\nfrom playwright._impl._video import Video as VideoImpl\nfrom playwright._impl._web_error import WebError as WebErrorImpl\n\n\nclass Request(AsyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"Request.url\n\n        URL of the request.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def resource_type(self) -> str:\n        \"\"\"Request.resource_type\n\n        Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the\n        following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`,\n        `eventsource`, `websocket`, `manifest`, `other`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.resource_type)\n\n    @property\n    def service_worker(self) -> typing.Optional[\"Worker\"]:\n        \"\"\"Request.service_worker\n\n        The Service `Worker` that is performing the request.\n\n        **Details**\n\n        This method is Chromium only. It's safe to call when using other browsers, but it will always be `null`.\n\n        Requests originated in a Service Worker do not have a `request.frame()` available.\n\n        Returns\n        -------\n        Union[Worker, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.service_worker)\n\n    @property\n    def method(self) -> str:\n        \"\"\"Request.method\n\n        Request's method (GET, POST, etc.)\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.method)\n\n    @property\n    def post_data(self) -> typing.Optional[str]:\n        \"\"\"Request.post_data\n\n        Request's post body, if any.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data)\n\n    @property\n    def post_data_json(self) -> typing.Optional[typing.Any]:\n        \"\"\"Request.post_data_json\n\n        Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any.\n\n        When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned.\n        Otherwise it will be parsed as JSON.\n\n        Returns\n        -------\n        Union[Any, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data_json)\n\n    @property\n    def post_data_buffer(self) -> typing.Optional[bytes]:\n        \"\"\"Request.post_data_buffer\n\n        Request's post body in a binary form, if any.\n\n        Returns\n        -------\n        Union[bytes, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data_buffer)\n\n    @property\n    def frame(self) -> \"Frame\":\n        \"\"\"Request.frame\n\n        Returns the `Frame` that initiated this request.\n\n        **Usage**\n\n        ```py\n        frame_url = request.frame.url\n        ```\n\n        **Details**\n\n        Note that in some cases the frame is not available, and this method will throw.\n        - When request originates in the Service Worker. You can use `request.serviceWorker()` to check that.\n        - When navigation request is issued before the corresponding frame is created. You can use\n          `request.is_navigation_request()` to check that.\n\n        Here is an example that handles all the cases:\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.frame)\n\n    @property\n    def redirected_from(self) -> typing.Optional[\"Request\"]:\n        \"\"\"Request.redirected_from\n\n        Request that was redirected by the server to this one, if any.\n\n        When the server responds with a redirect, Playwright creates a new `Request` object. The two requests are connected\n        by `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to\n        construct the whole redirect chain by repeatedly calling `redirectedFrom()`.\n\n        **Usage**\n\n        For example, if the website `http://example.com` redirects to `https://example.com`:\n\n        ```py\n        response = await page.goto(\\\"http://example.com\\\")\n        print(response.request.redirected_from.url) # \\\"http://example.com\\\"\n        ```\n\n        If the website `https://google.com` has no redirects:\n\n        ```py\n        response = await page.goto(\\\"https://google.com\\\")\n        print(response.request.redirected_from) # None\n        ```\n\n        Returns\n        -------\n        Union[Request, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.redirected_from)\n\n    @property\n    def redirected_to(self) -> typing.Optional[\"Request\"]:\n        \"\"\"Request.redirected_to\n\n        New request issued by the browser if the server responded with redirect.\n\n        **Usage**\n\n        This method is the opposite of `request.redirected_from()`:\n\n        ```py\n        assert request.redirected_from.redirected_to == request\n        ```\n\n        Returns\n        -------\n        Union[Request, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.redirected_to)\n\n    @property\n    def failure(self) -> typing.Optional[str]:\n        \"\"\"Request.failure\n\n        The method returns `null` unless this request has failed, as reported by `requestfailed` event.\n\n        **Usage**\n\n        Example of logging of all the failed requests:\n\n        ```py\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure))\n        ```\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.failure)\n\n    @property\n    def timing(self) -> ResourceTiming:\n        \"\"\"Request.timing\n\n        Returns resource timing information for given request. Most of the timing values become available upon the\n        response, `responseEnd` becomes available when request finishes. Find more information at\n        [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).\n\n        **Usage**\n\n        ```py\n        async with page.expect_event(\\\"requestfinished\\\") as request_info:\n            await page.goto(\\\"http://example.com\\\")\n        request = await request_info.value\n        print(request.timing)\n        ```\n\n        Returns\n        -------\n        {startTime: float, domainLookupStart: float, domainLookupEnd: float, connectStart: float, secureConnectionStart: float, connectEnd: float, requestStart: float, responseStart: float, responseEnd: float}\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.timing)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"Request.headers\n\n        An object with the request HTTP headers. The header names are lower-cased. Note that this method does not return\n        security-related headers, including cookie-related ones. You can use `request.all_headers()` for complete\n        list of headers that include `cookie` information.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    async def sizes(self) -> RequestSizes:\n        \"\"\"Request.sizes\n\n        Returns resource size information for given request.\n\n        Returns\n        -------\n        {requestBodySize: int, requestHeadersSize: int, responseBodySize: int, responseHeadersSize: int}\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.sizes())\n\n    async def response(self) -> typing.Optional[\"Response\"]:\n        \"\"\"Request.response\n\n        Returns the matching `Response` object, or `null` if the response was not received due to error.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.response())\n\n    def is_navigation_request(self) -> bool:\n        \"\"\"Request.is_navigation_request\n\n        Whether this request is driving frame's navigation.\n\n        Some navigation requests are issued before the corresponding frame is created, and therefore do not have\n        `request.frame()` available.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_navigation_request())\n\n    async def all_headers(self) -> typing.Dict[str, str]:\n        \"\"\"Request.all_headers\n\n        An object with all the request HTTP headers associated with this request. The header names are lower-cased.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.all_headers())\n\n    async def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"Request.headers_array\n\n        An array with all the request HTTP headers associated with this request. Unlike `request.all_headers()`,\n        header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple\n        times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.headers_array())\n\n    async def header_value(self, name: str) -> typing.Optional[str]:\n        \"\"\"Request.header_value\n\n        Returns the value of the header matching the name. The name is case-insensitive.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.header_value(name=name))\n\n\nmapping.register(RequestImpl, Request)\n\n\nclass Response(AsyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"Response.url\n\n        Contains the URL of the response.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Response.ok\n\n        Contains a boolean stating whether the response was successful (status in the range 200-299) or not.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.ok)\n\n    @property\n    def status(self) -> int:\n        \"\"\"Response.status\n\n        Contains the status code of the response (e.g., 200 for a success).\n\n        Returns\n        -------\n        int\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status)\n\n    @property\n    def status_text(self) -> str:\n        \"\"\"Response.status_text\n\n        Contains the status text of the response (e.g. usually an \\\"OK\\\" for a success).\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status_text)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"Response.headers\n\n        An object with the response HTTP headers. The header names are lower-cased. Note that this method does not return\n        security-related headers, including cookie-related ones. You can use `response.all_headers()` for complete\n        list of headers that include `cookie` information.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    @property\n    def from_service_worker(self) -> bool:\n        \"\"\"Response.from_service_worker\n\n        Indicates whether this Response was fulfilled by a Service Worker's Fetch Handler (i.e. via\n        [FetchEvent.respondWith](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith)).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.from_service_worker)\n\n    @property\n    def request(self) -> \"Request\":\n        \"\"\"Response.request\n\n        Returns the matching `Request` object.\n\n        Returns\n        -------\n        Request\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def frame(self) -> \"Frame\":\n        \"\"\"Response.frame\n\n        Returns the `Frame` that initiated this response.\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.frame)\n\n    async def all_headers(self) -> typing.Dict[str, str]:\n        \"\"\"Response.all_headers\n\n        An object with all the response HTTP headers associated with this response.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.all_headers())\n\n    async def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"Response.headers_array\n\n        An array with all the request HTTP headers associated with this response. Unlike `response.all_headers()`,\n        header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple\n        times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.headers_array())\n\n    async def header_value(self, name: str) -> typing.Optional[str]:\n        \"\"\"Response.header_value\n\n        Returns the value of the header matching the name. The name is case-insensitive. If multiple headers have the same\n        name (except `set-cookie`), they are returned as a list separated by `, `. For `set-cookie`, the `\\\\n` separator is\n        used. If no headers are found, `null` is returned.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.header_value(name=name))\n\n    async def header_values(self, name: str) -> typing.List[str]:\n        \"\"\"Response.header_values\n\n        Returns all values of the headers matching the name, for example `set-cookie`. The name is case-insensitive.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.header_values(name=name))\n\n    async def server_addr(self) -> typing.Optional[RemoteAddr]:\n        \"\"\"Response.server_addr\n\n        Returns the IP address and port of the server.\n\n        Returns\n        -------\n        Union[{ipAddress: str, port: int}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.server_addr())\n\n    async def security_details(self) -> typing.Optional[SecurityDetails]:\n        \"\"\"Response.security_details\n\n        Returns SSL and other security information.\n\n        Returns\n        -------\n        Union[{issuer: Union[str, None], protocol: Union[str, None], subjectName: Union[str, None], validFrom: Union[float, None], validTo: Union[float, None]}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.security_details())\n\n    async def finished(self) -> None:\n        \"\"\"Response.finished\n\n        Waits for this response to finish, returns always `null`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.finished())\n\n    async def body(self) -> bytes:\n        \"\"\"Response.body\n\n        Returns the buffer with response body.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.body())\n\n    async def text(self) -> str:\n        \"\"\"Response.text\n\n        Returns the text representation of response body.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.text())\n\n    async def json(self) -> typing.Any:\n        \"\"\"Response.json\n\n        Returns the JSON representation of response body.\n\n        This method will throw if the response body is not parsable via `JSON.parse`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.json())\n\n\nmapping.register(ResponseImpl, Response)\n\n\nclass Route(AsyncBase):\n\n    @property\n    def request(self) -> \"Request\":\n        \"\"\"Route.request\n\n        A request to be routed.\n\n        Returns\n        -------\n        Request\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    async def abort(self, error_code: typing.Optional[str] = None) -> None:\n        \"\"\"Route.abort\n\n        Aborts the route's request.\n\n        Parameters\n        ----------\n        error_code : Union[str, None]\n            Optional error code. Defaults to `failed`, could be one of the following:\n            - `'aborted'` - An operation was aborted (due to user action)\n            - `'accessdenied'` - Permission to access a resource, other than the network, was denied\n            - `'addressunreachable'` - The IP address is unreachable. This usually means that there is no route to the\n              specified host or network.\n            - `'blockedbyclient'` - The client chose to block the request.\n            - `'blockedbyresponse'` - The request failed because the response was delivered along with requirements which are\n              not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance).\n            - `'connectionaborted'` - A connection timed out as a result of not receiving an ACK for data sent.\n            - `'connectionclosed'` - A connection was closed (corresponding to a TCP FIN).\n            - `'connectionfailed'` - A connection attempt failed.\n            - `'connectionrefused'` - A connection attempt was refused.\n            - `'connectionreset'` - A connection was reset (corresponding to a TCP RST).\n            - `'internetdisconnected'` - The Internet connection has been lost.\n            - `'namenotresolved'` - The host name could not be resolved.\n            - `'timedout'` - An operation timed out.\n            - `'failed'` - A generic failure occurred.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.abort(errorCode=error_code))\n\n    async def fulfill(\n        self,\n        *,\n        status: typing.Optional[int] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        body: typing.Optional[typing.Union[str, bytes]] = None,\n        json: typing.Optional[typing.Any] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content_type: typing.Optional[str] = None,\n        response: typing.Optional[\"APIResponse\"] = None,\n    ) -> None:\n        \"\"\"Route.fulfill\n\n        Fulfills route's request with given response.\n\n        **Usage**\n\n        An example of fulfilling all requests with 404 responses:\n\n        ```py\n        await page.route(\\\"**/*\\\", lambda route: route.fulfill(\n            status=404,\n            content_type=\\\"text/plain\\\",\n            body=\\\"not found!\\\"))\n        ```\n\n        An example of serving static file:\n\n        ```py\n        await page.route(\\\"**/xhr_endpoint\\\", lambda route: route.fulfill(path=\\\"mock_data.json\\\"))\n        ```\n\n        Parameters\n        ----------\n        status : Union[int, None]\n            Response status code, defaults to `200`.\n        headers : Union[Dict[str, str], None]\n            Response headers. Header values will be converted to a string.\n        body : Union[bytes, str, None]\n            Response body.\n        json : Union[Any, None]\n            JSON response. This method will set the content type to `application/json` if not set.\n        path : Union[pathlib.Path, str, None]\n            File path to respond with. The content type will be inferred from file extension. If `path` is a relative path,\n            then it is resolved relative to the current working directory.\n        content_type : Union[str, None]\n            If set, equals to setting `Content-Type` response header.\n        response : Union[APIResponse, None]\n            `APIResponse` to fulfill route's request with. Individual fields of the response (such as headers) can be\n            overridden using fulfill options.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fulfill(\n                status=status,\n                headers=mapping.to_impl(headers),\n                body=body,\n                json=mapping.to_impl(json),\n                path=path,\n                contentType=content_type,\n                response=response._impl_obj if response else None,\n            )\n        )\n\n    async def fetch(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> \"APIResponse\":\n        \"\"\"Route.fetch\n\n        Performs the request and fetches result without fulfilling it, so that the response could be modified and then\n        fulfilled.\n\n        **Usage**\n\n        ```py\n        async def handle(route):\n            response = await route.fetch()\n            json = await response.json()\n            json[\\\"message\\\"][\\\"big_red_dog\\\"] = []\n            await route.fulfill(response=response, json=json)\n\n        await page.route(\\\"https://dog.ceo/api/breeds/list/all\\\", handle)\n        ```\n\n        **Details**\n\n        Note that `headers` option will apply to the fetched request as well as any redirects initiated by it. If you want\n        to only apply `headers` to the original request, but not to redirects, look into `route.continue_()`\n        instead.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.fetch(\n                url=url,\n                method=method,\n                headers=mapping.to_impl(headers),\n                postData=mapping.to_impl(post_data),\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n                timeout=timeout,\n            )\n        )\n\n    async def fallback(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n    ) -> None:\n        \"\"\"Route.fallback\n\n        Continues route's request with optional overrides. The method is similar to `route.continue_()` with the\n        difference that other matching handlers will be invoked before sending the request.\n\n        **Usage**\n\n        When several routes match the given pattern, they run in the order opposite to their registration. That way the\n        last registered route can always override all the previous ones. In the example below, request will be handled by\n        the bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first\n        registered route.\n\n        ```py\n        await page.route(\\\"**/*\\\", lambda route: route.abort())  # Runs last.\n        await page.route(\\\"**/*\\\", lambda route: route.fallback())  # Runs second.\n        await page.route(\\\"**/*\\\", lambda route: route.fallback())  # Runs first.\n        ```\n\n        Registering multiple routes is useful when you want separate handlers to handle different kinds of requests, for\n        example API calls vs page resources or GET requests vs POST requests as in the example below.\n\n        ```py\n        # Handle GET requests.\n        async def handle_get(route):\n            if route.request.method != \\\"GET\\\":\n                await route.fallback()\n                return\n          # Handling GET only.\n          # ...\n\n        # Handle POST requests.\n        async def handle_post(route):\n            if route.request.method != \\\"POST\\\":\n                await route.fallback()\n                return\n          # Handling POST only.\n          # ...\n\n        await page.route(\\\"**/*\\\", handle_get)\n        await page.route(\\\"**/*\\\", handle_post)\n        ```\n\n        One can also modify request while falling back to the subsequent handler, that way intermediate route handler can\n        modify url, method, headers and postData of the request.\n\n        ```py\n        async def handle(route, request):\n            # override headers\n            headers = {\n                **request.headers,\n                \\\"foo\\\": \\\"foo-value\\\", # set \\\"foo\\\" header\n                \\\"bar\\\": None # remove \\\"bar\\\" header\n            }\n            await route.fallback(headers=headers)\n\n        await page.route(\\\"**/*\\\", handle)\n        ```\n\n        Use `route.continue_()` to immediately send the request to the network, other matching handlers won't be\n        invoked in that case.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one. Changing the URL won't affect the\n            route matching, all the routes are matched using the original request URL.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            If set changes the post data of request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fallback(\n                url=url,\n                method=method,\n                headers=mapping.to_impl(headers),\n                postData=mapping.to_impl(post_data),\n            )\n        )\n\n    async def continue_(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n    ) -> None:\n        \"\"\"Route.continue_\n\n        Sends route's request to the network with optional overrides.\n\n        **Usage**\n\n        ```py\n        async def handle(route, request):\n            # override headers\n            headers = {\n                **request.headers,\n                \\\"foo\\\": \\\"foo-value\\\", # set \\\"foo\\\" header\n                \\\"bar\\\": None # remove \\\"bar\\\" header\n            }\n            await route.continue_(headers=headers)\n\n        await page.route(\\\"**/*\\\", handle)\n        ```\n\n        **Details**\n\n        The `headers` option applies to both the routed request and any redirects it initiates. However, `url`, `method`,\n        and `postData` only apply to the original request and are not carried over to redirected requests.\n\n        `route.continue_()` will immediately send the request to the network, other matching handlers won't be\n        invoked. Use `route.fallback()` If you want next matching handler in the chain to be invoked.\n\n        **NOTE** Some request headers are **forbidden** and cannot be overridden (for example, `Cookie`, `Host`,\n        `Content-Length` and others, see\n        [this MDN page](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header) for full list). If an\n        override is provided for a forbidden header, it will be ignored and the original request header will be used.\n\n        To set custom cookies, use `browser_context.add_cookies()`.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            If set changes the post data of request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.continue_(\n                url=url,\n                method=method,\n                headers=mapping.to_impl(headers),\n                postData=mapping.to_impl(post_data),\n            )\n        )\n\n\nmapping.register(RouteImpl, Route)\n\n\nclass WebSocket(AsyncBase):\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"WebSocket\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket closes.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framereceived\"],\n        f: typing.Callable[\n            [\"typing.Union[bytes, str]\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket receives a frame.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framesent\"],\n        f: typing.Callable[\n            [\"typing.Union[bytes, str]\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket sends a frame.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"socketerror\"],\n        f: typing.Callable[[\"str\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket has an error.\"\"\"\n\n    def on(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"WebSocket\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket closes.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framereceived\"],\n        f: typing.Callable[\n            [\"typing.Union[bytes, str]\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket receives a frame.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framesent\"],\n        f: typing.Callable[\n            [\"typing.Union[bytes, str]\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket sends a frame.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"socketerror\"],\n        f: typing.Callable[[\"str\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket has an error.\"\"\"\n\n    def once(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def url(self) -> str:\n        \"\"\"WebSocket.url\n\n        Contains the URL of the WebSocket.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager:\n        \"\"\"WebSocket.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the webSocket is closed before the event is fired. Returns the event data value.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one would pass into `webSocket.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    async def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"WebSocket.wait_for_event\n\n        **NOTE** In most cases, you should use `web_socket.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the socket is closed before the\n        `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            )\n        )\n\n    def is_closed(self) -> bool:\n        \"\"\"WebSocket.is_closed\n\n        Indicates that the web socket has been closed.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_closed())\n\n\nmapping.register(WebSocketImpl, WebSocket)\n\n\nclass WebSocketRoute(AsyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"WebSocketRoute.url\n\n        URL of the WebSocket created in the page.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    async def close(\n        self, *, code: typing.Optional[int] = None, reason: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"WebSocketRoute.close\n\n        Closes one side of the WebSocket connection.\n\n        Parameters\n        ----------\n        code : Union[int, None]\n            Optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code).\n        reason : Union[str, None]\n            Optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.close(code=code, reason=reason)\n        )\n\n    def connect_to_server(self) -> \"WebSocketRoute\":\n        \"\"\"WebSocketRoute.connect_to_server\n\n        By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This\n        method connects to the actual WebSocket server, and returns the server-side `WebSocketRoute` instance, giving the\n        ability to send and receive messages from the server.\n\n        Once connected to the server:\n        - Messages received from the server will be **automatically forwarded** to the WebSocket in the page, unless\n          `web_socket_route.on_message()` is called on the server-side `WebSocketRoute`.\n        - Messages sent by the [`WebSocket.send()`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send) call\n          in the page will be **automatically forwarded** to the server, unless `web_socket_route.on_message()` is\n          called on the original `WebSocketRoute`.\n\n        See examples at the top for more details.\n\n        Returns\n        -------\n        WebSocketRoute\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.connect_to_server())\n\n    def send(self, message: typing.Union[str, bytes]) -> None:\n        \"\"\"WebSocketRoute.send\n\n        Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called\n        on the result of `web_socket_route.connect_to_server()`, sends the message to the server. See examples at the\n        top for more details.\n\n        Parameters\n        ----------\n        message : Union[bytes, str]\n            Message to send.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.send(message=message))\n\n    def on_message(\n        self, handler: typing.Callable[[typing.Union[str, bytes]], typing.Any]\n    ) -> None:\n        \"\"\"WebSocketRoute.on_message\n\n        This method allows to handle messages that are sent by the WebSocket, either from the page or from the server.\n\n        When called on the original WebSocket route, this method handles messages sent from the page. You can handle this\n        messages by responding to them with `web_socket_route.send()`, forwarding them to the server-side connection\n        returned by `web_socket_route.connect_to_server()` or do something else.\n\n        Once this method is called, messages are not automatically forwarded to the server or to the page - you should do\n        that manually by calling `web_socket_route.send()`. See examples at the top for more details.\n\n        Calling this method again will override the handler with a new one.\n\n        Parameters\n        ----------\n        handler : Callable[[Union[bytes, str]], Any]\n            Function that will handle messages.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.on_message(handler=self._wrap_handler(handler))\n        )\n\n    def on_close(\n        self,\n        handler: typing.Callable[\n            [typing.Optional[int], typing.Optional[str]], typing.Any\n        ],\n    ) -> None:\n        \"\"\"WebSocketRoute.on_close\n\n        Allows to handle [`WebSocket.close`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close).\n\n        By default, closing one side of the connection, either in the page or on the server, will close the other side.\n        However, when `web_socket_route.on_close()` handler is set up, the default forwarding of closure is disabled,\n        and handler should take care of it.\n\n        Parameters\n        ----------\n        handler : Callable[[Union[int, None], Union[str, None]], Any]\n            Function that will handle WebSocket closure. Received an optional\n            [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional\n            [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.on_close(handler=self._wrap_handler(handler))\n        )\n\n\nmapping.register(WebSocketRouteImpl, WebSocketRoute)\n\n\nclass Keyboard(AsyncBase):\n\n    async def down(self, key: str) -> None:\n        \"\"\"Keyboard.down\n\n        Dispatches a `keydown` event.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that\n        modifier active. To release the modifier key, use `keyboard.up()`.\n\n        After the key is pressed once, subsequent calls to `keyboard.down()` will have\n        [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key,\n        use `keyboard.up()`.\n\n        **NOTE** Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.down(key=key))\n\n    async def up(self, key: str) -> None:\n        \"\"\"Keyboard.up\n\n        Dispatches a `keyup` event.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.up(key=key))\n\n    async def insert_text(self, text: str) -> None:\n        \"\"\"Keyboard.insert_text\n\n        Dispatches only `input` event, does not emit the `keydown`, `keyup` or `keypress` events.\n\n        **Usage**\n\n        ```py\n        await page.keyboard.insert_text(\\\"嗨\\\")\n        ```\n\n        **NOTE** Modifier keys DO NOT effect `keyboard.insertText`. Holding down `Shift` will not type the text in upper\n        case.\n\n        Parameters\n        ----------\n        text : str\n            Sets input to the specified text value.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.insert_text(text=text))\n\n    async def type(self, text: str, *, delay: typing.Optional[float] = None) -> None:\n        \"\"\"Keyboard.type\n\n        **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if\n        there is special keyboard handling on the page - in this case use `locator.press_sequentially()`.\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        ```py\n        await page.keyboard.type(\\\"Hello\\\") # types instantly\n        await page.keyboard.type(\\\"World\\\", delay=100) # types slower, like a user\n        ```\n\n        **NOTE** Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case.\n\n        **NOTE** For characters that are not on a US keyboard, only an `input` event will be sent.\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.type(text=text, delay=delay)\n        )\n\n    async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None:\n        \"\"\"Keyboard.press\n\n        **NOTE** In most cases, you should use `locator.press()` instead.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        **Usage**\n\n        ```py\n        page = await browser.new_page()\n        await page.goto(\\\"https://keycode.info\\\")\n        await page.keyboard.press(\\\"a\\\")\n        await page.screenshot(path=\\\"a.png\\\")\n        await page.keyboard.press(\\\"ArrowLeft\\\")\n        await page.screenshot(path=\\\"arrow_left.png\\\")\n        await page.keyboard.press(\\\"Shift+O\\\")\n        await page.screenshot(path=\\\"o.png\\\")\n        await browser.close()\n        ```\n\n        Shortcut for `keyboard.down()` and `keyboard.up()`.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.press(key=key, delay=delay))\n\n\nmapping.register(KeyboardImpl, Keyboard)\n\n\nclass Mouse(AsyncBase):\n\n    async def move(\n        self, x: float, y: float, *, steps: typing.Optional[int] = None\n    ) -> None:\n        \"\"\"Mouse.move\n\n        Dispatches a `mousemove` event.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.move(x=x, y=y, steps=steps))\n\n    async def down(\n        self,\n        *,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.down\n\n        Dispatches a `mousedown` event.\n\n        Parameters\n        ----------\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.down(button=button, clickCount=click_count)\n        )\n\n    async def up(\n        self,\n        *,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.up\n\n        Dispatches a `mouseup` event.\n\n        Parameters\n        ----------\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.up(button=button, clickCount=click_count)\n        )\n\n    async def click(\n        self,\n        x: float,\n        y: float,\n        *,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.click\n\n        Shortcut for `mouse.move()`, `mouse.down()`, `mouse.up()`.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.click(\n                x=x, y=y, delay=delay, button=button, clickCount=click_count\n            )\n        )\n\n    async def dblclick(\n        self,\n        x: float,\n        y: float,\n        *,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n    ) -> None:\n        \"\"\"Mouse.dblclick\n\n        Shortcut for `mouse.move()`, `mouse.down()`, `mouse.up()`, `mouse.down()` and\n        `mouse.up()`.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dblclick(x=x, y=y, delay=delay, button=button)\n        )\n\n    async def wheel(self, delta_x: float, delta_y: float) -> None:\n        \"\"\"Mouse.wheel\n\n        Dispatches a `wheel` event. This method is usually used to manually scroll the page. See\n        [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        **NOTE** Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling\n        to finish before returning.\n\n        Parameters\n        ----------\n        delta_x : float\n            Pixels to scroll horizontally.\n        delta_y : float\n            Pixels to scroll vertically.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wheel(deltaX=delta_x, deltaY=delta_y)\n        )\n\n\nmapping.register(MouseImpl, Mouse)\n\n\nclass Touchscreen(AsyncBase):\n\n    async def tap(self, x: float, y: float) -> None:\n        \"\"\"Touchscreen.tap\n\n        Dispatches a `touchstart` and `touchend` event with a single touch at the position (`x`,`y`).\n\n        **NOTE** `page.tap()` the method will throw if `hasTouch` option of the browser context is false.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.tap(x=x, y=y))\n\n\nmapping.register(TouchscreenImpl, Touchscreen)\n\n\nclass JSHandle(AsyncBase):\n\n    async def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"JSHandle.evaluate\n\n        Returns the return value of `expression`.\n\n        This method passes this handle as the first argument to `expression`.\n\n        If `expression` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its\n        value.\n\n        **Usage**\n\n        ```py\n        tweet_handle = await page.query_selector(\\\".tweet .retweets\\\")\n        assert await tweet_handle.evaluate(\\\"node => node.innerText\\\") == \\\"10 retweets\\\"\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"JSHandle.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        This method passes this handle as the first argument to `expression`.\n\n        The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle`\n        returns `JSHandle`.\n\n        If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would\n        wait for the promise to resolve and return its value.\n\n        See `page.evaluate_handle()` for more details.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.evaluate_handle(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def get_property(self, property_name: str) -> \"JSHandle\":\n        \"\"\"JSHandle.get_property\n\n        Fetches a single property from the referenced object.\n\n        Parameters\n        ----------\n        property_name : str\n            property to get\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.get_property(propertyName=property_name)\n        )\n\n    async def get_properties(self) -> typing.Dict[str, \"JSHandle\"]:\n        \"\"\"JSHandle.get_properties\n\n        The method returns a map with **own property names** as keys and JSHandle instances for the property values.\n\n        **Usage**\n\n        ```py\n        handle = await page.evaluate_handle(\\\"({ window, document })\\\")\n        properties = await handle.get_properties()\n        window_handle = properties.get(\\\"window\\\")\n        document_handle = properties.get(\\\"document\\\")\n        await handle.dispose()\n        ```\n\n        Returns\n        -------\n        Dict[str, JSHandle]\n        \"\"\"\n\n        return mapping.from_impl_dict(await self._impl_obj.get_properties())\n\n    def as_element(self) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"JSHandle.as_element\n\n        Returns either `null` or the object handle itself, if the object handle is an instance of `ElementHandle`.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._impl_obj.as_element())\n\n    async def dispose(self) -> None:\n        \"\"\"JSHandle.dispose\n\n        The `jsHandle.dispose` method stops referencing the element handle.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.dispose())\n\n    async def json_value(self) -> typing.Any:\n        \"\"\"JSHandle.json_value\n\n        Returns a JSON representation of the object. If the object has a `toJSON` function, it **will not be called**.\n\n        **NOTE** The method will return an empty JSON object if the referenced object is not stringifiable. It will throw\n        an error if the object has circular references.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.json_value())\n\n\nmapping.register(JSHandleImpl, JSHandle)\n\n\nclass ElementHandle(JSHandle):\n\n    def as_element(self) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.as_element\n\n        Returns either `null` or the object handle itself, if the object handle is an instance of `ElementHandle`.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._impl_obj.as_element())\n\n    async def owner_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"ElementHandle.owner_frame\n\n        Returns the frame containing the given element.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.owner_frame())\n\n    async def content_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"ElementHandle.content_frame\n\n        Returns the content frame for element handles referencing iframe nodes, or `null` otherwise\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.content_frame())\n\n    async def get_attribute(self, name: str) -> typing.Optional[str]:\n        \"\"\"ElementHandle.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name to get the value for.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.get_attribute(name=name))\n\n    async def text_content(self) -> typing.Optional[str]:\n        \"\"\"ElementHandle.text_content\n\n        Returns the `node.textContent`.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.text_content())\n\n    async def inner_text(self) -> str:\n        \"\"\"ElementHandle.inner_text\n\n        Returns the `element.innerText`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.inner_text())\n\n    async def inner_html(self) -> str:\n        \"\"\"ElementHandle.inner_html\n\n        Returns the `element.innerHTML`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.inner_html())\n\n    async def is_checked(self) -> bool:\n        \"\"\"ElementHandle.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_checked())\n\n    async def is_disabled(self) -> bool:\n        \"\"\"ElementHandle.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_disabled())\n\n    async def is_editable(self) -> bool:\n        \"\"\"ElementHandle.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_editable())\n\n    async def is_enabled(self) -> bool:\n        \"\"\"ElementHandle.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_enabled())\n\n    async def is_hidden(self) -> bool:\n        \"\"\"ElementHandle.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_hidden())\n\n    async def is_visible(self) -> bool:\n        \"\"\"ElementHandle.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_visible())\n\n    async def dispatch_event(\n        self, type: str, event_init: typing.Optional[typing.Dict] = None\n    ) -> None:\n        \"\"\"ElementHandle.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        await element_handle.dispatch_event(\\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = await page.evaluate_handle(\\\"new DataTransfer()\\\")\n        await element_handle.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", {\\\"dataTransfer\\\": data_transfer})\n        ```\n\n        Parameters\n        ----------\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dispatch_event(\n                type=type, eventInit=mapping.to_impl(event_init)\n            )\n        )\n\n    async def scroll_into_view_if_needed(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"ElementHandle.scroll_into_view_if_needed\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless\n        it is completely visible as defined by\n        [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.\n\n        Throws when `elementHandle` does not point to an element\n        [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.\n\n        See [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.scroll_into_view_if_needed(timeout=timeout)\n        )\n\n    async def hover(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.hover\n\n        This method hovers over the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.hover(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n                trial=trial,\n            )\n        )\n\n    async def click(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"ElementHandle.click\n\n        This method clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.click(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                clickCount=click_count,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def dblclick(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"ElementHandle.dblclick\n\n        This method double clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dblclick(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def select_option(\n        self,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"ElementHandle.select_option\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits until all specified options are present in\n        the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        await handle.select_option(\\\"blue\\\")\n        # single selection matching the label\n        await handle.select_option(label=\\\"blue\\\")\n        # multiple selection\n        await handle.select_option(value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_option(\n                value=mapping.to_impl(value),\n                index=mapping.to_impl(index),\n                label=mapping.to_impl(label),\n                element=mapping.to_impl(element),\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n            )\n        )\n\n    async def tap(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.tap\n\n        This method taps the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.tap(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def fill(\n        self,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.fill\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, fills it and triggers an\n        `input` event after filling. Note that you can pass an empty string to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        value : str\n            Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fill(\n                value=value, timeout=timeout, noWaitAfter=no_wait_after, force=force\n            )\n        )\n\n    async def select_text(\n        self,\n        *,\n        force: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"ElementHandle.select_text\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then focuses the element and selects all its\n        text content.\n\n        If the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), focuses and selects text in\n        the control instead.\n\n        Parameters\n        ----------\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_text(force=force, timeout=timeout)\n        )\n\n    async def input_value(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"ElementHandle.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.input_value(timeout=timeout)\n        )\n\n    async def set_input_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs\n        with a `[webkitdirectory]` attribute, only a single directory path is supported.\n\n        This method expects `ElementHandle` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_input_files(\n                files=mapping.to_impl(files), timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def focus(self) -> None:\n        \"\"\"ElementHandle.focus\n\n        Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.focus())\n\n    async def type(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.type\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `element_handle.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.type(\n                text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def press(\n        self,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.press\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.press(\n                key=key, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def set_checked(\n        self,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.set_checked\n\n        This method checks or unchecks an element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_checked(\n                checked=checked,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def check(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.check\n\n        This method checks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.check(\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def uncheck(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.uncheck\n\n        This method checks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.uncheck(\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def bounding_box(self) -> typing.Optional[FloatRect]:\n        \"\"\"ElementHandle.bounding_box\n\n        This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is\n        calculated relative to the main frame viewport - which is usually the same as the browser window.\n\n        Scrolling affects the returned bounding box, similarly to\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n        That means `x` and/or `y` may be negative.\n\n        Elements from child frames return the bounding box relative to the main frame, unlike the\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n\n        Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the\n        following snippet should click the center of the element.\n\n        **Usage**\n\n        ```py\n        box = await element_handle.bounding_box()\n        await page.mouse.click(box[\\\"x\\\"] + box[\\\"width\\\"] / 2, box[\\\"y\\\"] + box[\\\"height\\\"] / 2)\n        ```\n\n        Returns\n        -------\n        Union[{x: float, y: float, width: float, height: float}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.bounding_box())\n\n    async def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"ElementHandle.screenshot\n\n        This method captures a screenshot of the page, clipped to the size and position of this particular element. If the\n        element is covered by other elements, it will not be actually visible on the screenshot. If the element is a\n        scrollable container, only the currently scrolled content will be visible on the screenshot.\n\n        This method waits for the [actionability](https://playwright.dev/python/docs/actionability) checks, then scrolls element into view before taking\n        a screenshot. If the element is detached from DOM, the method throws an error.\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.screenshot(\n                timeout=timeout,\n                type=type,\n                path=path,\n                quality=quality,\n                omitBackground=omit_background,\n                animations=animations,\n                caret=caret,\n                scale=scale,\n                mask=mapping.to_impl(mask),\n                maskColor=mask_color,\n                style=style,\n            )\n        )\n\n    async def query_selector(self, selector: str) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.query_selector\n\n        The method finds an element matching the specified selector in the `ElementHandle`'s subtree. If no elements match\n        the selector, returns `null`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.query_selector(selector=selector)\n        )\n\n    async def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"ElementHandle.query_selector_all\n\n        The method finds all elements matching the specified selector in the `ElementHandle`s subtree. If no elements match\n        the selector, returns empty array.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            await self._impl_obj.query_selector_all(selector=selector)\n        )\n\n    async def eval_on_selector(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"ElementHandle.eval_on_selector\n\n        Returns the return value of `expression`.\n\n        The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a\n        first argument to `expression`. If no elements match the selector, the method throws an error.\n\n        If `expression` returns a [Promise], then `element_handle.eval_on_selector()` would wait for the promise to\n        resolve and return its value.\n\n        **Usage**\n\n        ```py\n        tweet_handle = await page.query_selector(\\\".tweet\\\")\n        assert await tweet_handle.eval_on_selector(\\\".like\\\", \\\"node => node.innerText\\\") == \\\"100\\\"\n        assert await tweet_handle.eval_on_selector(\\\".retweets\\\", \\\"node => node.innerText\\\") == \\\"10\\\"\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector(\n                selector=selector, expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"ElementHandle.eval_on_selector_all\n\n        Returns the return value of `expression`.\n\n        The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array\n        of matched elements as a first argument to `expression`.\n\n        If `expression` returns a [Promise], then `element_handle.eval_on_selector_all()` would wait for the promise to\n        resolve and return its value.\n\n        **Usage**\n\n        ```html\n        <div class=\\\"feed\\\">\n          <div class=\\\"tweet\\\">Hello!</div>\n          <div class=\\\"tweet\\\">Hi!</div>\n        </div>\n        ```\n\n        ```py\n        feed_handle = await page.query_selector(\\\".feed\\\")\n        assert await feed_handle.eval_on_selector_all(\\\".tweet\\\", \\\"nodes => nodes.map(n => n.innerText)\\\") == [\\\"hello!\\\", \\\"hi!\\\"]\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector_all(\n                selector=selector, expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def wait_for_element_state(\n        self,\n        state: Literal[\n            \"disabled\", \"editable\", \"enabled\", \"hidden\", \"stable\", \"visible\"\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"ElementHandle.wait_for_element_state\n\n        Returns when the element satisfies the `state`.\n\n        Depending on the `state` parameter, this method waits for one of the [actionability](https://playwright.dev/python/docs/actionability) checks to\n        pass. This method throws when the element is detached while waiting, unless waiting for the `\\\"hidden\\\"` state.\n        - `\\\"visible\\\"` Wait until the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n        - `\\\"hidden\\\"` Wait until the element is [not visible](https://playwright.dev/python/docs/actionability#visible) or not attached. Note that\n          waiting for hidden does not throw when the element detaches.\n        - `\\\"stable\\\"` Wait until the element is both [visible](https://playwright.dev/python/docs/actionability#visible) and\n          [stable](https://playwright.dev/python/docs/actionability#stable).\n        - `\\\"enabled\\\"` Wait until the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n        - `\\\"disabled\\\"` Wait until the element is [not enabled](https://playwright.dev/python/docs/actionability#enabled).\n        - `\\\"editable\\\"` Wait until the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.\n\n        Parameters\n        ----------\n        state : Union[\"disabled\", \"editable\", \"enabled\", \"hidden\", \"stable\", \"visible\"]\n            A state to wait for, see below for more details.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_element_state(state=state, timeout=timeout)\n        )\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.wait_for_selector\n\n        Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom,\n        or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the\n        method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the\n        function will throw.\n\n        **Usage**\n\n        ```py\n        await page.set_content(\\\"<div><span></span></div>\\\")\n        div = await page.query_selector(\\\"div\\\")\n        # waiting for the \\\"span\\\" selector relative to the div.\n        span = await div.wait_for_selector(\\\"span\\\", state=\\\"attached\\\")\n        ```\n\n        **NOTE** This method does not work across navigations, use `page.wait_for_selector()` instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.wait_for_selector(\n                selector=selector, state=state, timeout=timeout, strict=strict\n            )\n        )\n\n\nmapping.register(ElementHandleImpl, ElementHandle)\n\n\nclass FileChooser(AsyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"FileChooser.page\n\n        Returns page this file chooser belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def element(self) -> \"ElementHandle\":\n        \"\"\"FileChooser.element\n\n        Returns input element associated with this file chooser.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.element)\n\n    def is_multiple(self) -> bool:\n        \"\"\"FileChooser.is_multiple\n\n        Returns whether this file chooser accepts multiple files.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_multiple())\n\n    async def set_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"FileChooser.set_files\n\n        Sets the value of the file input this chooser is associated with. If some of the `filePaths` are relative paths,\n        then they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_files(\n                files=mapping.to_impl(files), timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n\nmapping.register(FileChooserImpl, FileChooser)\n\n\nclass Frame(AsyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Frame.page\n\n        Returns the page containing this frame.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def name(self) -> str:\n        \"\"\"Frame.name\n\n        Returns frame's name attribute as specified in the tag.\n\n        If the name is empty, returns the id attribute instead.\n\n        **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed\n        later.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.name)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Frame.url\n\n        Returns frame's url.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def parent_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"Frame.parent_frame\n\n        Parent frame, if any. Detached frames and main frames return `null`.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.parent_frame)\n\n    @property\n    def child_frames(self) -> typing.List[\"Frame\"]:\n        \"\"\"Frame.child_frames\n\n        Returns\n        -------\n        List[Frame]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.child_frames)\n\n    async def goto(\n        self,\n        url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        referer: typing.Optional[str] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Frame.goto\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect.\n\n        The method will throw an error if:\n        - there's an SSL error (e.g. in case of self-signed certificates).\n        - target URL is invalid.\n        - the `timeout` is exceeded during navigation.\n        - the remote server does not respond or is unreachable.\n        - the main resource failed to load.\n\n        The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404\n        \\\"Not Found\\\" and 500 \\\"Internal Server Error\\\".  The status code for such responses can be retrieved by calling\n        `response.status()`.\n\n        **NOTE** The method either throws an error or returns a main resource response. The only exceptions are navigation\n        to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.\n\n        **NOTE** Headless mode doesn't support navigation to a PDF document. See the\n        [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).\n\n        Parameters\n        ----------\n        url : str\n            URL to navigate frame to. The url should include scheme, e.g. `https://`.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        referer : Union[str, None]\n            Referer header value. If provided it will take preference over the referer header value set by\n            `page.set_extra_http_headers()`.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.goto(\n                url=url, timeout=timeout, waitUntil=wait_until, referer=referer\n            )\n        )\n\n    def expect_navigation(\n        self,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Response\"]:\n        \"\"\"Frame.expect_navigation\n\n        Waits for the frame navigation and returns the main resource response. In case of multiple redirects, the\n        navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or\n        navigation due to History API usage, the navigation will resolve with `null`.\n\n        **Usage**\n\n        This method waits for the frame to navigate to a new URL. It is useful for when you run code which will indirectly\n        cause the frame to navigate. Consider this example:\n\n        ```py\n        async with frame.expect_navigation():\n            await frame.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        # Resolves after navigation has finished\n        ```\n\n        **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL\n        is considered a navigation.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_navigation(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            ).future\n        )\n\n    async def wait_for_url(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        *,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.wait_for_url\n\n        Waits for the frame to navigate to the given URL.\n\n        **Usage**\n\n        ```py\n        await frame.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        await frame.wait_for_url(\\\"**/target.html\\\")\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_url(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            )\n        )\n\n    async def wait_for_load_state(\n        self,\n        state: typing.Optional[\n            Literal[\"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.wait_for_load_state\n\n        Waits for the required load state to be reached.\n\n        This returns when the frame reaches a required load state, `load` by default. The navigation must have been\n        committed when this method is called. If current document has already reached the required state, resolves\n        immediately.\n\n        **NOTE** Most of the time, this method is not needed because Playwright\n        [auto-waits before every action](https://playwright.dev/python/docs/actionability).\n\n        **Usage**\n\n        ```py\n        await frame.click(\\\"button\\\") # click triggers navigation.\n        await frame.wait_for_load_state() # the promise resolves after \\\"load\\\" event.\n        ```\n\n        Parameters\n        ----------\n        state : Union[\"domcontentloaded\", \"load\", \"networkidle\", None]\n            Optional load state to wait for, defaults to `load`. If the state has been already reached while loading current\n            document, the method resolves immediately. Can be one of:\n            - `'load'` - wait for the `load` event to be fired.\n            - `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired.\n            - `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use\n              this method for testing, rely on web assertions to assess readiness instead.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_load_state(state=state, timeout=timeout)\n        )\n\n    async def frame_element(self) -> \"ElementHandle\":\n        \"\"\"Frame.frame_element\n\n        Returns the `frame` or `iframe` element handle which corresponds to this frame.\n\n        This is an inverse of `element_handle.content_frame()`. Note that returned handle actually belongs to the\n        parent frame.\n\n        This method throws an error if the frame has been detached before `frameElement()` returns.\n\n        **Usage**\n\n        ```py\n        frame_element = await frame.frame_element()\n        content_frame = await frame_element.content_frame()\n        assert frame == content_frame\n        ```\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.frame_element())\n\n    async def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Frame.evaluate\n\n        Returns the return value of `expression`.\n\n        If the function passed to the `frame.evaluate()` returns a [Promise], then `frame.evaluate()` would\n        wait for the promise to resolve and return its value.\n\n        If the function passed to the `frame.evaluate()` returns a non-[Serializable] value, then\n        `frame.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that\n        are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        **Usage**\n\n        ```py\n        result = await frame.evaluate(\\\"([x, y]) => Promise.resolve(x * y)\\\", [7, 8])\n        print(result) # prints \\\"56\\\"\n        ```\n\n        A string can also be passed in instead of a function.\n\n        ```py\n        print(await frame.evaluate(\\\"1 + 2\\\")) # prints \\\"3\\\"\n        x = 10\n        print(await frame.evaluate(f\\\"1 + {x}\\\")) # prints \\\"11\\\"\n        ```\n\n        `ElementHandle` instances can be passed as an argument to the `frame.evaluate()`:\n\n        ```py\n        body_handle = await frame.evaluate(\\\"document.body\\\")\n        html = await frame.evaluate(\\\"([body, suffix]) => body.innerHTML + suffix\\\", [body_handle, \\\"hello\\\"])\n        await body_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Frame.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        The only difference between `frame.evaluate()` and `frame.evaluate_handle()` is that\n        `frame.evaluate_handle()` returns `JSHandle`.\n\n        If the function, passed to the `frame.evaluate_handle()`, returns a [Promise], then\n        `frame.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        **Usage**\n\n        ```py\n        a_window_handle = await frame.evaluate_handle(\\\"Promise.resolve(window)\\\")\n        a_window_handle # handle for the window object.\n        ```\n\n        A string can also be passed in instead of a function.\n\n        ```py\n        a_handle = await page.evaluate_handle(\\\"document\\\") # handle for the \\\"document\\\"\n        ```\n\n        `JSHandle` instances can be passed as an argument to the `frame.evaluate_handle()`:\n\n        ```py\n        a_handle = await page.evaluate_handle(\\\"document.body\\\")\n        result_handle = await page.evaluate_handle(\\\"body => body.innerHTML\\\", a_handle)\n        print(await result_handle.json_value())\n        await result_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.evaluate_handle(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def query_selector(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Frame.query_selector\n\n        Returns the ElementHandle pointing to the frame element.\n\n        **NOTE** The use of `ElementHandle` is discouraged, use `Locator` objects and web-first assertions instead.\n\n        The method finds an element matching the specified selector within the frame. If no elements match the selector,\n        returns `null`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.query_selector(selector=selector, strict=strict)\n        )\n\n    async def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Frame.query_selector_all\n\n        Returns the ElementHandles pointing to the frame elements.\n\n        **NOTE** The use of `ElementHandle` is discouraged, use `Locator` objects instead.\n\n        The method finds all elements matching the specified selector within the frame. If no elements match the selector,\n        returns empty array.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            await self._impl_obj.query_selector_all(selector=selector)\n        )\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Frame.wait_for_selector\n\n        Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        **NOTE** Playwright automatically waits for element to be ready before performing an action. Using `Locator`\n        objects and web-first assertions make the code wait-for-selector-free.\n\n        Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If\n        at the moment of calling the method `selector` already satisfies the condition, the method will return immediately.\n        If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.\n\n        **Usage**\n\n        This method works across navigations:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            chromium = playwright.chromium\n            browser = await chromium.launch()\n            page = await browser.new_page()\n            for current_url in [\\\"https://google.com\\\", \\\"https://bbc.com\\\"]:\n                await page.goto(current_url, wait_until=\\\"domcontentloaded\\\")\n                element = await page.main_frame.wait_for_selector(\\\"img\\\")\n                print(\\\"Loaded image: \\\" + str(await element.get_attribute(\\\"src\\\")))\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.wait_for_selector(\n                selector=selector, strict=strict, timeout=timeout, state=state\n            )\n        )\n\n    async def is_checked(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_checked(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_disabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_disabled(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_editable(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_editable(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_enabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_enabled(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_hidden(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> bool:\n        \"\"\"Frame.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).  `selector` that\n        does not match any elements is considered hidden.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_hidden(selector=selector, strict=strict)\n        )\n\n    async def is_visible(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> bool:\n        \"\"\"Frame.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible). `selector` that does not match any elements\n        is considered not visible.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_visible(selector=selector, strict=strict)\n        )\n\n    async def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        await frame.dispatch_event(\\\"button#submit\\\", \\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = await frame.evaluate_handle(\\\"new DataTransfer()\\\")\n        await frame.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", { \\\"dataTransfer\\\": data_transfer })\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dispatch_event(\n                selector=selector,\n                type=type,\n                eventInit=mapping.to_impl(event_init),\n                strict=strict,\n                timeout=timeout,\n            )\n        )\n\n    async def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Any:\n        \"\"\"Frame.eval_on_selector\n\n        Returns the return value of `expression`.\n\n        The method finds an element matching the specified selector within the frame and passes it as a first argument to\n        `expression`. If no elements match the selector, the method throws an error.\n\n        If `expression` returns a [Promise], then `frame.eval_on_selector()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        search_value = await frame.eval_on_selector(\\\"#search\\\", \\\"el => el.value\\\")\n        preload_href = await frame.eval_on_selector(\\\"link[rel=preload]\\\", \\\"el => el.href\\\")\n        html = await frame.eval_on_selector(\\\".main-container\\\", \\\"(e, suffix) => e.outerHTML + suffix\\\", \\\"hello\\\")\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector(\n                selector=selector,\n                expression=expression,\n                arg=mapping.to_impl(arg),\n                strict=strict,\n            )\n        )\n\n    async def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Frame.eval_on_selector_all\n\n        Returns the return value of `expression`.\n\n        The method finds all elements matching the specified selector within the frame and passes an array of matched\n        elements as a first argument to `expression`.\n\n        If `expression` returns a [Promise], then `frame.eval_on_selector_all()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        divs_counts = await frame.eval_on_selector_all(\\\"div\\\", \\\"(divs, min) => divs.length >= min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector_all(\n                selector=selector, expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def content(self) -> str:\n        \"\"\"Frame.content\n\n        Gets the full HTML contents of the frame, including the doctype.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.content())\n\n    async def set_content(\n        self,\n        html: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Frame.set_content\n\n        This method internally calls [document.write()](https://developer.mozilla.org/en-US/docs/Web/API/Document/write),\n        inheriting all its specific characteristics and behaviors.\n\n        Parameters\n        ----------\n        html : str\n            HTML markup to assign to the page.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_content(\n                html=html, timeout=timeout, waitUntil=wait_until\n            )\n        )\n\n    def is_detached(self) -> bool:\n        \"\"\"Frame.is_detached\n\n        Returns `true` if the frame has been detached, or `false` otherwise.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_detached())\n\n    async def add_script_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n        type: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Frame.add_script_tag\n\n        Returns the added tag when the script's onload fires or when the script content was injected into frame.\n\n        Adds a `<script>` tag into the page with the desired url or content.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of a script to be added.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative\n            to the current working directory.\n        content : Union[str, None]\n            Raw JavaScript content to be injected into frame.\n        type : Union[str, None]\n            Script type. Use 'module' in order to load a JavaScript ES6 module. See\n            [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.add_script_tag(\n                url=url, path=path, content=content, type=type\n            )\n        )\n\n    async def add_style_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Frame.add_style_tag\n\n        Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.\n\n        Adds a `<link rel=\\\"stylesheet\\\">` tag into the page with the desired url or a `<style type=\\\"text/css\\\">` tag with the\n        content.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of the `<link>` tag.\n        path : Union[pathlib.Path, str, None]\n            Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to the\n            current working directory.\n        content : Union[str, None]\n            Raw CSS content to be injected into frame.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.add_style_tag(url=url, path=path, content=content)\n        )\n\n    async def click(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.click\n\n        This method clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.click(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                clickCount=click_count,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def dblclick(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.dblclick\n\n        This method double clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`. if\n           the first click of the `dblclick()` triggers a navigation event, this method will throw.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `frame.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dblclick(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def tap(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.tap\n\n        This method taps an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.tap(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def fill(\n        self,\n        selector: str,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.fill\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks,\n        focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string\n        to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : str\n            Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fill(\n                selector=selector,\n                value=value,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                force=force,\n            )\n        )\n\n    def locator(\n        self,\n        selector: str,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.locator\n\n        The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved\n        to the element immediately before performing an action, so a series of actions on the same locator can in fact be\n        performed on different DOM elements. That would happen if the DOM structure between those actions has changed.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selector=selector,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        await page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        await page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        await page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        await page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        await expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        await page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        await page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        await page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        await expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Frame.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        **Usage**\n\n        Following snippet locates element with text \\\"Submit\\\" in the iframe with id `my-frame`, like `<iframe\n        id=\\\"my-frame\\\">`:\n\n        ```py\n        locator = frame.frame_locator(\\\"#my-iframe\\\").get_by_text(\\\"Submit\\\")\n        await locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    async def focus(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.focus\n\n        This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the\n        method waits until a matching element appears in the DOM.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.focus(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def text_content(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Frame.text_content\n\n        Returns `element.textContent`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.text_content(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def inner_text(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.inner_text\n\n        Returns `element.innerText`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.inner_text(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def inner_html(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.inner_html\n\n        Returns `element.innerHTML`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.inner_html(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def get_attribute(\n        self,\n        selector: str,\n        name: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Frame.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        name : str\n            Attribute name to get the value for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.get_attribute(\n                selector=selector, name=name, strict=strict, timeout=timeout\n            )\n        )\n\n    async def hover(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.hover\n\n        This method hovers over an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.hover(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        *,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Frame.drag_and_drop\n\n        Parameters\n        ----------\n        source : str\n            A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will\n            be used.\n        target : str\n            A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first\n            will be used.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.drag_and_drop(\n                source=source,\n                target=target,\n                sourcePosition=source_position,\n                targetPosition=target_position,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                timeout=timeout,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def select_option(\n        self,\n        selector: str,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Frame.select_option\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits\n        until all specified options are present in the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        await frame.select_option(\\\"select#colors\\\", \\\"blue\\\")\n        # single selection matching the label\n        await frame.select_option(\\\"select#colors\\\", label=\\\"blue\\\")\n        # multiple selection\n        await frame.select_option(\\\"select#colors\\\", value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_option(\n                selector=selector,\n                value=mapping.to_impl(value),\n                index=mapping.to_impl(index),\n                label=mapping.to_impl(label),\n                element=mapping.to_impl(element),\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                force=force,\n            )\n        )\n\n    async def input_value(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.input_value(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def set_input_files(\n        self,\n        selector: str,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        This method expects `selector` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_input_files(\n                selector=selector,\n                files=mapping.to_impl(files),\n                strict=strict,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n            )\n        )\n\n    async def type(\n        self,\n        selector: str,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.type\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `frame.type` can be used\n        to send fine-grained keyboard events. To fill values in form fields, use `frame.fill()`.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.type(\n                selector=selector,\n                text=text,\n                delay=delay,\n                strict=strict,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n            )\n        )\n\n    async def press(\n        self,\n        selector: str,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.press\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.press(\n                selector=selector,\n                key=key,\n                delay=delay,\n                strict=strict,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n            )\n        )\n\n    async def check(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.check\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.check(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def uncheck(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.uncheck\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.uncheck(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def wait_for_timeout(self, timeout: float) -> None:\n        \"\"\"Frame.wait_for_timeout\n\n        Waits for the given `timeout` in milliseconds.\n\n        Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going\n        to be flaky. Use signals such as network events, selectors becoming visible and others instead.\n\n        Parameters\n        ----------\n        timeout : float\n            A timeout to wait for\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_timeout(timeout=timeout)\n        )\n\n    async def wait_for_function(\n        self,\n        expression: str,\n        *,\n        arg: typing.Optional[typing.Any] = None,\n        timeout: typing.Optional[float] = None,\n        polling: typing.Optional[typing.Union[float, Literal[\"raf\"]]] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Frame.wait_for_function\n\n        Returns when the `expression` returns a truthy value, returns that value.\n\n        **Usage**\n\n        The `frame.wait_for_function()` can be used to observe viewport size change:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch()\n            page = await browser.new_page()\n            await page.evaluate(\\\"window.x = 0; setTimeout(() => { window.x = 100 }, 1000);\\\")\n            await page.main_frame.wait_for_function(\\\"() => window.x > 0\\\")\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        To pass an argument to the predicate of `frame.waitForFunction` function:\n\n        ```py\n        selector = \\\".foo\\\"\n        await frame.wait_for_function(\\\"selector => !!document.querySelector(selector)\\\", selector)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()` or\n            `page.set_default_timeout()` methods.\n        polling : Union[\"raf\", float, None]\n            If `polling` is `'raf'`, then `expression` is constantly executed in `requestAnimationFrame` callback. If `polling`\n            is a number, then it is treated as an interval in milliseconds at which the function would be executed. Defaults to\n            `raf`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.wait_for_function(\n                expression=expression,\n                arg=mapping.to_impl(arg),\n                timeout=timeout,\n                polling=polling,\n            )\n        )\n\n    async def title(self) -> str:\n        \"\"\"Frame.title\n\n        Returns the page title.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.title())\n\n    async def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.set_checked\n\n        This method checks or unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_checked(\n                selector=selector,\n                checked=checked,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n\nmapping.register(FrameImpl, Frame)\n\n\nclass FrameLocator(AsyncBase):\n\n    @property\n    def first(self) -> \"FrameLocator\":\n        \"\"\"FrameLocator.first\n\n        Returns locator to the first matching frame.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.first)\n\n    @property\n    def last(self) -> \"FrameLocator\":\n        \"\"\"FrameLocator.last\n\n        Returns locator to the last matching frame.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.last)\n\n    @property\n    def owner(self) -> \"Locator\":\n        \"\"\"FrameLocator.owner\n\n        Returns a `Locator` object pointing to the same `iframe` as this frame locator.\n\n        Useful when you have a `FrameLocator` object obtained somewhere, and later on would like to interact with the\n        `iframe` element.\n\n        For a reverse operation, use `locator.content_frame()`.\n\n        **Usage**\n\n        ```py\n        frame_locator = page.locator(\\\"iframe[name=\\\\\\\"embedded\\\\\\\"]\\\").content_frame\n        # ...\n        locator = frame_locator.owner\n        await expect(locator).to_be_visible()\n        ```\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.owner)\n\n    def locator(\n        self,\n        selector_or_locator: typing.Union[\"Locator\", str],\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.locator\n\n        The method finds an element matching the specified selector in the locator's subtree. It also accepts filter\n        options, similar to `locator.filter()` method.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector_or_locator : Union[Locator, str]\n            A selector or locator to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selectorOrLocator=selector_or_locator,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        await page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        await page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        await page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        await page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        await expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        await page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        await page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        await page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        await expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"FrameLocator.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    def nth(self, index: int) -> \"FrameLocator\":\n        \"\"\"FrameLocator.nth\n\n        Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects the first frame.\n\n        Parameters\n        ----------\n        index : int\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.nth(index=index))\n\n\nmapping.register(FrameLocatorImpl, FrameLocator)\n\n\nclass Worker(AsyncBase):\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is\n        terminated.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the worker calls one of console API methods, e.g. `console.log` or `console.dir`.\n        \"\"\"\n\n    def on(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is\n        terminated.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the worker calls one of console API methods, e.g. `console.log` or `console.dir`.\n        \"\"\"\n\n    def once(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Worker.url\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    async def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Worker.evaluate\n\n        Returns the return value of `expression`.\n\n        If the function passed to the `worker.evaluate()` returns a [Promise], then `worker.evaluate()`\n        would wait for the promise to resolve and return its value.\n\n        If the function passed to the `worker.evaluate()` returns a non-[Serializable] value, then\n        `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that\n        are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Worker.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        The only difference between `worker.evaluate()` and `worker.evaluate_handle()` is that\n        `worker.evaluate_handle()` returns `JSHandle`.\n\n        If the function passed to the `worker.evaluate_handle()` returns a [Promise], then\n        `worker.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.evaluate_handle(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager:\n        \"\"\"Worker.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the page is closed before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        async with worker.expect_event(\\\"console\\\") as event_info:\n            await worker.evaluate(\\\"console.log(42)\\\")\n        message = await event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n\nmapping.register(WorkerImpl, Worker)\n\n\nclass Selectors(AsyncBase):\n\n    async def register(\n        self,\n        name: str,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content_script: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Selectors.register\n\n        Selectors must be registered before creating the page.\n\n        **Usage**\n\n        An example of registering selector engine that queries elements based on a tag name:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            tag_selector = \\\"\\\"\\\"\n              {\n                  // Returns the first element matching given selector in the root's subtree.\n                  query(root, selector) {\n                      return root.querySelector(selector);\n                  },\n                  // Returns all elements matching given selector in the root's subtree.\n                  queryAll(root, selector) {\n                      return Array.from(root.querySelectorAll(selector));\n                  }\n              }\\\"\\\"\\\"\n\n            # Register the engine. Selectors will be prefixed with \\\"tag=\\\".\n            await playwright.selectors.register(\\\"tag\\\", tag_selector)\n            browser = await playwright.chromium.launch()\n            page = await browser.new_page()\n            await page.set_content('<div><button>Click me</button></div>')\n\n            # Use the selector prefixed with its name.\n            button = await page.query_selector('tag=button')\n            # Combine it with built-in locators.\n            await page.locator('tag=div').get_by_text('Click me').click()\n            # Can use it in any methods supporting selectors.\n            button_count = await page.locator('tag=button').count()\n            print(button_count)\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name that is used in selectors as a prefix, e.g. `{name: 'foo'}` enables `foo=myselectorbody` selectors. May only\n            contain `[a-zA-Z0-9_]` characters.\n        script : Union[str, None]\n            Raw script content.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory.\n        content_script : Union[bool, None]\n            Whether to run this selector engine in isolated JavaScript environment. This environment has access to the same\n            DOM, but not any JavaScript objects from the frame's scripts. Defaults to `false`. Note that running as a content\n            script is not guaranteed when this engine is used together with other registered engines.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.register(\n                name=name, script=script, path=path, contentScript=content_script\n            )\n        )\n\n    def set_test_id_attribute(self, attribute_name: str) -> None:\n        \"\"\"Selectors.set_test_id_attribute\n\n        Defines custom attribute name to be used in `page.get_by_test_id()`. `data-testid` is used by default.\n\n        Parameters\n        ----------\n        attribute_name : str\n            Test id attribute name.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_test_id_attribute(attributeName=attribute_name)\n        )\n\n\nmapping.register(SelectorsImpl, Selectors)\n\n\nclass Clock(AsyncBase):\n\n    async def install(\n        self,\n        *,\n        time: typing.Optional[typing.Union[float, str, datetime.datetime]] = None,\n    ) -> None:\n        \"\"\"Clock.install\n\n        Install fake implementations for the following time-related functions:\n        - `Date`\n        - `setTimeout`\n        - `clearTimeout`\n        - `setInterval`\n        - `clearInterval`\n        - `requestAnimationFrame`\n        - `cancelAnimationFrame`\n        - `requestIdleCallback`\n        - `cancelIdleCallback`\n        - `performance`\n\n        Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers,\n        and control the behavior of time-dependent functions. See `clock.run_for()` and\n        `clock.fast_forward()` for more information.\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str, None]\n            Time to initialize with, current system time by default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.install(time=time))\n\n    async def fast_forward(self, ticks: typing.Union[int, str]) -> None:\n        \"\"\"Clock.fast_forward\n\n        Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user\n        closing the laptop lid for a while and reopening it later, after given time.\n\n        **Usage**\n\n        ```py\n        await page.clock.fast_forward(1000)\n        await page.clock.fast_forward(\\\"30:00\\\")\n        ```\n\n        Parameters\n        ----------\n        ticks : Union[int, str]\n            Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are\n            \"08\" for eight seconds, \"01:00\" for one minute and \"02:34:10\" for two hours, 34 minutes and ten seconds.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.fast_forward(ticks=ticks))\n\n    async def pause_at(self, time: typing.Union[float, str, datetime.datetime]) -> None:\n        \"\"\"Clock.pause_at\n\n        Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired\n        unless `clock.run_for()`, `clock.fast_forward()`, `clock.pause_at()` or\n        `clock.resume()` is called.\n\n        Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it\n        at the specified time and pausing.\n\n        **Usage**\n\n        ```py\n        await page.clock.pause_at(datetime.datetime(2020, 2, 2))\n        await page.clock.pause_at(\\\"2020-02-02\\\")\n        ```\n\n        For best results, install the clock before navigating the page and set it to a time slightly before the intended\n        test time. This ensures that all timers run normally during page loading, preventing the page from getting stuck.\n        Once the page has fully loaded, you can safely use `clock.pause_at()` to pause the clock.\n\n        ```py\n        # Initialize clock with some time before the test time and let the page load\n        # naturally. `Date.now` will progress as the timers fire.\n        await page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))\n        await page.goto(\\\"http://localhost:3333\\\")\n        await page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to pause at.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.pause_at(time=time))\n\n    async def resume(self) -> None:\n        \"\"\"Clock.resume\n\n        Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.resume())\n\n    async def run_for(self, ticks: typing.Union[int, str]) -> None:\n        \"\"\"Clock.run_for\n\n        Advance the clock, firing all the time-related callbacks.\n\n        **Usage**\n\n        ```py\n        await page.clock.run_for(1000);\n        await page.clock.run_for(\\\"30:00\\\")\n        ```\n\n        Parameters\n        ----------\n        ticks : Union[int, str]\n            Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are\n            \"08\" for eight seconds, \"01:00\" for one minute and \"02:34:10\" for two hours, 34 minutes and ten seconds.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.run_for(ticks=ticks))\n\n    async def set_fixed_time(\n        self, time: typing.Union[float, str, datetime.datetime]\n    ) -> None:\n        \"\"\"Clock.set_fixed_time\n\n        Makes `Date.now` and `new Date()` return fixed fake time at all times, keeps all the timers running.\n\n        Use this method for simple scenarios where you only need to test with a predefined time. For more advanced\n        scenarios, use `clock.install()` instead. Read docs on [clock emulation](https://playwright.dev/python/docs/clock) to learn more.\n\n        **Usage**\n\n        ```py\n        await page.clock.set_fixed_time(datetime.datetime.now())\n        await page.clock.set_fixed_time(datetime.datetime(2020, 2, 2))\n        await page.clock.set_fixed_time(\\\"2020-02-02\\\")\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to be set.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.set_fixed_time(time=time))\n\n    async def set_system_time(\n        self, time: typing.Union[float, str, datetime.datetime]\n    ) -> None:\n        \"\"\"Clock.set_system_time\n\n        Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for\n        example switching from summer to winter time, or changing time zones.\n\n        **Usage**\n\n        ```py\n        await page.clock.set_system_time(datetime.datetime.now())\n        await page.clock.set_system_time(datetime.datetime(2020, 2, 2))\n        await page.clock.set_system_time(\\\"2020-02-02\\\")\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to be set.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.set_system_time(time=time))\n\n\nmapping.register(ClockImpl, Clock)\n\n\nclass ConsoleMessage(AsyncBase):\n\n    @property\n    def type(\n        self,\n    ) -> typing.Union[\n        Literal[\"assert\"],\n        Literal[\"clear\"],\n        Literal[\"count\"],\n        Literal[\"debug\"],\n        Literal[\"dir\"],\n        Literal[\"dirxml\"],\n        Literal[\"endGroup\"],\n        Literal[\"error\"],\n        Literal[\"info\"],\n        Literal[\"log\"],\n        Literal[\"profile\"],\n        Literal[\"profileEnd\"],\n        Literal[\"startGroup\"],\n        Literal[\"startGroupCollapsed\"],\n        Literal[\"table\"],\n        Literal[\"time\"],\n        Literal[\"timeEnd\"],\n        Literal[\"trace\"],\n        Literal[\"warning\"],\n    ]:\n        \"\"\"ConsoleMessage.type\n\n        Returns\n        -------\n        Union[\"assert\", \"clear\", \"count\", \"debug\", \"dir\", \"dirxml\", \"endGroup\", \"error\", \"info\", \"log\", \"profile\", \"profileEnd\", \"startGroup\", \"startGroupCollapsed\", \"table\", \"time\", \"timeEnd\", \"trace\", \"warning\"]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.type)\n\n    @property\n    def text(self) -> str:\n        \"\"\"ConsoleMessage.text\n\n        The text of the console message.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.text)\n\n    @property\n    def args(self) -> typing.List[\"JSHandle\"]:\n        \"\"\"ConsoleMessage.args\n\n        List of arguments passed to a `console` function call. See also `page.on('console')`.\n\n        Returns\n        -------\n        List[JSHandle]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.args)\n\n    @property\n    def location(self) -> SourceLocation:\n        \"\"\"ConsoleMessage.location\n\n        Returns\n        -------\n        {url: str, lineNumber: int, columnNumber: int}\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.location)\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"ConsoleMessage.page\n\n        The page that produced this console message, if any.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    @property\n    def worker(self) -> typing.Optional[\"Worker\"]:\n        \"\"\"ConsoleMessage.worker\n\n        The web worker or service worker that produced this console message, if any. Note that console messages from web\n        workers also have non-null `console_message.page()`.\n\n        Returns\n        -------\n        Union[Worker, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.worker)\n\n\nmapping.register(ConsoleMessageImpl, ConsoleMessage)\n\n\nclass Dialog(AsyncBase):\n\n    @property\n    def type(self) -> str:\n        \"\"\"Dialog.type\n\n        Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.type)\n\n    @property\n    def message(self) -> str:\n        \"\"\"Dialog.message\n\n        A message displayed in the dialog.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.message)\n\n    @property\n    def default_value(self) -> str:\n        \"\"\"Dialog.default_value\n\n        If dialog is prompt, returns default prompt value. Otherwise, returns empty string.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.default_value)\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"Dialog.page\n\n        The page that initiated this dialog, if available.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    async def accept(self, prompt_text: typing.Optional[str] = None) -> None:\n        \"\"\"Dialog.accept\n\n        Returns when the dialog has been accepted.\n\n        Parameters\n        ----------\n        prompt_text : Union[str, None]\n            A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.accept(promptText=prompt_text)\n        )\n\n    async def dismiss(self) -> None:\n        \"\"\"Dialog.dismiss\n\n        Returns when the dialog has been dismissed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.dismiss())\n\n\nmapping.register(DialogImpl, Dialog)\n\n\nclass Download(AsyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Download.page\n\n        Get the page that the download belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Download.url\n\n        Returns downloaded url.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def suggested_filename(self) -> str:\n        \"\"\"Download.suggested_filename\n\n        Returns suggested filename for this download. It is typically computed by the browser from the\n        [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) response\n        header or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources).\n        Different browsers can use different logic for computing it.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.suggested_filename)\n\n    async def delete(self) -> None:\n        \"\"\"Download.delete\n\n        Deletes the downloaded file. Will wait for the download to finish if necessary.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.delete())\n\n    async def failure(self) -> typing.Optional[str]:\n        \"\"\"Download.failure\n\n        Returns download error if any. Will wait for the download to finish if necessary.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.failure())\n\n    async def path(self) -> pathlib.Path:\n        \"\"\"Download.path\n\n        Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method\n        will wait for the download to finish if necessary. The method throws when connected remotely.\n\n        Note that the download's file name is a random GUID, use `download.suggested_filename()` to get suggested\n        file name.\n\n        Returns\n        -------\n        pathlib.Path\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.path())\n\n    async def save_as(self, path: typing.Union[str, pathlib.Path]) -> None:\n        \"\"\"Download.save_as\n\n        Copy the download to a user-specified path. It is safe to call this method while the download is still in progress.\n        Will wait for the download to finish if necessary.\n\n        **Usage**\n\n        ```py\n        await download.save_as(\\\"/path/to/save/at/\\\" + download.suggested_filename)\n        ```\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str]\n            Path where the download should be copied.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.save_as(path=path))\n\n    async def cancel(self) -> None:\n        \"\"\"Download.cancel\n\n        Cancels a download. Will not fail if the download is already finished or canceled. Upon successful cancellations,\n        `download.failure()` would resolve to `'canceled'`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.cancel())\n\n\nmapping.register(DownloadImpl, Download)\n\n\nclass Video(AsyncBase):\n\n    async def path(self) -> pathlib.Path:\n        \"\"\"Video.path\n\n        Returns the file system path this video will be recorded to. The video is guaranteed to be written to the\n        filesystem upon closing the browser context. This method throws when connected remotely.\n\n        Returns\n        -------\n        pathlib.Path\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.path())\n\n    async def save_as(self, path: typing.Union[str, pathlib.Path]) -> None:\n        \"\"\"Video.save_as\n\n        Saves the video to a user-specified path. It is safe to call this method while the video is still in progress, or\n        after the page has closed. This method waits until the page is closed and the video is fully saved.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str]\n            Path where the video should be saved.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.save_as(path=path))\n\n    async def delete(self) -> None:\n        \"\"\"Video.delete\n\n        Deletes the video file. Will wait for the video to finish if necessary.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.delete())\n\n\nmapping.register(VideoImpl, Video)\n\n\nclass Page(AsyncContextManager):\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page closes.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        async def print_args(msg):\n            values = []\n            for arg in msg.args:\n                values.append(await arg.json_value())\n            print(values)\n\n        page.on(\\\"console\\\", print_args)\n        await page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"crash\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page\n        crashes, ongoing and subsequent operations will throw.\n\n        The most common way to deal with crashes is to catch an exception:\n\n        ```py\n        try:\n            # crash might happen during a click.\n            await page.click(\\\"button\\\")\n            # or while waiting for an event.\n            await page.wait_for_event(\\\"popup\\\")\n        except Error as e:\n            pass\n            # when the page crashes, exception message contains \\\"crash\\\".\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"dialog\"],\n        f: typing.Callable[[\"Dialog\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        page.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"domcontentloaded\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript\n        [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"download\"],\n        f: typing.Callable[[\"Download\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when attachment download started. User can access basic file operations on downloaded content via the\n        passed `Download` instance.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"filechooser\"],\n        f: typing.Callable[\n            [\"FileChooser\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can\n        respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that.\n\n        ```py\n        page.on(\\\"filechooser\\\", lambda file_chooser: file_chooser.set_files(\\\"/tmp/myfile.pdf\\\"))\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"frameattached\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is attached.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framedetached\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is detached.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framenavigated\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is navigated to a new url.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"load\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"pageerror\"],\n        f: typing.Callable[[\"Error\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when an uncaught exception happens within the page.\n\n        ```py\n        # Log all uncaught errors to the terminal\n        page.on(\\\"pageerror\\\", lambda exc: print(f\\\"uncaught exception: {exc}\\\"))\n\n        # Navigate to a page with an exception.\n        await page.goto(\\\"data:text/html,<script>throw new Error('test')</script>\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"popup\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page opens a new tab or window. This event is emitted in addition to the\n        `browser_context.on('page')`, but only for popups relevant to this page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        async with page.expect_event(\\\"popup\\\") as page_info:\n            await page.get_by_text(\\\"open the popup\\\").click()\n        popup = await page_info.value\n        print(await popup.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"request\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests,\n        see `page.route()` or `browser_context.route()`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"requestfailed\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out.\n\n        ```python\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure.error_text))\n        ```\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will\n        only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network error\n        net::ERR_FAILED.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"requestfinished\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"response\"],\n        f: typing.Callable[[\"Response\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"websocket\"],\n        f: typing.Callable[[\"WebSocket\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when `WebSocket` request is sent.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"worker\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned\n        by the page.\"\"\"\n\n    def on(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page closes.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        async def print_args(msg):\n            values = []\n            for arg in msg.args:\n                values.append(await arg.json_value())\n            print(values)\n\n        page.on(\\\"console\\\", print_args)\n        await page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"crash\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page\n        crashes, ongoing and subsequent operations will throw.\n\n        The most common way to deal with crashes is to catch an exception:\n\n        ```py\n        try:\n            # crash might happen during a click.\n            await page.click(\\\"button\\\")\n            # or while waiting for an event.\n            await page.wait_for_event(\\\"popup\\\")\n        except Error as e:\n            pass\n            # when the page crashes, exception message contains \\\"crash\\\".\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"dialog\"],\n        f: typing.Callable[[\"Dialog\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        page.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"domcontentloaded\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript\n        [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"download\"],\n        f: typing.Callable[[\"Download\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when attachment download started. User can access basic file operations on downloaded content via the\n        passed `Download` instance.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"filechooser\"],\n        f: typing.Callable[\n            [\"FileChooser\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can\n        respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that.\n\n        ```py\n        page.on(\\\"filechooser\\\", lambda file_chooser: file_chooser.set_files(\\\"/tmp/myfile.pdf\\\"))\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"frameattached\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is attached.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framedetached\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is detached.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framenavigated\"],\n        f: typing.Callable[[\"Frame\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is navigated to a new url.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"load\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"pageerror\"],\n        f: typing.Callable[[\"Error\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when an uncaught exception happens within the page.\n\n        ```py\n        # Log all uncaught errors to the terminal\n        page.on(\\\"pageerror\\\", lambda exc: print(f\\\"uncaught exception: {exc}\\\"))\n\n        # Navigate to a page with an exception.\n        await page.goto(\\\"data:text/html,<script>throw new Error('test')</script>\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"popup\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when the page opens a new tab or window. This event is emitted in addition to the\n        `browser_context.on('page')`, but only for popups relevant to this page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        async with page.expect_event(\\\"popup\\\") as page_info:\n            await page.get_by_text(\\\"open the popup\\\").click()\n        popup = await page_info.value\n        print(await popup.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"request\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests,\n        see `page.route()` or `browser_context.route()`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"requestfailed\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out.\n\n        ```python\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure.error_text))\n        ```\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will\n        only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network error\n        net::ERR_FAILED.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"requestfinished\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"response\"],\n        f: typing.Callable[[\"Response\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"websocket\"],\n        f: typing.Callable[[\"WebSocket\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when `WebSocket` request is sent.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"worker\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned\n        by the page.\"\"\"\n\n    def once(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def keyboard(self) -> \"Keyboard\":\n        \"\"\"Page.keyboard\n\n        Returns\n        -------\n        Keyboard\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.keyboard)\n\n    @property\n    def mouse(self) -> \"Mouse\":\n        \"\"\"Page.mouse\n\n        Returns\n        -------\n        Mouse\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.mouse)\n\n    @property\n    def touchscreen(self) -> \"Touchscreen\":\n        \"\"\"Page.touchscreen\n\n        Returns\n        -------\n        Touchscreen\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.touchscreen)\n\n    @property\n    def context(self) -> \"BrowserContext\":\n        \"\"\"Page.context\n\n        Get the browser context that the page belongs to.\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.context)\n\n    @property\n    def clock(self) -> \"Clock\":\n        \"\"\"Page.clock\n\n        Playwright has ability to mock clock and passage of time.\n\n        Returns\n        -------\n        Clock\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.clock)\n\n    @property\n    def main_frame(self) -> \"Frame\":\n        \"\"\"Page.main_frame\n\n        The page's main frame. Page is guaranteed to have a main frame which persists during navigations.\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.main_frame)\n\n    @property\n    def frames(self) -> typing.List[\"Frame\"]:\n        \"\"\"Page.frames\n\n        An array of all frames attached to the page.\n\n        Returns\n        -------\n        List[Frame]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.frames)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Page.url\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def viewport_size(self) -> typing.Optional[ViewportSize]:\n        \"\"\"Page.viewport_size\n\n        Returns\n        -------\n        Union[{width: int, height: int}, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.viewport_size)\n\n    @property\n    def workers(self) -> typing.List[\"Worker\"]:\n        \"\"\"Page.workers\n\n        This method returns all of the dedicated\n        [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page.\n\n        **NOTE** This does not contain ServiceWorkers\n\n        Returns\n        -------\n        List[Worker]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.workers)\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        \"\"\"Page.request\n\n        API testing helper associated with this page. This method returns the same instance as\n        `browser_context.request` on the page's context. See `browser_context.request` for more\n        details.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def video(self) -> typing.Optional[\"Video\"]:\n        \"\"\"Page.video\n\n        Video object associated with this page.\n\n        Returns\n        -------\n        Union[Video, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.video)\n\n    async def opener(self) -> typing.Optional[\"Page\"]:\n        \"\"\"Page.opener\n\n        Returns the opener for popup pages and `null` for others. If the opener has been closed already the returns `null`.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(await self._impl_obj.opener())\n\n    def frame(\n        self,\n        name: typing.Optional[str] = None,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n    ) -> typing.Optional[\"Frame\"]:\n        \"\"\"Page.frame\n\n        Returns frame matching the specified criteria. Either `name` or `url` must be specified.\n\n        **Usage**\n\n        ```py\n        frame = page.frame(name=\\\"frame-name\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[str, None]\n            Frame name specified in the `iframe`'s `name` attribute. Optional.\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving frame's `url` as a [URL] object. Optional.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._impl_obj.frame(name=name, url=self._wrap_handler(url))\n        )\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        \"\"\"Page.set_default_navigation_timeout\n\n        This setting will change the default maximum navigation time for the following methods and related shortcuts:\n        - `page.go_back()`\n        - `page.go_forward()`\n        - `page.goto()`\n        - `page.reload()`\n        - `page.set_content()`\n        - `page.expect_navigation()`\n        - `page.wait_for_url()`\n\n        **NOTE** `page.set_default_navigation_timeout()` takes priority over `page.set_default_timeout()`,\n        `browser_context.set_default_timeout()` and `browser_context.set_default_navigation_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum navigation time in milliseconds\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_navigation_timeout(timeout=timeout)\n        )\n\n    def set_default_timeout(self, timeout: float) -> None:\n        \"\"\"Page.set_default_timeout\n\n        This setting will change the default maximum time for all the methods accepting `timeout` option.\n\n        **NOTE** `page.set_default_navigation_timeout()` takes priority over `page.set_default_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum time in milliseconds. Pass `0` to disable timeout.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_timeout(timeout=timeout)\n        )\n\n    async def query_selector(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Page.query_selector\n\n        The method finds an element matching the specified selector within the page. If no elements match the selector, the\n        return value resolves to `null`. To wait for an element on the page, use `locator.wait_for()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.query_selector(selector=selector, strict=strict)\n        )\n\n    async def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Page.query_selector_all\n\n        The method finds all elements matching the specified selector within the page. If no elements match the selector,\n        the return value resolves to `[]`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            await self._impl_obj.query_selector_all(selector=selector)\n        )\n\n    async def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Page.wait_for_selector\n\n        Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        **NOTE** Playwright automatically waits for element to be ready before performing an action. Using `Locator`\n        objects and web-first assertions makes the code wait-for-selector-free.\n\n        Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If\n        at the moment of calling the method `selector` already satisfies the condition, the method will return immediately.\n        If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.\n\n        **Usage**\n\n        This method works across navigations:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            chromium = playwright.chromium\n            browser = await chromium.launch()\n            page = await browser.new_page()\n            for current_url in [\\\"https://google.com\\\", \\\"https://bbc.com\\\"]:\n                await page.goto(current_url, wait_until=\\\"domcontentloaded\\\")\n                element = await page.wait_for_selector(\\\"img\\\")\n                print(\\\"Loaded image: \\\" + str(await element.get_attribute(\\\"src\\\")))\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.wait_for_selector(\n                selector=selector, timeout=timeout, state=state, strict=strict\n            )\n        )\n\n    async def is_checked(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_checked(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_disabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_disabled(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_editable(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_editable(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_enabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_enabled(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_hidden(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).  `selector` that\n        does not match any elements is considered hidden.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `page.is_hidden()` does not wait for the↵element to become hidden and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_hidden(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def is_visible(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible). `selector` that does not match any elements\n        is considered not visible.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `page.is_visible()` does not wait↵for the element to become visible and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_visible(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        await page.dispatch_event(\\\"button#submit\\\", \\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = await page.evaluate_handle(\\\"new DataTransfer()\\\")\n        await page.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", { \\\"dataTransfer\\\": data_transfer })\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dispatch_event(\n                selector=selector,\n                type=type,\n                eventInit=mapping.to_impl(event_init),\n                timeout=timeout,\n                strict=strict,\n            )\n        )\n\n    async def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Page.evaluate\n\n        Returns the value of the `expression` invocation.\n\n        If the function passed to the `page.evaluate()` returns a [Promise], then `page.evaluate()` would\n        wait for the promise to resolve and return its value.\n\n        If the function passed to the `page.evaluate()` returns a non-[Serializable] value, then\n        `page.evaluate()` resolves to `undefined`. Playwright also supports transferring some additional values\n        that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        **Usage**\n\n        Passing argument to `expression`:\n\n        ```py\n        result = await page.evaluate(\\\"([x, y]) => Promise.resolve(x * y)\\\", [7, 8])\n        print(result) # prints \\\"56\\\"\n        ```\n\n        A string can also be passed in instead of a function:\n\n        ```py\n        print(await page.evaluate(\\\"1 + 2\\\")) # prints \\\"3\\\"\n        x = 10\n        print(await page.evaluate(f\\\"1 + {x}\\\")) # prints \\\"11\\\"\n        ```\n\n        `ElementHandle` instances can be passed as an argument to the `page.evaluate()`:\n\n        ```py\n        body_handle = await page.evaluate(\\\"document.body\\\")\n        html = await page.evaluate(\\\"([body, suffix]) => body.innerHTML + suffix\\\", [body_handle, \\\"hello\\\"])\n        await body_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Page.evaluate_handle\n\n        Returns the value of the `expression` invocation as a `JSHandle`.\n\n        The only difference between `page.evaluate()` and `page.evaluate_handle()` is that\n        `page.evaluate_handle()` returns `JSHandle`.\n\n        If the function passed to the `page.evaluate_handle()` returns a [Promise], then\n        `page.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        **Usage**\n\n        ```py\n        a_window_handle = await page.evaluate_handle(\\\"Promise.resolve(window)\\\")\n        a_window_handle # handle for the window object.\n        ```\n\n        A string can also be passed in instead of a function:\n\n        ```py\n        a_handle = await page.evaluate_handle(\\\"document\\\") # handle for the \\\"document\\\"\n        ```\n\n        `JSHandle` instances can be passed as an argument to the `page.evaluate_handle()`:\n\n        ```py\n        a_handle = await page.evaluate_handle(\\\"document.body\\\")\n        result_handle = await page.evaluate_handle(\\\"body => body.innerHTML\\\", a_handle)\n        print(await result_handle.json_value())\n        await result_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.evaluate_handle(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Any:\n        \"\"\"Page.eval_on_selector\n\n        The method finds an element matching the specified selector within the page and passes it as a first argument to\n        `expression`. If no elements match the selector, the method throws an error. Returns the value of `expression`.\n\n        If `expression` returns a [Promise], then `page.eval_on_selector()` would wait for the promise to resolve and\n        return its value.\n\n        **Usage**\n\n        ```py\n        search_value = await page.eval_on_selector(\\\"#search\\\", \\\"el => el.value\\\")\n        preload_href = await page.eval_on_selector(\\\"link[rel=preload]\\\", \\\"el => el.href\\\")\n        html = await page.eval_on_selector(\\\".main-container\\\", \\\"(e, suffix) => e.outer_html + suffix\\\", \\\"hello\\\")\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector(\n                selector=selector,\n                expression=expression,\n                arg=mapping.to_impl(arg),\n                strict=strict,\n            )\n        )\n\n    async def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Page.eval_on_selector_all\n\n        The method finds all elements matching the specified selector within the page and passes an array of matched\n        elements as a first argument to `expression`. Returns the result of `expression` invocation.\n\n        If `expression` returns a [Promise], then `page.eval_on_selector_all()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        div_counts = await page.eval_on_selector_all(\\\"div\\\", \\\"(divs, min) => divs.length >= min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.eval_on_selector_all(\n                selector=selector, expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def add_script_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n        type: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Page.add_script_tag\n\n        Adds a `<script>` tag into the page with the desired url or content. Returns the added tag when the script's onload\n        fires or when the script content was injected into frame.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of a script to be added.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative\n            to the current working directory.\n        content : Union[str, None]\n            Raw JavaScript content to be injected into frame.\n        type : Union[str, None]\n            Script type. Use 'module' in order to load a JavaScript ES6 module. See\n            [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.add_script_tag(\n                url=url, path=path, content=content, type=type\n            )\n        )\n\n    async def add_style_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Page.add_style_tag\n\n        Adds a `<link rel=\\\"stylesheet\\\">` tag into the page with the desired url or a `<style type=\\\"text/css\\\">` tag with the\n        content. Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of the `<link>` tag.\n        path : Union[pathlib.Path, str, None]\n            Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to the\n            current working directory.\n        content : Union[str, None]\n            Raw CSS content to be injected into frame.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.add_style_tag(url=url, path=path, content=content)\n        )\n\n    async def expose_function(self, name: str, callback: typing.Callable) -> None:\n        \"\"\"Page.expose_function\n\n        The method adds a function called `name` on the `window` object of every frame in the page. When called, the\n        function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n\n        If the `callback` returns a [Promise], it will be awaited.\n\n        See `browser_context.expose_function()` for context-wide exposed function.\n\n        **NOTE** Functions installed via `page.expose_function()` survive navigations.\n\n        **Usage**\n\n        An example of adding a `sha256` function to the page:\n\n        ```py\n        import asyncio\n        import hashlib\n        from playwright.async_api import async_playwright, Playwright\n\n        def sha256(text):\n            m = hashlib.sha256()\n            m.update(bytes(text, \\\"utf8\\\"))\n            return m.hexdigest()\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch(headless=False)\n            page = await browser.new_page()\n            await page.expose_function(\\\"sha256\\\", sha256)\n            await page.set_content(\\\"\\\"\\\"\n                <script>\n                  async function onClick() {\n                    document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n                  }\n                </script>\n                <button onclick=\\\"onClick()\\\">Click me</button>\n                <div></div>\n            \\\"\\\"\\\")\n            await page.click(\\\"button\\\")\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object\n        callback : Callable\n            Callback function which will be called in Playwright's context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.expose_function(\n                name=name, callback=self._wrap_handler(callback)\n            )\n        )\n\n    async def expose_binding(\n        self,\n        name: str,\n        callback: typing.Callable,\n        *,\n        handle: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.expose_binding\n\n        The method adds a function called `name` on the `window` object of every frame in this page. When called, the\n        function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. If the\n        `callback` returns a [Promise], it will be awaited.\n\n        The first argument of the `callback` function contains information about the caller: `{ browserContext:\n        BrowserContext, page: Page, frame: Frame }`.\n\n        See `browser_context.expose_binding()` for the context-wide version.\n\n        **NOTE** Functions installed via `page.expose_binding()` survive navigations.\n\n        **Usage**\n\n        An example of exposing page URL to all frames in a page:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch(headless=False)\n            context = await browser.new_context()\n            page = await context.new_page()\n            await page.expose_binding(\\\"pageURL\\\", lambda source: source[\\\"page\\\"].url)\n            await page.set_content(\\\"\\\"\\\"\n            <script>\n              async function onClick() {\n                document.querySelector('div').textContent = await window.pageURL();\n              }\n            </script>\n            <button onclick=\\\"onClick()\\\">Click me</button>\n            <div></div>\n            \\\"\\\"\\\")\n            await page.click(\\\"button\\\")\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        handle : Union[bool, None]\n            Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is\n            supported. When passing by value, multiple arguments are supported.\n            Deprecated: This option will be removed in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.expose_binding(\n                name=name, callback=self._wrap_handler(callback), handle=handle\n            )\n        )\n\n    async def set_extra_http_headers(self, headers: typing.Dict[str, str]) -> None:\n        \"\"\"Page.set_extra_http_headers\n\n        The extra HTTP headers will be sent with every request the page initiates.\n\n        **NOTE** `page.set_extra_http_headers()` does not guarantee the order of headers in the outgoing requests.\n\n        Parameters\n        ----------\n        headers : Dict[str, str]\n            An object containing additional HTTP headers to be sent with every request. All header values must be strings.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_extra_http_headers(\n                headers=mapping.to_impl(headers)\n            )\n        )\n\n    async def content(self) -> str:\n        \"\"\"Page.content\n\n        Gets the full HTML contents of the page, including the doctype.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.content())\n\n    async def set_content(\n        self,\n        html: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Page.set_content\n\n        This method internally calls [document.write()](https://developer.mozilla.org/en-US/docs/Web/API/Document/write),\n        inheriting all its specific characteristics and behaviors.\n\n        Parameters\n        ----------\n        html : str\n            HTML markup to assign to the page.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_content(\n                html=html, timeout=timeout, waitUntil=wait_until\n            )\n        )\n\n    async def goto(\n        self,\n        url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        referer: typing.Optional[str] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.goto\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the first\n        non-redirect response.\n\n        The method will throw an error if:\n        - there's an SSL error (e.g. in case of self-signed certificates).\n        - target URL is invalid.\n        - the `timeout` is exceeded during navigation.\n        - the remote server does not respond or is unreachable.\n        - the main resource failed to load.\n\n        The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404\n        \\\"Not Found\\\" and 500 \\\"Internal Server Error\\\".  The status code for such responses can be retrieved by calling\n        `response.status()`.\n\n        **NOTE** The method either throws an error or returns a main resource response. The only exceptions are navigation\n        to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.\n\n        **NOTE** Headless mode doesn't support navigation to a PDF document. See the\n        [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).\n\n        Parameters\n        ----------\n        url : str\n            URL to navigate page to. The url should include scheme, e.g. `https://`. When a `baseURL` via the context options\n            was provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        referer : Union[str, None]\n            Referer header value. If provided it will take preference over the referer header value set by\n            `page.set_extra_http_headers()`.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.goto(\n                url=url, timeout=timeout, waitUntil=wait_until, referer=referer\n            )\n        )\n\n    async def reload(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.reload\n\n        This method reloads the current page, in the same way as if the user had triggered a browser refresh. Returns the\n        main resource response. In case of multiple redirects, the navigation will resolve with the response of the last\n        redirect.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.reload(timeout=timeout, waitUntil=wait_until)\n        )\n\n    async def wait_for_load_state(\n        self,\n        state: typing.Optional[\n            Literal[\"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.wait_for_load_state\n\n        Returns when the required load state has been reached.\n\n        This resolves when the page reaches a required load state, `load` by default. The navigation must have been\n        committed when this method is called. If current document has already reached the required state, resolves\n        immediately.\n\n        **NOTE** Most of the time, this method is not needed because Playwright\n        [auto-waits before every action](https://playwright.dev/python/docs/actionability).\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"button\\\").click() # click triggers navigation.\n        await page.wait_for_load_state() # the promise resolves after \\\"load\\\" event.\n        ```\n\n        ```py\n        async with page.expect_popup() as page_info:\n            await page.get_by_role(\\\"button\\\").click() # click triggers a popup.\n        popup = await page_info.value\n        # Wait for the \\\"DOMContentLoaded\\\" event.\n        await popup.wait_for_load_state(\\\"domcontentloaded\\\")\n        print(await popup.title()) # popup is ready to use.\n        ```\n\n        Parameters\n        ----------\n        state : Union[\"domcontentloaded\", \"load\", \"networkidle\", None]\n            Optional load state to wait for, defaults to `load`. If the state has been already reached while loading current\n            document, the method resolves immediately. Can be one of:\n            - `'load'` - wait for the `load` event to be fired.\n            - `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired.\n            - `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use\n              this method for testing, rely on web assertions to assess readiness instead.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_load_state(state=state, timeout=timeout)\n        )\n\n    async def wait_for_url(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        *,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.wait_for_url\n\n        Waits for the main frame to navigate to the given URL.\n\n        **Usage**\n\n        ```py\n        await page.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        await page.wait_for_url(\\\"**/target.html\\\")\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_url(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            )\n        )\n\n    async def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"Page.wait_for_event\n\n        **NOTE** In most cases, you should use `page.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the page is closed before the\n        `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            )\n        )\n\n    async def go_back(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.go_back\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect. If cannot go back, returns `null`.\n\n        Navigate to the previous page in history.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.go_back(timeout=timeout, waitUntil=wait_until)\n        )\n\n    async def go_forward(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.go_forward\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect. If cannot go forward, returns `null`.\n\n        Navigate to the next page in history.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.go_forward(timeout=timeout, waitUntil=wait_until)\n        )\n\n    async def request_gc(self) -> None:\n        \"\"\"Page.request_gc\n\n        Request the page to perform garbage collection. Note that there is no guarantee that all unreachable objects will\n        be collected.\n\n        This is useful to help detect memory leaks. For example, if your page has a large object `'suspect'` that might be\n        leaked, you can check that it does not leak by using a\n        [`WeakRef`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef).\n\n        ```py\n        # 1. In your page, save a WeakRef for the \\\"suspect\\\".\n        await page.evaluate(\\\"globalThis.suspectWeakRef = new WeakRef(suspect)\\\")\n        # 2. Request garbage collection.\n        await page.request_gc()\n        # 3. Check that weak ref does not deref to the original object.\n        assert await page.evaluate(\\\"!globalThis.suspectWeakRef.deref()\\\")\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.request_gc())\n\n    async def emulate_media(\n        self,\n        *,\n        media: typing.Optional[Literal[\"null\", \"print\", \"screen\"]] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n    ) -> None:\n        \"\"\"Page.emulate_media\n\n        This method changes the `CSS media type` through the `media` argument, and/or the `'prefers-colors-scheme'` media\n        feature, using the `colorScheme` argument.\n\n        **Usage**\n\n        ```py\n        await page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → True\n        await page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → False\n\n        await page.emulate_media(media=\\\"print\\\")\n        await page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → False\n        await page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → True\n\n        await page.emulate_media()\n        await page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → True\n        await page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → False\n        ```\n\n        ```py\n        await page.emulate_media(color_scheme=\\\"dark\\\")\n        await page.evaluate(\\\"matchMedia('(prefers-color-scheme: dark)').matches\\\")\n        # → True\n        await page.evaluate(\\\"matchMedia('(prefers-color-scheme: light)').matches\\\")\n        # → False\n        ```\n\n        Parameters\n        ----------\n        media : Union[\"null\", \"print\", \"screen\", None]\n            Changes the CSS media type of the page. The only allowed values are `'Screen'`, `'Print'` and `'Null'`. Passing\n            `'Null'` disables CSS media emulation.\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. Passing `'Null'` disables color scheme emulation.\n            `'no-preference'` is deprecated.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. Passing\n            `null` disables reduced motion emulation.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.emulate_media(\n                media=media,\n                colorScheme=color_scheme,\n                reducedMotion=reduced_motion,\n                forcedColors=forced_colors,\n                contrast=contrast,\n            )\n        )\n\n    async def set_viewport_size(self, viewport_size: ViewportSize) -> None:\n        \"\"\"Page.set_viewport_size\n\n        In the case of multiple pages in a single browser, each page can have its own viewport size. However,\n        `browser.new_context()` allows to set viewport size (and more) for all pages in the context at once.\n\n        `page.set_viewport_size()` will resize the page. A lot of websites don't expect phones to change size, so you\n        should set the viewport size before navigating to the page. `page.set_viewport_size()` will also reset\n        `screen` size, use `browser.new_context()` with `screen` and `viewport` parameters if you need better\n        control of these properties.\n\n        **Usage**\n\n        ```py\n        page = await browser.new_page()\n        await page.set_viewport_size({\\\"width\\\": 640, \\\"height\\\": 480})\n        await page.goto(\\\"https://example.com\\\")\n        ```\n\n        Parameters\n        ----------\n        viewport_size : {width: int, height: int}\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_viewport_size(viewportSize=viewport_size)\n        )\n\n    async def bring_to_front(self) -> None:\n        \"\"\"Page.bring_to_front\n\n        Brings page to front (activates tab).\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.bring_to_front())\n\n    async def add_init_script(\n        self,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n    ) -> None:\n        \"\"\"Page.add_init_script\n\n        Adds a script which would be evaluated in one of the following scenarios:\n        - Whenever the page is navigated.\n        - Whenever the child frame is attached or navigated. In this case, the script is evaluated in the context of the\n          newly attached frame.\n\n        The script is evaluated after the document was created but before any of its scripts were run. This is useful to\n        amend the JavaScript environment, e.g. to seed `Math.random`.\n\n        **Usage**\n\n        An example of overriding `Math.random` before the page loads:\n\n        ```py\n        # in your playwright script, assuming the preload.js file is in same directory\n        await page.add_init_script(path=\\\"./preload.js\\\")\n        ```\n\n        **NOTE** The order of evaluation of multiple scripts installed via `browser_context.add_init_script()` and\n        `page.add_init_script()` is not defined.\n\n        Parameters\n        ----------\n        script : Union[str, None]\n            Script to be evaluated in all pages in the browser context. Optional.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.add_init_script(script=script, path=path)\n        )\n\n    async def route(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Union[\n            typing.Callable[[\"Route\"], typing.Any],\n            typing.Callable[[\"Route\", \"Request\"], typing.Any],\n        ],\n        *,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.route\n\n        Routing provides the capability to modify network requests that are made by a page.\n\n        Once routing is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or\n        aborted.\n\n        **NOTE** The handler will only be called for the first url if the response is a redirect.\n\n        **NOTE** `page.route()` will not intercept requests intercepted by Service Worker. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        **NOTE** `page.route()` will not intercept the first request of a popup page. Use\n        `browser_context.route()` instead.\n\n        **Usage**\n\n        An example of a naive handler that aborts all image requests:\n\n        ```py\n        page = await browser.new_page()\n        await page.route(\\\"**/*.{png,jpg,jpeg}\\\", lambda route: route.abort())\n        await page.goto(\\\"https://example.com\\\")\n        await browser.close()\n        ```\n\n        or the same snippet using a regex pattern instead:\n\n        ```py\n        page = await browser.new_page()\n        await page.route(re.compile(r\\\"(\\\\.png$)|(\\\\.jpg$)\\\"), lambda route: route.abort())\n        await page.goto(\\\"https://example.com\\\")\n        await browser.close()\n        ```\n\n        It is possible to examine the request to decide the route action. For example, mocking all requests that contain\n        some post data, and leaving all other requests as is:\n\n        ```py\n        async def handle_route(route: Route):\n          if (\\\"my-string\\\" in route.request.post_data):\n            await route.fulfill(body=\\\"mocked-data\\\")\n          else:\n            await route.continue_()\n        await page.route(\\\"/api/**\\\", handle_route)\n        ```\n\n        Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request\n        matches both handlers.\n\n        To remove a route with its handler you can use `page.unroute()`.\n\n        **NOTE** Enabling routing disables http cache.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If `baseURL` is set in\n            the context options and the provided URL is a string that does not start with `*`, it is resolved using the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\n            handler function to route the request.\n        times : Union[int, None]\n            How often a route should be used. By default it will be used every time.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route(\n                url=self._wrap_handler(url),\n                handler=self._wrap_handler(handler),\n                times=times,\n            )\n        )\n\n    async def unroute(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Optional[\n            typing.Union[\n                typing.Callable[[\"Route\"], typing.Any],\n                typing.Callable[[\"Route\", \"Request\"], typing.Any],\n            ]\n        ] = None,\n    ) -> None:\n        \"\"\"Page.unroute\n\n        Removes a route created with `page.route()`. When `handler` is not specified, removes all routes for the\n        `url`.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while routing.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\n            Optional handler function to route the request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.unroute(\n                url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n            )\n        )\n\n    async def route_web_socket(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Callable[[\"WebSocketRoute\"], typing.Any],\n    ) -> None:\n        \"\"\"Page.route_web_socket\n\n        This method allows to modify websocket connections that are made by the page.\n\n        Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this\n        method before navigating the page.\n\n        **Usage**\n\n        Below is an example of a simple mock that responds to a single message. See `WebSocketRoute` for more details and\n        examples.\n\n        ```py\n        def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):\n          if message == \\\"request\\\":\n            ws.send(\\\"response\\\")\n\n        def handler(ws: WebSocketRoute):\n          ws.on_message(lambda message: message_handler(ws, message))\n\n        await page.route_web_socket(\\\"/ws\\\", handler)\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the\n            `baseURL` context option.\n        handler : Callable[[WebSocketRoute], Any]\n            Handler function to route the WebSocket.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route_web_socket(\n                url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n            )\n        )\n\n    async def unroute_all(\n        self,\n        *,\n        behavior: typing.Optional[Literal[\"default\", \"ignoreErrors\", \"wait\"]] = None,\n    ) -> None:\n        \"\"\"Page.unroute_all\n\n        Removes all routes created with `page.route()` and `page.route_from_har()`.\n\n        Parameters\n        ----------\n        behavior : Union[\"default\", \"ignoreErrors\", \"wait\", None]\n            Specifies whether to wait for already running handlers and what to do if they throw errors:\n            - `'default'` - do not wait for current handler calls (if any) to finish, if unrouted handler throws, it may\n              result in unhandled error\n            - `'wait'` - wait for current handler calls (if any) to finish\n            - `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers\n              after unrouting are silently caught\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.unroute_all(behavior=behavior)\n        )\n\n    async def route_from_har(\n        self,\n        har: typing.Union[pathlib.Path, str],\n        *,\n        url: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        not_found: typing.Optional[Literal[\"abort\", \"fallback\"]] = None,\n        update: typing.Optional[bool] = None,\n        update_content: typing.Optional[Literal[\"attach\", \"embed\"]] = None,\n        update_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n    ) -> None:\n        \"\"\"Page.route_from_har\n\n        If specified the network requests that are made in the page will be served from the HAR file. Read more about\n        [Replaying from HAR](https://playwright.dev/python/docs/mock#replaying-from-har).\n\n        Playwright will not serve requests intercepted by Service Worker from the HAR file. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        Parameters\n        ----------\n        har : Union[pathlib.Path, str]\n            Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a\n            relative path, then it is resolved relative to the current working directory.\n        url : Union[Pattern[str], str, None]\n            A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the\n            pattern will be served from the HAR file. If not specified, all requests are served from the HAR file.\n        not_found : Union[\"abort\", \"fallback\", None]\n            - If set to 'abort' any request not found in the HAR file will be aborted.\n            - If set to 'fallback' missing requests will be sent to the network.\n\n            Defaults to abort.\n        update : Union[bool, None]\n            If specified, updates the given HAR with the actual network information instead of serving from file. The file is\n            written to disk when `browser_context.close()` is called.\n        update_content : Union[\"attach\", \"embed\", None]\n            Optional setting to control resource content management. If `attach` is specified, resources are persisted as\n            separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.\n        update_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to\n            `minimal`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route_from_har(\n                har=har,\n                url=url,\n                notFound=not_found,\n                update=update,\n                updateContent=update_content,\n                updateMode=update_mode,\n            )\n        )\n\n    async def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        full_page: typing.Optional[bool] = None,\n        clip: typing.Optional[FloatRect] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"Page.screenshot\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        full_page : Union[bool, None]\n            When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to\n            `false`.\n        clip : Union[{x: float, y: float, width: float, height: float}, None]\n            An object which specifies clipping of the resulting image.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.screenshot(\n                timeout=timeout,\n                type=type,\n                path=path,\n                quality=quality,\n                omitBackground=omit_background,\n                fullPage=full_page,\n                clip=clip,\n                animations=animations,\n                caret=caret,\n                scale=scale,\n                mask=mapping.to_impl(mask),\n                maskColor=mask_color,\n                style=style,\n            )\n        )\n\n    async def title(self) -> str:\n        \"\"\"Page.title\n\n        Returns the page's title.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.title())\n\n    async def close(\n        self,\n        *,\n        run_before_unload: typing.Optional[bool] = None,\n        reason: typing.Optional[str] = None,\n    ) -> None:\n        \"\"\"Page.close\n\n        If `runBeforeUnload` is `false`, does not run any unload handlers and waits for the page to be closed. If\n        `runBeforeUnload` is `true` the method will run unload handlers, but will **not** wait for the page to close.\n\n        By default, `page.close()` **does not** run `beforeunload` handlers.\n\n        **NOTE** if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned and should be handled\n        manually via `page.on('dialog')` event.\n\n        Parameters\n        ----------\n        run_before_unload : Union[bool, None]\n            Defaults to `false`. Whether to run the\n            [before unload](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload) page handlers.\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the page closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.close(runBeforeUnload=run_before_unload, reason=reason)\n        )\n\n    def is_closed(self) -> bool:\n        \"\"\"Page.is_closed\n\n        Indicates that the page has been closed.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_closed())\n\n    async def click(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.click\n\n        This method clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.click(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                clickCount=click_count,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n                strict=strict,\n            )\n        )\n\n    async def dblclick(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.dblclick\n\n        This method double clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `page.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dblclick(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def tap(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.tap\n\n        This method taps an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `page.tap()` the method will throw if `hasTouch` option of the browser context is false.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.tap(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def fill(\n        self,\n        selector: str,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.fill\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks,\n        focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string\n        to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : str\n            Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fill(\n                selector=selector,\n                value=value,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                force=force,\n            )\n        )\n\n    def locator(\n        self,\n        selector: str,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.locator\n\n        The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved\n        to the element immediately before performing an action, so a series of actions on the same locator can in fact be\n        performed on different DOM elements. That would happen if the DOM structure between those actions has changed.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selector=selector,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        await page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        await page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        await page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        await page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        await expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        await page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        await page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        await page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        await expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Page.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        **Usage**\n\n        Following snippet locates element with text \\\"Submit\\\" in the iframe with id `my-frame`, like `<iframe\n        id=\\\"my-frame\\\">`:\n\n        ```py\n        locator = page.frame_locator(\\\"#my-iframe\\\").get_by_text(\\\"Submit\\\")\n        await locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    async def focus(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.focus\n\n        This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the\n        method waits until a matching element appears in the DOM.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.focus(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def text_content(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Page.text_content\n\n        Returns `element.textContent`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.text_content(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def inner_text(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.inner_text\n\n        Returns `element.innerText`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.inner_text(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def inner_html(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.inner_html\n\n        Returns `element.innerHTML`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.inner_html(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def get_attribute(\n        self,\n        selector: str,\n        name: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Page.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        name : str\n            Attribute name to get the value for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.get_attribute(\n                selector=selector, name=name, strict=strict, timeout=timeout\n            )\n        )\n\n    async def hover(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.hover\n\n        This method hovers over an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.hover(\n                selector=selector,\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        *,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.drag_and_drop\n\n        This method drags the source element to the target element. It will first move to the source element, perform a\n        `mousedown`, then move to the target element and perform a `mouseup`.\n\n        **Usage**\n\n        ```py\n        await page.drag_and_drop(\\\"#source\\\", \\\"#target\\\")\n        # or specify exact positions relative to the top-left corners of the elements:\n        await page.drag_and_drop(\n          \\\"#source\\\",\n          \\\"#target\\\",\n          source_position={\\\"x\\\": 34, \\\"y\\\": 7},\n          target_position={\\\"x\\\": 10, \\\"y\\\": 20}\n        )\n        ```\n\n        Parameters\n        ----------\n        source : str\n            A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will\n            be used.\n        target : str\n            A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first\n            will be used.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.drag_and_drop(\n                source=source,\n                target=target,\n                sourcePosition=source_position,\n                targetPosition=target_position,\n                force=force,\n                noWaitAfter=no_wait_after,\n                timeout=timeout,\n                strict=strict,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def select_option(\n        self,\n        selector: str,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Page.select_option\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits\n        until all specified options are present in the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        await page.select_option(\\\"select#colors\\\", \\\"blue\\\")\n        # single selection matching the label\n        await page.select_option(\\\"select#colors\\\", label=\\\"blue\\\")\n        # multiple selection\n        await page.select_option(\\\"select#colors\\\", value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_option(\n                selector=selector,\n                value=mapping.to_impl(value),\n                index=mapping.to_impl(index),\n                label=mapping.to_impl(label),\n                element=mapping.to_impl(element),\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n                strict=strict,\n            )\n        )\n\n    async def input_value(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.input_value(\n                selector=selector, strict=strict, timeout=timeout\n            )\n        )\n\n    async def set_input_files(\n        self,\n        selector: str,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs\n        with a `[webkitdirectory]` attribute, only a single directory path is supported.\n\n        This method expects `selector` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_input_files(\n                selector=selector,\n                files=mapping.to_impl(files),\n                timeout=timeout,\n                strict=strict,\n                noWaitAfter=no_wait_after,\n            )\n        )\n\n    async def type(\n        self,\n        selector: str,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.type\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `page.type` can be used to\n        send fine-grained keyboard events. To fill values in form fields, use `page.fill()`.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.type(\n                selector=selector,\n                text=text,\n                delay=delay,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n            )\n        )\n\n    async def press(\n        self,\n        selector: str,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.press\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        **Usage**\n\n        ```py\n        page = await browser.new_page()\n        await page.goto(\\\"https://keycode.info\\\")\n        await page.press(\\\"body\\\", \\\"A\\\")\n        await page.screenshot(path=\\\"a.png\\\")\n        await page.press(\\\"body\\\", \\\"ArrowLeft\\\")\n        await page.screenshot(path=\\\"arrow_left.png\\\")\n        await page.press(\\\"body\\\", \\\"Shift+O\\\")\n        await page.screenshot(path=\\\"o.png\\\")\n        await browser.close()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.press(\n                selector=selector,\n                key=key,\n                delay=delay,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n            )\n        )\n\n    async def check(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.check\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.check(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def uncheck(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.uncheck\n\n        This method unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.uncheck(\n                selector=selector,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def wait_for_timeout(self, timeout: float) -> None:\n        \"\"\"Page.wait_for_timeout\n\n        Waits for the given `timeout` in milliseconds.\n\n        Note that `page.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going\n        to be flaky. Use signals such as network events, selectors becoming visible and others instead.\n\n        **Usage**\n\n        ```py\n        # wait for 1 second\n        await page.wait_for_timeout(1000)\n        ```\n\n        Parameters\n        ----------\n        timeout : float\n            A timeout to wait for\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_timeout(timeout=timeout)\n        )\n\n    async def wait_for_function(\n        self,\n        expression: str,\n        *,\n        arg: typing.Optional[typing.Any] = None,\n        timeout: typing.Optional[float] = None,\n        polling: typing.Optional[typing.Union[float, Literal[\"raf\"]]] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Page.wait_for_function\n\n        Returns when the `expression` returns a truthy value. It resolves to a JSHandle of the truthy value.\n\n        **Usage**\n\n        The `page.wait_for_function()` can be used to observe viewport size change:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch()\n            page = await browser.new_page()\n            await page.evaluate(\\\"window.x = 0; setTimeout(() => { window.x = 100 }, 1000);\\\")\n            await page.wait_for_function(\\\"() => window.x > 0\\\")\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        To pass an argument to the predicate of `page.wait_for_function()` function:\n\n        ```py\n        selector = \\\".foo\\\"\n        await page.wait_for_function(\\\"selector => !!document.querySelector(selector)\\\", selector)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()` or\n            `page.set_default_timeout()` methods.\n        polling : Union[\"raf\", float, None]\n            If `polling` is `'raf'`, then `expression` is constantly executed in `requestAnimationFrame` callback. If `polling`\n            is a number, then it is treated as an interval in milliseconds at which the function would be executed. Defaults to\n            `raf`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.wait_for_function(\n                expression=expression,\n                arg=mapping.to_impl(arg),\n                timeout=timeout,\n                polling=polling,\n            )\n        )\n\n    async def pause(self) -> None:\n        \"\"\"Page.pause\n\n        Pauses script execution. Playwright will stop executing the script and wait for the user to either press the\n        'Resume' button in the page overlay or to call `playwright.resume()` in the DevTools console.\n\n        User can inspect selectors or perform manual steps while paused. Resume will continue running the original script\n        from the place it was paused.\n\n        **NOTE** This method requires Playwright to be started in a headed mode, with a falsy `headless` option.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.pause())\n\n    async def pdf(\n        self,\n        *,\n        scale: typing.Optional[float] = None,\n        display_header_footer: typing.Optional[bool] = None,\n        header_template: typing.Optional[str] = None,\n        footer_template: typing.Optional[str] = None,\n        print_background: typing.Optional[bool] = None,\n        landscape: typing.Optional[bool] = None,\n        page_ranges: typing.Optional[str] = None,\n        format: typing.Optional[str] = None,\n        width: typing.Optional[typing.Union[str, float]] = None,\n        height: typing.Optional[typing.Union[str, float]] = None,\n        prefer_css_page_size: typing.Optional[bool] = None,\n        margin: typing.Optional[PdfMargins] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        outline: typing.Optional[bool] = None,\n        tagged: typing.Optional[bool] = None,\n    ) -> bytes:\n        \"\"\"Page.pdf\n\n        Returns the PDF buffer.\n\n        `page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call\n        `page.emulate_media()` before calling `page.pdf()`:\n\n        **NOTE** By default, `page.pdf()` generates a pdf with modified colors for printing. Use the\n        [`-webkit-print-color-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust)\n        property to force rendering of exact colors.\n\n        **Usage**\n\n        ```py\n        # generates a pdf with \\\"screen\\\" media type.\n        await page.emulate_media(media=\\\"screen\\\")\n        await page.pdf(path=\\\"page.pdf\\\")\n        ```\n\n        The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as\n        pixels.\n\n        A few examples:\n        - `page.pdf({width: 100})` - prints with width set to 100 pixels\n        - `page.pdf({width: '100px'})` - prints with width set to 100 pixels\n        - `page.pdf({width: '10cm'})` - prints with width set to 10 centimeters.\n\n        All possible units are:\n        - `px` - pixel\n        - `in` - inch\n        - `cm` - centimeter\n        - `mm` - millimeter\n\n        The `format` options are:\n        - `Letter`: 8.5in x 11in\n        - `Legal`: 8.5in x 14in\n        - `Tabloid`: 11in x 17in\n        - `Ledger`: 17in x 11in\n        - `A0`: 33.1in x 46.8in\n        - `A1`: 23.4in x 33.1in\n        - `A2`: 16.54in x 23.4in\n        - `A3`: 11.7in x 16.54in\n        - `A4`: 8.27in x 11.7in\n        - `A5`: 5.83in x 8.27in\n        - `A6`: 4.13in x 5.83in\n\n        **NOTE** `headerTemplate` and `footerTemplate` markup have the following limitations: > 1. Script tags inside\n        templates are not evaluated. > 2. Page styles are not visible inside templates.\n\n        Parameters\n        ----------\n        scale : Union[float, None]\n            Scale of the webpage rendering. Defaults to `1`. Scale amount must be between 0.1 and 2.\n        display_header_footer : Union[bool, None]\n            Display header and footer. Defaults to `false`.\n        header_template : Union[str, None]\n            HTML template for the print header. Should be valid HTML markup with following classes used to inject printing\n            values into them:\n            - `'date'` formatted print date\n            - `'title'` document title\n            - `'url'` document location\n            - `'pageNumber'` current page number\n            - `'totalPages'` total pages in the document\n        footer_template : Union[str, None]\n            HTML template for the print footer. Should use the same format as the `headerTemplate`.\n        print_background : Union[bool, None]\n            Print background graphics. Defaults to `false`.\n        landscape : Union[bool, None]\n            Paper orientation. Defaults to `false`.\n        page_ranges : Union[str, None]\n            Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.\n        format : Union[str, None]\n            Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'.\n        width : Union[float, str, None]\n            Paper width, accepts values labeled with units.\n        height : Union[float, str, None]\n            Paper height, accepts values labeled with units.\n        prefer_css_page_size : Union[bool, None]\n            Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format`\n            options. Defaults to `false`, which will scale the content to fit the paper size.\n        margin : Union[{top: Union[float, str, None], right: Union[float, str, None], bottom: Union[float, str, None], left: Union[float, str, None]}, None]\n            Paper margins, defaults to none.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to the current working\n            directory. If no path is provided, the PDF won't be saved to the disk.\n        outline : Union[bool, None]\n            Whether or not to embed the document outline into the PDF. Defaults to `false`.\n        tagged : Union[bool, None]\n            Whether or not to generate tagged (accessible) PDF. Defaults to `false`.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.pdf(\n                scale=scale,\n                displayHeaderFooter=display_header_footer,\n                headerTemplate=header_template,\n                footerTemplate=footer_template,\n                printBackground=print_background,\n                landscape=landscape,\n                pageRanges=page_ranges,\n                format=format,\n                width=width,\n                height=height,\n                preferCSSPageSize=prefer_css_page_size,\n                margin=margin,\n                path=path,\n                outline=outline,\n                tagged=tagged,\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager:\n        \"\"\"Page.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the page is closed before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        async with page.expect_event(\\\"framenavigated\\\") as event_info:\n            await page.get_by_role(\\\"button\\\")\n        frame = await event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_console_message(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"ConsoleMessage\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"ConsoleMessage\"]:\n        \"\"\"Page.expect_console_message\n\n        Performs action and waits for a `ConsoleMessage` to be logged by in the page. If predicate is provided, it passes\n        `ConsoleMessage` value into the `predicate` function and waits for `predicate(message)` to return a truthy value.\n        Will throw an error if the page is closed before the `page.on('console')` event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[ConsoleMessage], bool], None]\n            Receives the `ConsoleMessage` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[ConsoleMessage]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_console_message(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_download(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Download\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Download\"]:\n        \"\"\"Page.expect_download\n\n        Performs action and waits for a new `Download`. If predicate is provided, it passes `Download` value into the\n        `predicate` function and waits for `predicate(download)` to return a truthy value. Will throw an error if the page\n        is closed before the download event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Download], bool], None]\n            Receives the `Download` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Download]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_download(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_file_chooser(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"FileChooser\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"FileChooser\"]:\n        \"\"\"Page.expect_file_chooser\n\n        Performs action and waits for a new `FileChooser` to be created. If predicate is provided, it passes `FileChooser`\n        value into the `predicate` function and waits for `predicate(fileChooser)` to return a truthy value. Will throw an\n        error if the page is closed before the file chooser is opened.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[FileChooser], bool], None]\n            Receives the `FileChooser` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[FileChooser]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_file_chooser(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_navigation(\n        self,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Response\"]:\n        \"\"\"Page.expect_navigation\n\n        Waits for the main frame navigation and returns the main resource response. In case of multiple redirects, the\n        navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or\n        navigation due to History API usage, the navigation will resolve with `null`.\n\n        **Usage**\n\n        This resolves when the page navigates to a new URL or reloads. It is useful for when you run code which will\n        indirectly cause the page to navigate. e.g. The click target has an `onclick` handler that triggers navigation from\n        a `setTimeout`. Consider this example:\n\n        ```py\n        async with page.expect_navigation():\n            # This action triggers the navigation after a timeout.\n            await page.get_by_text(\\\"Navigate after timeout\\\").click()\n        # Resolves after navigation has finished\n        ```\n\n        **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL\n        is considered a navigation.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_navigation(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            ).future\n        )\n\n    def expect_popup(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Page\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Page\"]:\n        \"\"\"Page.expect_popup\n\n        Performs action and waits for a popup `Page`. If predicate is provided, it passes [Popup] value into the\n        `predicate` function and waits for `predicate(page)` to return a truthy value. Will throw an error if the page is\n        closed before the popup event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Page], bool], None]\n            Receives the `Page` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Page]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_popup(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_request(\n        self,\n        url_or_predicate: typing.Union[\n            str, typing.Pattern[str], typing.Callable[[\"Request\"], bool]\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Request\"]:\n        \"\"\"Page.expect_request\n\n        Waits for the matching request and returns it. See [waiting for event](https://playwright.dev/python/docs/events#waiting-for-event) for more\n        details about events.\n\n        **Usage**\n\n        ```py\n        async with page.expect_request(\\\"http://example.com/resource\\\") as first:\n            await page.get_by_text(\\\"trigger request\\\").click()\n        first_request = await first.value\n\n        # or with a lambda\n        async with page.expect_request(lambda request: request.url == \\\"http://example.com\\\" and request.method == \\\"get\\\") as second:\n            await page.get_by_text(\\\"trigger request\\\").click()\n        second_request = await second.value\n        ```\n\n        Parameters\n        ----------\n        url_or_predicate : Union[Callable[[Request], bool], Pattern[str], str]\n            Request URL string, regex or predicate receiving `Request` object. When a `baseURL` via the context options was\n            provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can\n            be changed by using the `page.set_default_timeout()` method.\n\n        Returns\n        -------\n        EventContextManager[Request]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_request(\n                urlOrPredicate=self._wrap_handler(url_or_predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_request_finished(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Request\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Request\"]:\n        \"\"\"Page.expect_request_finished\n\n        Performs action and waits for a `Request` to finish loading. If predicate is provided, it passes `Request` value\n        into the `predicate` function and waits for `predicate(request)` to return a truthy value. Will throw an error if\n        the page is closed before the `page.on('request_finished')` event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Request], bool], None]\n            Receives the `Request` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Request]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_request_finished(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_response(\n        self,\n        url_or_predicate: typing.Union[\n            str, typing.Pattern[str], typing.Callable[[\"Response\"], bool]\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Response\"]:\n        \"\"\"Page.expect_response\n\n        Returns the matched response. See [waiting for event](https://playwright.dev/python/docs/events#waiting-for-event) for more details about\n        events.\n\n        **Usage**\n\n        ```py\n        async with page.expect_response(\\\"https://example.com/resource\\\") as response_info:\n            await page.get_by_text(\\\"trigger response\\\").click()\n        response = await response_info.value\n        return response.ok\n\n        # or with a lambda\n        async with page.expect_response(lambda response: response.url == \\\"https://example.com\\\" and response.status == 200 and response.request.method == \\\"get\\\") as response_info:\n            await page.get_by_text(\\\"trigger response\\\").click()\n        response = await response_info.value\n        return response.ok\n        ```\n\n        Parameters\n        ----------\n        url_or_predicate : Union[Callable[[Response], bool], Pattern[str], str]\n            Request URL string, regex or predicate receiving `Response` object. When a `baseURL` via the context options was\n            provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_response(\n                urlOrPredicate=self._wrap_handler(url_or_predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_websocket(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"WebSocket\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"WebSocket\"]:\n        \"\"\"Page.expect_websocket\n\n        Performs action and waits for a new `WebSocket`. If predicate is provided, it passes `WebSocket` value into the\n        `predicate` function and waits for `predicate(webSocket)` to return a truthy value. Will throw an error if the page\n        is closed before the WebSocket event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[WebSocket], bool], None]\n            Receives the `WebSocket` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[WebSocket]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_websocket(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_worker(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Worker\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Worker\"]:\n        \"\"\"Page.expect_worker\n\n        Performs action and waits for a new `Worker`. If predicate is provided, it passes `Worker` value into the\n        `predicate` function and waits for `predicate(worker)` to return a truthy value. Will throw an error if the page is\n        closed before the worker event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Worker], bool], None]\n            Receives the `Worker` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Worker]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_worker(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    async def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.set_checked\n\n        This method checks or unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_checked(\n                selector=selector,\n                checked=checked,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                strict=strict,\n                trial=trial,\n            )\n        )\n\n    async def add_locator_handler(\n        self,\n        locator: \"Locator\",\n        handler: typing.Union[\n            typing.Callable[[\"Locator\"], typing.Any], typing.Callable[[], typing.Any]\n        ],\n        *,\n        no_wait_after: typing.Optional[bool] = None,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.add_locator_handler\n\n        When testing a web page, sometimes unexpected overlays like a \\\"Sign up\\\" dialog appear and block actions you want to\n        automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making\n        them tricky to handle in automated tests.\n\n        This method lets you set up a special function, called a handler, that activates when it detects that overlay is\n        visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.\n\n        Things to keep in mind:\n        - When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as\n          a part of your normal test flow, instead of using `page.add_locator_handler()`.\n        - Playwright checks for the overlay every time before executing or retrying an action that requires an\n          [actionability check](https://playwright.dev/python/docs/actionability), or before performing an auto-waiting assertion check. When overlay\n          is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the\n          handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't\n          perform any actions, the handler will not be triggered.\n        - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible\n          anymore. You can opt-out of this behavior with `noWaitAfter`.\n        - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler.\n          If your handler takes too long, it might cause timeouts.\n        - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the\n          actions within a handler don't depend on another handler.\n\n        **NOTE** Running the handler will alter your page state mid-test. For example it will change the currently focused\n        element and move the mouse. Make sure that actions that run after the handler are self-contained and do not rely on\n        the focus and mouse state being unchanged.\n\n        For example, consider a test that calls `locator.focus()` followed by `keyboard.press()`. If your\n        handler clicks a button between these two actions, the focused element most likely will be wrong, and key press\n        will happen on the unexpected element. Use `locator.press()` instead to avoid this problem.\n\n        Another example is a series of mouse actions, where `mouse.move()` is followed by `mouse.down()`.\n        Again, when the handler runs between these two actions, the mouse position will be wrong during the mouse down.\n        Prefer self-contained actions like `locator.click()` that do not rely on the state being unchanged by a\n        handler.\n\n        **Usage**\n\n        An example that closes a \\\"Sign up to the newsletter\\\" dialog when it appears:\n\n        ```py\n        # Setup the handler.\n        def handler():\n          page.get_by_role(\\\"button\\\", name=\\\"No thanks\\\").click()\n        page.add_locator_handler(page.get_by_text(\\\"Sign up to the newsletter\\\"), handler)\n\n        # Write the test as usual.\n        page.goto(\\\"https://example.com\\\")\n        page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        An example that skips the \\\"Confirm your security details\\\" page when it is shown:\n\n        ```py\n        # Setup the handler.\n        def handler():\n          page.get_by_role(\\\"button\\\", name=\\\"Remind me later\\\").click()\n        page.add_locator_handler(page.get_by_text(\\\"Confirm your security details\\\"), handler)\n\n        # Write the test as usual.\n        page.goto(\\\"https://example.com\\\")\n        page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible,\n        so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the\n        handler does not hide the `<body>` element.\n\n        ```py\n        # Setup the handler.\n        def handler():\n          page.evaluate(\\\"window.removeObstructionsForTestIfNeeded()\\\")\n        page.add_locator_handler(page.locator(\\\"body\\\"), handler, no_wait_after=True)\n\n        # Write the test as usual.\n        page.goto(\\\"https://example.com\\\")\n        page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        Handler takes the original locator as an argument. You can also automatically remove the handler after a number of\n        invocations by setting `times`:\n\n        ```py\n        def handler(locator):\n          locator.click()\n        page.add_locator_handler(page.get_by_label(\\\"Close\\\"), handler, times=1)\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Locator that triggers the handler.\n        handler : Union[Callable[[Locator], Any], Callable[[], Any]]\n            Function that should be run once `locator` appears. This function should get rid of the element that blocks actions\n            like click.\n        no_wait_after : Union[bool, None]\n            By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then\n            Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of\n            this behavior, so that overlay can stay visible after the handler has run.\n        times : Union[int, None]\n            Specifies the maximum number of times this handler should be called. Unlimited by default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.add_locator_handler(\n                locator=locator._impl_obj,\n                handler=self._wrap_handler(handler),\n                noWaitAfter=no_wait_after,\n                times=times,\n            )\n        )\n\n    async def remove_locator_handler(self, locator: \"Locator\") -> None:\n        \"\"\"Page.remove_locator_handler\n\n        Removes all locator handlers added by `page.add_locator_handler()` for a specific locator.\n\n        Parameters\n        ----------\n        locator : Locator\n            Locator passed to `page.add_locator_handler()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.remove_locator_handler(locator=locator._impl_obj)\n        )\n\n    async def requests(self) -> typing.List[\"Request\"]:\n        \"\"\"Page.requests\n\n        Returns up to (currently) 100 last network request from this page. See `page.on('request')` for more details.\n\n        Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory\n        growth as new requests come in. Once collected, retrieving most information about the request is impossible.\n\n        Note that requests reported through the `page.on('request')` request are not collected, so there is a trade off\n        between efficient memory usage with `page.requests()` and the amount of available information reported\n        through `page.on('request')`.\n\n        Returns\n        -------\n        List[Request]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.requests())\n\n    async def console_messages(self) -> typing.List[\"ConsoleMessage\"]:\n        \"\"\"Page.console_messages\n\n        Returns up to (currently) 200 last console messages from this page. See `page.on('console')` for more details.\n\n        Returns\n        -------\n        List[ConsoleMessage]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.console_messages())\n\n    async def page_errors(self) -> typing.List[\"Error\"]:\n        \"\"\"Page.page_errors\n\n        Returns up to (currently) 200 last page errors from this page. See `page.on('page_error')` for more details.\n\n        Returns\n        -------\n        List[Error]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.page_errors())\n\n\nmapping.register(PageImpl, Page)\n\n\nclass WebError(AsyncBase):\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"WebError.page\n\n        The page that produced this unhandled exception, if any.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    @property\n    def error(self) -> \"Error\":\n        \"\"\"WebError.error\n\n        Unhandled error that was thrown.\n\n        Returns\n        -------\n        Error\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.error)\n\n\nmapping.register(WebErrorImpl, WebError)\n\n\nclass BrowserContext(AsyncContextManager):\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"backgroundpage\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        This event is not emitted.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[\n            [\"BrowserContext\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when Browser context gets closed. This might happen because of one of the following:\n        - Browser context is closed.\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` and the page are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        async def print_args(msg):\n            values = []\n            for arg in msg.args:\n                values.append(await arg.json_value())\n            print(values)\n\n        context.on(\\\"console\\\", print_args)\n        await page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"dialog\"],\n        f: typing.Callable[[\"Dialog\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        context.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"page\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event\n        will also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a\n        specific page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        async with context.expect_page() as page_info:\n            await page.get_by_text(\\\"open new page\\\").click(),\n        page = await page_info.value\n        print(await page.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"weberror\"],\n        f: typing.Callable[[\"WebError\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular\n        page, use `page.on('page_error')` instead.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"request\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To\n        only listen for requests from a particular page, use `page.on('request')`.\n\n        In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"requestfailed\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page,\n        use `page.on('request_failed')`.\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `browser_context.on('request_finished')` event and not with\n        `browser_context.on('request_failed')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"requestfinished\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a\n        particular page, use `page.on('request_finished')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"response\"],\n        f: typing.Callable[[\"Response\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use\n        `page.on('response')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"serviceworker\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        Emitted when new service worker is created in the context.\"\"\"\n\n    def on(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"backgroundpage\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        This event is not emitted.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"close\"],\n        f: typing.Callable[\n            [\"BrowserContext\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when Browser context gets closed. This might happen because of one of the following:\n        - Browser context is closed.\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"console\"],\n        f: typing.Callable[\n            [\"ConsoleMessage\"], \"typing.Union[typing.Awaitable[None], None]\"\n        ],\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` and the page are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        async def print_args(msg):\n            values = []\n            for arg in msg.args:\n                values.append(await arg.json_value())\n            print(values)\n\n        context.on(\\\"console\\\", print_args)\n        await page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"dialog\"],\n        f: typing.Callable[[\"Dialog\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        context.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"page\"],\n        f: typing.Callable[[\"Page\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event\n        will also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a\n        specific page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        async with context.expect_page() as page_info:\n            await page.get_by_text(\\\"open new page\\\").click(),\n        page = await page_info.value\n        print(await page.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"weberror\"],\n        f: typing.Callable[[\"WebError\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular\n        page, use `page.on('page_error')` instead.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"request\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To\n        only listen for requests from a particular page, use `page.on('request')`.\n\n        In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"requestfailed\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page,\n        use `page.on('request_failed')`.\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `browser_context.on('request_finished')` event and not with\n        `browser_context.on('request_failed')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"requestfinished\"],\n        f: typing.Callable[[\"Request\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a\n        particular page, use `page.on('request_finished')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"response\"],\n        f: typing.Callable[[\"Response\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use\n        `page.on('response')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"serviceworker\"],\n        f: typing.Callable[[\"Worker\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        Emitted when new service worker is created in the context.\"\"\"\n\n    def once(\n        self,\n        event: str,\n        f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]],\n    ) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def pages(self) -> typing.List[\"Page\"]:\n        \"\"\"BrowserContext.pages\n\n        Returns all open pages in the context.\n\n        Returns\n        -------\n        List[Page]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.pages)\n\n    @property\n    def browser(self) -> typing.Optional[\"Browser\"]:\n        \"\"\"BrowserContext.browser\n\n        Gets the browser instance that owns the context. Returns `null` if the context is created outside of normal\n        browser, e.g. Android or Electron.\n\n        Returns\n        -------\n        Union[Browser, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.browser)\n\n    @property\n    def background_pages(self) -> typing.List[\"Page\"]:\n        \"\"\"BrowserContext.background_pages\n\n        Returns an empty list.\n\n        Returns\n        -------\n        List[Page]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.background_pages)\n\n    @property\n    def service_workers(self) -> typing.List[\"Worker\"]:\n        \"\"\"BrowserContext.service_workers\n\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        All existing service workers in the context.\n\n        Returns\n        -------\n        List[Worker]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.service_workers)\n\n    @property\n    def tracing(self) -> \"Tracing\":\n        \"\"\"BrowserContext.tracing\n\n        Returns\n        -------\n        Tracing\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.tracing)\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        \"\"\"BrowserContext.request\n\n        API testing helper associated with this context. Requests made with this API will use context cookies.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def clock(self) -> \"Clock\":\n        \"\"\"BrowserContext.clock\n\n        Playwright has ability to mock clock and passage of time.\n\n        Returns\n        -------\n        Clock\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.clock)\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        \"\"\"BrowserContext.set_default_navigation_timeout\n\n        This setting will change the default maximum navigation time for the following methods and related shortcuts:\n        - `page.go_back()`\n        - `page.go_forward()`\n        - `page.goto()`\n        - `page.reload()`\n        - `page.set_content()`\n        - `page.expect_navigation()`\n\n        **NOTE** `page.set_default_navigation_timeout()` and `page.set_default_timeout()` take priority over\n        `browser_context.set_default_navigation_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum navigation time in milliseconds\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_navigation_timeout(timeout=timeout)\n        )\n\n    def set_default_timeout(self, timeout: float) -> None:\n        \"\"\"BrowserContext.set_default_timeout\n\n        This setting will change the default maximum time for all the methods accepting `timeout` option.\n\n        **NOTE** `page.set_default_navigation_timeout()`, `page.set_default_timeout()` and\n        `browser_context.set_default_navigation_timeout()` take priority over\n        `browser_context.set_default_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum time in milliseconds. Pass `0` to disable timeout.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_timeout(timeout=timeout)\n        )\n\n    async def new_page(self) -> \"Page\":\n        \"\"\"BrowserContext.new_page\n\n        Creates a new page in the browser context.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.new_page())\n\n    async def cookies(\n        self, urls: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None\n    ) -> typing.List[Cookie]:\n        \"\"\"BrowserContext.cookies\n\n        If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those\n        URLs are returned.\n\n        Parameters\n        ----------\n        urls : Union[Sequence[str], str, None]\n            Optional list of URLs.\n\n        Returns\n        -------\n        List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"], partitionKey: Union[str, None]}]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            await self._impl_obj.cookies(urls=mapping.to_impl(urls))\n        )\n\n    async def add_cookies(self, cookies: typing.Sequence[SetCookieParam]) -> None:\n        \"\"\"BrowserContext.add_cookies\n\n        Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies\n        can be obtained via `browser_context.cookies()`.\n\n        **Usage**\n\n        ```py\n        await browser_context.add_cookies([cookie_object1, cookie_object2])\n        ```\n\n        Parameters\n        ----------\n        cookies : Sequence[{name: str, value: str, url: Union[str, None], domain: Union[str, None], path: Union[str, None], expires: Union[float, None], httpOnly: Union[bool, None], secure: Union[bool, None], sameSite: Union[\"Lax\", \"None\", \"Strict\", None], partitionKey: Union[str, None]}]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.add_cookies(cookies=mapping.to_impl(cookies))\n        )\n\n    async def clear_cookies(\n        self,\n        *,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        domain: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        path: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.clear_cookies\n\n        Removes cookies from context. Accepts optional filter.\n\n        **Usage**\n\n        ```py\n        await context.clear_cookies()\n        await context.clear_cookies(name=\\\"session-id\\\")\n        await context.clear_cookies(domain=\\\"my-origin.com\\\")\n        await context.clear_cookies(path=\\\"/api/v1\\\")\n        await context.clear_cookies(name=\\\"session-id\\\", domain=\\\"my-origin.com\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str, None]\n            Only removes cookies with the given name.\n        domain : Union[Pattern[str], str, None]\n            Only removes cookies with the given domain.\n        path : Union[Pattern[str], str, None]\n            Only removes cookies with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.clear_cookies(name=name, domain=domain, path=path)\n        )\n\n    async def grant_permissions(\n        self, permissions: typing.Sequence[str], *, origin: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"BrowserContext.grant_permissions\n\n        Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if\n        specified.\n\n        Parameters\n        ----------\n        permissions : Sequence[str]\n            A list of permissions to grant.\n\n            **NOTE** Supported permissions differ between browsers, and even between different versions of the same browser.\n            Any permission may stop working after an update.\n\n            Here are some permissions that may be supported by some browsers:\n            - `'accelerometer'`\n            - `'ambient-light-sensor'`\n            - `'background-sync'`\n            - `'camera'`\n            - `'clipboard-read'`\n            - `'clipboard-write'`\n            - `'geolocation'`\n            - `'gyroscope'`\n            - `'local-fonts'`\n            - `'local-network-access'`\n            - `'magnetometer'`\n            - `'microphone'`\n            - `'midi-sysex'` (system-exclusive midi)\n            - `'midi'`\n            - `'notifications'`\n            - `'payment-handler'`\n            - `'storage-access'`\n        origin : Union[str, None]\n            The [origin] to grant permissions to, e.g. \"https://example.com\".\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.grant_permissions(\n                permissions=mapping.to_impl(permissions), origin=origin\n            )\n        )\n\n    async def clear_permissions(self) -> None:\n        \"\"\"BrowserContext.clear_permissions\n\n        Clears all permission overrides for the browser context.\n\n        **Usage**\n\n        ```py\n        context = await browser.new_context()\n        await context.grant_permissions([\\\"clipboard-read\\\"])\n        # do stuff ..\n        context.clear_permissions()\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.clear_permissions())\n\n    async def set_geolocation(\n        self, geolocation: typing.Optional[Geolocation] = None\n    ) -> None:\n        \"\"\"BrowserContext.set_geolocation\n\n        Sets the context's geolocation. Passing `null` or `undefined` emulates position unavailable.\n\n        **Usage**\n\n        ```py\n        await browser_context.set_geolocation({\\\"latitude\\\": 59.95, \\\"longitude\\\": 30.31667})\n        ```\n\n        **NOTE** Consider using `browser_context.grant_permissions()` to grant permissions for the browser context\n        pages to read its geolocation.\n\n        Parameters\n        ----------\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_geolocation(geolocation=geolocation)\n        )\n\n    async def set_extra_http_headers(self, headers: typing.Dict[str, str]) -> None:\n        \"\"\"BrowserContext.set_extra_http_headers\n\n        The extra HTTP headers will be sent with every request initiated by any page in the context. These headers are\n        merged with page-specific extra HTTP headers set with `page.set_extra_http_headers()`. If page overrides a\n        particular header, page-specific header value will be used instead of the browser context header value.\n\n        **NOTE** `browser_context.set_extra_http_headers()` does not guarantee the order of headers in the outgoing\n        requests.\n\n        Parameters\n        ----------\n        headers : Dict[str, str]\n            An object containing additional HTTP headers to be sent with every request. All header values must be strings.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_extra_http_headers(\n                headers=mapping.to_impl(headers)\n            )\n        )\n\n    async def set_offline(self, offline: bool) -> None:\n        \"\"\"BrowserContext.set_offline\n\n        Parameters\n        ----------\n        offline : bool\n            Whether to emulate network being offline for the browser context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_offline(offline=offline)\n        )\n\n    async def add_init_script(\n        self,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.add_init_script\n\n        Adds a script which would be evaluated in one of the following scenarios:\n        - Whenever a page is created in the browser context or is navigated.\n        - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is\n          evaluated in the context of the newly attached frame.\n\n        The script is evaluated after the document was created but before any of its scripts were run. This is useful to\n        amend the JavaScript environment, e.g. to seed `Math.random`.\n\n        **Usage**\n\n        An example of overriding `Math.random` before the page loads:\n\n        ```py\n        # in your playwright script, assuming the preload.js file is in same directory.\n        await browser_context.add_init_script(path=\\\"preload.js\\\")\n        ```\n\n        **NOTE** The order of evaluation of multiple scripts installed via `browser_context.add_init_script()` and\n        `page.add_init_script()` is not defined.\n\n        Parameters\n        ----------\n        script : Union[str, None]\n            Script to be evaluated in all pages in the browser context. Optional.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.add_init_script(script=script, path=path)\n        )\n\n    async def expose_binding(\n        self,\n        name: str,\n        callback: typing.Callable,\n        *,\n        handle: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"BrowserContext.expose_binding\n\n        The method adds a function called `name` on the `window` object of every frame in every page in the context. When\n        called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n        If the `callback` returns a [Promise], it will be awaited.\n\n        The first argument of the `callback` function contains information about the caller: `{ browserContext:\n        BrowserContext, page: Page, frame: Frame }`.\n\n        See `page.expose_binding()` for page-only version.\n\n        **Usage**\n\n        An example of exposing page URL to all frames in all pages in the context:\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch(headless=False)\n            context = await browser.new_context()\n            await context.expose_binding(\\\"pageURL\\\", lambda source: source[\\\"page\\\"].url)\n            page = await context.new_page()\n            await page.set_content(\\\"\\\"\\\"\n            <script>\n              async function onClick() {\n                document.querySelector('div').textContent = await window.pageURL();\n              }\n            </script>\n            <button onclick=\\\"onClick()\\\">Click me</button>\n            <div></div>\n            \\\"\\\"\\\")\n            await page.get_by_role(\\\"button\\\").click()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        handle : Union[bool, None]\n            Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is\n            supported. When passing by value, multiple arguments are supported.\n            Deprecated: This option will be removed in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.expose_binding(\n                name=name, callback=self._wrap_handler(callback), handle=handle\n            )\n        )\n\n    async def expose_function(self, name: str, callback: typing.Callable) -> None:\n        \"\"\"BrowserContext.expose_function\n\n        The method adds a function called `name` on the `window` object of every frame in every page in the context. When\n        called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n\n        If the `callback` returns a [Promise], it will be awaited.\n\n        See `page.expose_function()` for page-only version.\n\n        **Usage**\n\n        An example of adding a `sha256` function to all pages in the context:\n\n        ```py\n        import asyncio\n        import hashlib\n        from playwright.async_api import async_playwright, Playwright\n\n        def sha256(text: str) -> str:\n            m = hashlib.sha256()\n            m.update(bytes(text, \\\"utf8\\\"))\n            return m.hexdigest()\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = await webkit.launch(headless=False)\n            context = await browser.new_context()\n            await context.expose_function(\\\"sha256\\\", sha256)\n            page = await context.new_page()\n            await page.set_content(\\\"\\\"\\\"\n                <script>\n                  async function onClick() {\n                    document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n                  }\n                </script>\n                <button onclick=\\\"onClick()\\\">Click me</button>\n                <div></div>\n            \\\"\\\"\\\")\n            await page.get_by_role(\\\"button\\\").click()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.expose_function(\n                name=name, callback=self._wrap_handler(callback)\n            )\n        )\n\n    async def route(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Union[\n            typing.Callable[[\"Route\"], typing.Any],\n            typing.Callable[[\"Route\", \"Request\"], typing.Any],\n        ],\n        *,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"BrowserContext.route\n\n        Routing provides the capability to modify network requests that are made by any page in the browser context. Once\n        route is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.\n\n        **NOTE** `browser_context.route()` will not intercept requests intercepted by Service Worker. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        **Usage**\n\n        An example of a naive handler that aborts all image requests:\n\n        ```py\n        context = await browser.new_context()\n        page = await context.new_page()\n        await context.route(\\\"**/*.{png,jpg,jpeg}\\\", lambda route: route.abort())\n        await page.goto(\\\"https://example.com\\\")\n        await browser.close()\n        ```\n\n        or the same snippet using a regex pattern instead:\n\n        ```py\n        context = await browser.new_context()\n        page = await context.new_page()\n        await context.route(re.compile(r\\\"(\\\\.png$)|(\\\\.jpg$)\\\"), lambda route: route.abort())\n        page = await context.new_page()\n        await page.goto(\\\"https://example.com\\\")\n        await browser.close()\n        ```\n\n        It is possible to examine the request to decide the route action. For example, mocking all requests that contain\n        some post data, and leaving all other requests as is:\n\n        ```py\n        async def handle_route(route: Route):\n          if (\\\"my-string\\\" in route.request.post_data):\n            await route.fulfill(body=\\\"mocked-data\\\")\n          else:\n            await route.continue_()\n        await context.route(\\\"/api/**\\\", handle_route)\n        ```\n\n        Page routes (set up with `page.route()`) take precedence over browser context routes when request matches\n        both handlers.\n\n        To remove a route with its handler you can use `browser_context.unroute()`.\n\n        **NOTE** Enabling routing disables http cache.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If `baseURL` is set in\n            the context options and the provided URL is a string that does not start with `*`, it is resolved using the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\n            handler function to route the request.\n        times : Union[int, None]\n            How often a route should be used. By default it will be used every time.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route(\n                url=self._wrap_handler(url),\n                handler=self._wrap_handler(handler),\n                times=times,\n            )\n        )\n\n    async def unroute(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Optional[\n            typing.Union[\n                typing.Callable[[\"Route\"], typing.Any],\n                typing.Callable[[\"Route\", \"Request\"], typing.Any],\n            ]\n        ] = None,\n    ) -> None:\n        \"\"\"BrowserContext.unroute\n\n        Removes a route created with `browser_context.route()`. When `handler` is not specified, removes all routes\n        for the `url`.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with\n            `browser_context.route()`.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\n            Optional handler function used to register a routing with `browser_context.route()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.unroute(\n                url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n            )\n        )\n\n    async def route_web_socket(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Callable[[\"WebSocketRoute\"], typing.Any],\n    ) -> None:\n        \"\"\"BrowserContext.route_web_socket\n\n        This method allows to modify websocket connections that are made by any page in the browser context.\n\n        Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this\n        method before creating any pages.\n\n        **Usage**\n\n        Below is an example of a simple handler that blocks some websocket messages. See `WebSocketRoute` for more details\n        and examples.\n\n        ```py\n        def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):\n          if message == \\\"to-be-blocked\\\":\n            return\n          ws.send(message)\n\n        async def handler(ws: WebSocketRoute):\n          ws.route_send(lambda message: message_handler(ws, message))\n          await ws.connect()\n\n        await context.route_web_socket(\\\"/ws\\\", handler)\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the\n            `baseURL` context option.\n        handler : Callable[[WebSocketRoute], Any]\n            Handler function to route the WebSocket.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route_web_socket(\n                url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n            )\n        )\n\n    async def unroute_all(\n        self,\n        *,\n        behavior: typing.Optional[Literal[\"default\", \"ignoreErrors\", \"wait\"]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.unroute_all\n\n        Removes all routes created with `browser_context.route()` and `browser_context.route_from_har()`.\n\n        Parameters\n        ----------\n        behavior : Union[\"default\", \"ignoreErrors\", \"wait\", None]\n            Specifies whether to wait for already running handlers and what to do if they throw errors:\n            - `'default'` - do not wait for current handler calls (if any) to finish, if unrouted handler throws, it may\n              result in unhandled error\n            - `'wait'` - wait for current handler calls (if any) to finish\n            - `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers\n              after unrouting are silently caught\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.unroute_all(behavior=behavior)\n        )\n\n    async def route_from_har(\n        self,\n        har: typing.Union[pathlib.Path, str],\n        *,\n        url: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        not_found: typing.Optional[Literal[\"abort\", \"fallback\"]] = None,\n        update: typing.Optional[bool] = None,\n        update_content: typing.Optional[Literal[\"attach\", \"embed\"]] = None,\n        update_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.route_from_har\n\n        If specified the network requests that are made in the context will be served from the HAR file. Read more about\n        [Replaying from HAR](https://playwright.dev/python/docs/mock#replaying-from-har).\n\n        Playwright will not serve requests intercepted by Service Worker from the HAR file. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        Parameters\n        ----------\n        har : Union[pathlib.Path, str]\n            Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a\n            relative path, then it is resolved relative to the current working directory.\n        url : Union[Pattern[str], str, None]\n            A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the\n            pattern will be served from the HAR file. If not specified, all requests are served from the HAR file.\n        not_found : Union[\"abort\", \"fallback\", None]\n            - If set to 'abort' any request not found in the HAR file will be aborted.\n            - If set to 'fallback' falls through to the next route handler in the handler chain.\n\n            Defaults to abort.\n        update : Union[bool, None]\n            If specified, updates the given HAR with the actual network information instead of serving from file. The file is\n            written to disk when `browser_context.close()` is called.\n        update_content : Union[\"attach\", \"embed\", None]\n            Optional setting to control resource content management. If `attach` is specified, resources are persisted as\n            separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.\n        update_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to\n            `minimal`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.route_from_har(\n                har=har,\n                url=url,\n                notFound=not_found,\n                update=update,\n                updateContent=update_content,\n                updateMode=update_mode,\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager:\n        \"\"\"BrowserContext.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the context closes before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        async with context.expect_event(\\\"page\\\") as event_info:\n            await page.get_by_role(\\\"button\\\").click()\n        page = await event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one would pass into `browserContext.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    async def close(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"BrowserContext.close\n\n        Closes the browser context. All the pages that belong to the browser context will be closed.\n\n        **NOTE** The default browser context cannot be closed.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the context closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.close(reason=reason))\n\n    async def storage_state(\n        self,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        indexed_db: typing.Optional[bool] = None,\n    ) -> StorageState:\n        \"\"\"BrowserContext.storage_state\n\n        Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB\n        snapshot.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current\n            working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.\n        indexed_db : Union[bool, None]\n            Set to `true` to include [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) in the storage\n            state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase Authentication,\n            enable this.\n\n        Returns\n        -------\n        {cookies: List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: List[{origin: str, localStorage: List[{name: str, value: str}]}]}\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.storage_state(path=path, indexedDB=indexed_db)\n        )\n\n    async def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"BrowserContext.wait_for_event\n\n        **NOTE** In most cases, you should use `browser_context.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the browser context is closed\n        before the `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            )\n        )\n\n    def expect_console_message(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"ConsoleMessage\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"ConsoleMessage\"]:\n        \"\"\"BrowserContext.expect_console_message\n\n        Performs action and waits for a `ConsoleMessage` to be logged by in the pages in the context. If predicate is\n        provided, it passes `ConsoleMessage` value into the `predicate` function and waits for `predicate(message)` to\n        return a truthy value. Will throw an error if the page is closed before the `browser_context.on('console')` event\n        is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[ConsoleMessage], bool], None]\n            Receives the `ConsoleMessage` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[ConsoleMessage]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_console_message(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    def expect_page(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Page\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> AsyncEventContextManager[\"Page\"]:\n        \"\"\"BrowserContext.expect_page\n\n        Performs action and waits for a new `Page` to be created in the context. If predicate is provided, it passes `Page`\n        value into the `predicate` function and waits for `predicate(event)` to return a truthy value. Will throw an error\n        if the context closes before new `Page` is created.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Page], bool], None]\n            Receives the `Page` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Page]\n        \"\"\"\n\n        return AsyncEventContextManager(\n            self._impl_obj.expect_page(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future\n        )\n\n    async def new_cdp_session(\n        self, page: typing.Union[\"Page\", \"Frame\"]\n    ) -> \"CDPSession\":\n        \"\"\"BrowserContext.new_cdp_session\n\n        **NOTE** CDP sessions are only supported on Chromium-based browsers.\n\n        Returns the newly created session.\n\n        Parameters\n        ----------\n        page : Union[Frame, Page]\n            Target to create new session for. For backwards-compatibility, this parameter is named `page`, but it can be a\n            `Page` or `Frame` type.\n\n        Returns\n        -------\n        CDPSession\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.new_cdp_session(page=page))\n\n\nmapping.register(BrowserContextImpl, BrowserContext)\n\n\nclass CDPSession(AsyncBase):\n\n    async def send(\n        self, method: str, params: typing.Optional[typing.Dict] = None\n    ) -> typing.Dict:\n        \"\"\"CDPSession.send\n\n        Parameters\n        ----------\n        method : str\n            Protocol method name.\n        params : Union[Dict, None]\n            Optional method parameters.\n\n        Returns\n        -------\n        Dict\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.send(method=method, params=mapping.to_impl(params))\n        )\n\n    async def detach(self) -> None:\n        \"\"\"CDPSession.detach\n\n        Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be\n        used to send messages.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.detach())\n\n\nmapping.register(CDPSessionImpl, CDPSession)\n\n\nclass Browser(AsyncContextManager):\n\n    def on(\n        self,\n        event: Literal[\"disconnected\"],\n        f: typing.Callable[[\"Browser\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when Browser gets disconnected from the browser application. This might happen because of one of the\n        following:\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n        return super().on(event=event, f=f)\n\n    def once(\n        self,\n        event: Literal[\"disconnected\"],\n        f: typing.Callable[[\"Browser\"], \"typing.Union[typing.Awaitable[None], None]\"],\n    ) -> None:\n        \"\"\"\n        Emitted when Browser gets disconnected from the browser application. This might happen because of one of the\n        following:\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n        return super().once(event=event, f=f)\n\n    @property\n    def contexts(self) -> typing.List[\"BrowserContext\"]:\n        \"\"\"Browser.contexts\n\n        Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts.\n\n        **Usage**\n\n        ```py\n        browser = await pw.webkit.launch()\n        print(len(browser.contexts)) # prints `0`\n        context = await browser.new_context()\n        print(len(browser.contexts)) # prints `1`\n        ```\n\n        Returns\n        -------\n        List[BrowserContext]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.contexts)\n\n    @property\n    def browser_type(self) -> \"BrowserType\":\n        \"\"\"Browser.browser_type\n\n        Get the browser type (chromium, firefox or webkit) that the browser belongs to.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.browser_type)\n\n    @property\n    def version(self) -> str:\n        \"\"\"Browser.version\n\n        Returns the browser version.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.version)\n\n    def is_connected(self) -> bool:\n        \"\"\"Browser.is_connected\n\n        Indicates that the browser is connected.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_connected())\n\n    async def new_context(\n        self,\n        *,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        default_browser_type: typing.Optional[str] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"BrowserContext\":\n        \"\"\"Browser.new_context\n\n        Creates a new browser context. It won't share cookies/cache with other browser contexts.\n\n        **NOTE** If directly using this method to create `BrowserContext`s, it is best practice to explicitly close the\n        returned context via `browser_context.close()` when your code is done with the `BrowserContext`, and before\n        calling `browser.close()`. This will ensure the `context` is closed gracefully and any artifacts—like HARs\n        and videos—are fully flushed and saved.\n\n        **Usage**\n\n        ```py\n        browser = await playwright.firefox.launch() # or \\\"chromium\\\" or \\\"webkit\\\".\n        # create a new incognito browser context.\n        context = await browser.new_context()\n        # create a new page in a pristine context.\n        page = await context.new_page()\n        await page.goto(\\\"https://example.com\\\")\n\n        # gracefully close up everything\n        await context.close()\n        await browser.close()\n        ```\n\n        Parameters\n        ----------\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings to use with this context. Defaults to none.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Learn more about [storage state and auth](../auth.md).\n\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()`.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.new_context(\n                viewport=viewport,\n                screen=screen,\n                noViewport=no_viewport,\n                ignoreHTTPSErrors=ignore_https_errors,\n                javaScriptEnabled=java_script_enabled,\n                bypassCSP=bypass_csp,\n                userAgent=user_agent,\n                locale=locale,\n                timezoneId=timezone_id,\n                geolocation=geolocation,\n                permissions=mapping.to_impl(permissions),\n                extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                offline=offline,\n                httpCredentials=http_credentials,\n                deviceScaleFactor=device_scale_factor,\n                isMobile=is_mobile,\n                hasTouch=has_touch,\n                colorScheme=color_scheme,\n                reducedMotion=reduced_motion,\n                forcedColors=forced_colors,\n                contrast=contrast,\n                acceptDownloads=accept_downloads,\n                defaultBrowserType=default_browser_type,\n                proxy=proxy,\n                recordHarPath=record_har_path,\n                recordHarOmitContent=record_har_omit_content,\n                recordVideoDir=record_video_dir,\n                recordVideoSize=record_video_size,\n                storageState=storage_state,\n                baseURL=base_url,\n                strictSelectors=strict_selectors,\n                serviceWorkers=service_workers,\n                recordHarUrlFilter=record_har_url_filter,\n                recordHarMode=record_har_mode,\n                recordHarContent=record_har_content,\n                clientCertificates=client_certificates,\n            )\n        )\n\n    async def new_page(\n        self,\n        *,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        default_browser_type: typing.Optional[str] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"Page\":\n        \"\"\"Browser.new_page\n\n        Creates a new page in a new browser context. Closing this page will close the context as well.\n\n        This is a convenience API that should only be used for the single-page scenarios and short snippets. Production\n        code and testing frameworks should explicitly create `browser.new_context()` followed by the\n        `browser_context.new_page()` to control their exact life times.\n\n        Parameters\n        ----------\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings to use with this context. Defaults to none.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Learn more about [storage state and auth](../auth.md).\n\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()`.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        Page\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.new_page(\n                viewport=viewport,\n                screen=screen,\n                noViewport=no_viewport,\n                ignoreHTTPSErrors=ignore_https_errors,\n                javaScriptEnabled=java_script_enabled,\n                bypassCSP=bypass_csp,\n                userAgent=user_agent,\n                locale=locale,\n                timezoneId=timezone_id,\n                geolocation=geolocation,\n                permissions=mapping.to_impl(permissions),\n                extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                offline=offline,\n                httpCredentials=http_credentials,\n                deviceScaleFactor=device_scale_factor,\n                isMobile=is_mobile,\n                hasTouch=has_touch,\n                colorScheme=color_scheme,\n                forcedColors=forced_colors,\n                contrast=contrast,\n                reducedMotion=reduced_motion,\n                acceptDownloads=accept_downloads,\n                defaultBrowserType=default_browser_type,\n                proxy=proxy,\n                recordHarPath=record_har_path,\n                recordHarOmitContent=record_har_omit_content,\n                recordVideoDir=record_video_dir,\n                recordVideoSize=record_video_size,\n                storageState=storage_state,\n                baseURL=base_url,\n                strictSelectors=strict_selectors,\n                serviceWorkers=service_workers,\n                recordHarUrlFilter=record_har_url_filter,\n                recordHarMode=record_har_mode,\n                recordHarContent=record_har_content,\n                clientCertificates=client_certificates,\n            )\n        )\n\n    async def close(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"Browser.close\n\n        In case this browser is obtained using `browser_type.launch()`, closes the browser and all of its pages (if\n        any were opened).\n\n        In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from\n        the browser server.\n\n        **NOTE** This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close\n        events, call `browser_context.close()` on any `BrowserContext` instances you explicitly created earlier\n        using `browser.new_context()` **before** calling `browser.close()`.\n\n        The `Browser` object itself is considered to be disposed and cannot be used anymore.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the browser closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.close(reason=reason))\n\n    async def new_browser_cdp_session(self) -> \"CDPSession\":\n        \"\"\"Browser.new_browser_cdp_session\n\n        **NOTE** CDP Sessions are only supported on Chromium-based browsers.\n\n        Returns the newly created browser session.\n\n        Returns\n        -------\n        CDPSession\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.new_browser_cdp_session())\n\n    async def start_tracing(\n        self,\n        *,\n        page: typing.Optional[\"Page\"] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        screenshots: typing.Optional[bool] = None,\n        categories: typing.Optional[typing.Sequence[str]] = None,\n    ) -> None:\n        \"\"\"Browser.start_tracing\n\n        **NOTE** This API controls\n        [Chromium Tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool) which is a low-level\n        chromium-specific debugging tool. API to control [Playwright Tracing](https://playwright.dev/python/docs/trace-viewer) could be found\n        [here](https://playwright.dev/python/docs/api/class-tracing).\n\n        You can use `browser.start_tracing()` and `browser.stop_tracing()` to create a trace file that can be\n        opened in Chrome DevTools performance panel.\n\n        **Usage**\n\n        ```py\n        await browser.start_tracing(page, path=\\\"trace.json\\\")\n        await page.goto(\\\"https://www.google.com\\\")\n        await browser.stop_tracing()\n        ```\n\n        Parameters\n        ----------\n        page : Union[Page, None]\n            Optional, if specified, tracing includes screenshots of the given page.\n        path : Union[pathlib.Path, str, None]\n            A path to write the trace file to.\n        screenshots : Union[bool, None]\n            captures screenshots in the trace.\n        categories : Union[Sequence[str], None]\n            specify custom categories to use instead of default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.start_tracing(\n                page=page._impl_obj if page else None,\n                path=path,\n                screenshots=screenshots,\n                categories=mapping.to_impl(categories),\n            )\n        )\n\n    async def stop_tracing(self) -> bytes:\n        \"\"\"Browser.stop_tracing\n\n        **NOTE** This API controls\n        [Chromium Tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool) which is a low-level\n        chromium-specific debugging tool. API to control [Playwright Tracing](https://playwright.dev/python/docs/trace-viewer) could be found\n        [here](https://playwright.dev/python/docs/api/class-tracing).\n\n        Returns the buffer with trace data.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.stop_tracing())\n\n\nmapping.register(BrowserImpl, Browser)\n\n\nclass BrowserType(AsyncBase):\n\n    @property\n    def name(self) -> str:\n        \"\"\"BrowserType.name\n\n        Returns browser name. For example: `'chromium'`, `'webkit'` or `'firefox'`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.name)\n\n    @property\n    def executable_path(self) -> str:\n        \"\"\"BrowserType.executable_path\n\n        A path where Playwright expects to find a bundled browser executable.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.executable_path)\n\n    async def launch(\n        self,\n        *,\n        executable_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        channel: typing.Optional[str] = None,\n        args: typing.Optional[typing.Sequence[str]] = None,\n        ignore_default_args: typing.Optional[\n            typing.Union[bool, typing.Sequence[str]]\n        ] = None,\n        handle_sigint: typing.Optional[bool] = None,\n        handle_sigterm: typing.Optional[bool] = None,\n        handle_sighup: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        env: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        headless: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        downloads_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        slow_mo: typing.Optional[float] = None,\n        traces_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        chromium_sandbox: typing.Optional[bool] = None,\n        firefox_user_prefs: typing.Optional[\n            typing.Dict[str, typing.Union[str, float, bool]]\n        ] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.launch\n\n        Returns the browser instance.\n\n        **Usage**\n\n        You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments:\n\n        ```py\n        browser = await playwright.chromium.launch( # or \\\"firefox\\\" or \\\"webkit\\\".\n            ignore_default_args=[\\\"--mute-audio\\\"]\n        )\n        ```\n\n        > **Chromium-only** Playwright can also be used to control the Google Chrome or Microsoft Edge browsers, but it\n        works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other\n        version. Use `executablePath` option with extreme caution.\n        >\n        > If Google Chrome (rather than Chromium) is preferred, a\n        [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or\n        [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested.\n        >\n        > Stock browsers like Google Chrome and Microsoft Edge are suitable for tests that require proprietary media codecs\n        for video playback. See\n        [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for\n        other differences between Chromium and Chrome.\n        [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md)\n        describes some differences for Linux users.\n\n        Parameters\n        ----------\n        executable_path : Union[pathlib.Path, str, None]\n            Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is\n            resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium,\n            Firefox or WebKit, use at your own risk.\n        channel : Union[str, None]\n            Browser distribution channel.\n\n            Use \"chromium\" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).\n\n            Use \"chrome\", \"chrome-beta\", \"chrome-dev\", \"chrome-canary\", \"msedge\", \"msedge-beta\", \"msedge-dev\", or\n            \"msedge-canary\" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).\n        args : Union[Sequence[str], None]\n            **NOTE** Use custom browser args at your own risk, as some of them may break Playwright functionality.\n\n            Additional arguments to pass to the browser instance. The list of Chromium flags can be found\n            [here](https://peter.sh/experiments/chromium-command-line-switches/).\n        ignore_default_args : Union[Sequence[str], bool, None]\n            If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is\n            given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.\n        handle_sigint : Union[bool, None]\n            Close the browser process on Ctrl-C. Defaults to `true`.\n        handle_sigterm : Union[bool, None]\n            Close the browser process on SIGTERM. Defaults to `true`.\n        handle_sighup : Union[bool, None]\n            Close the browser process on SIGHUP. Defaults to `true`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0`\n            to disable timeout.\n        env : Union[Dict[str, Union[bool, float, str]], None]\n            Specify environment variables that will be visible to the browser. Defaults to `process.env`.\n        headless : Union[bool, None]\n            Whether to run browser in headless mode. More details for\n            [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and\n            [Firefox](https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/). Defaults to `true`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        downloads_path : Union[pathlib.Path, str, None]\n            If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and\n            is deleted when browser is closed. In either case, the downloads are deleted when the browser context they were\n            created in is closed.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on.\n        traces_dir : Union[pathlib.Path, str, None]\n            If specified, traces are saved into this directory.\n        chromium_sandbox : Union[bool, None]\n            Enable Chromium sandboxing. Defaults to `false`.\n        firefox_user_prefs : Union[Dict[str, Union[bool, float, str]], None]\n            Firefox user preferences. Learn more about the Firefox user preferences at\n            [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).\n\n            You can also provide a path to a custom [`policies.json` file](https://mozilla.github.io/policy-templates/) via\n            `PLAYWRIGHT_FIREFOX_POLICIES_JSON` environment variable.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.launch(\n                executablePath=executable_path,\n                channel=channel,\n                args=mapping.to_impl(args),\n                ignoreDefaultArgs=mapping.to_impl(ignore_default_args),\n                handleSIGINT=handle_sigint,\n                handleSIGTERM=handle_sigterm,\n                handleSIGHUP=handle_sighup,\n                timeout=timeout,\n                env=mapping.to_impl(env),\n                headless=headless,\n                proxy=proxy,\n                downloadsPath=downloads_path,\n                slowMo=slow_mo,\n                tracesDir=traces_dir,\n                chromiumSandbox=chromium_sandbox,\n                firefoxUserPrefs=mapping.to_impl(firefox_user_prefs),\n            )\n        )\n\n    async def launch_persistent_context(\n        self,\n        user_data_dir: typing.Union[str, pathlib.Path],\n        *,\n        channel: typing.Optional[str] = None,\n        executable_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        args: typing.Optional[typing.Sequence[str]] = None,\n        ignore_default_args: typing.Optional[\n            typing.Union[bool, typing.Sequence[str]]\n        ] = None,\n        handle_sigint: typing.Optional[bool] = None,\n        handle_sigterm: typing.Optional[bool] = None,\n        handle_sighup: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        env: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        headless: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        downloads_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        slow_mo: typing.Optional[float] = None,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        traces_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        chromium_sandbox: typing.Optional[bool] = None,\n        firefox_user_prefs: typing.Optional[\n            typing.Dict[str, typing.Union[str, float, bool]]\n        ] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"BrowserContext\":\n        \"\"\"BrowserType.launch_persistent_context\n\n        Returns the persistent browser context instance.\n\n        Launches browser that uses persistent storage located at `userDataDir` and returns the only context. Closing this\n        context will automatically close the browser.\n\n        Parameters\n        ----------\n        user_data_dir : Union[pathlib.Path, str]\n            Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty\n            string to create a temporary directory.\n\n            More details for\n            [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and\n            [Firefox](https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile). Chromium's user data directory is the\n            **parent** directory of the \"Profile Path\" seen at `chrome://version`.\n\n            Note that browsers do not allow launching multiple instances with the same User Data Directory.\n\n            **NOTE** Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not\n            supported. Pointing `userDataDir` to Chrome's main \"User Data\" directory (the profile used for your regular\n            browsing) may result in pages not loading or the browser exiting. Create and use a separate directory (for example,\n            an empty folder) as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port\n            for details.\n\n        channel : Union[str, None]\n            Browser distribution channel.\n\n            Use \"chromium\" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).\n\n            Use \"chrome\", \"chrome-beta\", \"chrome-dev\", \"chrome-canary\", \"msedge\", \"msedge-beta\", \"msedge-dev\", or\n            \"msedge-canary\" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).\n        executable_path : Union[pathlib.Path, str, None]\n            Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is\n            resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium,\n            Firefox or WebKit, use at your own risk.\n        args : Union[Sequence[str], None]\n            **NOTE** Use custom browser args at your own risk, as some of them may break Playwright functionality.\n\n            Additional arguments to pass to the browser instance. The list of Chromium flags can be found\n            [here](https://peter.sh/experiments/chromium-command-line-switches/).\n        ignore_default_args : Union[Sequence[str], bool, None]\n            If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is\n            given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.\n        handle_sigint : Union[bool, None]\n            Close the browser process on Ctrl-C. Defaults to `true`.\n        handle_sigterm : Union[bool, None]\n            Close the browser process on SIGTERM. Defaults to `true`.\n        handle_sighup : Union[bool, None]\n            Close the browser process on SIGHUP. Defaults to `true`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0`\n            to disable timeout.\n        env : Union[Dict[str, Union[bool, float, str]], None]\n            Specify environment variables that will be visible to the browser. Defaults to `process.env`.\n        headless : Union[bool, None]\n            Whether to run browser in headless mode. More details for\n            [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and\n            [Firefox](https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/). Defaults to `true`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        downloads_path : Union[pathlib.Path, str, None]\n            If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and\n            is deleted when browser is closed. In either case, the downloads are deleted when the browser context they were\n            created in is closed.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on.\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        traces_dir : Union[pathlib.Path, str, None]\n            If specified, traces are saved into this directory.\n        chromium_sandbox : Union[bool, None]\n            Enable Chromium sandboxing. Defaults to `false`.\n        firefox_user_prefs : Union[Dict[str, Union[bool, float, str]], None]\n            Firefox user preferences. Learn more about the Firefox user preferences at\n            [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).\n\n            You can also provide a path to a custom [`policies.json` file](https://mozilla.github.io/policy-templates/) via\n            `PLAYWRIGHT_FIREFOX_POLICIES_JSON` environment variable.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.launch_persistent_context(\n                userDataDir=user_data_dir,\n                channel=channel,\n                executablePath=executable_path,\n                args=mapping.to_impl(args),\n                ignoreDefaultArgs=mapping.to_impl(ignore_default_args),\n                handleSIGINT=handle_sigint,\n                handleSIGTERM=handle_sigterm,\n                handleSIGHUP=handle_sighup,\n                timeout=timeout,\n                env=mapping.to_impl(env),\n                headless=headless,\n                proxy=proxy,\n                downloadsPath=downloads_path,\n                slowMo=slow_mo,\n                viewport=viewport,\n                screen=screen,\n                noViewport=no_viewport,\n                ignoreHTTPSErrors=ignore_https_errors,\n                javaScriptEnabled=java_script_enabled,\n                bypassCSP=bypass_csp,\n                userAgent=user_agent,\n                locale=locale,\n                timezoneId=timezone_id,\n                geolocation=geolocation,\n                permissions=mapping.to_impl(permissions),\n                extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                offline=offline,\n                httpCredentials=http_credentials,\n                deviceScaleFactor=device_scale_factor,\n                isMobile=is_mobile,\n                hasTouch=has_touch,\n                colorScheme=color_scheme,\n                reducedMotion=reduced_motion,\n                forcedColors=forced_colors,\n                contrast=contrast,\n                acceptDownloads=accept_downloads,\n                tracesDir=traces_dir,\n                chromiumSandbox=chromium_sandbox,\n                firefoxUserPrefs=mapping.to_impl(firefox_user_prefs),\n                recordHarPath=record_har_path,\n                recordHarOmitContent=record_har_omit_content,\n                recordVideoDir=record_video_dir,\n                recordVideoSize=record_video_size,\n                baseURL=base_url,\n                strictSelectors=strict_selectors,\n                serviceWorkers=service_workers,\n                recordHarUrlFilter=record_har_url_filter,\n                recordHarMode=record_har_mode,\n                recordHarContent=record_har_content,\n                clientCertificates=client_certificates,\n            )\n        )\n\n    async def connect_over_cdp(\n        self,\n        endpoint_url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        slow_mo: typing.Optional[float] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        is_local: typing.Optional[bool] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.connect_over_cdp\n\n        This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.\n\n        The default browser context is accessible via `browser.contexts()`.\n\n        **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.\n\n        **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via\n        `browser_type.connect()`. If you are experiencing issues or attempting to use advanced functionality, you\n        probably want to use `browser_type.connect()`.\n\n        **Usage**\n\n        ```py\n        browser = await playwright.chromium.connect_over_cdp(\\\"http://localhost:9222\\\")\n        default_context = browser.contexts[0]\n        page = default_context.pages[0]\n        ```\n\n        Parameters\n        ----------\n        endpoint_url : str\n            A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or\n            `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass\n            `0` to disable timeout.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on. Defaults to 0.\n        headers : Union[Dict[str, str], None]\n            Additional HTTP headers to be sent with connect request. Optional.\n        is_local : Union[bool, None]\n            Tells Playwright that it runs on the same host as the CDP server. It will enable certain optimizations that rely\n            upon the file system being the same between Playwright and the Browser.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.connect_over_cdp(\n                endpointURL=endpoint_url,\n                timeout=timeout,\n                slowMo=slow_mo,\n                headers=mapping.to_impl(headers),\n                isLocal=is_local,\n            )\n        )\n\n    async def connect(\n        self,\n        ws_endpoint: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        slow_mo: typing.Optional[float] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        expose_network: typing.Optional[str] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.connect\n\n        This method attaches Playwright to an existing browser instance created via `BrowserType.launchServer` in Node.js.\n\n        **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of\n        Playwright that launches the browser (1.2.3 → is compatible with 1.2.x).\n\n        Parameters\n        ----------\n        ws_endpoint : str\n            A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the connection to be established. Defaults to `0` (no timeout).\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on. Defaults to 0.\n        headers : Union[Dict[str, str], None]\n            Additional HTTP headers to be sent with web socket connect request. Optional.\n        expose_network : Union[str, None]\n            This option exposes network available on the connecting client to the browser being connected to. Consists of a\n            list of rules separated by comma.\n\n            Available rules:\n            1. Hostname pattern, for example: `example.com`, `*.org:99`, `x.*.y.com`, `*foo.org`.\n            1. IP literal, for example: `127.0.0.1`, `0.0.0.0:99`, `[::1]`, `[0:0::1]:99`.\n            1. `<loopback>` that matches local loopback interfaces: `localhost`, `*.localhost`, `127.0.0.1`, `[::1]`.\n\n            Some common examples:\n            1. `\"*\"` to expose all network.\n            1. `\"<loopback>\"` to expose localhost network.\n            1. `\"*.test.internal-domain,*.staging.internal-domain,<loopback>\"` to expose test/staging deployments and\n               localhost.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.connect(\n                wsEndpoint=ws_endpoint,\n                timeout=timeout,\n                slowMo=slow_mo,\n                headers=mapping.to_impl(headers),\n                exposeNetwork=expose_network,\n            )\n        )\n\n\nmapping.register(BrowserTypeImpl, BrowserType)\n\n\nclass Playwright(AsyncBase):\n\n    @property\n    def devices(self) -> typing.Dict:\n        \"\"\"Playwright.devices\n\n        Returns a dictionary of devices to be used with `browser.new_context()` or `browser.new_page()`.\n\n        ```py\n        import asyncio\n        from playwright.async_api import async_playwright, Playwright\n\n        async def run(playwright: Playwright):\n            webkit = playwright.webkit\n            iphone = playwright.devices[\\\"iPhone 6\\\"]\n            browser = await webkit.launch()\n            context = await browser.new_context(**iphone)\n            page = await context.new_page()\n            await page.goto(\\\"http://example.com\\\")\n            # other actions...\n            await browser.close()\n\n        async def main():\n            async with async_playwright() as playwright:\n                await run(playwright)\n        asyncio.run(main())\n        ```\n\n        Returns\n        -------\n        Dict\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.devices)\n\n    @property\n    def selectors(self) -> \"Selectors\":\n        \"\"\"Playwright.selectors\n\n        Selectors can be used to install custom selector engines. See [extensibility](https://playwright.dev/python/docs/extensibility) for more\n        information.\n\n        Returns\n        -------\n        Selectors\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.selectors)\n\n    @property\n    def chromium(self) -> \"BrowserType\":\n        \"\"\"Playwright.chromium\n\n        This object can be used to launch or connect to Chromium, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.chromium)\n\n    @property\n    def firefox(self) -> \"BrowserType\":\n        \"\"\"Playwright.firefox\n\n        This object can be used to launch or connect to Firefox, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.firefox)\n\n    @property\n    def webkit(self) -> \"BrowserType\":\n        \"\"\"Playwright.webkit\n\n        This object can be used to launch or connect to WebKit, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.webkit)\n\n    @property\n    def request(self) -> \"APIRequest\":\n        \"\"\"Playwright.request\n\n        Exposes API that can be used for the Web API testing.\n\n        Returns\n        -------\n        APIRequest\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    def __getitem__(self, value: str) -> \"BrowserType\":\n\n        return mapping.from_impl(self._impl_obj.__getitem__(value=value))\n\n    async def stop(self) -> None:\n        \"\"\"Playwright.stop\n\n        Terminates this instance of Playwright in case it was created bypassing the Python context manager. This is useful\n        in REPL applications.\n\n        ```py\n        from playwright.sync_api import sync_playwright\n\n        playwright = sync_playwright().start()\n\n        browser = playwright.chromium.launch()\n        page = browser.new_page()\n        page.goto(\\\"https://playwright.dev/\\\")\n        page.screenshot(path=\\\"example.png\\\")\n        browser.close()\n\n        playwright.stop()\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.stop())\n\n\nmapping.register(PlaywrightImpl, Playwright)\n\n\nclass Tracing(AsyncBase):\n\n    async def start(\n        self,\n        *,\n        name: typing.Optional[str] = None,\n        title: typing.Optional[str] = None,\n        snapshots: typing.Optional[bool] = None,\n        screenshots: typing.Optional[bool] = None,\n        sources: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Tracing.start\n\n        Start tracing.\n\n        **NOTE** You probably want to\n        [enable tracing in your config file](https://playwright.dev/docs/api/class-testoptions#test-options-trace) instead\n        of using `Tracing.start`.\n\n        The `context.tracing` API captures browser operations and network activity, but it doesn't record test assertions\n        (like `expect` calls). We recommend\n        [enabling tracing through Playwright Test configuration](https://playwright.dev/docs/api/class-testoptions#test-options-trace),\n        which includes those assertions and provides a more complete trace for debugging test failures.\n\n        **Usage**\n\n        ```py\n        await context.tracing.start(screenshots=True, snapshots=True)\n        page = await context.new_page()\n        await page.goto(\\\"https://playwright.dev\\\")\n        await context.tracing.stop(path = \\\"trace.zip\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[str, None]\n            If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the\n            `tracesDir` directory specified in `browser_type.launch()`. To specify the final trace zip file name, you\n            need to pass `path` option to `tracing.stop()` instead.\n        title : Union[str, None]\n            Trace name to be shown in the Trace Viewer.\n        snapshots : Union[bool, None]\n            If this option is true tracing will\n            - capture DOM snapshot on every action\n            - record network activity\n        screenshots : Union[bool, None]\n            Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview.\n        sources : Union[bool, None]\n            Whether to include source files for trace actions.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.start(\n                name=name,\n                title=title,\n                snapshots=snapshots,\n                screenshots=screenshots,\n                sources=sources,\n            )\n        )\n\n    async def start_chunk(\n        self, *, title: typing.Optional[str] = None, name: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"Tracing.start_chunk\n\n        Start a new trace chunk. If you'd like to record multiple traces on the same `BrowserContext`, use\n        `tracing.start()` once, and then create multiple trace chunks with `tracing.start_chunk()` and\n        `tracing.stop_chunk()`.\n\n        **Usage**\n\n        ```py\n        await context.tracing.start(screenshots=True, snapshots=True)\n        page = await context.new_page()\n        await page.goto(\\\"https://playwright.dev\\\")\n\n        await context.tracing.start_chunk()\n        await page.get_by_text(\\\"Get Started\\\").click()\n        # Everything between start_chunk and stop_chunk will be recorded in the trace.\n        await context.tracing.stop_chunk(path = \\\"trace1.zip\\\")\n\n        await context.tracing.start_chunk()\n        await page.goto(\\\"http://example.com\\\")\n        # Save a second trace file with different actions.\n        await context.tracing.stop_chunk(path = \\\"trace2.zip\\\")\n        ```\n\n        Parameters\n        ----------\n        title : Union[str, None]\n            Trace name to be shown in the Trace Viewer.\n        name : Union[str, None]\n            If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the\n            `tracesDir` directory specified in `browser_type.launch()`. To specify the final trace zip file name, you\n            need to pass `path` option to `tracing.stop_chunk()` instead.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.start_chunk(title=title, name=name)\n        )\n\n    async def stop_chunk(\n        self, *, path: typing.Optional[typing.Union[pathlib.Path, str]] = None\n    ) -> None:\n        \"\"\"Tracing.stop_chunk\n\n        Stop the trace chunk. See `tracing.start_chunk()` for more details about multiple trace chunks.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            Export trace collected since the last `tracing.start_chunk()` call into the file with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.stop_chunk(path=path))\n\n    async def stop(\n        self, *, path: typing.Optional[typing.Union[pathlib.Path, str]] = None\n    ) -> None:\n        \"\"\"Tracing.stop\n\n        Stop tracing.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            Export trace into the file with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.stop(path=path))\n\n    async def group(\n        self, name: str, *, location: typing.Optional[TracingGroupLocation] = None\n    ) -> None:\n        \"\"\"Tracing.group\n\n        **NOTE** Use `test.step` instead when available.\n\n        Creates a new group within the trace, assigning any subsequent API calls to this group, until\n        `tracing.group_end()` is called. Groups can be nested and will be visible in the trace viewer.\n\n        **Usage**\n\n        ```py\n        # All actions between group and group_end\n        # will be shown in the trace viewer as a group.\n        page.context.tracing.group(\\\"Open Playwright.dev > API\\\")\n        page.goto(\\\"https://playwright.dev/\\\")\n        page.get_by_role(\\\"link\\\", name=\\\"API\\\").click()\n        page.context.tracing.group_end()\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Group name shown in the trace viewer.\n        location : Union[{file: str, line: Union[int, None], column: Union[int, None]}, None]\n            Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the\n            `tracing.group()` call.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.group(name=name, location=location)\n        )\n\n    async def group_end(self) -> None:\n        \"\"\"Tracing.group_end\n\n        Closes the last group created by `tracing.group()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.group_end())\n\n\nmapping.register(TracingImpl, Tracing)\n\n\nclass Locator(AsyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Locator.page\n\n        A page this locator belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def first(self) -> \"Locator\":\n        \"\"\"Locator.first\n\n        Returns locator to the first matching element.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.first)\n\n    @property\n    def last(self) -> \"Locator\":\n        \"\"\"Locator.last\n\n        Returns locator to the last matching element.\n\n        **Usage**\n\n        ```py\n        banana = await page.get_by_role(\\\"listitem\\\").last\n        ```\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.last)\n\n    @property\n    def content_frame(self) -> \"FrameLocator\":\n        \"\"\"Locator.content_frame\n\n        Returns a `FrameLocator` object pointing to the same `iframe` as this locator.\n\n        Useful when you have a `Locator` object obtained somewhere, and later on would like to interact with the content\n        inside the frame.\n\n        For a reverse operation, use `frame_locator.owner()`.\n\n        **Usage**\n\n        ```py\n        locator = page.locator(\\\"iframe[name=\\\\\\\"embedded\\\\\\\"]\\\")\n        # ...\n        frame_locator = locator.content_frame\n        await frame_locator.get_by_role(\\\"button\\\").click()\n        ```\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.content_frame)\n\n    @property\n    def description(self) -> typing.Optional[str]:\n        \"\"\"Locator.description\n\n        Returns locator description previously set with `locator.describe()`. Returns `null` if no custom\n        description has been set.\n\n        **Usage**\n\n        ```py\n        button = page.get_by_role(\\\"button\\\").describe(\\\"Subscribe button\\\")\n        print(button.description())  # \\\"Subscribe button\\\"\n\n        input = page.get_by_role(\\\"textbox\\\")\n        print(input.description())  # None\n        ```\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.description)\n\n    async def bounding_box(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[FloatRect]:\n        \"\"\"Locator.bounding_box\n\n        This method returns the bounding box of the element matching the locator, or `null` if the element is not visible.\n        The bounding box is calculated relative to the main frame viewport - which is usually the same as the browser\n        window.\n\n        **Details**\n\n        Scrolling affects the returned bounding box, similarly to\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n        That means `x` and/or `y` may be negative.\n\n        Elements from child frames return the bounding box relative to the main frame, unlike the\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n\n        Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the\n        following snippet should click the center of the element.\n\n        **Usage**\n\n        ```py\n        box = await page.get_by_role(\\\"button\\\").bounding_box()\n        await page.mouse.click(box[\\\"x\\\"] + box[\\\"width\\\"] / 2, box[\\\"y\\\"] + box[\\\"height\\\"] / 2)\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[{x: float, y: float, width: float, height: float}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            await self._impl_obj.bounding_box(timeout=timeout)\n        )\n\n    async def check(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.check\n\n        Ensure that checkbox or radio element is checked.\n\n        **Details**\n\n        Performs the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"checkbox\\\").check()\n        ```\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.check(\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def click(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.click\n\n        Click an element.\n\n        **Details**\n\n        This method clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **Usage**\n\n        Click a button:\n\n        ```py\n        await page.get_by_role(\\\"button\\\").click()\n        ```\n\n        Shift-right-click at a specific position on a canvas:\n\n        ```py\n        await page.locator(\\\"canvas\\\").click(\n            button=\\\"right\\\", modifiers=[\\\"Shift\\\"], position={\\\"x\\\": 23, \\\"y\\\": 32}\n        )\n        ```\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.click(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                clickCount=click_count,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def dblclick(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.dblclick\n\n        Double-click an element.\n\n        **Details**\n\n        This method double clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `element.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dblclick(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                delay=delay,\n                button=button,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n                steps=steps,\n            )\n        )\n\n    async def dispatch_event(\n        self,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Locator.dispatch_event\n\n        Programmatically dispatch an event on the matching element.\n\n        **Usage**\n\n        ```py\n        await locator.dispatch_event(\\\"click\\\")\n        ```\n\n        **Details**\n\n        The snippet above dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        data_transfer = await page.evaluate_handle(\\\"new DataTransfer()\\\")\n        await locator.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", {\\\"dataTransfer\\\": data_transfer})\n        ```\n\n        Parameters\n        ----------\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.dispatch_event(\n                type=type, eventInit=mapping.to_impl(event_init), timeout=timeout\n            )\n        )\n\n    async def evaluate(\n        self,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"Locator.evaluate\n\n        Execute JavaScript code in the page, taking the matching element as an argument.\n\n        **Details**\n\n        Returns the return value of `expression`, called with the matching element as a first argument, and `arg` as a\n        second argument.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        **Usage**\n\n        Passing argument to `expression`:\n\n        ```py\n        result = await page.get_by_testid(\\\"myId\\\").evaluate(\\\"(element, [x, y]) => element.textContent + ' ' + x * y\\\", [7, 8])\n        print(result) # prints \\\"myId text 56\\\"\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,\n            evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate(\n                expression=expression, arg=mapping.to_impl(arg), timeout=timeout\n            )\n        )\n\n    async def evaluate_all(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Locator.evaluate_all\n\n        Execute JavaScript code in the page, taking all matching elements as an argument.\n\n        **Details**\n\n        Returns the return value of `expression`, called with an array of all matching elements as a first argument, and\n        `arg` as a second argument.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        **Usage**\n\n        ```py\n        locator = page.locator(\\\"div\\\")\n        more_than_ten = await locator.evaluate_all(\\\"(divs, min) => divs.length > min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.evaluate_all(\n                expression=expression, arg=mapping.to_impl(arg)\n            )\n        )\n\n    async def evaluate_handle(\n        self,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Locator.evaluate_handle\n\n        Execute JavaScript code in the page, taking the matching element as an argument, and return a `JSHandle` with the\n        result.\n\n        **Details**\n\n        Returns the return value of `expression` as a`JSHandle`, called with the matching element as a first argument, and\n        `arg` as a second argument.\n\n        The only difference between `locator.evaluate()` and `locator.evaluate_handle()` is that\n        `locator.evaluate_handle()` returns `JSHandle`.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        See `page.evaluate_handle()` for more details.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,\n            evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.evaluate_handle(\n                expression=expression, arg=mapping.to_impl(arg), timeout=timeout\n            )\n        )\n\n    async def fill(\n        self,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.fill\n\n        Set a value to the input field.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"textbox\\\").fill(\\\"example value\\\")\n        ```\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, fills it and triggers an\n        `input` event after filling. Note that you can pass an empty string to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        value : str\n            Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.fill(\n                value=value, timeout=timeout, noWaitAfter=no_wait_after, force=force\n            )\n        )\n\n    async def clear(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.clear\n\n        Clear the input field.\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, clears it and triggers an\n        `input` event after clearing.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be cleared\n        instead.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"textbox\\\").clear()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.clear(\n                timeout=timeout, noWaitAfter=no_wait_after, force=force\n            )\n        )\n\n    def locator(\n        self,\n        selector_or_locator: typing.Union[str, \"Locator\"],\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.locator\n\n        The method finds an element matching the specified selector in the locator's subtree. It also accepts filter\n        options, similar to `locator.filter()` method.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector_or_locator : Union[Locator, str]\n            A selector or locator to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selectorOrLocator=selector_or_locator,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        await page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        await page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        await page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        await page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        await expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        await page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        await page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        await page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        await expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Locator.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow locating elements in\n        that iframe:\n\n        **Usage**\n\n        ```py\n        locator = page.frame_locator(\\\"iframe\\\").get_by_text(\\\"Submit\\\")\n        await locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    async def element_handle(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> \"ElementHandle\":\n        \"\"\"Locator.element_handle\n\n        Resolves given locator to the first matching DOM element. If there are no matching elements, waits for one. If\n        multiple elements match the locator, throws.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(await self._impl_obj.element_handle(timeout=timeout))\n\n    async def element_handles(self) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Locator.element_handles\n\n        Resolves given locator to all matching DOM elements. If there are no matching elements, returns an empty list.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.element_handles())\n\n    def nth(self, index: int) -> \"Locator\":\n        \"\"\"Locator.nth\n\n        Returns locator to the n-th matching element. It's zero based, `nth(0)` selects the first element.\n\n        **Usage**\n\n        ```py\n        banana = await page.get_by_role(\\\"listitem\\\").nth(2)\n        ```\n\n        Parameters\n        ----------\n        index : int\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.nth(index=index))\n\n    def describe(self, description: str) -> \"Locator\":\n        \"\"\"Locator.describe\n\n        Describes the locator, description is used in the trace viewer and reports. Returns the locator pointing to the\n        same element.\n\n        **Usage**\n\n        ```py\n        button = page.get_by_test_id(\\\"btn-sub\\\").describe(\\\"Subscribe button\\\")\n        await button.click()\n        ```\n\n        Parameters\n        ----------\n        description : str\n            Locator description.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.describe(description=description))\n\n    def filter(\n        self,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n        visible: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.filter\n\n        This method narrows existing locator according to the options, for example filters by text. It can be chained to\n        filter multiple times.\n\n        **Usage**\n\n        ```py\n        row_locator = page.locator(\\\"tr\\\")\n        # ...\n        await row_locator.filter(has_text=\\\"text in column 1\\\").filter(\n            has=page.get_by_role(\\\"button\\\", name=\\\"column 2 button\\\")\n        ).screenshot()\n\n        ```\n\n        Parameters\n        ----------\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        visible : Union[bool, None]\n            Only matches visible or invisible elements.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.filter(\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n                visible=visible,\n            )\n        )\n\n    def or_(self, locator: \"Locator\") -> \"Locator\":\n        \"\"\"Locator.or_\n\n        Creates a locator matching all elements that match one or both of the two locators.\n\n        Note that when both locators match something, the resulting locator will have multiple matches, potentially causing\n        a [locator strictness](https://playwright.dev/python/docs/locators#strictness) violation.\n\n        **Usage**\n\n        Consider a scenario where you'd like to click on a \\\"New email\\\" button, but sometimes a security settings dialog\n        shows up instead. In this case, you can wait for either a \\\"New email\\\" button, or a dialog and act accordingly.\n\n        **NOTE** If both \\\"New email\\\" button and security dialog appear on screen, the \\\"or\\\" locator will match both of them,\n        possibly throwing the [\\\"strict mode violation\\\" error](https://playwright.dev/python/docs/locators#strictness). In this case, you can use\n        `locator.first()` to only match one of them.\n\n        ```py\n        new_email = page.get_by_role(\\\"button\\\", name=\\\"New\\\")\n        dialog = page.get_by_text(\\\"Confirm security settings\\\")\n        await expect(new_email.or_(dialog).first).to_be_visible()\n        if (await dialog.is_visible()):\n          await page.get_by_role(\\\"button\\\", name=\\\"Dismiss\\\").click()\n        await new_email.click()\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Alternative locator to match.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.or_(locator=locator._impl_obj))\n\n    def and_(self, locator: \"Locator\") -> \"Locator\":\n        \"\"\"Locator.and_\n\n        Creates a locator that matches both this locator and the argument locator.\n\n        **Usage**\n\n        The following example finds a button with a specific title.\n\n        ```py\n        button = page.get_by_role(\\\"button\\\").and_(page.get_by_title(\\\"Subscribe\\\"))\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Additional locator to match.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.and_(locator=locator._impl_obj))\n\n    async def focus(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"Locator.focus\n\n        Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.focus(timeout=timeout))\n\n    async def blur(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"Locator.blur\n\n        Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) on the element.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.blur(timeout=timeout))\n\n    async def all(self) -> typing.List[\"Locator\"]:\n        \"\"\"Locator.all\n\n        When the locator points to a list of elements, this returns an array of locators, pointing to their respective\n        elements.\n\n        **NOTE** `locator.all()` does not wait for elements to match the locator, and instead immediately returns\n        whatever is present in the page.\n\n        When the list of elements changes dynamically, `locator.all()` will produce unpredictable and flaky\n        results.\n\n        When the list of elements is stable, but loaded dynamically, wait for the full list to finish loading before\n        calling `locator.all()`.\n\n        **Usage**\n\n        ```py\n        for li in await page.get_by_role('listitem').all():\n          await li.click();\n        ```\n\n        Returns\n        -------\n        List[Locator]\n        \"\"\"\n\n        return mapping.from_impl_list(await self._impl_obj.all())\n\n    async def count(self) -> int:\n        \"\"\"Locator.count\n\n        Returns the number of elements matching the locator.\n\n        **NOTE** If you need to assert the number of elements on the page, prefer `locator_assertions.to_have_count()`\n        to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        count = await page.get_by_role(\\\"listitem\\\").count()\n        ```\n\n        Returns\n        -------\n        int\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.count())\n\n    async def drag_to(\n        self,\n        target: \"Locator\",\n        *,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        trial: typing.Optional[bool] = None,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.drag_to\n\n        Drag the source element towards the target element and drop it.\n\n        **Details**\n\n        This method drags the locator to another target locator or target position. It will first move to the source\n        element, perform a `mousedown`, then move to the target element or position and perform a `mouseup`.\n\n        **Usage**\n\n        ```py\n        source = page.locator(\\\"#source\\\")\n        target = page.locator(\\\"#target\\\")\n\n        await source.drag_to(target)\n        # or specify exact positions relative to the top-left corners of the elements:\n        await source.drag_to(\n          target,\n          source_position={\\\"x\\\": 34, \\\"y\\\": 7},\n          target_position={\\\"x\\\": 10, \\\"y\\\": 20}\n        )\n        ```\n\n        Parameters\n        ----------\n        target : Locator\n            Locator of the element to drag to.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.drag_to(\n                target=target._impl_obj,\n                force=force,\n                noWaitAfter=no_wait_after,\n                timeout=timeout,\n                trial=trial,\n                sourcePosition=source_position,\n                targetPosition=target_position,\n                steps=steps,\n            )\n        )\n\n    async def get_attribute(\n        self, name: str, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[str]:\n        \"\"\"Locator.get_attribute\n\n        Returns the matching element's attribute value.\n\n        **NOTE** If you need to assert an element's attribute, prefer `locator_assertions.to_have_attribute()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name to get the value for.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.get_attribute(name=name, timeout=timeout)\n        )\n\n    async def hover(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.hover\n\n        Hover over the matching element.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"link\\\").hover()\n        ```\n\n        **Details**\n\n        This method hovers over the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.hover(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n                trial=trial,\n            )\n        )\n\n    async def inner_html(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.inner_html\n\n        Returns the [`element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML).\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.inner_html(timeout=timeout))\n\n    async def inner_text(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.inner_text\n\n        Returns the [`element.innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText).\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` with\n        `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.inner_text(timeout=timeout))\n\n    async def input_value(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.input_value\n\n        Returns the value for the matching `<input>` or `<textarea>` or `<select>` element.\n\n        **NOTE** If you need to assert input value, prefer `locator_assertions.to_have_value()` to avoid flakiness.\n        See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        value = await page.get_by_role(\\\"textbox\\\").input_value()\n        ```\n\n        **Details**\n\n        Throws elements that are not an input, textarea or a select. However, if the element is inside the `<label>`\n        element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.input_value(timeout=timeout)\n        )\n\n    async def is_checked(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        **NOTE** If you need to assert that checkbox is checked, prefer `locator_assertions.to_be_checked()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        checked = await page.get_by_role(\\\"checkbox\\\").is_checked()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_checked(timeout=timeout))\n\n    async def is_disabled(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        **NOTE** If you need to assert that an element is disabled, prefer `locator_assertions.to_be_disabled()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        disabled = await page.get_by_role(\\\"button\\\").is_disabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_disabled(timeout=timeout)\n        )\n\n    async def is_editable(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable). If the target element is not an `<input>`,\n        `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method\n        throws an error.\n\n        **NOTE** If you need to assert that an element is editable, prefer `locator_assertions.to_be_editable()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        editable = await page.get_by_role(\\\"textbox\\\").is_editable()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.is_editable(timeout=timeout)\n        )\n\n    async def is_enabled(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        **NOTE** If you need to assert that an element is enabled, prefer `locator_assertions.to_be_enabled()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        enabled = await page.get_by_role(\\\"button\\\").is_enabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_enabled(timeout=timeout))\n\n    async def is_hidden(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        **NOTE** If you need to assert that element is hidden, prefer `locator_assertions.to_be_hidden()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        hidden = await page.get_by_role(\\\"button\\\").is_hidden()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `locator.is_hidden()` does not wait for the element to become hidden and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_hidden(timeout=timeout))\n\n    async def is_visible(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        **NOTE** If you need to assert that element is visible, prefer `locator_assertions.to_be_visible()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        visible = await page.get_by_role(\\\"button\\\").is_visible()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `locator.is_visible()` does not wait for the element to become visible and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.is_visible(timeout=timeout))\n\n    async def press(\n        self,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.press\n\n        Focuses the matching element and presses a combination of the keys.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"textbox\\\").press(\\\"Backspace\\\")\n        ```\n\n        **Details**\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.press(\n                key=key, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"Locator.screenshot\n\n        Take a screenshot of the element matching the locator.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"link\\\").screenshot()\n        ```\n\n        Disable animations and save screenshot to a file:\n\n        ```py\n        await page.get_by_role(\\\"link\\\").screenshot(animations=\\\"disabled\\\", path=\\\"link.png\\\")\n        ```\n\n        **Details**\n\n        This method captures a screenshot of the page, clipped to the size and position of a particular element matching\n        the locator. If the element is covered by other elements, it will not be actually visible on the screenshot. If the\n        element is a scrollable container, only the currently scrolled content will be visible on the screenshot.\n\n        This method waits for the [actionability](https://playwright.dev/python/docs/actionability) checks, then scrolls element into view before taking\n        a screenshot. If the element is detached from DOM, the method throws an error.\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.screenshot(\n                timeout=timeout,\n                type=type,\n                path=path,\n                quality=quality,\n                omitBackground=omit_background,\n                animations=animations,\n                caret=caret,\n                scale=scale,\n                mask=mapping.to_impl(mask),\n                maskColor=mask_color,\n                style=style,\n            )\n        )\n\n    async def aria_snapshot(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.aria_snapshot\n\n        Captures the aria snapshot of the given element. Read more about [aria snapshots](https://playwright.dev/python/docs/aria-snapshots) and\n        `locator_assertions.to_match_aria_snapshot()` for the corresponding assertion.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"link\\\").aria_snapshot()\n        ```\n\n        **Details**\n\n        This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of\n        the element and its children. The snapshot can be used to assert the state of the element in the test, or to\n        compare it to state in the future.\n\n        The ARIA snapshot is represented using [YAML](https://yaml.org/spec/1.2.2/) markup language:\n        - The keys of the objects are the roles and optional accessible names of the elements.\n        - The values are either text content or an array of child elements.\n        - Generic static text can be represented with the `text` key.\n\n        Below is the HTML markup and the respective ARIA snapshot:\n\n        ```html\n        <ul aria-label=\\\"Links\\\">\n          <li><a href=\\\"/\\\">Home</a></li>\n          <li><a href=\\\"/about\\\">About</a></li>\n        <ul>\n        ```\n\n        ```yml\n        - list \\\"Links\\\":\n          - listitem:\n            - link \\\"Home\\\"\n          - listitem:\n            - link \\\"About\\\"\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.aria_snapshot(timeout=timeout)\n        )\n\n    async def scroll_into_view_if_needed(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"Locator.scroll_into_view_if_needed\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless\n        it is completely visible as defined by\n        [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.\n\n        See [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.scroll_into_view_if_needed(timeout=timeout)\n        )\n\n    async def select_option(\n        self,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Locator.select_option\n\n        Selects option or options in `<select>`.\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits until all specified options are present in\n        the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```html\n        <select multiple>\n          <option value=\\\"red\\\">Red</option>\n          <option value=\\\"green\\\">Green</option>\n          <option value=\\\"blue\\\">Blue</option>\n        </select>\n        ```\n\n        ```py\n        # single selection matching the value or label\n        await element.select_option(\\\"blue\\\")\n        # single selection matching the label\n        await element.select_option(label=\\\"blue\\\")\n        # multiple selection for blue, red and second option\n        await element.select_option(value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_option(\n                value=mapping.to_impl(value),\n                index=mapping.to_impl(index),\n                label=mapping.to_impl(label),\n                element=mapping.to_impl(element),\n                timeout=timeout,\n                noWaitAfter=no_wait_after,\n                force=force,\n            )\n        )\n\n    async def select_text(\n        self,\n        *,\n        force: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Locator.select_text\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then focuses the element and selects all its\n        text content.\n\n        If the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), focuses and selects text in\n        the control instead.\n\n        Parameters\n        ----------\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.select_text(force=force, timeout=timeout)\n        )\n\n    async def set_input_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.set_input_files\n\n        Upload file or multiple files into `<input type=file>`. For inputs with a `[webkitdirectory]` attribute, only a\n        single directory path is supported.\n\n        **Usage**\n\n        ```py\n        # Select one file\n        await page.get_by_label(\\\"Upload file\\\").set_input_files('myfile.pdf')\n\n        # Select multiple files\n        await page.get_by_label(\\\"Upload files\\\").set_input_files(['file1.txt', 'file2.txt'])\n\n        # Select a directory\n        await page.get_by_label(\\\"Upload directory\\\").set_input_files('mydir')\n\n        # Remove all the selected files\n        await page.get_by_label(\\\"Upload file\\\").set_input_files([])\n\n        # Upload buffer from memory\n        await page.get_by_label(\\\"Upload file\\\").set_input_files(\n            files=[\n                {\\\"name\\\": \\\"test.txt\\\", \\\"mimeType\\\": \\\"text/plain\\\", \\\"buffer\\\": b\\\"this is a test\\\"}\n            ],\n        )\n        ```\n\n        **Details**\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        This method expects `Locator` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_input_files(\n                files=mapping.to_impl(files), timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def tap(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.tap\n\n        Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually\n        dispatching touch events, see the [emulating legacy touch events](https://playwright.dev/python/docs/touch-events) page.\n\n        **Details**\n\n        This method taps the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `element.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.tap(\n                modifiers=mapping.to_impl(modifiers),\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def text_content(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[str]:\n        \"\"\"Locator.text_content\n\n        Returns the [`node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent).\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.text_content(timeout=timeout)\n        )\n\n    async def type(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.type\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `locator.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.type(\n                text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def press_sequentially(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.press_sequentially\n\n        **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if\n        there is special keyboard handling on the page.\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `locator.press()`.\n\n        **Usage**\n\n        ```py\n        await locator.press_sequentially(\\\"hello\\\") # types instantly\n        await locator.press_sequentially(\\\"world\\\", delay=100) # types slower, like a user\n        ```\n\n        An example of typing into a text field and then submitting the form:\n\n        ```py\n        locator = page.get_by_label(\\\"Password\\\")\n        await locator.press_sequentially(\\\"my password\\\")\n        await locator.press(\\\"Enter\\\")\n        ```\n\n        Parameters\n        ----------\n        text : str\n            String of characters to sequentially press into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.press_sequentially(\n                text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n            )\n        )\n\n    async def uncheck(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.uncheck\n\n        Ensure that checkbox or radio element is unchecked.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"checkbox\\\").uncheck()\n        ```\n\n        **Details**\n\n        This method unchecks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.uncheck(\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def all_inner_texts(self) -> typing.List[str]:\n        \"\"\"Locator.all_inner_texts\n\n        Returns an array of `node.innerText` values for all matching nodes.\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` with\n        `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        texts = await page.get_by_role(\\\"link\\\").all_inner_texts()\n        ```\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.all_inner_texts())\n\n    async def all_text_contents(self) -> typing.List[str]:\n        \"\"\"Locator.all_text_contents\n\n        Returns an array of `node.textContent` values for all matching nodes.\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        texts = await page.get_by_role(\\\"link\\\").all_text_contents()\n        ```\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.all_text_contents())\n\n    async def wait_for(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Locator.wait_for\n\n        Returns when element specified by locator satisfies the `state` option.\n\n        If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to\n        `timeout` milliseconds until the condition is met.\n\n        **Usage**\n\n        ```py\n        order_sent = page.locator(\\\"#order-sent\\\")\n        await order_sent.wait_for()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.wait_for(timeout=timeout, state=state)\n        )\n\n    async def set_checked(\n        self,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.set_checked\n\n        Set the state of a checkbox or a radio element.\n\n        **Usage**\n\n        ```py\n        await page.get_by_role(\\\"checkbox\\\").set_checked(True)\n        ```\n\n        **Details**\n\n        This method checks or unchecks an element by performing the following steps:\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.set_checked(\n                checked=checked,\n                position=position,\n                timeout=timeout,\n                force=force,\n                noWaitAfter=no_wait_after,\n                trial=trial,\n            )\n        )\n\n    async def highlight(self) -> None:\n        \"\"\"Locator.highlight\n\n        Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses\n        `locator.highlight()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.highlight())\n\n\nmapping.register(LocatorImpl, Locator)\n\n\nclass APIResponse(AsyncBase):\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"APIResponse.ok\n\n        Contains a boolean stating whether the response was successful (status in the range 200-299) or not.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.ok)\n\n    @property\n    def url(self) -> str:\n        \"\"\"APIResponse.url\n\n        Contains the URL of the response.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def status(self) -> int:\n        \"\"\"APIResponse.status\n\n        Contains the status code of the response (e.g., 200 for a success).\n\n        Returns\n        -------\n        int\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status)\n\n    @property\n    def status_text(self) -> str:\n        \"\"\"APIResponse.status_text\n\n        Contains the status text of the response (e.g. usually an \\\"OK\\\" for a success).\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status_text)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"APIResponse.headers\n\n        An object with all the response HTTP headers associated with this response.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    @property\n    def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"APIResponse.headers_array\n\n        An array with all the response HTTP headers associated with this response. Header names are not lower-cased.\n        Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.headers_array)\n\n    async def body(self) -> bytes:\n        \"\"\"APIResponse.body\n\n        Returns the buffer with response body.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.body())\n\n    async def text(self) -> str:\n        \"\"\"APIResponse.text\n\n        Returns the text representation of response body.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.text())\n\n    async def json(self) -> typing.Any:\n        \"\"\"APIResponse.json\n\n        Returns the JSON representation of response body.\n\n        This method will throw if the response body is not parsable via `JSON.parse`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.json())\n\n    async def dispose(self) -> None:\n        \"\"\"APIResponse.dispose\n\n        Disposes the body of this response. If not called then the body will stay in memory until the context closes.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.dispose())\n\n\nmapping.register(APIResponseImpl, APIResponse)\n\n\nclass APIRequestContext(AsyncBase):\n\n    async def dispose(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"APIRequestContext.dispose\n\n        All responses returned by `a_pi_request_context.get()` and similar methods are stored in the memory, so that\n        you can later call `a_pi_response.body()`.This method discards all its resources, calling any method on\n        disposed `APIRequestContext` will throw an exception.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the context disposal.\n        \"\"\"\n\n        return mapping.from_maybe_impl(await self._impl_obj.dispose(reason=reason))\n\n    async def delete(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.delete\n\n        Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.delete(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def head(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.head\n\n        Sends HTTP(S) [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.head(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def get(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.get\n\n        Sends HTTP(S) [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        **Usage**\n\n        Request parameters can be configured with `params` option, they will be serialized into the URL search parameters:\n\n        ```python\n        query_params = {\n          \\\"isbn\\\": \\\"1234\\\",\n          \\\"page\\\": \\\"23\\\"\n        }\n        api_request_context.get(\\\"https://example.com/api/getText\\\", params=query_params)\n        ```\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.get(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def patch(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.patch\n\n        Sends HTTP(S) [PATCH](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.patch(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def put(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.put\n\n        Sends HTTP(S) [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.put(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def post(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.post\n\n        Sends HTTP(S) [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        **Usage**\n\n        JSON objects can be passed directly to the request:\n\n        ```python\n        data = {\n            \\\"title\\\": \\\"Book Title\\\",\n            \\\"body\\\": \\\"John Doe\\\",\n        }\n        api_request_context.post(\\\"https://example.com/api/createBook\\\", data=data)\n        ```\n\n        To send form data to the server use `form` option. Its value will be encoded into the request body with\n        `application/x-www-form-urlencoded` encoding (see below how to use `multipart/form-data` form encoding to send\n        files):\n\n        The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data`\n        encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter:\n\n        ```python\n        api_request_context.post(\n          \\\"https://example.com/api/uploadScript'\\\",\n          multipart={\n            \\\"fileField\\\": {\n              \\\"name\\\": \\\"f.js\\\",\n              \\\"mimeType\\\": \\\"text/javascript\\\",\n              \\\"buffer\\\": b\\\"console.log(2022);\\\",\n            },\n          })\n        ```\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.post(\n                url=url,\n                params=mapping.to_impl(params),\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def fetch(\n        self,\n        url_or_request: typing.Union[str, \"Request\"],\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.fetch\n\n        Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and\n        update context cookies from the response. The method will automatically follow redirects.\n\n        **Usage**\n\n        JSON objects can be passed directly to the request:\n\n        ```python\n        data = {\n            \\\"title\\\": \\\"Book Title\\\",\n            \\\"body\\\": \\\"John Doe\\\",\n        }\n        api_request_context.fetch(\\\"https://example.com/api/createBook\\\", method=\\\"post\\\", data=data)\n        ```\n\n        The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data`\n        encoding, by specifiying the `multipart` parameter:\n\n        Parameters\n        ----------\n        url_or_request : Union[Request, str]\n            Target URL or Request to get all parameters from.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        method : Union[str, None]\n            If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or\n            [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)). If not specified, GET method is used.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.fetch(\n                urlOrRequest=url_or_request,\n                params=mapping.to_impl(params),\n                method=method,\n                headers=mapping.to_impl(headers),\n                data=mapping.to_impl(data),\n                form=mapping.to_impl(form),\n                multipart=mapping.to_impl(multipart),\n                timeout=timeout,\n                failOnStatusCode=fail_on_status_code,\n                ignoreHTTPSErrors=ignore_https_errors,\n                maxRedirects=max_redirects,\n                maxRetries=max_retries,\n            )\n        )\n\n    async def storage_state(\n        self,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        indexed_db: typing.Optional[bool] = None,\n    ) -> StorageState:\n        \"\"\"APIRequestContext.storage_state\n\n        Returns storage state for this request context, contains current cookies and local storage snapshot if it was\n        passed to the constructor.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current\n            working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.\n        indexed_db : Union[bool, None]\n            Set to `true` to include IndexedDB in the storage state snapshot.\n\n        Returns\n        -------\n        {cookies: List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: List[{origin: str, localStorage: List[{name: str, value: str}]}]}\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.storage_state(path=path, indexedDB=indexed_db)\n        )\n\n\nmapping.register(APIRequestContextImpl, APIRequestContext)\n\n\nclass APIRequest(AsyncBase):\n\n    async def new_context(\n        self,\n        *,\n        base_url: typing.Optional[str] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        user_agent: typing.Optional[str] = None,\n        timeout: typing.Optional[float] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n    ) -> \"APIRequestContext\":\n        \"\"\"APIRequest.new_context\n\n        Creates new instances of `APIRequestContext`.\n\n        Parameters\n        ----------\n        base_url : Union[str, None]\n            Methods like `a_pi_request_context.get()` take the base URL into consideration by using the\n            [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding URL.\n            Examples:\n            - baseURL: `http://localhost:3000` and sending request to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and sending request to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the response. Defaults to `30000` (30 seconds). Pass `0` to disable\n            timeout.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()` or `a_pi_request_context.storage_state()`.\n            Either a path to the file with saved storage, or the value returned by one of\n            `browser_context.storage_state()` or `a_pi_request_context.storage_state()` methods.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request\n            individually.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n\n        return mapping.from_impl(\n            await self._impl_obj.new_context(\n                baseURL=base_url,\n                extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                httpCredentials=http_credentials,\n                ignoreHTTPSErrors=ignore_https_errors,\n                proxy=proxy,\n                userAgent=user_agent,\n                timeout=timeout,\n                storageState=storage_state,\n                clientCertificates=client_certificates,\n                failOnStatusCode=fail_on_status_code,\n                maxRedirects=max_redirects,\n            )\n        )\n\n\nmapping.register(APIRequestImpl, APIRequest)\n\n\nclass PageAssertions(AsyncBase):\n\n    async def to_have_title(\n        self,\n        title_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"PageAssertions.to_have_title\n\n        Ensures the page has the given title.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        # ...\n        await expect(page).to_have_title(re.compile(r\\\".*checkout\\\"))\n        ```\n\n        Parameters\n        ----------\n        title_or_reg_exp : Union[Pattern[str], str]\n            Expected title or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_title(\n                titleOrRegExp=title_or_reg_exp, timeout=timeout\n            )\n        )\n\n    async def not_to_have_title(\n        self,\n        title_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"PageAssertions.not_to_have_title\n\n        The opposite of `page_assertions.to_have_title()`.\n\n        Parameters\n        ----------\n        title_or_reg_exp : Union[Pattern[str], str]\n            Expected title or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_title(\n                titleOrRegExp=title_or_reg_exp, timeout=timeout\n            )\n        )\n\n    async def to_have_url(\n        self,\n        url_or_reg_exp: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"PageAssertions.to_have_url\n\n        Ensures the page is navigated to the given URL.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        # ...\n        await expect(page).to_have_url(re.compile(\\\".*checkout\\\"))\n        ```\n\n        Parameters\n        ----------\n        url_or_reg_exp : Union[Pattern[str], str]\n            Expected URL string or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression parameter if specified. A provided predicate ignores this flag.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_url(\n                urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case\n            )\n        )\n\n    async def not_to_have_url(\n        self,\n        url_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"PageAssertions.not_to_have_url\n\n        The opposite of `page_assertions.to_have_url()`.\n\n        Parameters\n        ----------\n        url_or_reg_exp : Union[Pattern[str], str]\n            Expected URL string or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_url(\n                urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case\n            )\n        )\n\n\nmapping.register(PageAssertionsImpl, PageAssertions)\n\n\nclass LocatorAssertions(AsyncBase):\n\n    async def to_contain_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_contain_text\n\n        Ensures the `Locator` points to an element that contains the given text. All nested elements will be considered\n        when computing the text content of the element. You can use regular expressions for the value as well.\n\n        **Details**\n\n        When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual\n        text and in the expected string before matching. When regular expression is used, the actual text is matched as is.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        locator = page.locator('.title')\n        await expect(locator).to_contain_text(\\\"substring\\\")\n        await expect(locator).to_contain_text(re.compile(r\\\"\\\\d messages\\\"))\n        ```\n\n        If you pass an array as an expected value, the expectations are:\n        1. Locator resolves to a list of elements.\n        1. Elements from a **subset** of this list contain text from the expected array, respectively.\n        1. The matching subset of elements has the same order as the expected array.\n        1. Each text value from the expected array is matched by some element from the list.\n\n        For example, consider the following list:\n\n        ```html\n        <ul>\n          <li>Item Text 1</li>\n          <li>Item Text 2</li>\n          <li>Item Text 3</li>\n        </ul>\n        ```\n\n        Let's see how we can use the assertion:\n\n        ```py\n        from playwright.async_api import expect\n\n        # ✓ Contains the right items in the right order\n        await expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Text 1\\\", \\\"Text 3\\\"])\n\n        # ✖ Wrong order\n        await expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Text 3\\\", \\\"Text 2\\\"])\n\n        # ✖ No item contains this text\n        await expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Some 33\\\"])\n\n        # ✖ Locator points to the outer list element, not to the list items\n        await expect(page.locator(\\\"ul\\\")).to_contain_text([\\\"Text 3\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected substring or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_contain_text(\n                expected=mapping.to_impl(expected),\n                useInnerText=use_inner_text,\n                timeout=timeout,\n                ignoreCase=ignore_case,\n            )\n        )\n\n    async def not_to_contain_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_contain_text\n\n        The opposite of `locator_assertions.to_contain_text()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected substring or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_contain_text(\n                expected=mapping.to_impl(expected),\n                useInnerText=use_inner_text,\n                timeout=timeout,\n                ignoreCase=ignore_case,\n            )\n        )\n\n    async def to_have_attribute(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_attribute\n\n        Ensures the `Locator` points to an element with given attribute.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"input\\\")\n        await expect(locator).to_have_attribute(\\\"type\\\", \\\"text\\\")\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Attribute name.\n        value : Union[Pattern[str], str]\n            Expected attribute value.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_attribute(\n                name=name, value=value, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def not_to_have_attribute(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_attribute\n\n        The opposite of `locator_assertions.to_have_attribute()`.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name.\n        value : Union[Pattern[str], str]\n            Expected attribute value.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_attribute(\n                name=name, value=value, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def to_have_class(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_class\n\n        Ensures the `Locator` points to an element with given CSS classes. When a string is provided, it must fully match\n        the element's `class` attribute. To match individual classes use `locator_assertions.to_contain_class()`.\n\n        **Usage**\n\n        ```html\n        <div class='middle selected row' id='component'></div>\n        ```\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"#component\\\")\n        await expect(locator).to_have_class(\\\"middle selected row\\\")\n        await expect(locator).to_have_class(re.compile(r\\\"(^|\\\\\\\\s)selected(\\\\\\\\s|$)\\\"))\n        ```\n\n        When an array is passed, the method asserts that the list of elements located matches the corresponding list of\n        expected class values. Each element's class attribute is matched against the corresponding string or regular\n        expression in the array:\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\".list > .component\\\")\n        await expect(locator).to_have_class([\\\"component\\\", \\\"component selected\\\", \\\"component\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_class(\n                expected=mapping.to_impl(expected), timeout=timeout\n            )\n        )\n\n    async def not_to_have_class(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_class\n\n        The opposite of `locator_assertions.to_have_class()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_class(\n                expected=mapping.to_impl(expected), timeout=timeout\n            )\n        )\n\n    async def to_contain_class(\n        self,\n        expected: typing.Union[typing.Sequence[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_contain_class\n\n        Ensures the `Locator` points to an element with given CSS classes. All classes from the asserted value, separated\n        by spaces, must be present in the\n        [Element.classList](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) in any order.\n\n        **Usage**\n\n        ```html\n        <div class='middle selected row' id='component'></div>\n        ```\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"#component\\\")\n        await expect(locator).to_contain_class(\\\"middle selected row\\\")\n        await expect(locator).to_contain_class(\\\"selected\\\")\n        await expect(locator).to_contain_class(\\\"row middle\\\")\n        ```\n\n        When an array is passed, the method asserts that the list of elements located matches the corresponding list of\n        expected class lists. Each element's class attribute is matched against the corresponding class in the array:\n\n        ```html\n        <div class='list'>\n          <div class='component inactive'></div>\n          <div class='component active'></div>\n          <div class='component inactive'></div>\n        </div>\n        ```\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\".list > .component\\\")\n        await expect(locator).to_contain_class([\\\"inactive\\\", \\\"active\\\", \\\"inactive\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Sequence[str], str]\n            A string containing expected class names, separated by spaces, or a list of such strings to assert multiple\n            elements.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_contain_class(\n                expected=mapping.to_impl(expected), timeout=timeout\n            )\n        )\n\n    async def not_to_contain_class(\n        self,\n        expected: typing.Union[typing.Sequence[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_contain_class\n\n        The opposite of `locator_assertions.to_contain_class()`.\n\n        Parameters\n        ----------\n        expected : Union[Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_contain_class(\n                expected=mapping.to_impl(expected), timeout=timeout\n            )\n        )\n\n    async def to_have_count(\n        self, count: int, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_count\n\n        Ensures the `Locator` resolves to an exact number of DOM nodes.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"list > .component\\\")\n        await expect(locator).to_have_count(3)\n        ```\n\n        Parameters\n        ----------\n        count : int\n            Expected count.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_count(count=count, timeout=timeout)\n        )\n\n    async def not_to_have_count(\n        self, count: int, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_count\n\n        The opposite of `locator_assertions.to_have_count()`.\n\n        Parameters\n        ----------\n        count : int\n            Expected count.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_count(count=count, timeout=timeout)\n        )\n\n    async def to_have_css(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_css\n\n        Ensures the `Locator` resolves to an element with the given computed CSS style.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_role(\\\"button\\\")\n        await expect(locator).to_have_css(\\\"display\\\", \\\"flex\\\")\n        ```\n\n        Parameters\n        ----------\n        name : str\n            CSS property name.\n        value : Union[Pattern[str], str]\n            CSS property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_css(name=name, value=value, timeout=timeout)\n        )\n\n    async def not_to_have_css(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_css\n\n        The opposite of `locator_assertions.to_have_css()`.\n\n        Parameters\n        ----------\n        name : str\n            CSS property name.\n        value : Union[Pattern[str], str]\n            CSS property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_css(\n                name=name, value=value, timeout=timeout\n            )\n        )\n\n    async def to_have_id(\n        self,\n        id: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_id\n\n        Ensures the `Locator` points to an element with the given DOM Node ID.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        await expect(locator).to_have_id(\\\"lastname\\\")\n        ```\n\n        Parameters\n        ----------\n        id : Union[Pattern[str], str]\n            Element id.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_id(id=id, timeout=timeout)\n        )\n\n    async def not_to_have_id(\n        self,\n        id: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_id\n\n        The opposite of `locator_assertions.to_have_id()`.\n\n        Parameters\n        ----------\n        id : Union[Pattern[str], str]\n            Element id.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_id(id=id, timeout=timeout)\n        )\n\n    async def to_have_js_property(\n        self, name: str, value: typing.Any, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_js_property\n\n        Ensures the `Locator` points to an element with given JavaScript property. Note that this property can be of a\n        primitive type as well as a plain serializable JavaScript object.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\".component\\\")\n        await expect(locator).to_have_js_property(\\\"loaded\\\", True)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Property name.\n        value : Any\n            Property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_js_property(\n                name=name, value=mapping.to_impl(value), timeout=timeout\n            )\n        )\n\n    async def not_to_have_js_property(\n        self, name: str, value: typing.Any, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_js_property\n\n        The opposite of `locator_assertions.to_have_js_property()`.\n\n        Parameters\n        ----------\n        name : str\n            Property name.\n        value : Any\n            Property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_js_property(\n                name=name, value=mapping.to_impl(value), timeout=timeout\n            )\n        )\n\n    async def to_have_value(\n        self,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_value\n\n        Ensures the `Locator` points to an element with the given input value. You can use regular expressions for the\n        value as well.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"input[type=number]\\\")\n        await expect(locator).to_have_value(re.compile(r\\\"[0-9]\\\"))\n        ```\n\n        Parameters\n        ----------\n        value : Union[Pattern[str], str]\n            Expected value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_value(value=value, timeout=timeout)\n        )\n\n    async def not_to_have_value(\n        self,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_value\n\n        The opposite of `locator_assertions.to_have_value()`.\n\n        Parameters\n        ----------\n        value : Union[Pattern[str], str]\n            Expected value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_value(value=value, timeout=timeout)\n        )\n\n    async def to_have_values(\n        self,\n        values: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_values\n\n        Ensures the `Locator` points to multi-select/combobox (i.e. a `select` with the `multiple` attribute) and the\n        specified values are selected.\n\n        **Usage**\n\n        For example, given the following element:\n\n        ```html\n        <select id=\\\"favorite-colors\\\" multiple>\n          <option value=\\\"R\\\">Red</option>\n          <option value=\\\"G\\\">Green</option>\n          <option value=\\\"B\\\">Blue</option>\n        </select>\n        ```\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"id=favorite-colors\\\")\n        await locator.select_option([\\\"R\\\", \\\"G\\\"])\n        await expect(locator).to_have_values([re.compile(r\\\"R\\\"), re.compile(r\\\"G\\\")])\n        ```\n\n        Parameters\n        ----------\n        values : Union[Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str]]\n            Expected options currently selected.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_values(\n                values=mapping.to_impl(values), timeout=timeout\n            )\n        )\n\n    async def not_to_have_values(\n        self,\n        values: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_values\n\n        The opposite of `locator_assertions.to_have_values()`.\n\n        Parameters\n        ----------\n        values : Union[Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str]]\n            Expected options currently selected.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_values(\n                values=mapping.to_impl(values), timeout=timeout\n            )\n        )\n\n    async def to_have_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_text\n\n        Ensures the `Locator` points to an element with the given text. All nested elements will be considered when\n        computing the text content of the element. You can use regular expressions for the value as well.\n\n        **Details**\n\n        When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual\n        text and in the expected string before matching. When regular expression is used, the actual text is matched as is.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\".title\\\")\n        await expect(locator).to_have_text(re.compile(r\\\"Welcome, Test User\\\"))\n        await expect(locator).to_have_text(re.compile(r\\\"Welcome, .*\\\"))\n        ```\n\n        If you pass an array as an expected value, the expectations are:\n        1. Locator resolves to a list of elements.\n        1. The number of elements equals the number of expected values in the array.\n        1. Elements from the list have text matching expected array values, one by one, in order.\n\n        For example, consider the following list:\n\n        ```html\n        <ul>\n          <li>Text 1</li>\n          <li>Text 2</li>\n          <li>Text 3</li>\n        </ul>\n        ```\n\n        Let's see how we can use the assertion:\n\n        ```py\n        from playwright.async_api import expect\n\n        # ✓ Has the right items in the right order\n        await expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text 3\\\"])\n\n        # ✖ Wrong order\n        await expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 3\\\", \\\"Text 2\\\", \\\"Text 1\\\"])\n\n        # ✖ Last item does not match\n        await expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text\\\"])\n\n        # ✖ Locator points to the outer list element, not to the list items\n        await expect(page.locator(\\\"ul\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text 3\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected string or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_text(\n                expected=mapping.to_impl(expected),\n                useInnerText=use_inner_text,\n                timeout=timeout,\n                ignoreCase=ignore_case,\n            )\n        )\n\n    async def not_to_have_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_text\n\n        The opposite of `locator_assertions.to_have_text()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected string or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_text(\n                expected=mapping.to_impl(expected),\n                useInnerText=use_inner_text,\n                timeout=timeout,\n                ignoreCase=ignore_case,\n            )\n        )\n\n    async def to_be_attached(\n        self,\n        *,\n        attached: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_attached\n\n        Ensures that `Locator` points to an element that is\n        [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.\n\n        **Usage**\n\n        ```py\n        await expect(page.get_by_text(\\\"Hidden text\\\")).to_be_attached()\n        ```\n\n        Parameters\n        ----------\n        attached : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_attached(attached=attached, timeout=timeout)\n        )\n\n    async def to_be_checked(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        checked: typing.Optional[bool] = None,\n        indeterminate: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_checked\n\n        Ensures the `Locator` points to a checked input.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_label(\\\"Subscribe to newsletter\\\")\n        await expect(locator).to_be_checked()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        checked : Union[bool, None]\n            Provides state to assert for. Asserts for input to be checked by default. This option can't be used when\n            `indeterminate` is set to true.\n        indeterminate : Union[bool, None]\n            Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons.\n            This option can't be true when `checked` is provided.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_checked(\n                timeout=timeout, checked=checked, indeterminate=indeterminate\n            )\n        )\n\n    async def not_to_be_attached(\n        self,\n        *,\n        attached: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_attached\n\n        The opposite of `locator_assertions.to_be_attached()`.\n\n        Parameters\n        ----------\n        attached : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_attached(attached=attached, timeout=timeout)\n        )\n\n    async def not_to_be_checked(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_checked\n\n        The opposite of `locator_assertions.to_be_checked()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_checked(timeout=timeout)\n        )\n\n    async def to_be_disabled(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_disabled\n\n        Ensures the `Locator` points to a disabled element. Element is disabled if it has \\\"disabled\\\" attribute or is\n        disabled via\n        ['aria-disabled'](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled). Note\n        that only native control elements such as HTML `button`, `input`, `select`, `textarea`, `option`, `optgroup` can be\n        disabled by setting \\\"disabled\\\" attribute. \\\"disabled\\\" attribute on other elements is ignored by the browser.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"button.submit\\\")\n        await expect(locator).to_be_disabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_disabled(timeout=timeout)\n        )\n\n    async def not_to_be_disabled(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_disabled\n\n        The opposite of `locator_assertions.to_be_disabled()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_disabled(timeout=timeout)\n        )\n\n    async def to_be_editable(\n        self,\n        *,\n        editable: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_editable\n\n        Ensures the `Locator` points to an editable element.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        await expect(locator).to_be_editable()\n        ```\n\n        Parameters\n        ----------\n        editable : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_editable(editable=editable, timeout=timeout)\n        )\n\n    async def not_to_be_editable(\n        self,\n        *,\n        editable: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_editable\n\n        The opposite of `locator_assertions.to_be_editable()`.\n\n        Parameters\n        ----------\n        editable : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_editable(editable=editable, timeout=timeout)\n        )\n\n    async def to_be_empty(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_empty\n\n        Ensures the `Locator` points to an empty editable element or to a DOM node that has no text.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"div.warning\\\")\n        await expect(locator).to_be_empty()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_empty(timeout=timeout)\n        )\n\n    async def not_to_be_empty(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_empty\n\n        The opposite of `locator_assertions.to_be_empty()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_empty(timeout=timeout)\n        )\n\n    async def to_be_enabled(\n        self,\n        *,\n        enabled: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_enabled\n\n        Ensures the `Locator` points to an enabled element.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator(\\\"button.submit\\\")\n        await expect(locator).to_be_enabled()\n        ```\n\n        Parameters\n        ----------\n        enabled : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_enabled(enabled=enabled, timeout=timeout)\n        )\n\n    async def not_to_be_enabled(\n        self,\n        *,\n        enabled: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_enabled\n\n        The opposite of `locator_assertions.to_be_enabled()`.\n\n        Parameters\n        ----------\n        enabled : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_enabled(enabled=enabled, timeout=timeout)\n        )\n\n    async def to_be_hidden(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_hidden\n\n        Ensures that `Locator` either does not resolve to any DOM node, or resolves to a\n        [non-visible](https://playwright.dev/python/docs/actionability#visible) one.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.locator('.my-element')\n        await expect(locator).to_be_hidden()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_hidden(timeout=timeout)\n        )\n\n    async def not_to_be_hidden(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_hidden\n\n        The opposite of `locator_assertions.to_be_hidden()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_hidden(timeout=timeout)\n        )\n\n    async def to_be_visible(\n        self,\n        *,\n        visible: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_visible\n\n        Ensures that `Locator` points to an attached and [visible](https://playwright.dev/python/docs/actionability#visible) DOM node.\n\n        To check that at least one element from the list is visible, use `locator.first()`.\n\n        **Usage**\n\n        ```py\n        # A specific element is visible.\n        await expect(page.get_by_text(\\\"Welcome\\\")).to_be_visible()\n\n        # At least one item in the list is visible.\n        await expect(page.get_by_test_id(\\\"todo-item\\\").first).to_be_visible()\n\n        # At least one of the two elements is visible, possibly both.\n        await expect(\n            page.get_by_role(\\\"button\\\", name=\\\"Sign in\\\")\n            .or_(page.get_by_role(\\\"button\\\", name=\\\"Sign up\\\"))\n            .first\n        ).to_be_visible()\n        ```\n\n        Parameters\n        ----------\n        visible : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_visible(visible=visible, timeout=timeout)\n        )\n\n    async def not_to_be_visible(\n        self,\n        *,\n        visible: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_visible\n\n        The opposite of `locator_assertions.to_be_visible()`.\n\n        Parameters\n        ----------\n        visible : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_visible(visible=visible, timeout=timeout)\n        )\n\n    async def to_be_focused(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_focused\n\n        Ensures the `Locator` points to a focused DOM node.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        await expect(locator).to_be_focused()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_focused(timeout=timeout)\n        )\n\n    async def not_to_be_focused(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_focused\n\n        The opposite of `locator_assertions.to_be_focused()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_focused(timeout=timeout)\n        )\n\n    async def to_be_in_viewport(\n        self,\n        *,\n        ratio: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_in_viewport\n\n        Ensures the `Locator` points to an element that intersects viewport, according to the\n        [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        locator = page.get_by_role(\\\"button\\\")\n        # Make sure at least some part of element intersects viewport.\n        await expect(locator).to_be_in_viewport()\n        # Make sure element is fully outside of viewport.\n        await expect(locator).not_to_be_in_viewport()\n        # Make sure that at least half of the element intersects viewport.\n        await expect(locator).to_be_in_viewport(ratio=0.5)\n        ```\n\n        Parameters\n        ----------\n        ratio : Union[float, None]\n            The minimal ratio of the element to intersect viewport. If equals to `0`, then element should intersect viewport at\n            any positive ratio. Defaults to `0`.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_be_in_viewport(ratio=ratio, timeout=timeout)\n        )\n\n    async def not_to_be_in_viewport(\n        self,\n        *,\n        ratio: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_in_viewport\n\n        The opposite of `locator_assertions.to_be_in_viewport()`.\n\n        Parameters\n        ----------\n        ratio : Union[float, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_be_in_viewport(ratio=ratio, timeout=timeout)\n        )\n\n    async def to_have_accessible_description(\n        self,\n        description: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_description\n\n        Ensures the `Locator` points to an element with a given\n        [accessible description](https://w3c.github.io/accname/#dfn-accessible-description).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        await expect(locator).to_have_accessible_description(\\\"Save results to disk\\\")\n        ```\n\n        Parameters\n        ----------\n        description : Union[Pattern[str], str]\n            Expected accessible description.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_accessible_description(\n                description=description, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def not_to_have_accessible_description(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_description\n\n        The opposite of `locator_assertions.to_have_accessible_description()`.\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible description.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_accessible_description(\n                name=name, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def to_have_accessible_name(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_name\n\n        Ensures the `Locator` points to an element with a given\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        await expect(locator).to_have_accessible_name(\\\"Save to disk\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible name.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_accessible_name(\n                name=name, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def not_to_have_accessible_name(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_name\n\n        The opposite of `locator_assertions.to_have_accessible_name()`.\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible name.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_accessible_name(\n                name=name, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def to_have_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_role\n\n        Ensures the `Locator` points to an element with a given [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles).\n\n        Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting  a superclass\n        role `\\\"checkbox\\\"` on an element with a subclass role `\\\"switch\\\"` will fail.\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        await expect(locator).to_have_role(\\\"button\\\")\n        ```\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_role(role=role, timeout=timeout)\n        )\n\n    async def to_have_accessible_error_message(\n        self,\n        error_message: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_error_message\n\n        Ensures the `Locator` points to an element with a given\n        [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"username-input\\\")\n        await expect(locator).to_have_accessible_error_message(\\\"Username is required.\\\")\n        ```\n\n        Parameters\n        ----------\n        error_message : Union[Pattern[str], str]\n            Expected accessible error message.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_have_accessible_error_message(\n                errorMessage=error_message, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def not_to_have_accessible_error_message(\n        self,\n        error_message: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_error_message\n\n        The opposite of `locator_assertions.to_have_accessible_error_message()`.\n\n        Parameters\n        ----------\n        error_message : Union[Pattern[str], str]\n            Expected accessible error message.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_accessible_error_message(\n                errorMessage=error_message, ignoreCase=ignore_case, timeout=timeout\n            )\n        )\n\n    async def not_to_have_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_role\n\n        The opposite of `locator_assertions.to_have_role()`.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_have_role(role=role, timeout=timeout)\n        )\n\n    async def to_match_aria_snapshot(\n        self, expected: str, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_match_aria_snapshot\n\n        Asserts that the target element matches the given [accessibility snapshot](https://playwright.dev/python/docs/aria-snapshots).\n\n        **Usage**\n\n        ```py\n        await page.goto(\\\"https://demo.playwright.dev/todomvc/\\\")\n        await expect(page.locator('body')).to_match_aria_snapshot('''\n          - heading \\\"todos\\\"\n          - textbox \\\"What needs to be done?\\\"\n        ''')\n        ```\n\n        Parameters\n        ----------\n        expected : str\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.to_match_aria_snapshot(\n                expected=expected, timeout=timeout\n            )\n        )\n\n    async def not_to_match_aria_snapshot(\n        self, expected: str, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_match_aria_snapshot\n\n        The opposite of `locator_assertions.to_match_aria_snapshot()`.\n\n        Parameters\n        ----------\n        expected : str\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            await self._impl_obj.not_to_match_aria_snapshot(\n                expected=expected, timeout=timeout\n            )\n        )\n\n\nmapping.register(LocatorAssertionsImpl, LocatorAssertions)\n\n\nclass APIResponseAssertions(AsyncBase):\n\n    async def to_be_ok(self) -> None:\n        \"\"\"APIResponseAssertions.to_be_ok\n\n        Ensures the response status code is within `200..299` range.\n\n        **Usage**\n\n        ```py\n        from playwright.async_api import expect\n\n        # ...\n        await expect(response).to_be_ok()\n        ```\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(await self._impl_obj.to_be_ok())\n\n    async def not_to_be_ok(self) -> None:\n        \"\"\"APIResponseAssertions.not_to_be_ok\n\n        The opposite of `a_pi_response_assertions.to_be_ok()`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(await self._impl_obj.not_to_be_ok())\n\n\nmapping.register(APIResponseAssertionsImpl, APIResponseAssertions)\n"
  },
  {
    "path": "playwright/py.typed",
    "content": ""
  },
  {
    "path": "playwright/sync_api/__init__.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nPython package `playwright` is a Python library to automate Chromium,\nFirefox and WebKit with a single API. Playwright is built to enable cross-browser\nweb automation that is ever-green, capable, reliable and fast.\n\"\"\"\n\nfrom typing import Any, Optional, Union, overload\n\nimport playwright._impl._api_structures\nimport playwright._impl._errors\nimport playwright.sync_api._generated\nfrom playwright._impl._assertions import (\n    APIResponseAssertions as APIResponseAssertionsImpl,\n)\nfrom playwright._impl._assertions import LocatorAssertions as LocatorAssertionsImpl\nfrom playwright._impl._assertions import PageAssertions as PageAssertionsImpl\nfrom playwright.sync_api._context_manager import PlaywrightContextManager\nfrom playwright.sync_api._generated import (\n    APIRequest,\n    APIRequestContext,\n    APIResponse,\n    APIResponseAssertions,\n    Browser,\n    BrowserContext,\n    BrowserType,\n    CDPSession,\n    ConsoleMessage,\n    Dialog,\n    Download,\n    ElementHandle,\n    FileChooser,\n    Frame,\n    FrameLocator,\n    JSHandle,\n    Keyboard,\n    Locator,\n    LocatorAssertions,\n    Mouse,\n    Page,\n    PageAssertions,\n    Playwright,\n    Request,\n    Response,\n    Route,\n    Selectors,\n    Touchscreen,\n    Video,\n    WebError,\n    WebSocket,\n    WebSocketRoute,\n    Worker,\n)\n\nChromiumBrowserContext = BrowserContext\n\nCookie = playwright._impl._api_structures.Cookie\nFilePayload = playwright._impl._api_structures.FilePayload\nFloatRect = playwright._impl._api_structures.FloatRect\nGeolocation = playwright._impl._api_structures.Geolocation\nHttpCredentials = playwright._impl._api_structures.HttpCredentials\nPdfMargins = playwright._impl._api_structures.PdfMargins\nPosition = playwright._impl._api_structures.Position\nProxySettings = playwright._impl._api_structures.ProxySettings\nResourceTiming = playwright._impl._api_structures.ResourceTiming\nSourceLocation = playwright._impl._api_structures.SourceLocation\nStorageState = playwright._impl._api_structures.StorageState\nStorageStateCookie = playwright._impl._api_structures.StorageStateCookie\nViewportSize = playwright._impl._api_structures.ViewportSize\n\nError = playwright._impl._errors.Error\nTimeoutError = playwright._impl._errors.TimeoutError\n\n\ndef sync_playwright() -> PlaywrightContextManager:\n    return PlaywrightContextManager()\n\n\nclass Expect:\n    _unset: Any = object()\n\n    def __init__(self) -> None:\n        self._timeout: Optional[float] = None\n\n    def set_options(self, timeout: Optional[float] = _unset) -> None:\n        \"\"\"\n        This method sets global `expect()` options.\n\n        Args:\n            timeout (float): Timeout value in milliseconds. Default to 5000 milliseconds.\n\n        Returns:\n            None\n        \"\"\"\n        if timeout is not self._unset:\n            self._timeout = timeout\n\n    @overload\n    def __call__(\n        self, actual: Page, message: Optional[str] = None\n    ) -> PageAssertions: ...\n\n    @overload\n    def __call__(\n        self, actual: Locator, message: Optional[str] = None\n    ) -> LocatorAssertions: ...\n\n    @overload\n    def __call__(\n        self, actual: APIResponse, message: Optional[str] = None\n    ) -> APIResponseAssertions: ...\n\n    def __call__(\n        self, actual: Union[Page, Locator, APIResponse], message: Optional[str] = None\n    ) -> Union[PageAssertions, LocatorAssertions, APIResponseAssertions]:\n        if isinstance(actual, Page):\n            return PageAssertions(\n                PageAssertionsImpl(actual._impl_obj, self._timeout, message=message)\n            )\n        elif isinstance(actual, Locator):\n            return LocatorAssertions(\n                LocatorAssertionsImpl(actual._impl_obj, self._timeout, message=message)\n            )\n        elif isinstance(actual, APIResponse):\n            return APIResponseAssertions(\n                APIResponseAssertionsImpl(\n                    actual._impl_obj, self._timeout, message=message\n                )\n            )\n        raise ValueError(f\"Unsupported type: {type(actual)}\")\n\n\nexpect = Expect()\n\n\n__all__ = [\n    \"expect\",\n    \"APIRequest\",\n    \"APIRequestContext\",\n    \"APIResponse\",\n    \"Browser\",\n    \"BrowserContext\",\n    \"BrowserType\",\n    \"CDPSession\",\n    \"ChromiumBrowserContext\",\n    \"ConsoleMessage\",\n    \"Cookie\",\n    \"Dialog\",\n    \"Download\",\n    \"ElementHandle\",\n    \"Error\",\n    \"FileChooser\",\n    \"FilePayload\",\n    \"FloatRect\",\n    \"Frame\",\n    \"FrameLocator\",\n    \"Geolocation\",\n    \"HttpCredentials\",\n    \"JSHandle\",\n    \"Keyboard\",\n    \"Locator\",\n    \"Mouse\",\n    \"Page\",\n    \"PdfMargins\",\n    \"Position\",\n    \"Playwright\",\n    \"ProxySettings\",\n    \"Request\",\n    \"ResourceTiming\",\n    \"Response\",\n    \"Route\",\n    \"Selectors\",\n    \"SourceLocation\",\n    \"StorageState\",\n    \"StorageStateCookie\",\n    \"sync_playwright\",\n    \"TimeoutError\",\n    \"Touchscreen\",\n    \"Video\",\n    \"ViewportSize\",\n    \"WebError\",\n    \"WebSocket\",\n    \"WebSocketRoute\",\n    \"Worker\",\n]\n"
  },
  {
    "path": "playwright/sync_api/_context_manager.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import TYPE_CHECKING, Any, Optional, cast\n\nfrom greenlet import greenlet\n\nfrom playwright._impl._connection import ChannelOwner, Connection\nfrom playwright._impl._errors import Error\nfrom playwright._impl._greenlets import MainGreenlet\nfrom playwright._impl._object_factory import create_remote_object\nfrom playwright._impl._playwright import Playwright\nfrom playwright._impl._transport import PipeTransport\nfrom playwright.sync_api._generated import Playwright as SyncPlaywright\n\nif TYPE_CHECKING:\n    from asyncio.unix_events import AbstractChildWatcher\n\n\nclass PlaywrightContextManager:\n    def __init__(self) -> None:\n        self._playwright: SyncPlaywright\n        self._loop: asyncio.AbstractEventLoop\n        self._own_loop = False\n        self._watcher: Optional[AbstractChildWatcher] = None\n        self._exit_was_called = False\n\n    def __enter__(self) -> SyncPlaywright:\n        try:\n            self._loop = asyncio.get_running_loop()\n        except RuntimeError:\n            self._loop = asyncio.new_event_loop()\n            self._own_loop = True\n        if self._loop.is_running():\n            raise Error(\n                \"\"\"It looks like you are using Playwright Sync API inside the asyncio loop.\nPlease use the Async API instead.\"\"\"\n            )\n\n        # Create a new fiber for the protocol dispatcher. It will be pumping events\n        # until the end of times. We will pass control to that fiber every time we\n        # block while waiting for a response.\n        def greenlet_main() -> None:\n            self._loop.run_until_complete(self._connection.run_as_sync())\n\n        dispatcher_fiber = MainGreenlet(greenlet_main)\n\n        self._connection = Connection(\n            dispatcher_fiber,\n            create_remote_object,\n            PipeTransport(self._loop),\n            self._loop,\n        )\n\n        g_self = greenlet.getcurrent()\n\n        def callback_wrapper(channel_owner: ChannelOwner) -> None:\n            playwright_impl = cast(Playwright, channel_owner)\n            self._playwright = SyncPlaywright(playwright_impl)\n            g_self.switch()\n\n        # Switch control to the dispatcher, it'll fire an event and pass control to\n        # the calling greenlet.\n        self._connection.call_on_object_with_known_name(\"Playwright\", callback_wrapper)\n        dispatcher_fiber.switch()\n\n        playwright = self._playwright\n        playwright.stop = self.__exit__  # type: ignore\n        return playwright\n\n    def start(self) -> SyncPlaywright:\n        return self.__enter__()\n\n    def __exit__(self, *args: Any) -> None:\n        if self._exit_was_called:\n            return\n        self._exit_was_called = True\n        self._connection.stop_sync()\n        if self._watcher:\n            self._watcher.close()\n        if self._own_loop:\n            tasks = asyncio.all_tasks(self._loop)\n            for t in [t for t in tasks if not (t.done() or t.cancelled())]:\n                t.cancel()\n            self._loop.run_until_complete(self._loop.shutdown_asyncgens())\n            self._loop.close()\n"
  },
  {
    "path": "playwright/sync_api/_generated.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport datetime\nimport pathlib\nimport typing\nfrom typing import Literal\n\nfrom playwright._impl._api_structures import (\n    ClientCertificate,\n    Cookie,\n    FilePayload,\n    FloatRect,\n    Geolocation,\n    HttpCredentials,\n    NameValue,\n    PdfMargins,\n    Position,\n    ProxySettings,\n    RemoteAddr,\n    RequestSizes,\n    ResourceTiming,\n    SecurityDetails,\n    SetCookieParam,\n    SourceLocation,\n    StorageState,\n    TracingGroupLocation,\n    ViewportSize,\n)\nfrom playwright._impl._assertions import (\n    APIResponseAssertions as APIResponseAssertionsImpl,\n)\nfrom playwright._impl._assertions import LocatorAssertions as LocatorAssertionsImpl\nfrom playwright._impl._assertions import PageAssertions as PageAssertionsImpl\nfrom playwright._impl._browser import Browser as BrowserImpl\nfrom playwright._impl._browser_context import BrowserContext as BrowserContextImpl\nfrom playwright._impl._browser_type import BrowserType as BrowserTypeImpl\nfrom playwright._impl._cdp_session import CDPSession as CDPSessionImpl\nfrom playwright._impl._clock import Clock as ClockImpl\nfrom playwright._impl._console_message import ConsoleMessage as ConsoleMessageImpl\nfrom playwright._impl._dialog import Dialog as DialogImpl\nfrom playwright._impl._download import Download as DownloadImpl\nfrom playwright._impl._element_handle import ElementHandle as ElementHandleImpl\nfrom playwright._impl._errors import Error\nfrom playwright._impl._fetch import APIRequest as APIRequestImpl\nfrom playwright._impl._fetch import APIRequestContext as APIRequestContextImpl\nfrom playwright._impl._fetch import APIResponse as APIResponseImpl\nfrom playwright._impl._file_chooser import FileChooser as FileChooserImpl\nfrom playwright._impl._frame import Frame as FrameImpl\nfrom playwright._impl._input import Keyboard as KeyboardImpl\nfrom playwright._impl._input import Mouse as MouseImpl\nfrom playwright._impl._input import Touchscreen as TouchscreenImpl\nfrom playwright._impl._js_handle import JSHandle as JSHandleImpl\nfrom playwright._impl._locator import FrameLocator as FrameLocatorImpl\nfrom playwright._impl._locator import Locator as LocatorImpl\nfrom playwright._impl._network import Request as RequestImpl\nfrom playwright._impl._network import Response as ResponseImpl\nfrom playwright._impl._network import Route as RouteImpl\nfrom playwright._impl._network import WebSocket as WebSocketImpl\nfrom playwright._impl._network import WebSocketRoute as WebSocketRouteImpl\nfrom playwright._impl._page import Page as PageImpl\nfrom playwright._impl._page import Worker as WorkerImpl\nfrom playwright._impl._playwright import Playwright as PlaywrightImpl\nfrom playwright._impl._selectors import Selectors as SelectorsImpl\nfrom playwright._impl._sync_base import (\n    EventContextManager,\n    SyncBase,\n    SyncContextManager,\n    mapping,\n)\nfrom playwright._impl._tracing import Tracing as TracingImpl\nfrom playwright._impl._video import Video as VideoImpl\nfrom playwright._impl._web_error import WebError as WebErrorImpl\n\n\nclass Request(SyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"Request.url\n\n        URL of the request.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def resource_type(self) -> str:\n        \"\"\"Request.resource_type\n\n        Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the\n        following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`,\n        `eventsource`, `websocket`, `manifest`, `other`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.resource_type)\n\n    @property\n    def service_worker(self) -> typing.Optional[\"Worker\"]:\n        \"\"\"Request.service_worker\n\n        The Service `Worker` that is performing the request.\n\n        **Details**\n\n        This method is Chromium only. It's safe to call when using other browsers, but it will always be `null`.\n\n        Requests originated in a Service Worker do not have a `request.frame()` available.\n\n        Returns\n        -------\n        Union[Worker, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.service_worker)\n\n    @property\n    def method(self) -> str:\n        \"\"\"Request.method\n\n        Request's method (GET, POST, etc.)\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.method)\n\n    @property\n    def post_data(self) -> typing.Optional[str]:\n        \"\"\"Request.post_data\n\n        Request's post body, if any.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data)\n\n    @property\n    def post_data_json(self) -> typing.Optional[typing.Any]:\n        \"\"\"Request.post_data_json\n\n        Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any.\n\n        When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned.\n        Otherwise it will be parsed as JSON.\n\n        Returns\n        -------\n        Union[Any, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data_json)\n\n    @property\n    def post_data_buffer(self) -> typing.Optional[bytes]:\n        \"\"\"Request.post_data_buffer\n\n        Request's post body in a binary form, if any.\n\n        Returns\n        -------\n        Union[bytes, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.post_data_buffer)\n\n    @property\n    def frame(self) -> \"Frame\":\n        \"\"\"Request.frame\n\n        Returns the `Frame` that initiated this request.\n\n        **Usage**\n\n        ```py\n        frame_url = request.frame.url\n        ```\n\n        **Details**\n\n        Note that in some cases the frame is not available, and this method will throw.\n        - When request originates in the Service Worker. You can use `request.serviceWorker()` to check that.\n        - When navigation request is issued before the corresponding frame is created. You can use\n          `request.is_navigation_request()` to check that.\n\n        Here is an example that handles all the cases:\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.frame)\n\n    @property\n    def redirected_from(self) -> typing.Optional[\"Request\"]:\n        \"\"\"Request.redirected_from\n\n        Request that was redirected by the server to this one, if any.\n\n        When the server responds with a redirect, Playwright creates a new `Request` object. The two requests are connected\n        by `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to\n        construct the whole redirect chain by repeatedly calling `redirectedFrom()`.\n\n        **Usage**\n\n        For example, if the website `http://example.com` redirects to `https://example.com`:\n\n        ```py\n        response = page.goto(\\\"http://example.com\\\")\n        print(response.request.redirected_from.url) # \\\"http://example.com\\\"\n        ```\n\n        If the website `https://google.com` has no redirects:\n\n        ```py\n        response = page.goto(\\\"https://google.com\\\")\n        print(response.request.redirected_from) # None\n        ```\n\n        Returns\n        -------\n        Union[Request, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.redirected_from)\n\n    @property\n    def redirected_to(self) -> typing.Optional[\"Request\"]:\n        \"\"\"Request.redirected_to\n\n        New request issued by the browser if the server responded with redirect.\n\n        **Usage**\n\n        This method is the opposite of `request.redirected_from()`:\n\n        ```py\n        assert request.redirected_from.redirected_to == request\n        ```\n\n        Returns\n        -------\n        Union[Request, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.redirected_to)\n\n    @property\n    def failure(self) -> typing.Optional[str]:\n        \"\"\"Request.failure\n\n        The method returns `null` unless this request has failed, as reported by `requestfailed` event.\n\n        **Usage**\n\n        Example of logging of all the failed requests:\n\n        ```py\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure))\n        ```\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.failure)\n\n    @property\n    def timing(self) -> ResourceTiming:\n        \"\"\"Request.timing\n\n        Returns resource timing information for given request. Most of the timing values become available upon the\n        response, `responseEnd` becomes available when request finishes. Find more information at\n        [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).\n\n        **Usage**\n\n        ```py\n        with page.expect_event(\\\"requestfinished\\\") as request_info:\n            page.goto(\\\"http://example.com\\\")\n        request = request_info.value\n        print(request.timing)\n        ```\n\n        Returns\n        -------\n        {startTime: float, domainLookupStart: float, domainLookupEnd: float, connectStart: float, secureConnectionStart: float, connectEnd: float, requestStart: float, responseStart: float, responseEnd: float}\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.timing)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"Request.headers\n\n        An object with the request HTTP headers. The header names are lower-cased. Note that this method does not return\n        security-related headers, including cookie-related ones. You can use `request.all_headers()` for complete\n        list of headers that include `cookie` information.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    def sizes(self) -> RequestSizes:\n        \"\"\"Request.sizes\n\n        Returns resource size information for given request.\n\n        Returns\n        -------\n        {requestBodySize: int, requestHeadersSize: int, responseBodySize: int, responseHeadersSize: int}\n        \"\"\"\n\n        return mapping.from_impl(self._sync(self._impl_obj.sizes()))\n\n    def response(self) -> typing.Optional[\"Response\"]:\n        \"\"\"Request.response\n\n        Returns the matching `Response` object, or `null` if the response was not received due to error.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.response()))\n\n    def is_navigation_request(self) -> bool:\n        \"\"\"Request.is_navigation_request\n\n        Whether this request is driving frame's navigation.\n\n        Some navigation requests are issued before the corresponding frame is created, and therefore do not have\n        `request.frame()` available.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_navigation_request())\n\n    def all_headers(self) -> typing.Dict[str, str]:\n        \"\"\"Request.all_headers\n\n        An object with all the request HTTP headers associated with this request. The header names are lower-cased.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.all_headers()))\n\n    def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"Request.headers_array\n\n        An array with all the request HTTP headers associated with this request. Unlike `request.all_headers()`,\n        header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple\n        times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.headers_array()))\n\n    def header_value(self, name: str) -> typing.Optional[str]:\n        \"\"\"Request.header_value\n\n        Returns the value of the header matching the name. The name is case-insensitive.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.header_value(name=name))\n        )\n\n\nmapping.register(RequestImpl, Request)\n\n\nclass Response(SyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"Response.url\n\n        Contains the URL of the response.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Response.ok\n\n        Contains a boolean stating whether the response was successful (status in the range 200-299) or not.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.ok)\n\n    @property\n    def status(self) -> int:\n        \"\"\"Response.status\n\n        Contains the status code of the response (e.g., 200 for a success).\n\n        Returns\n        -------\n        int\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status)\n\n    @property\n    def status_text(self) -> str:\n        \"\"\"Response.status_text\n\n        Contains the status text of the response (e.g. usually an \\\"OK\\\" for a success).\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status_text)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"Response.headers\n\n        An object with the response HTTP headers. The header names are lower-cased. Note that this method does not return\n        security-related headers, including cookie-related ones. You can use `response.all_headers()` for complete\n        list of headers that include `cookie` information.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    @property\n    def from_service_worker(self) -> bool:\n        \"\"\"Response.from_service_worker\n\n        Indicates whether this Response was fulfilled by a Service Worker's Fetch Handler (i.e. via\n        [FetchEvent.respondWith](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith)).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.from_service_worker)\n\n    @property\n    def request(self) -> \"Request\":\n        \"\"\"Response.request\n\n        Returns the matching `Request` object.\n\n        Returns\n        -------\n        Request\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def frame(self) -> \"Frame\":\n        \"\"\"Response.frame\n\n        Returns the `Frame` that initiated this response.\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.frame)\n\n    def all_headers(self) -> typing.Dict[str, str]:\n        \"\"\"Response.all_headers\n\n        An object with all the response HTTP headers associated with this response.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.all_headers()))\n\n    def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"Response.headers_array\n\n        An array with all the request HTTP headers associated with this response. Unlike `response.all_headers()`,\n        header names are NOT lower-cased. Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple\n        times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.headers_array()))\n\n    def header_value(self, name: str) -> typing.Optional[str]:\n        \"\"\"Response.header_value\n\n        Returns the value of the header matching the name. The name is case-insensitive. If multiple headers have the same\n        name (except `set-cookie`), they are returned as a list separated by `, `. For `set-cookie`, the `\\\\n` separator is\n        used. If no headers are found, `null` is returned.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.header_value(name=name))\n        )\n\n    def header_values(self, name: str) -> typing.List[str]:\n        \"\"\"Response.header_values\n\n        Returns all values of the headers matching the name, for example `set-cookie`. The name is case-insensitive.\n\n        Parameters\n        ----------\n        name : str\n            Name of the header.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.header_values(name=name))\n        )\n\n    def server_addr(self) -> typing.Optional[RemoteAddr]:\n        \"\"\"Response.server_addr\n\n        Returns the IP address and port of the server.\n\n        Returns\n        -------\n        Union[{ipAddress: str, port: int}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.server_addr()))\n\n    def security_details(self) -> typing.Optional[SecurityDetails]:\n        \"\"\"Response.security_details\n\n        Returns SSL and other security information.\n\n        Returns\n        -------\n        Union[{issuer: Union[str, None], protocol: Union[str, None], subjectName: Union[str, None], validFrom: Union[float, None], validTo: Union[float, None]}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.security_details()))\n\n    def finished(self) -> None:\n        \"\"\"Response.finished\n\n        Waits for this response to finish, returns always `null`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.finished()))\n\n    def body(self) -> bytes:\n        \"\"\"Response.body\n\n        Returns the buffer with response body.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.body()))\n\n    def text(self) -> str:\n        \"\"\"Response.text\n\n        Returns the text representation of response body.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.text()))\n\n    def json(self) -> typing.Any:\n        \"\"\"Response.json\n\n        Returns the JSON representation of response body.\n\n        This method will throw if the response body is not parsable via `JSON.parse`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.json()))\n\n\nmapping.register(ResponseImpl, Response)\n\n\nclass Route(SyncBase):\n\n    @property\n    def request(self) -> \"Request\":\n        \"\"\"Route.request\n\n        A request to be routed.\n\n        Returns\n        -------\n        Request\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    def abort(self, error_code: typing.Optional[str] = None) -> None:\n        \"\"\"Route.abort\n\n        Aborts the route's request.\n\n        Parameters\n        ----------\n        error_code : Union[str, None]\n            Optional error code. Defaults to `failed`, could be one of the following:\n            - `'aborted'` - An operation was aborted (due to user action)\n            - `'accessdenied'` - Permission to access a resource, other than the network, was denied\n            - `'addressunreachable'` - The IP address is unreachable. This usually means that there is no route to the\n              specified host or network.\n            - `'blockedbyclient'` - The client chose to block the request.\n            - `'blockedbyresponse'` - The request failed because the response was delivered along with requirements which are\n              not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance).\n            - `'connectionaborted'` - A connection timed out as a result of not receiving an ACK for data sent.\n            - `'connectionclosed'` - A connection was closed (corresponding to a TCP FIN).\n            - `'connectionfailed'` - A connection attempt failed.\n            - `'connectionrefused'` - A connection attempt was refused.\n            - `'connectionreset'` - A connection was reset (corresponding to a TCP RST).\n            - `'internetdisconnected'` - The Internet connection has been lost.\n            - `'namenotresolved'` - The host name could not be resolved.\n            - `'timedout'` - An operation timed out.\n            - `'failed'` - A generic failure occurred.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.abort(errorCode=error_code))\n        )\n\n    def fulfill(\n        self,\n        *,\n        status: typing.Optional[int] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        body: typing.Optional[typing.Union[str, bytes]] = None,\n        json: typing.Optional[typing.Any] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content_type: typing.Optional[str] = None,\n        response: typing.Optional[\"APIResponse\"] = None,\n    ) -> None:\n        \"\"\"Route.fulfill\n\n        Fulfills route's request with given response.\n\n        **Usage**\n\n        An example of fulfilling all requests with 404 responses:\n\n        ```py\n        page.route(\\\"**/*\\\", lambda route: route.fulfill(\n            status=404,\n            content_type=\\\"text/plain\\\",\n            body=\\\"not found!\\\"))\n        ```\n\n        An example of serving static file:\n\n        ```py\n        page.route(\\\"**/xhr_endpoint\\\", lambda route: route.fulfill(path=\\\"mock_data.json\\\"))\n        ```\n\n        Parameters\n        ----------\n        status : Union[int, None]\n            Response status code, defaults to `200`.\n        headers : Union[Dict[str, str], None]\n            Response headers. Header values will be converted to a string.\n        body : Union[bytes, str, None]\n            Response body.\n        json : Union[Any, None]\n            JSON response. This method will set the content type to `application/json` if not set.\n        path : Union[pathlib.Path, str, None]\n            File path to respond with. The content type will be inferred from file extension. If `path` is a relative path,\n            then it is resolved relative to the current working directory.\n        content_type : Union[str, None]\n            If set, equals to setting `Content-Type` response header.\n        response : Union[APIResponse, None]\n            `APIResponse` to fulfill route's request with. Individual fields of the response (such as headers) can be\n            overridden using fulfill options.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fulfill(\n                    status=status,\n                    headers=mapping.to_impl(headers),\n                    body=body,\n                    json=mapping.to_impl(json),\n                    path=path,\n                    contentType=content_type,\n                    response=response._impl_obj if response else None,\n                )\n            )\n        )\n\n    def fetch(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> \"APIResponse\":\n        \"\"\"Route.fetch\n\n        Performs the request and fetches result without fulfilling it, so that the response could be modified and then\n        fulfilled.\n\n        **Usage**\n\n        ```py\n        def handle(route):\n            response = route.fetch()\n            json = response.json()\n            json[\\\"message\\\"][\\\"big_red_dog\\\"] = []\n            route.fulfill(response=response, json=json)\n\n        page.route(\\\"https://dog.ceo/api/breeds/list/all\\\", handle)\n        ```\n\n        **Details**\n\n        Note that `headers` option will apply to the fetched request as well as any redirects initiated by it. If you want\n        to only apply `headers` to the original request, but not to redirects, look into `route.continue_()`\n        instead.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.fetch(\n                    url=url,\n                    method=method,\n                    headers=mapping.to_impl(headers),\n                    postData=mapping.to_impl(post_data),\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                    timeout=timeout,\n                )\n            )\n        )\n\n    def fallback(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n    ) -> None:\n        \"\"\"Route.fallback\n\n        Continues route's request with optional overrides. The method is similar to `route.continue_()` with the\n        difference that other matching handlers will be invoked before sending the request.\n\n        **Usage**\n\n        When several routes match the given pattern, they run in the order opposite to their registration. That way the\n        last registered route can always override all the previous ones. In the example below, request will be handled by\n        the bottom-most handler first, then it'll fall back to the previous one and in the end will be aborted by the first\n        registered route.\n\n        ```py\n        page.route(\\\"**/*\\\", lambda route: route.abort())  # Runs last.\n        page.route(\\\"**/*\\\", lambda route: route.fallback())  # Runs second.\n        page.route(\\\"**/*\\\", lambda route: route.fallback())  # Runs first.\n        ```\n\n        Registering multiple routes is useful when you want separate handlers to handle different kinds of requests, for\n        example API calls vs page resources or GET requests vs POST requests as in the example below.\n\n        ```py\n        # Handle GET requests.\n        def handle_get(route):\n            if route.request.method != \\\"GET\\\":\n                route.fallback()\n                return\n          # Handling GET only.\n          # ...\n\n        # Handle POST requests.\n        def handle_post(route):\n            if route.request.method != \\\"POST\\\":\n                route.fallback()\n                return\n          # Handling POST only.\n          # ...\n\n        page.route(\\\"**/*\\\", handle_get)\n        page.route(\\\"**/*\\\", handle_post)\n        ```\n\n        One can also modify request while falling back to the subsequent handler, that way intermediate route handler can\n        modify url, method, headers and postData of the request.\n\n        ```py\n        def handle(route, request):\n            # override headers\n            headers = {\n                **request.headers,\n                \\\"foo\\\": \\\"foo-value\\\", # set \\\"foo\\\" header\n                \\\"bar\\\": None # remove \\\"bar\\\" header\n            }\n            route.fallback(headers=headers)\n\n        page.route(\\\"**/*\\\", handle)\n        ```\n\n        Use `route.continue_()` to immediately send the request to the network, other matching handlers won't be\n        invoked in that case.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one. Changing the URL won't affect the\n            route matching, all the routes are matched using the original request URL.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            If set changes the post data of request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fallback(\n                    url=url,\n                    method=method,\n                    headers=mapping.to_impl(headers),\n                    postData=mapping.to_impl(post_data),\n                )\n            )\n        )\n\n    def continue_(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        post_data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n    ) -> None:\n        \"\"\"Route.continue_\n\n        Sends route's request to the network with optional overrides.\n\n        **Usage**\n\n        ```py\n        def handle(route, request):\n            # override headers\n            headers = {\n                **request.headers,\n                \\\"foo\\\": \\\"foo-value\\\", # set \\\"foo\\\" header\n                \\\"bar\\\": None # remove \\\"bar\\\" header\n            }\n            route.continue_(headers=headers)\n\n        page.route(\\\"**/*\\\", handle)\n        ```\n\n        **Details**\n\n        The `headers` option applies to both the routed request and any redirects it initiates. However, `url`, `method`,\n        and `postData` only apply to the original request and are not carried over to redirected requests.\n\n        `route.continue_()` will immediately send the request to the network, other matching handlers won't be\n        invoked. Use `route.fallback()` If you want next matching handler in the chain to be invoked.\n\n        **NOTE** Some request headers are **forbidden** and cannot be overridden (for example, `Cookie`, `Host`,\n        `Content-Length` and others, see\n        [this MDN page](https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_request_header) for full list). If an\n        override is provided for a forbidden header, it will be ignored and the original request header will be used.\n\n        To set custom cookies, use `browser_context.add_cookies()`.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            If set changes the request URL. New URL must have same protocol as original one.\n        method : Union[str, None]\n            If set changes the request method (e.g. GET or POST).\n        headers : Union[Dict[str, str], None]\n            If set changes the request HTTP headers. Header values will be converted to a string.\n        post_data : Union[Any, bytes, str, None]\n            If set changes the post data of request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.continue_(\n                    url=url,\n                    method=method,\n                    headers=mapping.to_impl(headers),\n                    postData=mapping.to_impl(post_data),\n                )\n            )\n        )\n\n\nmapping.register(RouteImpl, Route)\n\n\nclass WebSocket(SyncBase):\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"WebSocket\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Fired when the websocket closes.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framereceived\"],\n        f: typing.Callable[[\"typing.Union[bytes, str]\"], \"None\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket receives a frame.\"\"\"\n\n    @typing.overload\n    def on(\n        self,\n        event: Literal[\"framesent\"],\n        f: typing.Callable[[\"typing.Union[bytes, str]\"], \"None\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket sends a frame.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"socketerror\"], f: typing.Callable[[\"str\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Fired when the websocket has an error.\"\"\"\n\n    def on(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"WebSocket\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Fired when the websocket closes.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framereceived\"],\n        f: typing.Callable[[\"typing.Union[bytes, str]\"], \"None\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket receives a frame.\"\"\"\n\n    @typing.overload\n    def once(\n        self,\n        event: Literal[\"framesent\"],\n        f: typing.Callable[[\"typing.Union[bytes, str]\"], \"None\"],\n    ) -> None:\n        \"\"\"\n        Fired when the websocket sends a frame.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"socketerror\"], f: typing.Callable[[\"str\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Fired when the websocket has an error.\"\"\"\n\n    def once(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def url(self) -> str:\n        \"\"\"WebSocket.url\n\n        Contains the URL of the WebSocket.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager:\n        \"\"\"WebSocket.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the webSocket is closed before the event is fired. Returns the event data value.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one would pass into `webSocket.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"WebSocket.wait_for_event\n\n        **NOTE** In most cases, you should use `web_socket.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the socket is closed before the\n        `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_event(\n                    event=event,\n                    predicate=self._wrap_handler(predicate),\n                    timeout=timeout,\n                )\n            )\n        )\n\n    def is_closed(self) -> bool:\n        \"\"\"WebSocket.is_closed\n\n        Indicates that the web socket has been closed.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_closed())\n\n\nmapping.register(WebSocketImpl, WebSocket)\n\n\nclass WebSocketRoute(SyncBase):\n\n    @property\n    def url(self) -> str:\n        \"\"\"WebSocketRoute.url\n\n        URL of the WebSocket created in the page.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    def close(\n        self, *, code: typing.Optional[int] = None, reason: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"WebSocketRoute.close\n\n        Closes one side of the WebSocket connection.\n\n        Parameters\n        ----------\n        code : Union[int, None]\n            Optional [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code).\n        reason : Union[str, None]\n            Optional [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.close(code=code, reason=reason))\n        )\n\n    def connect_to_server(self) -> \"WebSocketRoute\":\n        \"\"\"WebSocketRoute.connect_to_server\n\n        By default, routed WebSocket does not connect to the server, so you can mock entire WebSocket communication. This\n        method connects to the actual WebSocket server, and returns the server-side `WebSocketRoute` instance, giving the\n        ability to send and receive messages from the server.\n\n        Once connected to the server:\n        - Messages received from the server will be **automatically forwarded** to the WebSocket in the page, unless\n          `web_socket_route.on_message()` is called on the server-side `WebSocketRoute`.\n        - Messages sent by the [`WebSocket.send()`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send) call\n          in the page will be **automatically forwarded** to the server, unless `web_socket_route.on_message()` is\n          called on the original `WebSocketRoute`.\n\n        See examples at the top for more details.\n\n        Returns\n        -------\n        WebSocketRoute\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.connect_to_server())\n\n    def send(self, message: typing.Union[str, bytes]) -> None:\n        \"\"\"WebSocketRoute.send\n\n        Sends a message to the WebSocket. When called on the original WebSocket, sends the message to the page. When called\n        on the result of `web_socket_route.connect_to_server()`, sends the message to the server. See examples at the\n        top for more details.\n\n        Parameters\n        ----------\n        message : Union[bytes, str]\n            Message to send.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.send(message=message))\n\n    def on_message(\n        self, handler: typing.Callable[[typing.Union[str, bytes]], typing.Any]\n    ) -> None:\n        \"\"\"WebSocketRoute.on_message\n\n        This method allows to handle messages that are sent by the WebSocket, either from the page or from the server.\n\n        When called on the original WebSocket route, this method handles messages sent from the page. You can handle this\n        messages by responding to them with `web_socket_route.send()`, forwarding them to the server-side connection\n        returned by `web_socket_route.connect_to_server()` or do something else.\n\n        Once this method is called, messages are not automatically forwarded to the server or to the page - you should do\n        that manually by calling `web_socket_route.send()`. See examples at the top for more details.\n\n        Calling this method again will override the handler with a new one.\n\n        Parameters\n        ----------\n        handler : Callable[[Union[bytes, str]], Any]\n            Function that will handle messages.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.on_message(handler=self._wrap_handler(handler))\n        )\n\n    def on_close(\n        self,\n        handler: typing.Callable[\n            [typing.Optional[int], typing.Optional[str]], typing.Any\n        ],\n    ) -> None:\n        \"\"\"WebSocketRoute.on_close\n\n        Allows to handle [`WebSocket.close`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close).\n\n        By default, closing one side of the connection, either in the page or on the server, will close the other side.\n        However, when `web_socket_route.on_close()` handler is set up, the default forwarding of closure is disabled,\n        and handler should take care of it.\n\n        Parameters\n        ----------\n        handler : Callable[[Union[int, None], Union[str, None]], Any]\n            Function that will handle WebSocket closure. Received an optional\n            [close code](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#code) and an optional\n            [close reason](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#reason).\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.on_close(handler=self._wrap_handler(handler))\n        )\n\n\nmapping.register(WebSocketRouteImpl, WebSocketRoute)\n\n\nclass Keyboard(SyncBase):\n\n    def down(self, key: str) -> None:\n        \"\"\"Keyboard.down\n\n        Dispatches a `keydown` event.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that\n        modifier active. To release the modifier key, use `keyboard.up()`.\n\n        After the key is pressed once, subsequent calls to `keyboard.down()` will have\n        [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key,\n        use `keyboard.up()`.\n\n        **NOTE** Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.down(key=key)))\n\n    def up(self, key: str) -> None:\n        \"\"\"Keyboard.up\n\n        Dispatches a `keyup` event.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.up(key=key)))\n\n    def insert_text(self, text: str) -> None:\n        \"\"\"Keyboard.insert_text\n\n        Dispatches only `input` event, does not emit the `keydown`, `keyup` or `keypress` events.\n\n        **Usage**\n\n        ```py\n        page.keyboard.insert_text(\\\"嗨\\\")\n        ```\n\n        **NOTE** Modifier keys DO NOT effect `keyboard.insertText`. Holding down `Shift` will not type the text in upper\n        case.\n\n        Parameters\n        ----------\n        text : str\n            Sets input to the specified text value.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.insert_text(text=text))\n        )\n\n    def type(self, text: str, *, delay: typing.Optional[float] = None) -> None:\n        \"\"\"Keyboard.type\n\n        **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if\n        there is special keyboard handling on the page - in this case use `locator.press_sequentially()`.\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        ```py\n        page.keyboard.type(\\\"Hello\\\") # types instantly\n        page.keyboard.type(\\\"World\\\", delay=100) # types slower, like a user\n        ```\n\n        **NOTE** Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case.\n\n        **NOTE** For characters that are not on a US keyboard, only an `input` event will be sent.\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.type(text=text, delay=delay))\n        )\n\n    def press(self, key: str, *, delay: typing.Optional[float] = None) -> None:\n        \"\"\"Keyboard.press\n\n        **NOTE** In most cases, you should use `locator.press()` instead.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        **Usage**\n\n        ```py\n        page = browser.new_page()\n        page.goto(\\\"https://keycode.info\\\")\n        page.keyboard.press(\\\"a\\\")\n        page.screenshot(path=\\\"a.png\\\")\n        page.keyboard.press(\\\"ArrowLeft\\\")\n        page.screenshot(path=\\\"arrow_left.png\\\")\n        page.keyboard.press(\\\"Shift+O\\\")\n        page.screenshot(path=\\\"o.png\\\")\n        browser.close()\n        ```\n\n        Shortcut for `keyboard.down()` and `keyboard.up()`.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.press(key=key, delay=delay))\n        )\n\n\nmapping.register(KeyboardImpl, Keyboard)\n\n\nclass Mouse(SyncBase):\n\n    def move(self, x: float, y: float, *, steps: typing.Optional[int] = None) -> None:\n        \"\"\"Mouse.move\n\n        Dispatches a `mousemove` event.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.move(x=x, y=y, steps=steps))\n        )\n\n    def down(\n        self,\n        *,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.down\n\n        Dispatches a `mousedown` event.\n\n        Parameters\n        ----------\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.down(button=button, clickCount=click_count))\n        )\n\n    def up(\n        self,\n        *,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.up\n\n        Dispatches a `mouseup` event.\n\n        Parameters\n        ----------\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.up(button=button, clickCount=click_count))\n        )\n\n    def click(\n        self,\n        x: float,\n        y: float,\n        *,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Mouse.click\n\n        Shortcut for `mouse.move()`, `mouse.down()`, `mouse.up()`.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.click(\n                    x=x, y=y, delay=delay, button=button, clickCount=click_count\n                )\n            )\n        )\n\n    def dblclick(\n        self,\n        x: float,\n        y: float,\n        *,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n    ) -> None:\n        \"\"\"Mouse.dblclick\n\n        Shortcut for `mouse.move()`, `mouse.down()`, `mouse.up()`, `mouse.down()` and\n        `mouse.up()`.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.dblclick(x=x, y=y, delay=delay, button=button))\n        )\n\n    def wheel(self, delta_x: float, delta_y: float) -> None:\n        \"\"\"Mouse.wheel\n\n        Dispatches a `wheel` event. This method is usually used to manually scroll the page. See\n        [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        **NOTE** Wheel events may cause scrolling if they are not handled, and this method does not wait for the scrolling\n        to finish before returning.\n\n        Parameters\n        ----------\n        delta_x : float\n            Pixels to scroll horizontally.\n        delta_y : float\n            Pixels to scroll vertically.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wheel(deltaX=delta_x, deltaY=delta_y))\n        )\n\n\nmapping.register(MouseImpl, Mouse)\n\n\nclass Touchscreen(SyncBase):\n\n    def tap(self, x: float, y: float) -> None:\n        \"\"\"Touchscreen.tap\n\n        Dispatches a `touchstart` and `touchend` event with a single touch at the position (`x`,`y`).\n\n        **NOTE** `page.tap()` the method will throw if `hasTouch` option of the browser context is false.\n\n        Parameters\n        ----------\n        x : float\n            X coordinate relative to the main frame's viewport in CSS pixels.\n        y : float\n            Y coordinate relative to the main frame's viewport in CSS pixels.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.tap(x=x, y=y)))\n\n\nmapping.register(TouchscreenImpl, Touchscreen)\n\n\nclass JSHandle(SyncBase):\n\n    def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"JSHandle.evaluate\n\n        Returns the return value of `expression`.\n\n        This method passes this handle as the first argument to `expression`.\n\n        If `expression` returns a [Promise], then `handle.evaluate` would wait for the promise to resolve and return its\n        value.\n\n        **Usage**\n\n        ```py\n        tweet_handle = page.query_selector(\\\".tweet .retweets\\\")\n        assert tweet_handle.evaluate(\\\"node => node.innerText\\\") == \\\"10 retweets\\\"\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate(expression=expression, arg=mapping.to_impl(arg))\n            )\n        )\n\n    def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"JSHandle.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        This method passes this handle as the first argument to `expression`.\n\n        The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle`\n        returns `JSHandle`.\n\n        If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would\n        wait for the promise to resolve and return its value.\n\n        See `page.evaluate_handle()` for more details.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.evaluate_handle(\n                    expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def get_property(self, property_name: str) -> \"JSHandle\":\n        \"\"\"JSHandle.get_property\n\n        Fetches a single property from the referenced object.\n\n        Parameters\n        ----------\n        property_name : str\n            property to get\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(self._impl_obj.get_property(propertyName=property_name))\n        )\n\n    def get_properties(self) -> typing.Dict[str, \"JSHandle\"]:\n        \"\"\"JSHandle.get_properties\n\n        The method returns a map with **own property names** as keys and JSHandle instances for the property values.\n\n        **Usage**\n\n        ```py\n        handle = page.evaluate_handle(\\\"({ window, document })\\\")\n        properties = handle.get_properties()\n        window_handle = properties.get(\\\"window\\\")\n        document_handle = properties.get(\\\"document\\\")\n        handle.dispose()\n        ```\n\n        Returns\n        -------\n        Dict[str, JSHandle]\n        \"\"\"\n\n        return mapping.from_impl_dict(self._sync(self._impl_obj.get_properties()))\n\n    def as_element(self) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"JSHandle.as_element\n\n        Returns either `null` or the object handle itself, if the object handle is an instance of `ElementHandle`.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._impl_obj.as_element())\n\n    def dispose(self) -> None:\n        \"\"\"JSHandle.dispose\n\n        The `jsHandle.dispose` method stops referencing the element handle.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.dispose()))\n\n    def json_value(self) -> typing.Any:\n        \"\"\"JSHandle.json_value\n\n        Returns a JSON representation of the object. If the object has a `toJSON` function, it **will not be called**.\n\n        **NOTE** The method will return an empty JSON object if the referenced object is not stringifiable. It will throw\n        an error if the object has circular references.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.json_value()))\n\n\nmapping.register(JSHandleImpl, JSHandle)\n\n\nclass ElementHandle(JSHandle):\n\n    def as_element(self) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.as_element\n\n        Returns either `null` or the object handle itself, if the object handle is an instance of `ElementHandle`.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._impl_obj.as_element())\n\n    def owner_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"ElementHandle.owner_frame\n\n        Returns the frame containing the given element.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.owner_frame()))\n\n    def content_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"ElementHandle.content_frame\n\n        Returns the content frame for element handles referencing iframe nodes, or `null` otherwise\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.content_frame()))\n\n    def get_attribute(self, name: str) -> typing.Optional[str]:\n        \"\"\"ElementHandle.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name to get the value for.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.get_attribute(name=name))\n        )\n\n    def text_content(self) -> typing.Optional[str]:\n        \"\"\"ElementHandle.text_content\n\n        Returns the `node.textContent`.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.text_content()))\n\n    def inner_text(self) -> str:\n        \"\"\"ElementHandle.inner_text\n\n        Returns the `element.innerText`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.inner_text()))\n\n    def inner_html(self) -> str:\n        \"\"\"ElementHandle.inner_html\n\n        Returns the `element.innerHTML`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.inner_html()))\n\n    def is_checked(self) -> bool:\n        \"\"\"ElementHandle.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_checked()))\n\n    def is_disabled(self) -> bool:\n        \"\"\"ElementHandle.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_disabled()))\n\n    def is_editable(self) -> bool:\n        \"\"\"ElementHandle.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_editable()))\n\n    def is_enabled(self) -> bool:\n        \"\"\"ElementHandle.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_enabled()))\n\n    def is_hidden(self) -> bool:\n        \"\"\"ElementHandle.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_hidden()))\n\n    def is_visible(self) -> bool:\n        \"\"\"ElementHandle.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.is_visible()))\n\n    def dispatch_event(\n        self, type: str, event_init: typing.Optional[typing.Dict] = None\n    ) -> None:\n        \"\"\"ElementHandle.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        element_handle.dispatch_event(\\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = page.evaluate_handle(\\\"new DataTransfer()\\\")\n        element_handle.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", {\\\"dataTransfer\\\": data_transfer})\n        ```\n\n        Parameters\n        ----------\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dispatch_event(\n                    type=type, eventInit=mapping.to_impl(event_init)\n                )\n            )\n        )\n\n    def scroll_into_view_if_needed(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"ElementHandle.scroll_into_view_if_needed\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless\n        it is completely visible as defined by\n        [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.\n\n        Throws when `elementHandle` does not point to an element\n        [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.\n\n        See [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.scroll_into_view_if_needed(timeout=timeout))\n        )\n\n    def hover(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.hover\n\n        This method hovers over the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.hover(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                    trial=trial,\n                )\n            )\n        )\n\n    def click(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"ElementHandle.click\n\n        This method clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.click(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    clickCount=click_count,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def dblclick(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"ElementHandle.dblclick\n\n        This method double clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dblclick(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def select_option(\n        self,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"ElementHandle.select_option\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits until all specified options are present in\n        the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        handle.select_option(\\\"blue\\\")\n        # single selection matching both the label\n        handle.select_option(label=\\\"blue\\\")\n        # multiple selection\n        handle.select_option(value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.select_option(\n                    value=mapping.to_impl(value),\n                    index=mapping.to_impl(index),\n                    label=mapping.to_impl(label),\n                    element=mapping.to_impl(element),\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def tap(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.tap\n\n        This method taps the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.tap(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def fill(\n        self,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.fill\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, fills it and triggers an\n        `input` event after filling. Note that you can pass an empty string to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        value : str\n            Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fill(\n                    value=value, timeout=timeout, noWaitAfter=no_wait_after, force=force\n                )\n            )\n        )\n\n    def select_text(\n        self,\n        *,\n        force: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"ElementHandle.select_text\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then focuses the element and selects all its\n        text content.\n\n        If the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), focuses and selects text in\n        the control instead.\n\n        Parameters\n        ----------\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.select_text(force=force, timeout=timeout))\n        )\n\n    def input_value(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"ElementHandle.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.input_value(timeout=timeout))\n        )\n\n    def set_input_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs\n        with a `[webkitdirectory]` attribute, only a single directory path is supported.\n\n        This method expects `ElementHandle` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_input_files(\n                    files=mapping.to_impl(files),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def focus(self) -> None:\n        \"\"\"ElementHandle.focus\n\n        Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.focus()))\n\n    def type(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.type\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `element_handle.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.type(\n                    text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n                )\n            )\n        )\n\n    def press(\n        self,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.press\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.press(\n                    key=key, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n                )\n            )\n        )\n\n    def set_checked(\n        self,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.set_checked\n\n        This method checks or unchecks an element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_checked(\n                    checked=checked,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def check(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.check\n\n        This method checks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.check(\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def uncheck(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"ElementHandle.uncheck\n\n        This method checks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.uncheck(\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def bounding_box(self) -> typing.Optional[FloatRect]:\n        \"\"\"ElementHandle.bounding_box\n\n        This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is\n        calculated relative to the main frame viewport - which is usually the same as the browser window.\n\n        Scrolling affects the returned bounding box, similarly to\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n        That means `x` and/or `y` may be negative.\n\n        Elements from child frames return the bounding box relative to the main frame, unlike the\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n\n        Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the\n        following snippet should click the center of the element.\n\n        **Usage**\n\n        ```py\n        box = element_handle.bounding_box()\n        page.mouse.click(box[\\\"x\\\"] + box[\\\"width\\\"] / 2, box[\\\"y\\\"] + box[\\\"height\\\"] / 2)\n        ```\n\n        Returns\n        -------\n        Union[{x: float, y: float, width: float, height: float}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.bounding_box()))\n\n    def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"ElementHandle.screenshot\n\n        This method captures a screenshot of the page, clipped to the size and position of this particular element. If the\n        element is covered by other elements, it will not be actually visible on the screenshot. If the element is a\n        scrollable container, only the currently scrolled content will be visible on the screenshot.\n\n        This method waits for the [actionability](https://playwright.dev/python/docs/actionability) checks, then scrolls element into view before taking\n        a screenshot. If the element is detached from DOM, the method throws an error.\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.screenshot(\n                    timeout=timeout,\n                    type=type,\n                    path=path,\n                    quality=quality,\n                    omitBackground=omit_background,\n                    animations=animations,\n                    caret=caret,\n                    scale=scale,\n                    mask=mapping.to_impl(mask),\n                    maskColor=mask_color,\n                    style=style,\n                )\n            )\n        )\n\n    def query_selector(self, selector: str) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.query_selector\n\n        The method finds an element matching the specified selector in the `ElementHandle`'s subtree. If no elements match\n        the selector, returns `null`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.query_selector(selector=selector))\n        )\n\n    def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"ElementHandle.query_selector_all\n\n        The method finds all elements matching the specified selector in the `ElementHandle`s subtree. If no elements match\n        the selector, returns empty array.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            self._sync(self._impl_obj.query_selector_all(selector=selector))\n        )\n\n    def eval_on_selector(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"ElementHandle.eval_on_selector\n\n        Returns the return value of `expression`.\n\n        The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a\n        first argument to `expression`. If no elements match the selector, the method throws an error.\n\n        If `expression` returns a [Promise], then `element_handle.eval_on_selector()` would wait for the promise to\n        resolve and return its value.\n\n        **Usage**\n\n        ```py\n        tweet_handle = page.query_selector(\\\".tweet\\\")\n        assert tweet_handle.eval_on_selector(\\\".like\\\", \\\"node => node.innerText\\\") == \\\"100\\\"\n        assert tweet_handle.eval_on_selector(\\\".retweets\\\", \\\"node => node.innerText\\\") == \\\"10\\\"\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector(\n                    selector=selector, expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"ElementHandle.eval_on_selector_all\n\n        Returns the return value of `expression`.\n\n        The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array\n        of matched elements as a first argument to `expression`.\n\n        If `expression` returns a [Promise], then `element_handle.eval_on_selector_all()` would wait for the promise to\n        resolve and return its value.\n\n        **Usage**\n\n        ```html\n        <div class=\\\"feed\\\">\n          <div class=\\\"tweet\\\">Hello!</div>\n          <div class=\\\"tweet\\\">Hi!</div>\n        </div>\n        ```\n\n        ```py\n        feed_handle = page.query_selector(\\\".feed\\\")\n        assert feed_handle.eval_on_selector_all(\\\".tweet\\\", \\\"nodes => nodes.map(n => n.innerText)\\\") == [\\\"hello!\\\", \\\"hi!\\\"]\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector_all(\n                    selector=selector, expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def wait_for_element_state(\n        self,\n        state: Literal[\n            \"disabled\", \"editable\", \"enabled\", \"hidden\", \"stable\", \"visible\"\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"ElementHandle.wait_for_element_state\n\n        Returns when the element satisfies the `state`.\n\n        Depending on the `state` parameter, this method waits for one of the [actionability](https://playwright.dev/python/docs/actionability) checks to\n        pass. This method throws when the element is detached while waiting, unless waiting for the `\\\"hidden\\\"` state.\n        - `\\\"visible\\\"` Wait until the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n        - `\\\"hidden\\\"` Wait until the element is [not visible](https://playwright.dev/python/docs/actionability#visible) or not attached. Note that\n          waiting for hidden does not throw when the element detaches.\n        - `\\\"stable\\\"` Wait until the element is both [visible](https://playwright.dev/python/docs/actionability#visible) and\n          [stable](https://playwright.dev/python/docs/actionability#stable).\n        - `\\\"enabled\\\"` Wait until the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n        - `\\\"disabled\\\"` Wait until the element is [not enabled](https://playwright.dev/python/docs/actionability#enabled).\n        - `\\\"editable\\\"` Wait until the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.\n\n        Parameters\n        ----------\n        state : Union[\"disabled\", \"editable\", \"enabled\", \"hidden\", \"stable\", \"visible\"]\n            A state to wait for, see below for more details.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_element_state(state=state, timeout=timeout)\n            )\n        )\n\n    def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"ElementHandle.wait_for_selector\n\n        Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom,\n        or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the\n        method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the\n        function will throw.\n\n        **Usage**\n\n        ```py\n        page.set_content(\\\"<div><span></span></div>\\\")\n        div = page.query_selector(\\\"div\\\")\n        # waiting for the \\\"span\\\" selector relative to the div.\n        span = div.wait_for_selector(\\\"span\\\", state=\\\"attached\\\")\n        ```\n\n        **NOTE** This method does not work across navigations, use `page.wait_for_selector()` instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(\n                self._impl_obj.wait_for_selector(\n                    selector=selector, state=state, timeout=timeout, strict=strict\n                )\n            )\n        )\n\n\nmapping.register(ElementHandleImpl, ElementHandle)\n\n\nclass FileChooser(SyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"FileChooser.page\n\n        Returns page this file chooser belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def element(self) -> \"ElementHandle\":\n        \"\"\"FileChooser.element\n\n        Returns input element associated with this file chooser.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.element)\n\n    def is_multiple(self) -> bool:\n        \"\"\"FileChooser.is_multiple\n\n        Returns whether this file chooser accepts multiple files.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_multiple())\n\n    def set_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"FileChooser.set_files\n\n        Sets the value of the file input this chooser is associated with. If some of the `filePaths` are relative paths,\n        then they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_files(\n                    files=mapping.to_impl(files),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n\nmapping.register(FileChooserImpl, FileChooser)\n\n\nclass Frame(SyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Frame.page\n\n        Returns the page containing this frame.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def name(self) -> str:\n        \"\"\"Frame.name\n\n        Returns frame's name attribute as specified in the tag.\n\n        If the name is empty, returns the id attribute instead.\n\n        **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed\n        later.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.name)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Frame.url\n\n        Returns frame's url.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def parent_frame(self) -> typing.Optional[\"Frame\"]:\n        \"\"\"Frame.parent_frame\n\n        Parent frame, if any. Detached frames and main frames return `null`.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.parent_frame)\n\n    @property\n    def child_frames(self) -> typing.List[\"Frame\"]:\n        \"\"\"Frame.child_frames\n\n        Returns\n        -------\n        List[Frame]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.child_frames)\n\n    def goto(\n        self,\n        url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        referer: typing.Optional[str] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Frame.goto\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect.\n\n        The method will throw an error if:\n        - there's an SSL error (e.g. in case of self-signed certificates).\n        - target URL is invalid.\n        - the `timeout` is exceeded during navigation.\n        - the remote server does not respond or is unreachable.\n        - the main resource failed to load.\n\n        The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404\n        \\\"Not Found\\\" and 500 \\\"Internal Server Error\\\".  The status code for such responses can be retrieved by calling\n        `response.status()`.\n\n        **NOTE** The method either throws an error or returns a main resource response. The only exceptions are navigation\n        to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.\n\n        **NOTE** Headless mode doesn't support navigation to a PDF document. See the\n        [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).\n\n        Parameters\n        ----------\n        url : str\n            URL to navigate frame to. The url should include scheme, e.g. `https://`.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        referer : Union[str, None]\n            Referer header value. If provided it will take preference over the referer header value set by\n            `page.set_extra_http_headers()`.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(\n                self._impl_obj.goto(\n                    url=url, timeout=timeout, waitUntil=wait_until, referer=referer\n                )\n            )\n        )\n\n    def expect_navigation(\n        self,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Response\"]:\n        \"\"\"Frame.expect_navigation\n\n        Waits for the frame navigation and returns the main resource response. In case of multiple redirects, the\n        navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or\n        navigation due to History API usage, the navigation will resolve with `null`.\n\n        **Usage**\n\n        This method waits for the frame to navigate to a new URL. It is useful for when you run code which will indirectly\n        cause the frame to navigate. Consider this example:\n\n        ```py\n        with frame.expect_navigation():\n            frame.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        # Resolves after navigation has finished\n        ```\n\n        **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL\n        is considered a navigation.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_navigation(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            ).future,\n        )\n\n    def wait_for_url(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        *,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.wait_for_url\n\n        Waits for the frame to navigate to the given URL.\n\n        **Usage**\n\n        ```py\n        frame.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        frame.wait_for_url(\\\"**/target.html\\\")\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_url(\n                    url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n                )\n            )\n        )\n\n    def wait_for_load_state(\n        self,\n        state: typing.Optional[\n            Literal[\"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.wait_for_load_state\n\n        Waits for the required load state to be reached.\n\n        This returns when the frame reaches a required load state, `load` by default. The navigation must have been\n        committed when this method is called. If current document has already reached the required state, resolves\n        immediately.\n\n        **NOTE** Most of the time, this method is not needed because Playwright\n        [auto-waits before every action](https://playwright.dev/python/docs/actionability).\n\n        **Usage**\n\n        ```py\n        frame.click(\\\"button\\\") # click triggers navigation.\n        frame.wait_for_load_state() # the promise resolves after \\\"load\\\" event.\n        ```\n\n        Parameters\n        ----------\n        state : Union[\"domcontentloaded\", \"load\", \"networkidle\", None]\n            Optional load state to wait for, defaults to `load`. If the state has been already reached while loading current\n            document, the method resolves immediately. Can be one of:\n            - `'load'` - wait for the `load` event to be fired.\n            - `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired.\n            - `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use\n              this method for testing, rely on web assertions to assess readiness instead.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wait_for_load_state(state=state, timeout=timeout))\n        )\n\n    def frame_element(self) -> \"ElementHandle\":\n        \"\"\"Frame.frame_element\n\n        Returns the `frame` or `iframe` element handle which corresponds to this frame.\n\n        This is an inverse of `element_handle.content_frame()`. Note that returned handle actually belongs to the\n        parent frame.\n\n        This method throws an error if the frame has been detached before `frameElement()` returns.\n\n        **Usage**\n\n        ```py\n        frame_element = frame.frame_element()\n        content_frame = frame_element.content_frame()\n        assert frame == content_frame\n        ```\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(self._sync(self._impl_obj.frame_element()))\n\n    def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Frame.evaluate\n\n        Returns the return value of `expression`.\n\n        If the function passed to the `frame.evaluate()` returns a [Promise], then `frame.evaluate()` would\n        wait for the promise to resolve and return its value.\n\n        If the function passed to the `frame.evaluate()` returns a non-[Serializable] value, then\n        `frame.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that\n        are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        **Usage**\n\n        ```py\n        result = frame.evaluate(\\\"([x, y]) => Promise.resolve(x * y)\\\", [7, 8])\n        print(result) # prints \\\"56\\\"\n        ```\n\n        A string can also be passed in instead of a function.\n\n        ```py\n        print(frame.evaluate(\\\"1 + 2\\\")) # prints \\\"3\\\"\n        x = 10\n        print(frame.evaluate(f\\\"1 + {x}\\\")) # prints \\\"11\\\"\n        ```\n\n        `ElementHandle` instances can be passed as an argument to the `frame.evaluate()`:\n\n        ```py\n        body_handle = frame.evaluate(\\\"document.body\\\")\n        html = frame.evaluate(\\\"([body, suffix]) => body.innerHTML + suffix\\\", [body_handle, \\\"hello\\\"])\n        body_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate(expression=expression, arg=mapping.to_impl(arg))\n            )\n        )\n\n    def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Frame.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        The only difference between `frame.evaluate()` and `frame.evaluate_handle()` is that\n        `frame.evaluate_handle()` returns `JSHandle`.\n\n        If the function, passed to the `frame.evaluate_handle()`, returns a [Promise], then\n        `frame.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        **Usage**\n\n        ```py\n        a_window_handle = frame.evaluate_handle(\\\"Promise.resolve(window)\\\")\n        a_window_handle # handle for the window object.\n        ```\n\n        A string can also be passed in instead of a function.\n\n        ```py\n        a_handle = page.evaluate_handle(\\\"document\\\") # handle for the \\\"document\\\"\n        ```\n\n        `JSHandle` instances can be passed as an argument to the `frame.evaluate_handle()`:\n\n        ```py\n        a_handle = page.evaluate_handle(\\\"document.body\\\")\n        result_handle = page.evaluate_handle(\\\"body => body.innerHTML\\\", a_handle)\n        print(result_handle.json_value())\n        result_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.evaluate_handle(\n                    expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def query_selector(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Frame.query_selector\n\n        Returns the ElementHandle pointing to the frame element.\n\n        **NOTE** The use of `ElementHandle` is discouraged, use `Locator` objects and web-first assertions instead.\n\n        The method finds an element matching the specified selector within the frame. If no elements match the selector,\n        returns `null`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.query_selector(selector=selector, strict=strict))\n        )\n\n    def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Frame.query_selector_all\n\n        Returns the ElementHandles pointing to the frame elements.\n\n        **NOTE** The use of `ElementHandle` is discouraged, use `Locator` objects instead.\n\n        The method finds all elements matching the specified selector within the frame. If no elements match the selector,\n        returns empty array.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            self._sync(self._impl_obj.query_selector_all(selector=selector))\n        )\n\n    def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Frame.wait_for_selector\n\n        Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        **NOTE** Playwright automatically waits for element to be ready before performing an action. Using `Locator`\n        objects and web-first assertions make the code wait-for-selector-free.\n\n        Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If\n        at the moment of calling the method `selector` already satisfies the condition, the method will return immediately.\n        If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.\n\n        **Usage**\n\n        This method works across navigations:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            chromium = playwright.chromium\n            browser = chromium.launch()\n            page = browser.new_page()\n            for current_url in [\\\"https://google.com\\\", \\\"https://bbc.com\\\"]:\n                page.goto(current_url, wait_until=\\\"domcontentloaded\\\")\n                element = page.main_frame.wait_for_selector(\\\"img\\\")\n                print(\\\"Loaded image: \\\" + str(element.get_attribute(\\\"src\\\")))\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(\n                self._impl_obj.wait_for_selector(\n                    selector=selector, strict=strict, timeout=timeout, state=state\n                )\n            )\n        )\n\n    def is_checked(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_checked(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_disabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_disabled(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_editable(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_editable(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_enabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Frame.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_enabled(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_hidden(self, selector: str, *, strict: typing.Optional[bool] = None) -> bool:\n        \"\"\"Frame.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).  `selector` that\n        does not match any elements is considered hidden.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_hidden(selector=selector, strict=strict))\n        )\n\n    def is_visible(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> bool:\n        \"\"\"Frame.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible). `selector` that does not match any elements\n        is considered not visible.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_visible(selector=selector, strict=strict))\n        )\n\n    def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        frame.dispatch_event(\\\"button#submit\\\", \\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = frame.evaluate_handle(\\\"new DataTransfer()\\\")\n        frame.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", { \\\"dataTransfer\\\": data_transfer })\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dispatch_event(\n                    selector=selector,\n                    type=type,\n                    eventInit=mapping.to_impl(event_init),\n                    strict=strict,\n                    timeout=timeout,\n                )\n            )\n        )\n\n    def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Any:\n        \"\"\"Frame.eval_on_selector\n\n        Returns the return value of `expression`.\n\n        The method finds an element matching the specified selector within the frame and passes it as a first argument to\n        `expression`. If no elements match the selector, the method throws an error.\n\n        If `expression` returns a [Promise], then `frame.eval_on_selector()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        search_value = frame.eval_on_selector(\\\"#search\\\", \\\"el => el.value\\\")\n        preload_href = frame.eval_on_selector(\\\"link[rel=preload]\\\", \\\"el => el.href\\\")\n        html = frame.eval_on_selector(\\\".main-container\\\", \\\"(e, suffix) => e.outerHTML + suffix\\\", \\\"hello\\\")\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector(\n                    selector=selector,\n                    expression=expression,\n                    arg=mapping.to_impl(arg),\n                    strict=strict,\n                )\n            )\n        )\n\n    def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Frame.eval_on_selector_all\n\n        Returns the return value of `expression`.\n\n        The method finds all elements matching the specified selector within the frame and passes an array of matched\n        elements as a first argument to `expression`.\n\n        If `expression` returns a [Promise], then `frame.eval_on_selector_all()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        divs_counts = frame.eval_on_selector_all(\\\"div\\\", \\\"(divs, min) => divs.length >= min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector_all(\n                    selector=selector, expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def content(self) -> str:\n        \"\"\"Frame.content\n\n        Gets the full HTML contents of the frame, including the doctype.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.content()))\n\n    def set_content(\n        self,\n        html: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Frame.set_content\n\n        This method internally calls [document.write()](https://developer.mozilla.org/en-US/docs/Web/API/Document/write),\n        inheriting all its specific characteristics and behaviors.\n\n        Parameters\n        ----------\n        html : str\n            HTML markup to assign to the page.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_content(\n                    html=html, timeout=timeout, waitUntil=wait_until\n                )\n            )\n        )\n\n    def is_detached(self) -> bool:\n        \"\"\"Frame.is_detached\n\n        Returns `true` if the frame has been detached, or `false` otherwise.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_detached())\n\n    def add_script_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n        type: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Frame.add_script_tag\n\n        Returns the added tag when the script's onload fires or when the script content was injected into frame.\n\n        Adds a `<script>` tag into the page with the desired url or content.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of a script to be added.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative\n            to the current working directory.\n        content : Union[str, None]\n            Raw JavaScript content to be injected into frame.\n        type : Union[str, None]\n            Script type. Use 'module' in order to load a JavaScript ES6 module. See\n            [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.add_script_tag(\n                    url=url, path=path, content=content, type=type\n                )\n            )\n        )\n\n    def add_style_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Frame.add_style_tag\n\n        Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.\n\n        Adds a `<link rel=\\\"stylesheet\\\">` tag into the page with the desired url or a `<style type=\\\"text/css\\\">` tag with the\n        content.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of the `<link>` tag.\n        path : Union[pathlib.Path, str, None]\n            Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to the\n            current working directory.\n        content : Union[str, None]\n            Raw CSS content to be injected into frame.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.add_style_tag(url=url, path=path, content=content)\n            )\n        )\n\n    def click(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.click\n\n        This method clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.click(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    clickCount=click_count,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def dblclick(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.dblclick\n\n        This method double clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`. if\n           the first click of the `dblclick()` triggers a navigation event, this method will throw.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `frame.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dblclick(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def tap(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.tap\n\n        This method taps an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.tap(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def fill(\n        self,\n        selector: str,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.fill\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks,\n        focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string\n        to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : str\n            Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fill(\n                    selector=selector,\n                    value=value,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    force=force,\n                )\n            )\n        )\n\n    def locator(\n        self,\n        selector: str,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.locator\n\n        The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved\n        to the element immediately before performing an action, so a series of actions on the same locator can in fact be\n        performed on different DOM elements. That would happen if the DOM structure between those actions has changed.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selector=selector,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Frame.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Frame.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        **Usage**\n\n        Following snippet locates element with text \\\"Submit\\\" in the iframe with id `my-frame`, like `<iframe\n        id=\\\"my-frame\\\">`:\n\n        ```py\n        locator = frame.frame_locator(\\\"#my-iframe\\\").get_by_text(\\\"Submit\\\")\n        locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    def focus(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Frame.focus\n\n        This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the\n        method waits until a matching element appears in the DOM.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.focus(selector=selector, strict=strict, timeout=timeout)\n            )\n        )\n\n    def text_content(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Frame.text_content\n\n        Returns `element.textContent`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.text_content(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def inner_text(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.inner_text\n\n        Returns `element.innerText`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.inner_text(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def inner_html(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.inner_html\n\n        Returns `element.innerHTML`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.inner_html(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def get_attribute(\n        self,\n        selector: str,\n        name: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Frame.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        name : str\n            Attribute name to get the value for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.get_attribute(\n                    selector=selector, name=name, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def hover(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.hover\n\n        This method hovers over an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.hover(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        *,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Frame.drag_and_drop\n\n        Parameters\n        ----------\n        source : str\n            A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will\n            be used.\n        target : str\n            A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first\n            will be used.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.drag_and_drop(\n                    source=source,\n                    target=target,\n                    sourcePosition=source_position,\n                    targetPosition=target_position,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    timeout=timeout,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def select_option(\n        self,\n        selector: str,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Frame.select_option\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits\n        until all specified options are present in the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        frame.select_option(\\\"select#colors\\\", \\\"blue\\\")\n        # single selection matching both the label\n        frame.select_option(\\\"select#colors\\\", label=\\\"blue\\\")\n        # multiple selection\n        frame.select_option(\\\"select#colors\\\", value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.select_option(\n                    selector=selector,\n                    value=mapping.to_impl(value),\n                    index=mapping.to_impl(index),\n                    label=mapping.to_impl(label),\n                    element=mapping.to_impl(element),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    force=force,\n                )\n            )\n        )\n\n    def input_value(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Frame.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.input_value(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def set_input_files(\n        self,\n        selector: str,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        This method expects `selector` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_input_files(\n                    selector=selector,\n                    files=mapping.to_impl(files),\n                    strict=strict,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def type(\n        self,\n        selector: str,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.type\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `frame.type` can be used\n        to send fine-grained keyboard events. To fill values in form fields, use `frame.fill()`.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.type(\n                    selector=selector,\n                    text=text,\n                    delay=delay,\n                    strict=strict,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def press(\n        self,\n        selector: str,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.press\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.press(\n                    selector=selector,\n                    key=key,\n                    delay=delay,\n                    strict=strict,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def check(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.check\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.check(\n                    selector=selector,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def uncheck(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.uncheck\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.uncheck(\n                    selector=selector,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def wait_for_timeout(self, timeout: float) -> None:\n        \"\"\"Frame.wait_for_timeout\n\n        Waits for the given `timeout` in milliseconds.\n\n        Note that `frame.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going\n        to be flaky. Use signals such as network events, selectors becoming visible and others instead.\n\n        Parameters\n        ----------\n        timeout : float\n            A timeout to wait for\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wait_for_timeout(timeout=timeout))\n        )\n\n    def wait_for_function(\n        self,\n        expression: str,\n        *,\n        arg: typing.Optional[typing.Any] = None,\n        timeout: typing.Optional[float] = None,\n        polling: typing.Optional[typing.Union[float, Literal[\"raf\"]]] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Frame.wait_for_function\n\n        Returns when the `expression` returns a truthy value, returns that value.\n\n        **Usage**\n\n        The `frame.wait_for_function()` can be used to observe viewport size change:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch()\n            page = browser.new_page()\n            page.evaluate(\\\"window.x = 0; setTimeout(() => { window.x = 100 }, 1000);\\\")\n            page.main_frame.wait_for_function(\\\"() => window.x > 0\\\")\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        To pass an argument to the predicate of `frame.waitForFunction` function:\n\n        ```py\n        selector = \\\".foo\\\"\n        frame.wait_for_function(\\\"selector => !!document.querySelector(selector)\\\", selector)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()` or\n            `page.set_default_timeout()` methods.\n        polling : Union[\"raf\", float, None]\n            If `polling` is `'raf'`, then `expression` is constantly executed in `requestAnimationFrame` callback. If `polling`\n            is a number, then it is treated as an interval in milliseconds at which the function would be executed. Defaults to\n            `raf`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.wait_for_function(\n                    expression=expression,\n                    arg=mapping.to_impl(arg),\n                    timeout=timeout,\n                    polling=polling,\n                )\n            )\n        )\n\n    def title(self) -> str:\n        \"\"\"Frame.title\n\n        Returns the page title.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.title()))\n\n    def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Frame.set_checked\n\n        This method checks or unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_checked(\n                    selector=selector,\n                    checked=checked,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n\nmapping.register(FrameImpl, Frame)\n\n\nclass FrameLocator(SyncBase):\n\n    @property\n    def first(self) -> \"FrameLocator\":\n        \"\"\"FrameLocator.first\n\n        Returns locator to the first matching frame.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.first)\n\n    @property\n    def last(self) -> \"FrameLocator\":\n        \"\"\"FrameLocator.last\n\n        Returns locator to the last matching frame.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.last)\n\n    @property\n    def owner(self) -> \"Locator\":\n        \"\"\"FrameLocator.owner\n\n        Returns a `Locator` object pointing to the same `iframe` as this frame locator.\n\n        Useful when you have a `FrameLocator` object obtained somewhere, and later on would like to interact with the\n        `iframe` element.\n\n        For a reverse operation, use `locator.content_frame()`.\n\n        **Usage**\n\n        ```py\n        frame_locator = page.locator(\\\"iframe[name=\\\\\\\"embedded\\\\\\\"]\\\").content_frame\n        # ...\n        locator = frame_locator.owner\n        expect(locator).to_be_visible()\n        ```\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.owner)\n\n    def locator(\n        self,\n        selector_or_locator: typing.Union[\"Locator\", str],\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.locator\n\n        The method finds an element matching the specified selector in the locator's subtree. It also accepts filter\n        options, similar to `locator.filter()` method.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector_or_locator : Union[Locator, str]\n            A selector or locator to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selectorOrLocator=selector_or_locator,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"FrameLocator.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"FrameLocator.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    def nth(self, index: int) -> \"FrameLocator\":\n        \"\"\"FrameLocator.nth\n\n        Returns locator to the n-th matching frame. It's zero based, `nth(0)` selects the first frame.\n\n        Parameters\n        ----------\n        index : int\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.nth(index=index))\n\n\nmapping.register(FrameLocatorImpl, FrameLocator)\n\n\nclass Worker(SyncBase):\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is\n        terminated.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the worker calls one of console API methods, e.g. `console.log` or `console.dir`.\n        \"\"\"\n\n    def on(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is\n        terminated.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the worker calls one of console API methods, e.g. `console.log` or `console.dir`.\n        \"\"\"\n\n    def once(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Worker.url\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Worker.evaluate\n\n        Returns the return value of `expression`.\n\n        If the function passed to the `worker.evaluate()` returns a [Promise], then `worker.evaluate()`\n        would wait for the promise to resolve and return its value.\n\n        If the function passed to the `worker.evaluate()` returns a non-[Serializable] value, then\n        `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that\n        are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate(expression=expression, arg=mapping.to_impl(arg))\n            )\n        )\n\n    def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Worker.evaluate_handle\n\n        Returns the return value of `expression` as a `JSHandle`.\n\n        The only difference between `worker.evaluate()` and `worker.evaluate_handle()` is that\n        `worker.evaluate_handle()` returns `JSHandle`.\n\n        If the function passed to the `worker.evaluate_handle()` returns a [Promise], then\n        `worker.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.evaluate_handle(\n                    expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager:\n        \"\"\"Worker.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the page is closed before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        with worker.expect_event(\\\"console\\\") as event_info:\n            worker.evaluate(\\\"console.log(42)\\\")\n        message = event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n\nmapping.register(WorkerImpl, Worker)\n\n\nclass Selectors(SyncBase):\n\n    def register(\n        self,\n        name: str,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content_script: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Selectors.register\n\n        Selectors must be registered before creating the page.\n\n        **Usage**\n\n        An example of registering selector engine that queries elements based on a tag name:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            tag_selector = \\\"\\\"\\\"\n              {\n                  // Returns the first element matching given selector in the root's subtree.\n                  query(root, selector) {\n                      return root.querySelector(selector);\n                  },\n                  // Returns all elements matching given selector in the root's subtree.\n                  queryAll(root, selector) {\n                      return Array.from(root.querySelectorAll(selector));\n                  }\n              }\\\"\\\"\\\"\n\n            # Register the engine. Selectors will be prefixed with \\\"tag=\\\".\n            playwright.selectors.register(\\\"tag\\\", tag_selector)\n            browser = playwright.chromium.launch()\n            page = browser.new_page()\n            page.set_content('<div><button>Click me</button></div>')\n\n            # Use the selector prefixed with its name.\n            button = page.locator('tag=button')\n            # Combine it with built-in locators.\n            page.locator('tag=div').get_by_text('Click me').click()\n            # Can use it in any methods supporting selectors.\n            button_count = page.locator('tag=button').count()\n            print(button_count)\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name that is used in selectors as a prefix, e.g. `{name: 'foo'}` enables `foo=myselectorbody` selectors. May only\n            contain `[a-zA-Z0-9_]` characters.\n        script : Union[str, None]\n            Raw script content.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory.\n        content_script : Union[bool, None]\n            Whether to run this selector engine in isolated JavaScript environment. This environment has access to the same\n            DOM, but not any JavaScript objects from the frame's scripts. Defaults to `false`. Note that running as a content\n            script is not guaranteed when this engine is used together with other registered engines.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.register(\n                    name=name, script=script, path=path, contentScript=content_script\n                )\n            )\n        )\n\n    def set_test_id_attribute(self, attribute_name: str) -> None:\n        \"\"\"Selectors.set_test_id_attribute\n\n        Defines custom attribute name to be used in `page.get_by_test_id()`. `data-testid` is used by default.\n\n        Parameters\n        ----------\n        attribute_name : str\n            Test id attribute name.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_test_id_attribute(attributeName=attribute_name)\n        )\n\n\nmapping.register(SelectorsImpl, Selectors)\n\n\nclass Clock(SyncBase):\n\n    def install(\n        self,\n        *,\n        time: typing.Optional[typing.Union[float, str, datetime.datetime]] = None,\n    ) -> None:\n        \"\"\"Clock.install\n\n        Install fake implementations for the following time-related functions:\n        - `Date`\n        - `setTimeout`\n        - `clearTimeout`\n        - `setInterval`\n        - `clearInterval`\n        - `requestAnimationFrame`\n        - `cancelAnimationFrame`\n        - `requestIdleCallback`\n        - `cancelIdleCallback`\n        - `performance`\n\n        Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers,\n        and control the behavior of time-dependent functions. See `clock.run_for()` and\n        `clock.fast_forward()` for more information.\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str, None]\n            Time to initialize with, current system time by default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.install(time=time)))\n\n    def fast_forward(self, ticks: typing.Union[int, str]) -> None:\n        \"\"\"Clock.fast_forward\n\n        Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user\n        closing the laptop lid for a while and reopening it later, after given time.\n\n        **Usage**\n\n        ```py\n        page.clock.fast_forward(1000)\n        page.clock.fast_forward(\\\"30:00\\\")\n        ```\n\n        Parameters\n        ----------\n        ticks : Union[int, str]\n            Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are\n            \"08\" for eight seconds, \"01:00\" for one minute and \"02:34:10\" for two hours, 34 minutes and ten seconds.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.fast_forward(ticks=ticks))\n        )\n\n    def pause_at(self, time: typing.Union[float, str, datetime.datetime]) -> None:\n        \"\"\"Clock.pause_at\n\n        Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired\n        unless `clock.run_for()`, `clock.fast_forward()`, `clock.pause_at()` or\n        `clock.resume()` is called.\n\n        Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it\n        at the specified time and pausing.\n\n        **Usage**\n\n        ```py\n        page.clock.pause_at(datetime.datetime(2020, 2, 2))\n        page.clock.pause_at(\\\"2020-02-02\\\")\n        ```\n\n        For best results, install the clock before navigating the page and set it to a time slightly before the intended\n        test time. This ensures that all timers run normally during page loading, preventing the page from getting stuck.\n        Once the page has fully loaded, you can safely use `clock.pause_at()` to pause the clock.\n\n        ```py\n        # Initialize clock with some time before the test time and let the page load\n        # naturally. `Date.now` will progress as the timers fire.\n        page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))\n        page.goto(\\\"http://localhost:3333\\\")\n        page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to pause at.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.pause_at(time=time)))\n\n    def resume(self) -> None:\n        \"\"\"Clock.resume\n\n        Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.resume()))\n\n    def run_for(self, ticks: typing.Union[int, str]) -> None:\n        \"\"\"Clock.run_for\n\n        Advance the clock, firing all the time-related callbacks.\n\n        **Usage**\n\n        ```py\n        page.clock.run_for(1000);\n        page.clock.run_for(\\\"30:00\\\")\n        ```\n\n        Parameters\n        ----------\n        ticks : Union[int, str]\n            Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are\n            \"08\" for eight seconds, \"01:00\" for one minute and \"02:34:10\" for two hours, 34 minutes and ten seconds.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.run_for(ticks=ticks)))\n\n    def set_fixed_time(self, time: typing.Union[float, str, datetime.datetime]) -> None:\n        \"\"\"Clock.set_fixed_time\n\n        Makes `Date.now` and `new Date()` return fixed fake time at all times, keeps all the timers running.\n\n        Use this method for simple scenarios where you only need to test with a predefined time. For more advanced\n        scenarios, use `clock.install()` instead. Read docs on [clock emulation](https://playwright.dev/python/docs/clock) to learn more.\n\n        **Usage**\n\n        ```py\n        page.clock.set_fixed_time(datetime.datetime.now())\n        page.clock.set_fixed_time(datetime.datetime(2020, 2, 2))\n        page.clock.set_fixed_time(\\\"2020-02-02\\\")\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to be set.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.set_fixed_time(time=time))\n        )\n\n    def set_system_time(\n        self, time: typing.Union[float, str, datetime.datetime]\n    ) -> None:\n        \"\"\"Clock.set_system_time\n\n        Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for\n        example switching from summer to winter time, or changing time zones.\n\n        **Usage**\n\n        ```py\n        page.clock.set_system_time(datetime.datetime.now())\n        page.clock.set_system_time(datetime.datetime(2020, 2, 2))\n        page.clock.set_system_time(\\\"2020-02-02\\\")\n        ```\n\n        Parameters\n        ----------\n        time : Union[datetime.datetime, float, str]\n            Time to be set.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.set_system_time(time=time))\n        )\n\n\nmapping.register(ClockImpl, Clock)\n\n\nclass ConsoleMessage(SyncBase):\n\n    @property\n    def type(\n        self,\n    ) -> typing.Union[\n        Literal[\"assert\"],\n        Literal[\"clear\"],\n        Literal[\"count\"],\n        Literal[\"debug\"],\n        Literal[\"dir\"],\n        Literal[\"dirxml\"],\n        Literal[\"endGroup\"],\n        Literal[\"error\"],\n        Literal[\"info\"],\n        Literal[\"log\"],\n        Literal[\"profile\"],\n        Literal[\"profileEnd\"],\n        Literal[\"startGroup\"],\n        Literal[\"startGroupCollapsed\"],\n        Literal[\"table\"],\n        Literal[\"time\"],\n        Literal[\"timeEnd\"],\n        Literal[\"trace\"],\n        Literal[\"warning\"],\n    ]:\n        \"\"\"ConsoleMessage.type\n\n        Returns\n        -------\n        Union[\"assert\", \"clear\", \"count\", \"debug\", \"dir\", \"dirxml\", \"endGroup\", \"error\", \"info\", \"log\", \"profile\", \"profileEnd\", \"startGroup\", \"startGroupCollapsed\", \"table\", \"time\", \"timeEnd\", \"trace\", \"warning\"]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.type)\n\n    @property\n    def text(self) -> str:\n        \"\"\"ConsoleMessage.text\n\n        The text of the console message.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.text)\n\n    @property\n    def args(self) -> typing.List[\"JSHandle\"]:\n        \"\"\"ConsoleMessage.args\n\n        List of arguments passed to a `console` function call. See also `page.on('console')`.\n\n        Returns\n        -------\n        List[JSHandle]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.args)\n\n    @property\n    def location(self) -> SourceLocation:\n        \"\"\"ConsoleMessage.location\n\n        Returns\n        -------\n        {url: str, lineNumber: int, columnNumber: int}\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.location)\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"ConsoleMessage.page\n\n        The page that produced this console message, if any.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    @property\n    def worker(self) -> typing.Optional[\"Worker\"]:\n        \"\"\"ConsoleMessage.worker\n\n        The web worker or service worker that produced this console message, if any. Note that console messages from web\n        workers also have non-null `console_message.page()`.\n\n        Returns\n        -------\n        Union[Worker, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.worker)\n\n\nmapping.register(ConsoleMessageImpl, ConsoleMessage)\n\n\nclass Dialog(SyncBase):\n\n    @property\n    def type(self) -> str:\n        \"\"\"Dialog.type\n\n        Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.type)\n\n    @property\n    def message(self) -> str:\n        \"\"\"Dialog.message\n\n        A message displayed in the dialog.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.message)\n\n    @property\n    def default_value(self) -> str:\n        \"\"\"Dialog.default_value\n\n        If dialog is prompt, returns default prompt value. Otherwise, returns empty string.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.default_value)\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"Dialog.page\n\n        The page that initiated this dialog, if available.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    def accept(self, prompt_text: typing.Optional[str] = None) -> None:\n        \"\"\"Dialog.accept\n\n        Returns when the dialog has been accepted.\n\n        Parameters\n        ----------\n        prompt_text : Union[str, None]\n            A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.accept(promptText=prompt_text))\n        )\n\n    def dismiss(self) -> None:\n        \"\"\"Dialog.dismiss\n\n        Returns when the dialog has been dismissed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.dismiss()))\n\n\nmapping.register(DialogImpl, Dialog)\n\n\nclass Download(SyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Download.page\n\n        Get the page that the download belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Download.url\n\n        Returns downloaded url.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def suggested_filename(self) -> str:\n        \"\"\"Download.suggested_filename\n\n        Returns suggested filename for this download. It is typically computed by the browser from the\n        [`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) response\n        header or the `download` attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources).\n        Different browsers can use different logic for computing it.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.suggested_filename)\n\n    def delete(self) -> None:\n        \"\"\"Download.delete\n\n        Deletes the downloaded file. Will wait for the download to finish if necessary.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.delete()))\n\n    def failure(self) -> typing.Optional[str]:\n        \"\"\"Download.failure\n\n        Returns download error if any. Will wait for the download to finish if necessary.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.failure()))\n\n    def path(self) -> pathlib.Path:\n        \"\"\"Download.path\n\n        Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method\n        will wait for the download to finish if necessary. The method throws when connected remotely.\n\n        Note that the download's file name is a random GUID, use `download.suggested_filename()` to get suggested\n        file name.\n\n        Returns\n        -------\n        pathlib.Path\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.path()))\n\n    def save_as(self, path: typing.Union[str, pathlib.Path]) -> None:\n        \"\"\"Download.save_as\n\n        Copy the download to a user-specified path. It is safe to call this method while the download is still in progress.\n        Will wait for the download to finish if necessary.\n\n        **Usage**\n\n        ```py\n        download.save_as(\\\"/path/to/save/at/\\\" + download.suggested_filename)\n        ```\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str]\n            Path where the download should be copied.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.save_as(path=path)))\n\n    def cancel(self) -> None:\n        \"\"\"Download.cancel\n\n        Cancels a download. Will not fail if the download is already finished or canceled. Upon successful cancellations,\n        `download.failure()` would resolve to `'canceled'`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.cancel()))\n\n\nmapping.register(DownloadImpl, Download)\n\n\nclass Video(SyncBase):\n\n    def path(self) -> pathlib.Path:\n        \"\"\"Video.path\n\n        Returns the file system path this video will be recorded to. The video is guaranteed to be written to the\n        filesystem upon closing the browser context. This method throws when connected remotely.\n\n        Returns\n        -------\n        pathlib.Path\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.path()))\n\n    def save_as(self, path: typing.Union[str, pathlib.Path]) -> None:\n        \"\"\"Video.save_as\n\n        Saves the video to a user-specified path. It is safe to call this method while the video is still in progress, or\n        after the page has closed. This method waits until the page is closed and the video is fully saved.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str]\n            Path where the video should be saved.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.save_as(path=path)))\n\n    def delete(self) -> None:\n        \"\"\"Video.delete\n\n        Deletes the video file. Will wait for the video to finish if necessary.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.delete()))\n\n\nmapping.register(VideoImpl, Video)\n\n\nclass Page(SyncContextManager):\n\n    @typing.overload\n    def on(self, event: Literal[\"close\"], f: typing.Callable[[\"Page\"], \"None\"]) -> None:\n        \"\"\"\n        Emitted when the page closes.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        def print_args(msg):\n            for arg in msg.args:\n                print(arg.json_value())\n\n        page.on(\\\"console\\\", print_args)\n        page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(self, event: Literal[\"crash\"], f: typing.Callable[[\"Page\"], \"None\"]) -> None:\n        \"\"\"\n        Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page\n        crashes, ongoing and subsequent operations will throw.\n\n        The most common way to deal with crashes is to catch an exception:\n\n        ```py\n        try:\n            # crash might happen during a click.\n            page.click(\\\"button\\\")\n            # or while waiting for an event.\n            page.wait_for_event(\\\"popup\\\")\n        except Error as e:\n            pass\n            # when the page crashes, exception message contains \\\"crash\\\".\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"dialog\"], f: typing.Callable[[\"Dialog\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        page.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"domcontentloaded\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript\n        [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"download\"], f: typing.Callable[[\"Download\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when attachment download started. User can access basic file operations on downloaded content via the\n        passed `Download` instance.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"filechooser\"], f: typing.Callable[[\"FileChooser\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can\n        respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that.\n\n        ```py\n        page.on(\\\"filechooser\\\", lambda file_chooser: file_chooser.set_files(\\\"/tmp/myfile.pdf\\\"))\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"frameattached\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is attached.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"framedetached\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is detached.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"framenavigated\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is navigated to a new url.\"\"\"\n\n    @typing.overload\n    def on(self, event: Literal[\"load\"], f: typing.Callable[[\"Page\"], \"None\"]) -> None:\n        \"\"\"\n        Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"pageerror\"], f: typing.Callable[[\"Error\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when an uncaught exception happens within the page.\n\n        ```py\n        # Log all uncaught errors to the terminal\n        page.on(\\\"pageerror\\\", lambda exc: print(f\\\"uncaught exception: {exc}\\\"))\n\n        # Navigate to a page with an exception.\n        page.goto(\\\"data:text/html,<script>throw new Error('test')</script>\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(self, event: Literal[\"popup\"], f: typing.Callable[[\"Page\"], \"None\"]) -> None:\n        \"\"\"\n        Emitted when the page opens a new tab or window. This event is emitted in addition to the\n        `browser_context.on('page')`, but only for popups relevant to this page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        with page.expect_event(\\\"popup\\\") as page_info:\n            page.get_by_text(\\\"open the popup\\\").click()\n        popup = page_info.value\n        print(popup.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"request\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests,\n        see `page.route()` or `browser_context.route()`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"requestfailed\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out.\n\n        ```python\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure.error_text))\n        ```\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will\n        only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network error\n        net::ERR_FAILED.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"requestfinished\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"response\"], f: typing.Callable[[\"Response\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"websocket\"], f: typing.Callable[[\"WebSocket\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when `WebSocket` request is sent.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"worker\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned\n        by the page.\"\"\"\n\n    def on(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the page closes.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        def print_args(msg):\n            for arg in msg.args:\n                print(arg.json_value())\n\n        page.on(\\\"console\\\", print_args)\n        page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"crash\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page\n        crashes, ongoing and subsequent operations will throw.\n\n        The most common way to deal with crashes is to catch an exception:\n\n        ```py\n        try:\n            # crash might happen during a click.\n            page.click(\\\"button\\\")\n            # or while waiting for an event.\n            page.wait_for_event(\\\"popup\\\")\n        except Error as e:\n            pass\n            # when the page crashes, exception message contains \\\"crash\\\".\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"dialog\"], f: typing.Callable[[\"Dialog\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        page.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"domcontentloaded\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript\n        [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"download\"], f: typing.Callable[[\"Download\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when attachment download started. User can access basic file operations on downloaded content via the\n        passed `Download` instance.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"filechooser\"], f: typing.Callable[[\"FileChooser\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can\n        respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that.\n\n        ```py\n        page.on(\\\"filechooser\\\", lambda file_chooser: file_chooser.set_files(\\\"/tmp/myfile.pdf\\\"))\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"frameattached\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is attached.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"framedetached\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is detached.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"framenavigated\"], f: typing.Callable[[\"Frame\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a frame is navigated to a new url.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"load\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"pageerror\"], f: typing.Callable[[\"Error\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when an uncaught exception happens within the page.\n\n        ```py\n        # Log all uncaught errors to the terminal\n        page.on(\\\"pageerror\\\", lambda exc: print(f\\\"uncaught exception: {exc}\\\"))\n\n        # Navigate to a page with an exception.\n        page.goto(\\\"data:text/html,<script>throw new Error('test')</script>\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"popup\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when the page opens a new tab or window. This event is emitted in addition to the\n        `browser_context.on('page')`, but only for popups relevant to this page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        with page.expect_event(\\\"popup\\\") as page_info:\n            page.get_by_text(\\\"open the popup\\\").click()\n        popup = page_info.value\n        print(popup.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"request\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests,\n        see `page.route()` or `browser_context.route()`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"requestfailed\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out.\n\n        ```python\n        page.on(\\\"requestfailed\\\", lambda request: print(request.url + \\\" \\\" + request.failure.error_text))\n        ```\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will\n        only be considered failed when the client cannot get an HTTP response from the server, e.g. due to network error\n        net::ERR_FAILED.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"requestfinished\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"response\"], f: typing.Callable[[\"Response\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"websocket\"], f: typing.Callable[[\"WebSocket\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when `WebSocket` request is sent.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"worker\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned\n        by the page.\"\"\"\n\n    def once(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def keyboard(self) -> \"Keyboard\":\n        \"\"\"Page.keyboard\n\n        Returns\n        -------\n        Keyboard\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.keyboard)\n\n    @property\n    def mouse(self) -> \"Mouse\":\n        \"\"\"Page.mouse\n\n        Returns\n        -------\n        Mouse\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.mouse)\n\n    @property\n    def touchscreen(self) -> \"Touchscreen\":\n        \"\"\"Page.touchscreen\n\n        Returns\n        -------\n        Touchscreen\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.touchscreen)\n\n    @property\n    def context(self) -> \"BrowserContext\":\n        \"\"\"Page.context\n\n        Get the browser context that the page belongs to.\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.context)\n\n    @property\n    def clock(self) -> \"Clock\":\n        \"\"\"Page.clock\n\n        Playwright has ability to mock clock and passage of time.\n\n        Returns\n        -------\n        Clock\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.clock)\n\n    @property\n    def main_frame(self) -> \"Frame\":\n        \"\"\"Page.main_frame\n\n        The page's main frame. Page is guaranteed to have a main frame which persists during navigations.\n\n        Returns\n        -------\n        Frame\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.main_frame)\n\n    @property\n    def frames(self) -> typing.List[\"Frame\"]:\n        \"\"\"Page.frames\n\n        An array of all frames attached to the page.\n\n        Returns\n        -------\n        List[Frame]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.frames)\n\n    @property\n    def url(self) -> str:\n        \"\"\"Page.url\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def viewport_size(self) -> typing.Optional[ViewportSize]:\n        \"\"\"Page.viewport_size\n\n        Returns\n        -------\n        Union[{width: int, height: int}, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.viewport_size)\n\n    @property\n    def workers(self) -> typing.List[\"Worker\"]:\n        \"\"\"Page.workers\n\n        This method returns all of the dedicated\n        [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page.\n\n        **NOTE** This does not contain ServiceWorkers\n\n        Returns\n        -------\n        List[Worker]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.workers)\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        \"\"\"Page.request\n\n        API testing helper associated with this page. This method returns the same instance as\n        `browser_context.request` on the page's context. See `browser_context.request` for more\n        details.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def video(self) -> typing.Optional[\"Video\"]:\n        \"\"\"Page.video\n\n        Video object associated with this page.\n\n        Returns\n        -------\n        Union[Video, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.video)\n\n    def opener(self) -> typing.Optional[\"Page\"]:\n        \"\"\"Page.opener\n\n        Returns the opener for popup pages and `null` for others. If the opener has been closed already the returns `null`.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(self._sync(self._impl_obj.opener()))\n\n    def frame(\n        self,\n        name: typing.Optional[str] = None,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n    ) -> typing.Optional[\"Frame\"]:\n        \"\"\"Page.frame\n\n        Returns frame matching the specified criteria. Either `name` or `url` must be specified.\n\n        **Usage**\n\n        ```py\n        frame = page.frame(url=r\\\".*domain.*\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[str, None]\n            Frame name specified in the `iframe`'s `name` attribute. Optional.\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving frame's `url` as a [URL] object. Optional.\n\n        Returns\n        -------\n        Union[Frame, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._impl_obj.frame(name=name, url=self._wrap_handler(url))\n        )\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        \"\"\"Page.set_default_navigation_timeout\n\n        This setting will change the default maximum navigation time for the following methods and related shortcuts:\n        - `page.go_back()`\n        - `page.go_forward()`\n        - `page.goto()`\n        - `page.reload()`\n        - `page.set_content()`\n        - `page.expect_navigation()`\n        - `page.wait_for_url()`\n\n        **NOTE** `page.set_default_navigation_timeout()` takes priority over `page.set_default_timeout()`,\n        `browser_context.set_default_timeout()` and `browser_context.set_default_navigation_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum navigation time in milliseconds\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_navigation_timeout(timeout=timeout)\n        )\n\n    def set_default_timeout(self, timeout: float) -> None:\n        \"\"\"Page.set_default_timeout\n\n        This setting will change the default maximum time for all the methods accepting `timeout` option.\n\n        **NOTE** `page.set_default_navigation_timeout()` takes priority over `page.set_default_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum time in milliseconds. Pass `0` to disable timeout.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_timeout(timeout=timeout)\n        )\n\n    def query_selector(\n        self, selector: str, *, strict: typing.Optional[bool] = None\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Page.query_selector\n\n        The method finds an element matching the specified selector within the page. If no elements match the selector, the\n        return value resolves to `null`. To wait for an element on the page, use `locator.wait_for()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.query_selector(selector=selector, strict=strict))\n        )\n\n    def query_selector_all(self, selector: str) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Page.query_selector_all\n\n        The method finds all elements matching the specified selector within the page. If no elements match the selector,\n        the return value resolves to `[]`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            self._sync(self._impl_obj.query_selector_all(selector=selector))\n        )\n\n    def wait_for_selector(\n        self,\n        selector: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Optional[\"ElementHandle\"]:\n        \"\"\"Page.wait_for_selector\n\n        Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or\n        `detached`.\n\n        **NOTE** Playwright automatically waits for element to be ready before performing an action. Using `Locator`\n        objects and web-first assertions makes the code wait-for-selector-free.\n\n        Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If\n        at the moment of calling the method `selector` already satisfies the condition, the method will return immediately.\n        If the selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.\n\n        **Usage**\n\n        This method works across navigations:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            chromium = playwright.chromium\n            browser = chromium.launch()\n            page = browser.new_page()\n            for current_url in [\\\"https://google.com\\\", \\\"https://bbc.com\\\"]:\n                page.goto(current_url, wait_until=\\\"domcontentloaded\\\")\n                element = page.wait_for_selector(\\\"img\\\")\n                print(\\\"Loaded image: \\\" + str(element.get_attribute(\\\"src\\\")))\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Union[ElementHandle, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(\n                self._impl_obj.wait_for_selector(\n                    selector=selector, timeout=timeout, state=state, strict=strict\n                )\n            )\n        )\n\n    def is_checked(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_checked(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_disabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_disabled(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_editable(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_editable(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_enabled(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_enabled(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_hidden(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).  `selector` that\n        does not match any elements is considered hidden.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `page.is_hidden()` does not wait for the↵element to become hidden and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_hidden(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def is_visible(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> bool:\n        \"\"\"Page.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible). `selector` that does not match any elements\n        is considered not visible.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `page.is_visible()` does not wait↵for the element to become visible and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.is_visible(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def dispatch_event(\n        self,\n        selector: str,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.dispatch_event\n\n        The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        **Usage**\n\n        ```py\n        page.dispatch_event(\\\"button#submit\\\", \\\"click\\\")\n        ```\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        # note you can only create data_transfer in chromium and firefox\n        data_transfer = page.evaluate_handle(\\\"new DataTransfer()\\\")\n        page.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", { \\\"dataTransfer\\\": data_transfer })\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dispatch_event(\n                    selector=selector,\n                    type=type,\n                    eventInit=mapping.to_impl(event_init),\n                    timeout=timeout,\n                    strict=strict,\n                )\n            )\n        )\n\n    def evaluate(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Page.evaluate\n\n        Returns the value of the `expression` invocation.\n\n        If the function passed to the `page.evaluate()` returns a [Promise], then `page.evaluate()` would\n        wait for the promise to resolve and return its value.\n\n        If the function passed to the `page.evaluate()` returns a non-[Serializable] value, then\n        `page.evaluate()` resolves to `undefined`. Playwright also supports transferring some additional values\n        that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.\n\n        **Usage**\n\n        Passing argument to `expression`:\n\n        ```py\n        result = page.evaluate(\\\"([x, y]) => Promise.resolve(x * y)\\\", [7, 8])\n        print(result) # prints \\\"56\\\"\n        ```\n\n        A string can also be passed in instead of a function:\n\n        ```py\n        print(page.evaluate(\\\"1 + 2\\\")) # prints \\\"3\\\"\n        x = 10\n        print(page.evaluate(f\\\"1 + {x}\\\")) # prints \\\"11\\\"\n        ```\n\n        `ElementHandle` instances can be passed as an argument to the `page.evaluate()`:\n\n        ```py\n        body_handle = page.evaluate(\\\"document.body\\\")\n        html = page.evaluate(\\\"([body, suffix]) => body.innerHTML + suffix\\\", [body_handle, \\\"hello\\\"])\n        body_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate(expression=expression, arg=mapping.to_impl(arg))\n            )\n        )\n\n    def evaluate_handle(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> \"JSHandle\":\n        \"\"\"Page.evaluate_handle\n\n        Returns the value of the `expression` invocation as a `JSHandle`.\n\n        The only difference between `page.evaluate()` and `page.evaluate_handle()` is that\n        `page.evaluate_handle()` returns `JSHandle`.\n\n        If the function passed to the `page.evaluate_handle()` returns a [Promise], then\n        `page.evaluate_handle()` would wait for the promise to resolve and return its value.\n\n        **Usage**\n\n        ```py\n        a_window_handle = page.evaluate_handle(\\\"Promise.resolve(window)\\\")\n        a_window_handle # handle for the window object.\n        ```\n\n        A string can also be passed in instead of a function:\n\n        ```py\n        a_handle = page.evaluate_handle(\\\"document\\\") # handle for the \\\"document\\\"\n        ```\n\n        `JSHandle` instances can be passed as an argument to the `page.evaluate_handle()`:\n\n        ```py\n        a_handle = page.evaluate_handle(\\\"document.body\\\")\n        result_handle = page.evaluate_handle(\\\"body => body.innerHTML\\\", a_handle)\n        print(result_handle.json_value())\n        result_handle.dispose()\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.evaluate_handle(\n                    expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def eval_on_selector(\n        self,\n        selector: str,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.Any:\n        \"\"\"Page.eval_on_selector\n\n        The method finds an element matching the specified selector within the page and passes it as a first argument to\n        `expression`. If no elements match the selector, the method throws an error. Returns the value of `expression`.\n\n        If `expression` returns a [Promise], then `page.eval_on_selector()` would wait for the promise to resolve and\n        return its value.\n\n        **Usage**\n\n        ```py\n        search_value = page.eval_on_selector(\\\"#search\\\", \\\"el => el.value\\\")\n        preload_href = page.eval_on_selector(\\\"link[rel=preload]\\\", \\\"el => el.href\\\")\n        html = page.eval_on_selector(\\\".main-container\\\", \\\"(e, suffix) => e.outer_html + suffix\\\", \\\"hello\\\")\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector(\n                    selector=selector,\n                    expression=expression,\n                    arg=mapping.to_impl(arg),\n                    strict=strict,\n                )\n            )\n        )\n\n    def eval_on_selector_all(\n        self, selector: str, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Page.eval_on_selector_all\n\n        The method finds all elements matching the specified selector within the page and passes an array of matched\n        elements as a first argument to `expression`. Returns the result of `expression` invocation.\n\n        If `expression` returns a [Promise], then `page.eval_on_selector_all()` would wait for the promise to resolve\n        and return its value.\n\n        **Usage**\n\n        ```py\n        div_counts = page.eval_on_selector_all(\\\"div\\\", \\\"(divs, min) => divs.length >= min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to query for.\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.eval_on_selector_all(\n                    selector=selector, expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def add_script_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n        type: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Page.add_script_tag\n\n        Adds a `<script>` tag into the page with the desired url or content. Returns the added tag when the script's onload\n        fires or when the script content was injected into frame.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of a script to be added.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative\n            to the current working directory.\n        content : Union[str, None]\n            Raw JavaScript content to be injected into frame.\n        type : Union[str, None]\n            Script type. Use 'module' in order to load a JavaScript ES6 module. See\n            [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.add_script_tag(\n                    url=url, path=path, content=content, type=type\n                )\n            )\n        )\n\n    def add_style_tag(\n        self,\n        *,\n        url: typing.Optional[str] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        content: typing.Optional[str] = None,\n    ) -> \"ElementHandle\":\n        \"\"\"Page.add_style_tag\n\n        Adds a `<link rel=\\\"stylesheet\\\">` tag into the page with the desired url or a `<style type=\\\"text/css\\\">` tag with the\n        content. Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.\n\n        Parameters\n        ----------\n        url : Union[str, None]\n            URL of the `<link>` tag.\n        path : Union[pathlib.Path, str, None]\n            Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to the\n            current working directory.\n        content : Union[str, None]\n            Raw CSS content to be injected into frame.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.add_style_tag(url=url, path=path, content=content)\n            )\n        )\n\n    def expose_function(self, name: str, callback: typing.Callable) -> None:\n        \"\"\"Page.expose_function\n\n        The method adds a function called `name` on the `window` object of every frame in the page. When called, the\n        function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n\n        If the `callback` returns a [Promise], it will be awaited.\n\n        See `browser_context.expose_function()` for context-wide exposed function.\n\n        **NOTE** Functions installed via `page.expose_function()` survive navigations.\n\n        **Usage**\n\n        An example of adding a `sha256` function to the page:\n\n        ```py\n        import hashlib\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def sha256(text):\n            m = hashlib.sha256()\n            m.update(bytes(text, \\\"utf8\\\"))\n            return m.hexdigest()\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch(headless=False)\n            page = browser.new_page()\n            page.expose_function(\\\"sha256\\\", sha256)\n            page.set_content(\\\"\\\"\\\"\n                <script>\n                  async function onClick() {\n                    document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n                  }\n                </script>\n                <button onclick=\\\"onClick()\\\">Click me</button>\n                <div></div>\n            \\\"\\\"\\\")\n            page.click(\\\"button\\\")\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object\n        callback : Callable\n            Callback function which will be called in Playwright's context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.expose_function(\n                    name=name, callback=self._wrap_handler(callback)\n                )\n            )\n        )\n\n    def expose_binding(\n        self,\n        name: str,\n        callback: typing.Callable,\n        *,\n        handle: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.expose_binding\n\n        The method adds a function called `name` on the `window` object of every frame in this page. When called, the\n        function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. If the\n        `callback` returns a [Promise], it will be awaited.\n\n        The first argument of the `callback` function contains information about the caller: `{ browserContext:\n        BrowserContext, page: Page, frame: Frame }`.\n\n        See `browser_context.expose_binding()` for the context-wide version.\n\n        **NOTE** Functions installed via `page.expose_binding()` survive navigations.\n\n        **Usage**\n\n        An example of exposing page URL to all frames in a page:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch(headless=False)\n            context = browser.new_context()\n            page = context.new_page()\n            page.expose_binding(\\\"pageURL\\\", lambda source: source[\\\"page\\\"].url)\n            page.set_content(\\\"\\\"\\\"\n            <script>\n              async function onClick() {\n                document.querySelector('div').textContent = await window.pageURL();\n              }\n            </script>\n            <button onclick=\\\"onClick()\\\">Click me</button>\n            <div></div>\n            \\\"\\\"\\\")\n            page.click(\\\"button\\\")\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        handle : Union[bool, None]\n            Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is\n            supported. When passing by value, multiple arguments are supported.\n            Deprecated: This option will be removed in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.expose_binding(\n                    name=name, callback=self._wrap_handler(callback), handle=handle\n                )\n            )\n        )\n\n    def set_extra_http_headers(self, headers: typing.Dict[str, str]) -> None:\n        \"\"\"Page.set_extra_http_headers\n\n        The extra HTTP headers will be sent with every request the page initiates.\n\n        **NOTE** `page.set_extra_http_headers()` does not guarantee the order of headers in the outgoing requests.\n\n        Parameters\n        ----------\n        headers : Dict[str, str]\n            An object containing additional HTTP headers to be sent with every request. All header values must be strings.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_extra_http_headers(headers=mapping.to_impl(headers))\n            )\n        )\n\n    def content(self) -> str:\n        \"\"\"Page.content\n\n        Gets the full HTML contents of the page, including the doctype.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.content()))\n\n    def set_content(\n        self,\n        html: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Page.set_content\n\n        This method internally calls [document.write()](https://developer.mozilla.org/en-US/docs/Web/API/Document/write),\n        inheriting all its specific characteristics and behaviors.\n\n        Parameters\n        ----------\n        html : str\n            HTML markup to assign to the page.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_content(\n                    html=html, timeout=timeout, waitUntil=wait_until\n                )\n            )\n        )\n\n    def goto(\n        self,\n        url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        referer: typing.Optional[str] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.goto\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the first\n        non-redirect response.\n\n        The method will throw an error if:\n        - there's an SSL error (e.g. in case of self-signed certificates).\n        - target URL is invalid.\n        - the `timeout` is exceeded during navigation.\n        - the remote server does not respond or is unreachable.\n        - the main resource failed to load.\n\n        The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404\n        \\\"Not Found\\\" and 500 \\\"Internal Server Error\\\".  The status code for such responses can be retrieved by calling\n        `response.status()`.\n\n        **NOTE** The method either throws an error or returns a main resource response. The only exceptions are navigation\n        to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.\n\n        **NOTE** Headless mode doesn't support navigation to a PDF document. See the\n        [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).\n\n        Parameters\n        ----------\n        url : str\n            URL to navigate page to. The url should include scheme, e.g. `https://`. When a `baseURL` via the context options\n            was provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        referer : Union[str, None]\n            Referer header value. If provided it will take preference over the referer header value set by\n            `page.set_extra_http_headers()`.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(\n                self._impl_obj.goto(\n                    url=url, timeout=timeout, waitUntil=wait_until, referer=referer\n                )\n            )\n        )\n\n    def reload(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.reload\n\n        This method reloads the current page, in the same way as if the user had triggered a browser refresh. Returns the\n        main resource response. In case of multiple redirects, the navigation will resolve with the response of the last\n        redirect.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.reload(timeout=timeout, waitUntil=wait_until))\n        )\n\n    def wait_for_load_state(\n        self,\n        state: typing.Optional[\n            Literal[\"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.wait_for_load_state\n\n        Returns when the required load state has been reached.\n\n        This resolves when the page reaches a required load state, `load` by default. The navigation must have been\n        committed when this method is called. If current document has already reached the required state, resolves\n        immediately.\n\n        **NOTE** Most of the time, this method is not needed because Playwright\n        [auto-waits before every action](https://playwright.dev/python/docs/actionability).\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"button\\\").click() # click triggers navigation.\n        page.wait_for_load_state() # the promise resolves after \\\"load\\\" event.\n        ```\n\n        ```py\n        with page.expect_popup() as page_info:\n            page.get_by_role(\\\"button\\\").click() # click triggers a popup.\n        popup = page_info.value\n        # Wait for the \\\"DOMContentLoaded\\\" event.\n        popup.wait_for_load_state(\\\"domcontentloaded\\\")\n        print(popup.title()) # popup is ready to use.\n        ```\n\n        Parameters\n        ----------\n        state : Union[\"domcontentloaded\", \"load\", \"networkidle\", None]\n            Optional load state to wait for, defaults to `load`. If the state has been already reached while loading current\n            document, the method resolves immediately. Can be one of:\n            - `'load'` - wait for the `load` event to be fired.\n            - `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired.\n            - `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use\n              this method for testing, rely on web assertions to assess readiness instead.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wait_for_load_state(state=state, timeout=timeout))\n        )\n\n    def wait_for_url(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        *,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.wait_for_url\n\n        Waits for the main frame to navigate to the given URL.\n\n        **Usage**\n\n        ```py\n        page.click(\\\"a.delayed-navigation\\\") # clicking the link will indirectly cause a navigation\n        page.wait_for_url(\\\"**/target.html\\\")\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_url(\n                    url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n                )\n            )\n        )\n\n    def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"Page.wait_for_event\n\n        **NOTE** In most cases, you should use `page.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the page is closed before the\n        `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_event(\n                    event=event,\n                    predicate=self._wrap_handler(predicate),\n                    timeout=timeout,\n                )\n            )\n        )\n\n    def go_back(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.go_back\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect. If cannot go back, returns `null`.\n\n        Navigate to the previous page in history.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.go_back(timeout=timeout, waitUntil=wait_until))\n        )\n\n    def go_forward(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n    ) -> typing.Optional[\"Response\"]:\n        \"\"\"Page.go_forward\n\n        Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of\n        the last redirect. If cannot go forward, returns `null`.\n\n        Navigate to the next page in history.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n\n        Returns\n        -------\n        Union[Response, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.go_forward(timeout=timeout, waitUntil=wait_until))\n        )\n\n    def request_gc(self) -> None:\n        \"\"\"Page.request_gc\n\n        Request the page to perform garbage collection. Note that there is no guarantee that all unreachable objects will\n        be collected.\n\n        This is useful to help detect memory leaks. For example, if your page has a large object `'suspect'` that might be\n        leaked, you can check that it does not leak by using a\n        [`WeakRef`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef).\n\n        ```py\n        # 1. In your page, save a WeakRef for the \\\"suspect\\\".\n        page.evaluate(\\\"globalThis.suspectWeakRef = new WeakRef(suspect)\\\")\n        # 2. Request garbage collection.\n        page.request_gc()\n        # 3. Check that weak ref does not deref to the original object.\n        assert page.evaluate(\\\"!globalThis.suspectWeakRef.deref()\\\")\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.request_gc()))\n\n    def emulate_media(\n        self,\n        *,\n        media: typing.Optional[Literal[\"null\", \"print\", \"screen\"]] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n    ) -> None:\n        \"\"\"Page.emulate_media\n\n        This method changes the `CSS media type` through the `media` argument, and/or the `'prefers-colors-scheme'` media\n        feature, using the `colorScheme` argument.\n\n        **Usage**\n\n        ```py\n        page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → True\n        page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → False\n\n        page.emulate_media(media=\\\"print\\\")\n        page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → False\n        page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → True\n\n        page.emulate_media()\n        page.evaluate(\\\"matchMedia('screen').matches\\\")\n        # → True\n        page.evaluate(\\\"matchMedia('print').matches\\\")\n        # → False\n        ```\n\n        ```py\n        page.emulate_media(color_scheme=\\\"dark\\\")\n        page.evaluate(\\\"matchMedia('(prefers-color-scheme: dark)').matches\\\")\n        # → True\n        page.evaluate(\\\"matchMedia('(prefers-color-scheme: light)').matches\\\")\n        # → False\n        ```\n\n        Parameters\n        ----------\n        media : Union[\"null\", \"print\", \"screen\", None]\n            Changes the CSS media type of the page. The only allowed values are `'Screen'`, `'Print'` and `'Null'`. Passing\n            `'Null'` disables CSS media emulation.\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. Passing `'Null'` disables color scheme emulation.\n            `'no-preference'` is deprecated.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. Passing\n            `null` disables reduced motion emulation.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.emulate_media(\n                    media=media,\n                    colorScheme=color_scheme,\n                    reducedMotion=reduced_motion,\n                    forcedColors=forced_colors,\n                    contrast=contrast,\n                )\n            )\n        )\n\n    def set_viewport_size(self, viewport_size: ViewportSize) -> None:\n        \"\"\"Page.set_viewport_size\n\n        In the case of multiple pages in a single browser, each page can have its own viewport size. However,\n        `browser.new_context()` allows to set viewport size (and more) for all pages in the context at once.\n\n        `page.set_viewport_size()` will resize the page. A lot of websites don't expect phones to change size, so you\n        should set the viewport size before navigating to the page. `page.set_viewport_size()` will also reset\n        `screen` size, use `browser.new_context()` with `screen` and `viewport` parameters if you need better\n        control of these properties.\n\n        **Usage**\n\n        ```py\n        page = browser.new_page()\n        page.set_viewport_size({\\\"width\\\": 640, \\\"height\\\": 480})\n        page.goto(\\\"https://example.com\\\")\n        ```\n\n        Parameters\n        ----------\n        viewport_size : {width: int, height: int}\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.set_viewport_size(viewportSize=viewport_size))\n        )\n\n    def bring_to_front(self) -> None:\n        \"\"\"Page.bring_to_front\n\n        Brings page to front (activates tab).\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.bring_to_front()))\n\n    def add_init_script(\n        self,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n    ) -> None:\n        \"\"\"Page.add_init_script\n\n        Adds a script which would be evaluated in one of the following scenarios:\n        - Whenever the page is navigated.\n        - Whenever the child frame is attached or navigated. In this case, the script is evaluated in the context of the\n          newly attached frame.\n\n        The script is evaluated after the document was created but before any of its scripts were run. This is useful to\n        amend the JavaScript environment, e.g. to seed `Math.random`.\n\n        **Usage**\n\n        An example of overriding `Math.random` before the page loads:\n\n        ```py\n        # in your playwright script, assuming the preload.js file is in same directory\n        page.add_init_script(path=\\\"./preload.js\\\")\n        ```\n\n        **NOTE** The order of evaluation of multiple scripts installed via `browser_context.add_init_script()` and\n        `page.add_init_script()` is not defined.\n\n        Parameters\n        ----------\n        script : Union[str, None]\n            Script to be evaluated in all pages in the browser context. Optional.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.add_init_script(script=script, path=path))\n        )\n\n    def route(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Union[\n            typing.Callable[[\"Route\"], typing.Any],\n            typing.Callable[[\"Route\", \"Request\"], typing.Any],\n        ],\n        *,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.route\n\n        Routing provides the capability to modify network requests that are made by a page.\n\n        Once routing is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or\n        aborted.\n\n        **NOTE** The handler will only be called for the first url if the response is a redirect.\n\n        **NOTE** `page.route()` will not intercept requests intercepted by Service Worker. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        **NOTE** `page.route()` will not intercept the first request of a popup page. Use\n        `browser_context.route()` instead.\n\n        **Usage**\n\n        An example of a naive handler that aborts all image requests:\n\n        ```py\n        page = browser.new_page()\n        page.route(\\\"**/*.{png,jpg,jpeg}\\\", lambda route: route.abort())\n        page.goto(\\\"https://example.com\\\")\n        browser.close()\n        ```\n\n        or the same snippet using a regex pattern instead:\n\n        ```py\n        page = browser.new_page()\n        page.route(re.compile(r\\\"(\\\\.png$)|(\\\\.jpg$)\\\"), lambda route: route.abort())\n        page.goto(\\\"https://example.com\\\")\n        browser.close()\n        ```\n\n        It is possible to examine the request to decide the route action. For example, mocking all requests that contain\n        some post data, and leaving all other requests as is:\n\n        ```py\n        def handle_route(route: Route):\n          if (\\\"my-string\\\" in route.request.post_data):\n            route.fulfill(body=\\\"mocked-data\\\")\n          else:\n            route.continue_()\n        page.route(\\\"/api/**\\\", handle_route)\n        ```\n\n        Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request\n        matches both handlers.\n\n        To remove a route with its handler you can use `page.unroute()`.\n\n        **NOTE** Enabling routing disables http cache.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If `baseURL` is set in\n            the context options and the provided URL is a string that does not start with `*`, it is resolved using the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\n            handler function to route the request.\n        times : Union[int, None]\n            How often a route should be used. By default it will be used every time.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route(\n                    url=self._wrap_handler(url),\n                    handler=self._wrap_handler(handler),\n                    times=times,\n                )\n            )\n        )\n\n    def unroute(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Optional[\n            typing.Union[\n                typing.Callable[[\"Route\"], typing.Any],\n                typing.Callable[[\"Route\", \"Request\"], typing.Any],\n            ]\n        ] = None,\n    ) -> None:\n        \"\"\"Page.unroute\n\n        Removes a route created with `page.route()`. When `handler` is not specified, removes all routes for the\n        `url`.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while routing.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\n            Optional handler function to route the request.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.unroute(\n                    url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n                )\n            )\n        )\n\n    def route_web_socket(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Callable[[\"WebSocketRoute\"], typing.Any],\n    ) -> None:\n        \"\"\"Page.route_web_socket\n\n        This method allows to modify websocket connections that are made by the page.\n\n        Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this\n        method before navigating the page.\n\n        **Usage**\n\n        Below is an example of a simple mock that responds to a single message. See `WebSocketRoute` for more details and\n        examples.\n\n        ```py\n        def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):\n          if message == \\\"request\\\":\n            ws.send(\\\"response\\\")\n\n        def handler(ws: WebSocketRoute):\n          ws.on_message(lambda message: message_handler(ws, message))\n\n        page.route_web_socket(\\\"/ws\\\", handler)\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the\n            `baseURL` context option.\n        handler : Callable[[WebSocketRoute], Any]\n            Handler function to route the WebSocket.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route_web_socket(\n                    url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n                )\n            )\n        )\n\n    def unroute_all(\n        self,\n        *,\n        behavior: typing.Optional[Literal[\"default\", \"ignoreErrors\", \"wait\"]] = None,\n    ) -> None:\n        \"\"\"Page.unroute_all\n\n        Removes all routes created with `page.route()` and `page.route_from_har()`.\n\n        Parameters\n        ----------\n        behavior : Union[\"default\", \"ignoreErrors\", \"wait\", None]\n            Specifies whether to wait for already running handlers and what to do if they throw errors:\n            - `'default'` - do not wait for current handler calls (if any) to finish, if unrouted handler throws, it may\n              result in unhandled error\n            - `'wait'` - wait for current handler calls (if any) to finish\n            - `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers\n              after unrouting are silently caught\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.unroute_all(behavior=behavior))\n        )\n\n    def route_from_har(\n        self,\n        har: typing.Union[pathlib.Path, str],\n        *,\n        url: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        not_found: typing.Optional[Literal[\"abort\", \"fallback\"]] = None,\n        update: typing.Optional[bool] = None,\n        update_content: typing.Optional[Literal[\"attach\", \"embed\"]] = None,\n        update_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n    ) -> None:\n        \"\"\"Page.route_from_har\n\n        If specified the network requests that are made in the page will be served from the HAR file. Read more about\n        [Replaying from HAR](https://playwright.dev/python/docs/mock#replaying-from-har).\n\n        Playwright will not serve requests intercepted by Service Worker from the HAR file. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        Parameters\n        ----------\n        har : Union[pathlib.Path, str]\n            Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a\n            relative path, then it is resolved relative to the current working directory.\n        url : Union[Pattern[str], str, None]\n            A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the\n            pattern will be served from the HAR file. If not specified, all requests are served from the HAR file.\n        not_found : Union[\"abort\", \"fallback\", None]\n            - If set to 'abort' any request not found in the HAR file will be aborted.\n            - If set to 'fallback' missing requests will be sent to the network.\n\n            Defaults to abort.\n        update : Union[bool, None]\n            If specified, updates the given HAR with the actual network information instead of serving from file. The file is\n            written to disk when `browser_context.close()` is called.\n        update_content : Union[\"attach\", \"embed\", None]\n            Optional setting to control resource content management. If `attach` is specified, resources are persisted as\n            separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.\n        update_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to\n            `minimal`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route_from_har(\n                    har=har,\n                    url=url,\n                    notFound=not_found,\n                    update=update,\n                    updateContent=update_content,\n                    updateMode=update_mode,\n                )\n            )\n        )\n\n    def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        full_page: typing.Optional[bool] = None,\n        clip: typing.Optional[FloatRect] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"Page.screenshot\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        full_page : Union[bool, None]\n            When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to\n            `false`.\n        clip : Union[{x: float, y: float, width: float, height: float}, None]\n            An object which specifies clipping of the resulting image.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.screenshot(\n                    timeout=timeout,\n                    type=type,\n                    path=path,\n                    quality=quality,\n                    omitBackground=omit_background,\n                    fullPage=full_page,\n                    clip=clip,\n                    animations=animations,\n                    caret=caret,\n                    scale=scale,\n                    mask=mapping.to_impl(mask),\n                    maskColor=mask_color,\n                    style=style,\n                )\n            )\n        )\n\n    def title(self) -> str:\n        \"\"\"Page.title\n\n        Returns the page's title.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.title()))\n\n    def close(\n        self,\n        *,\n        run_before_unload: typing.Optional[bool] = None,\n        reason: typing.Optional[str] = None,\n    ) -> None:\n        \"\"\"Page.close\n\n        If `runBeforeUnload` is `false`, does not run any unload handlers and waits for the page to be closed. If\n        `runBeforeUnload` is `true` the method will run unload handlers, but will **not** wait for the page to close.\n\n        By default, `page.close()` **does not** run `beforeunload` handlers.\n\n        **NOTE** if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned and should be handled\n        manually via `page.on('dialog')` event.\n\n        Parameters\n        ----------\n        run_before_unload : Union[bool, None]\n            Defaults to `false`. Whether to run the\n            [before unload](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload) page handlers.\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the page closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.close(runBeforeUnload=run_before_unload, reason=reason)\n            )\n        )\n\n    def is_closed(self) -> bool:\n        \"\"\"Page.is_closed\n\n        Indicates that the page has been closed.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_closed())\n\n    def click(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.click\n\n        This method clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.click(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    clickCount=click_count,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                    strict=strict,\n                )\n            )\n        )\n\n    def dblclick(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.dblclick\n\n        This method double clicks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `page.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dblclick(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def tap(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.tap\n\n        This method taps an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `page.tap()` the method will throw if `hasTouch` option of the browser context is false.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.tap(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def fill(\n        self,\n        selector: str,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.fill\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks,\n        focuses the element, fills it and triggers an `input` event after filling. Note that you can pass an empty string\n        to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : str\n            Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fill(\n                    selector=selector,\n                    value=value,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    force=force,\n                )\n            )\n        )\n\n    def locator(\n        self,\n        selector: str,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.locator\n\n        The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved\n        to the element immediately before performing an action, so a series of actions on the same locator can in fact be\n        performed on different DOM elements. That would happen if the DOM structure between those actions has changed.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selector=selector,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Page.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Page.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements\n        in that iframe.\n\n        **Usage**\n\n        Following snippet locates element with text \\\"Submit\\\" in the iframe with id `my-frame`, like `<iframe\n        id=\\\"my-frame\\\">`:\n\n        ```py\n        locator = page.frame_locator(\\\"#my-iframe\\\").get_by_text(\\\"Submit\\\")\n        locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    def focus(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Page.focus\n\n        This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the\n        method waits until a matching element appears in the DOM.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.focus(selector=selector, strict=strict, timeout=timeout)\n            )\n        )\n\n    def text_content(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Page.text_content\n\n        Returns `element.textContent`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.text_content(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def inner_text(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.inner_text\n\n        Returns `element.innerText`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.inner_text(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def inner_html(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.inner_html\n\n        Returns `element.innerHTML`.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.inner_html(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def get_attribute(\n        self,\n        selector: str,\n        name: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Optional[str]:\n        \"\"\"Page.get_attribute\n\n        Returns element attribute value.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        name : str\n            Attribute name to get the value for.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.get_attribute(\n                    selector=selector, name=name, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def hover(\n        self,\n        selector: str,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.hover\n\n        This method hovers over an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.hover(\n                    selector=selector,\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def drag_and_drop(\n        self,\n        source: str,\n        target: str,\n        *,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.drag_and_drop\n\n        This method drags the source element to the target element. It will first move to the source element, perform a\n        `mousedown`, then move to the target element and perform a `mouseup`.\n\n        **Usage**\n\n        ```py\n        page.drag_and_drop(\\\"#source\\\", \\\"#target\\\")\n        # or specify exact positions relative to the top-left corners of the elements:\n        page.drag_and_drop(\n          \\\"#source\\\",\n          \\\"#target\\\",\n          source_position={\\\"x\\\": 34, \\\"y\\\": 7},\n          target_position={\\\"x\\\": 10, \\\"y\\\": 20}\n        )\n        ```\n\n        Parameters\n        ----------\n        source : str\n            A selector to search for an element to drag. If there are multiple elements satisfying the selector, the first will\n            be used.\n        target : str\n            A selector to search for an element to drop onto. If there are multiple elements satisfying the selector, the first\n            will be used.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.drag_and_drop(\n                    source=source,\n                    target=target,\n                    sourcePosition=source_position,\n                    targetPosition=target_position,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    timeout=timeout,\n                    strict=strict,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def select_option(\n        self,\n        selector: str,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Page.select_option\n\n        This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits\n        until all specified options are present in the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```py\n        # Single selection matching the value or label\n        page.select_option(\\\"select#colors\\\", \\\"blue\\\")\n        # single selection matching both the label\n        page.select_option(\\\"select#colors\\\", label=\\\"blue\\\")\n        # multiple selection\n        page.select_option(\\\"select#colors\\\", value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.select_option(\n                    selector=selector,\n                    value=mapping.to_impl(value),\n                    index=mapping.to_impl(index),\n                    label=mapping.to_impl(label),\n                    element=mapping.to_impl(element),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                    strict=strict,\n                )\n            )\n        )\n\n    def input_value(\n        self,\n        selector: str,\n        *,\n        strict: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> str:\n        \"\"\"Page.input_value\n\n        Returns `input.value` for the selected `<input>` or `<textarea>` or `<select>` element.\n\n        Throws for non-input elements. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.input_value(\n                    selector=selector, strict=strict, timeout=timeout\n                )\n            )\n        )\n\n    def set_input_files(\n        self,\n        selector: str,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        strict: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.set_input_files\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files. For inputs\n        with a `[webkitdirectory]` attribute, only a single directory path is supported.\n\n        This method expects `selector` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_input_files(\n                    selector=selector,\n                    files=mapping.to_impl(files),\n                    timeout=timeout,\n                    strict=strict,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def type(\n        self,\n        selector: str,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.type\n\n        Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `page.type` can be used to\n        send fine-grained keyboard events. To fill values in form fields, use `page.fill()`.\n\n        To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.type(\n                    selector=selector,\n                    text=text,\n                    delay=delay,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                )\n            )\n        )\n\n    def press(\n        self,\n        selector: str,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.press\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        **Usage**\n\n        ```py\n        page = browser.new_page()\n        page.goto(\\\"https://keycode.info\\\")\n        page.press(\\\"body\\\", \\\"A\\\")\n        page.screenshot(path=\\\"a.png\\\")\n        page.press(\\\"body\\\", \\\"ArrowLeft\\\")\n        page.screenshot(path=\\\"arrow_left.png\\\")\n        page.press(\\\"body\\\", \\\"Shift+O\\\")\n        page.screenshot(path=\\\"o.png\\\")\n        browser.close()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.press(\n                    selector=selector,\n                    key=key,\n                    delay=delay,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                )\n            )\n        )\n\n    def check(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.check\n\n        This method checks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.check(\n                    selector=selector,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def uncheck(\n        self,\n        selector: str,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.uncheck\n\n        This method unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is\n           already unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.uncheck(\n                    selector=selector,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def wait_for_timeout(self, timeout: float) -> None:\n        \"\"\"Page.wait_for_timeout\n\n        Waits for the given `timeout` in milliseconds.\n\n        Note that `page.waitForTimeout()` should only be used for debugging. Tests using the timer in production are going\n        to be flaky. Use signals such as network events, selectors becoming visible and others instead.\n\n        **Usage**\n\n        ```py\n        # wait for 1 second\n        page.wait_for_timeout(1000)\n        ```\n\n        Parameters\n        ----------\n        timeout : float\n            A timeout to wait for\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wait_for_timeout(timeout=timeout))\n        )\n\n    def wait_for_function(\n        self,\n        expression: str,\n        *,\n        arg: typing.Optional[typing.Any] = None,\n        timeout: typing.Optional[float] = None,\n        polling: typing.Optional[typing.Union[float, Literal[\"raf\"]]] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Page.wait_for_function\n\n        Returns when the `expression` returns a truthy value. It resolves to a JSHandle of the truthy value.\n\n        **Usage**\n\n        The `page.wait_for_function()` can be used to observe viewport size change:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch()\n            page = browser.new_page()\n            page.evaluate(\\\"window.x = 0; setTimeout(() => { window.x = 100 }, 1000);\\\")\n            page.wait_for_function(\\\"() => window.x > 0\\\")\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        To pass an argument to the predicate of `page.wait_for_function()` function:\n\n        ```py\n        selector = \\\".foo\\\"\n        page.wait_for_function(\\\"selector => !!document.querySelector(selector)\\\", selector)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()` or\n            `page.set_default_timeout()` methods.\n        polling : Union[\"raf\", float, None]\n            If `polling` is `'raf'`, then `expression` is constantly executed in `requestAnimationFrame` callback. If `polling`\n            is a number, then it is treated as an interval in milliseconds at which the function would be executed. Defaults to\n            `raf`.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.wait_for_function(\n                    expression=expression,\n                    arg=mapping.to_impl(arg),\n                    timeout=timeout,\n                    polling=polling,\n                )\n            )\n        )\n\n    def pause(self) -> None:\n        \"\"\"Page.pause\n\n        Pauses script execution. Playwright will stop executing the script and wait for the user to either press the\n        'Resume' button in the page overlay or to call `playwright.resume()` in the DevTools console.\n\n        User can inspect selectors or perform manual steps while paused. Resume will continue running the original script\n        from the place it was paused.\n\n        **NOTE** This method requires Playwright to be started in a headed mode, with a falsy `headless` option.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.pause()))\n\n    def pdf(\n        self,\n        *,\n        scale: typing.Optional[float] = None,\n        display_header_footer: typing.Optional[bool] = None,\n        header_template: typing.Optional[str] = None,\n        footer_template: typing.Optional[str] = None,\n        print_background: typing.Optional[bool] = None,\n        landscape: typing.Optional[bool] = None,\n        page_ranges: typing.Optional[str] = None,\n        format: typing.Optional[str] = None,\n        width: typing.Optional[typing.Union[str, float]] = None,\n        height: typing.Optional[typing.Union[str, float]] = None,\n        prefer_css_page_size: typing.Optional[bool] = None,\n        margin: typing.Optional[PdfMargins] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        outline: typing.Optional[bool] = None,\n        tagged: typing.Optional[bool] = None,\n    ) -> bytes:\n        \"\"\"Page.pdf\n\n        Returns the PDF buffer.\n\n        `page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call\n        `page.emulate_media()` before calling `page.pdf()`:\n\n        **NOTE** By default, `page.pdf()` generates a pdf with modified colors for printing. Use the\n        [`-webkit-print-color-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust)\n        property to force rendering of exact colors.\n\n        **Usage**\n\n        ```py\n        # generates a pdf with \\\"screen\\\" media type.\n        page.emulate_media(media=\\\"screen\\\")\n        page.pdf(path=\\\"page.pdf\\\")\n        ```\n\n        The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as\n        pixels.\n\n        A few examples:\n        - `page.pdf({width: 100})` - prints with width set to 100 pixels\n        - `page.pdf({width: '100px'})` - prints with width set to 100 pixels\n        - `page.pdf({width: '10cm'})` - prints with width set to 10 centimeters.\n\n        All possible units are:\n        - `px` - pixel\n        - `in` - inch\n        - `cm` - centimeter\n        - `mm` - millimeter\n\n        The `format` options are:\n        - `Letter`: 8.5in x 11in\n        - `Legal`: 8.5in x 14in\n        - `Tabloid`: 11in x 17in\n        - `Ledger`: 17in x 11in\n        - `A0`: 33.1in x 46.8in\n        - `A1`: 23.4in x 33.1in\n        - `A2`: 16.54in x 23.4in\n        - `A3`: 11.7in x 16.54in\n        - `A4`: 8.27in x 11.7in\n        - `A5`: 5.83in x 8.27in\n        - `A6`: 4.13in x 5.83in\n\n        **NOTE** `headerTemplate` and `footerTemplate` markup have the following limitations: > 1. Script tags inside\n        templates are not evaluated. > 2. Page styles are not visible inside templates.\n\n        Parameters\n        ----------\n        scale : Union[float, None]\n            Scale of the webpage rendering. Defaults to `1`. Scale amount must be between 0.1 and 2.\n        display_header_footer : Union[bool, None]\n            Display header and footer. Defaults to `false`.\n        header_template : Union[str, None]\n            HTML template for the print header. Should be valid HTML markup with following classes used to inject printing\n            values into them:\n            - `'date'` formatted print date\n            - `'title'` document title\n            - `'url'` document location\n            - `'pageNumber'` current page number\n            - `'totalPages'` total pages in the document\n        footer_template : Union[str, None]\n            HTML template for the print footer. Should use the same format as the `headerTemplate`.\n        print_background : Union[bool, None]\n            Print background graphics. Defaults to `false`.\n        landscape : Union[bool, None]\n            Paper orientation. Defaults to `false`.\n        page_ranges : Union[str, None]\n            Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.\n        format : Union[str, None]\n            Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'.\n        width : Union[float, str, None]\n            Paper width, accepts values labeled with units.\n        height : Union[float, str, None]\n            Paper height, accepts values labeled with units.\n        prefer_css_page_size : Union[bool, None]\n            Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format`\n            options. Defaults to `false`, which will scale the content to fit the paper size.\n        margin : Union[{top: Union[float, str, None], right: Union[float, str, None], bottom: Union[float, str, None], left: Union[float, str, None]}, None]\n            Paper margins, defaults to none.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to the current working\n            directory. If no path is provided, the PDF won't be saved to the disk.\n        outline : Union[bool, None]\n            Whether or not to embed the document outline into the PDF. Defaults to `false`.\n        tagged : Union[bool, None]\n            Whether or not to generate tagged (accessible) PDF. Defaults to `false`.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.pdf(\n                    scale=scale,\n                    displayHeaderFooter=display_header_footer,\n                    headerTemplate=header_template,\n                    footerTemplate=footer_template,\n                    printBackground=print_background,\n                    landscape=landscape,\n                    pageRanges=page_ranges,\n                    format=format,\n                    width=width,\n                    height=height,\n                    preferCSSPageSize=prefer_css_page_size,\n                    margin=margin,\n                    path=path,\n                    outline=outline,\n                    tagged=tagged,\n                )\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager:\n        \"\"\"Page.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the page is closed before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        with page.expect_event(\\\"framenavigated\\\") as event_info:\n            page.get_by_role(\\\"button\\\")\n        frame = event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_console_message(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"ConsoleMessage\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"ConsoleMessage\"]:\n        \"\"\"Page.expect_console_message\n\n        Performs action and waits for a `ConsoleMessage` to be logged by in the page. If predicate is provided, it passes\n        `ConsoleMessage` value into the `predicate` function and waits for `predicate(message)` to return a truthy value.\n        Will throw an error if the page is closed before the `page.on('console')` event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[ConsoleMessage], bool], None]\n            Receives the `ConsoleMessage` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[ConsoleMessage]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_console_message(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_download(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Download\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Download\"]:\n        \"\"\"Page.expect_download\n\n        Performs action and waits for a new `Download`. If predicate is provided, it passes `Download` value into the\n        `predicate` function and waits for `predicate(download)` to return a truthy value. Will throw an error if the page\n        is closed before the download event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Download], bool], None]\n            Receives the `Download` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Download]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_download(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_file_chooser(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"FileChooser\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"FileChooser\"]:\n        \"\"\"Page.expect_file_chooser\n\n        Performs action and waits for a new `FileChooser` to be created. If predicate is provided, it passes `FileChooser`\n        value into the `predicate` function and waits for `predicate(fileChooser)` to return a truthy value. Will throw an\n        error if the page is closed before the file chooser is opened.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[FileChooser], bool], None]\n            Receives the `FileChooser` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[FileChooser]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_file_chooser(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_navigation(\n        self,\n        *,\n        url: typing.Optional[\n            typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]]\n        ] = None,\n        wait_until: typing.Optional[\n            Literal[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\"]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Response\"]:\n        \"\"\"Page.expect_navigation\n\n        Waits for the main frame navigation and returns the main resource response. In case of multiple redirects, the\n        navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or\n        navigation due to History API usage, the navigation will resolve with `null`.\n\n        **Usage**\n\n        This resolves when the page navigates to a new URL or reloads. It is useful for when you run code which will\n        indirectly cause the page to navigate. e.g. The click target has an `onclick` handler that triggers navigation from\n        a `setTimeout`. Consider this example:\n\n        ```py\n        with page.expect_navigation():\n            # This action triggers the navigation after a timeout.\n            page.get_by_text(\\\"Navigate after timeout\\\").click()\n        # Resolves after navigation has finished\n        ```\n\n        **NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL\n        is considered a navigation.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str, None]\n            A glob pattern, regex pattern or predicate receiving [URL] to match while waiting for the navigation. Note that if\n            the parameter is a string without wildcard characters, the method will wait for navigation to URL that is exactly\n            equal to the string.\n        wait_until : Union[\"commit\", \"domcontentloaded\", \"load\", \"networkidle\", None]\n            When to consider operation succeeded, defaults to `load`. Events can be either:\n            - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.\n            - `'load'` - consider operation to be finished when the `load` event is fired.\n            - `'networkidle'` - **DISCOURAGED** consider operation to be finished when there are no network connections for\n              at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n            - `'commit'` - consider operation to be finished when network response is received and the document started\n              loading.\n        timeout : Union[float, None]\n            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_navigation_timeout()`,\n            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or\n            `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_navigation(\n                url=self._wrap_handler(url), waitUntil=wait_until, timeout=timeout\n            ).future,\n        )\n\n    def expect_popup(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Page\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Page\"]:\n        \"\"\"Page.expect_popup\n\n        Performs action and waits for a popup `Page`. If predicate is provided, it passes [Popup] value into the\n        `predicate` function and waits for `predicate(page)` to return a truthy value. Will throw an error if the page is\n        closed before the popup event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Page], bool], None]\n            Receives the `Page` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Page]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_popup(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_request(\n        self,\n        url_or_predicate: typing.Union[\n            str, typing.Pattern[str], typing.Callable[[\"Request\"], bool]\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Request\"]:\n        \"\"\"Page.expect_request\n\n        Waits for the matching request and returns it. See [waiting for event](https://playwright.dev/python/docs/events#waiting-for-event) for more\n        details about events.\n\n        **Usage**\n\n        ```py\n        with page.expect_request(\\\"http://example.com/resource\\\") as first:\n            page.get_by_text(\\\"trigger request\\\").click()\n        first_request = first.value\n\n        # or with a lambda\n        with page.expect_request(lambda request: request.url == \\\"http://example.com\\\" and request.method == \\\"get\\\") as second:\n            page.get_by_text(\\\"trigger request\\\").click()\n        second_request = second.value\n        ```\n\n        Parameters\n        ----------\n        url_or_predicate : Union[Callable[[Request], bool], Pattern[str], str]\n            Request URL string, regex or predicate receiving `Request` object. When a `baseURL` via the context options was\n            provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can\n            be changed by using the `page.set_default_timeout()` method.\n\n        Returns\n        -------\n        EventContextManager[Request]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_request(\n                urlOrPredicate=self._wrap_handler(url_or_predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_request_finished(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Request\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Request\"]:\n        \"\"\"Page.expect_request_finished\n\n        Performs action and waits for a `Request` to finish loading. If predicate is provided, it passes `Request` value\n        into the `predicate` function and waits for `predicate(request)` to return a truthy value. Will throw an error if\n        the page is closed before the `page.on('request_finished')` event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Request], bool], None]\n            Receives the `Request` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Request]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_request_finished(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_response(\n        self,\n        url_or_predicate: typing.Union[\n            str, typing.Pattern[str], typing.Callable[[\"Response\"], bool]\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Response\"]:\n        \"\"\"Page.expect_response\n\n        Returns the matched response. See [waiting for event](https://playwright.dev/python/docs/events#waiting-for-event) for more details about\n        events.\n\n        **Usage**\n\n        ```py\n        with page.expect_response(\\\"https://example.com/resource\\\") as response_info:\n            page.get_by_text(\\\"trigger response\\\").click()\n        response = response_info.value\n        return response.ok\n\n        # or with a lambda\n        with page.expect_response(lambda response: response.url == \\\"https://example.com\\\" and response.status == 200 and response.request.method == \\\"get\\\") as response_info:\n            page.get_by_text(\\\"trigger response\\\").click()\n        response = response_info.value\n        return response.ok\n        ```\n\n        Parameters\n        ----------\n        url_or_predicate : Union[Callable[[Response], bool], Pattern[str], str]\n            Request URL string, regex or predicate receiving `Response` object. When a `baseURL` via the context options was\n            provided and the passed URL is a path, it gets merged via the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        timeout : Union[float, None]\n            Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        EventContextManager[Response]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_response(\n                urlOrPredicate=self._wrap_handler(url_or_predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_websocket(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"WebSocket\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"WebSocket\"]:\n        \"\"\"Page.expect_websocket\n\n        Performs action and waits for a new `WebSocket`. If predicate is provided, it passes `WebSocket` value into the\n        `predicate` function and waits for `predicate(webSocket)` to return a truthy value. Will throw an error if the page\n        is closed before the WebSocket event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[WebSocket], bool], None]\n            Receives the `WebSocket` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[WebSocket]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_websocket(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_worker(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Worker\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Worker\"]:\n        \"\"\"Page.expect_worker\n\n        Performs action and waits for a new `Worker`. If predicate is provided, it passes `Worker` value into the\n        `predicate` function and waits for `predicate(worker)` to return a truthy value. Will throw an error if the page is\n        closed before the worker event is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Worker], bool], None]\n            Receives the `Worker` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Worker]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_worker(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def set_checked(\n        self,\n        selector: str,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        strict: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Page.set_checked\n\n        This method checks or unchecks an element matching `selector` by performing the following steps:\n        1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        selector : str\n            A selector to search for an element. If there are multiple elements satisfying the selector, the first will be\n            used.\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        strict : Union[bool, None]\n            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one\n            element, the call throws an exception.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_checked(\n                    selector=selector,\n                    checked=checked,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    strict=strict,\n                    trial=trial,\n                )\n            )\n        )\n\n    def add_locator_handler(\n        self,\n        locator: \"Locator\",\n        handler: typing.Union[\n            typing.Callable[[\"Locator\"], typing.Any], typing.Callable[[], typing.Any]\n        ],\n        *,\n        no_wait_after: typing.Optional[bool] = None,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Page.add_locator_handler\n\n        When testing a web page, sometimes unexpected overlays like a \\\"Sign up\\\" dialog appear and block actions you want to\n        automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making\n        them tricky to handle in automated tests.\n\n        This method lets you set up a special function, called a handler, that activates when it detects that overlay is\n        visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.\n\n        Things to keep in mind:\n        - When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as\n          a part of your normal test flow, instead of using `page.add_locator_handler()`.\n        - Playwright checks for the overlay every time before executing or retrying an action that requires an\n          [actionability check](https://playwright.dev/python/docs/actionability), or before performing an auto-waiting assertion check. When overlay\n          is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the\n          handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't\n          perform any actions, the handler will not be triggered.\n        - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible\n          anymore. You can opt-out of this behavior with `noWaitAfter`.\n        - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler.\n          If your handler takes too long, it might cause timeouts.\n        - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the\n          actions within a handler don't depend on another handler.\n\n        **NOTE** Running the handler will alter your page state mid-test. For example it will change the currently focused\n        element and move the mouse. Make sure that actions that run after the handler are self-contained and do not rely on\n        the focus and mouse state being unchanged.\n\n        For example, consider a test that calls `locator.focus()` followed by `keyboard.press()`. If your\n        handler clicks a button between these two actions, the focused element most likely will be wrong, and key press\n        will happen on the unexpected element. Use `locator.press()` instead to avoid this problem.\n\n        Another example is a series of mouse actions, where `mouse.move()` is followed by `mouse.down()`.\n        Again, when the handler runs between these two actions, the mouse position will be wrong during the mouse down.\n        Prefer self-contained actions like `locator.click()` that do not rely on the state being unchanged by a\n        handler.\n\n        **Usage**\n\n        An example that closes a \\\"Sign up to the newsletter\\\" dialog when it appears:\n\n        ```py\n        # Setup the handler.\n        async def handler():\n          await page.get_by_role(\\\"button\\\", name=\\\"No thanks\\\").click()\n        await page.add_locator_handler(page.get_by_text(\\\"Sign up to the newsletter\\\"), handler)\n\n        # Write the test as usual.\n        await page.goto(\\\"https://example.com\\\")\n        await page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        An example that skips the \\\"Confirm your security details\\\" page when it is shown:\n\n        ```py\n        # Setup the handler.\n        async def handler():\n          await page.get_by_role(\\\"button\\\", name=\\\"Remind me later\\\").click()\n        await page.add_locator_handler(page.get_by_text(\\\"Confirm your security details\\\"), handler)\n\n        # Write the test as usual.\n        await page.goto(\\\"https://example.com\\\")\n        await page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible,\n        so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the\n        handler does not hide the `<body>` element.\n\n        ```py\n        # Setup the handler.\n        async def handler():\n          await page.evaluate(\\\"window.removeObstructionsForTestIfNeeded()\\\")\n        await page.add_locator_handler(page.locator(\\\"body\\\"), handler, no_wait_after=True)\n\n        # Write the test as usual.\n        await page.goto(\\\"https://example.com\\\")\n        await page.get_by_role(\\\"button\\\", name=\\\"Start here\\\").click()\n        ```\n\n        Handler takes the original locator as an argument. You can also automatically remove the handler after a number of\n        invocations by setting `times`:\n\n        ```py\n        async def handler(locator):\n          await locator.click()\n        await page.add_locator_handler(page.get_by_label(\\\"Close\\\"), handler, times=1)\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Locator that triggers the handler.\n        handler : Union[Callable[[Locator], Any], Callable[[], Any]]\n            Function that should be run once `locator` appears. This function should get rid of the element that blocks actions\n            like click.\n        no_wait_after : Union[bool, None]\n            By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then\n            Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of\n            this behavior, so that overlay can stay visible after the handler has run.\n        times : Union[int, None]\n            Specifies the maximum number of times this handler should be called. Unlimited by default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.add_locator_handler(\n                    locator=locator._impl_obj,\n                    handler=self._wrap_handler(handler),\n                    noWaitAfter=no_wait_after,\n                    times=times,\n                )\n            )\n        )\n\n    def remove_locator_handler(self, locator: \"Locator\") -> None:\n        \"\"\"Page.remove_locator_handler\n\n        Removes all locator handlers added by `page.add_locator_handler()` for a specific locator.\n\n        Parameters\n        ----------\n        locator : Locator\n            Locator passed to `page.add_locator_handler()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.remove_locator_handler(locator=locator._impl_obj))\n        )\n\n    def requests(self) -> typing.List[\"Request\"]:\n        \"\"\"Page.requests\n\n        Returns up to (currently) 100 last network request from this page. See `page.on('request')` for more details.\n\n        Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory\n        growth as new requests come in. Once collected, retrieving most information about the request is impossible.\n\n        Note that requests reported through the `page.on('request')` request are not collected, so there is a trade off\n        between efficient memory usage with `page.requests()` and the amount of available information reported\n        through `page.on('request')`.\n\n        Returns\n        -------\n        List[Request]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.requests()))\n\n    def console_messages(self) -> typing.List[\"ConsoleMessage\"]:\n        \"\"\"Page.console_messages\n\n        Returns up to (currently) 200 last console messages from this page. See `page.on('console')` for more details.\n\n        Returns\n        -------\n        List[ConsoleMessage]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.console_messages()))\n\n    def page_errors(self) -> typing.List[\"Error\"]:\n        \"\"\"Page.page_errors\n\n        Returns up to (currently) 200 last page errors from this page. See `page.on('page_error')` for more details.\n\n        Returns\n        -------\n        List[Error]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.page_errors()))\n\n\nmapping.register(PageImpl, Page)\n\n\nclass WebError(SyncBase):\n\n    @property\n    def page(self) -> typing.Optional[\"Page\"]:\n        \"\"\"WebError.page\n\n        The page that produced this unhandled exception, if any.\n\n        Returns\n        -------\n        Union[Page, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.page)\n\n    @property\n    def error(self) -> \"Error\":\n        \"\"\"WebError.error\n\n        Unhandled error that was thrown.\n\n        Returns\n        -------\n        Error\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.error)\n\n\nmapping.register(WebErrorImpl, WebError)\n\n\nclass BrowserContext(SyncContextManager):\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"backgroundpage\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        This event is not emitted.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"BrowserContext\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when Browser context gets closed. This might happen because of one of the following:\n        - Browser context is closed.\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` and the page are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        def print_args(msg):\n            for arg in msg.args:\n                print(arg.json_value())\n\n        context.on(\\\"console\\\", print_args)\n        page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"dialog\"], f: typing.Callable[[\"Dialog\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        context.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def on(self, event: Literal[\"page\"], f: typing.Callable[[\"Page\"], \"None\"]) -> None:\n        \"\"\"\n        The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event\n        will also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a\n        specific page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        with context.expect_page() as page_info:\n            page.get_by_text(\\\"open new page\\\").click(),\n        page = page_info.value\n        print(page.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"weberror\"], f: typing.Callable[[\"WebError\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular\n        page, use `page.on('page_error')` instead.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"request\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To\n        only listen for requests from a particular page, use `page.on('request')`.\n\n        In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.\n        \"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"requestfailed\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page,\n        use `page.on('request_failed')`.\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `browser_context.on('request_finished')` event and not with\n        `browser_context.on('request_failed')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"requestfinished\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a\n        particular page, use `page.on('request_finished')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"response\"], f: typing.Callable[[\"Response\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use\n        `page.on('response')`.\"\"\"\n\n    @typing.overload\n    def on(\n        self, event: Literal[\"serviceworker\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        Emitted when new service worker is created in the context.\"\"\"\n\n    def on(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().on(event=event, f=f)\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"backgroundpage\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        This event is not emitted.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"close\"], f: typing.Callable[[\"BrowserContext\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when Browser context gets closed. This might happen because of one of the following:\n        - Browser context is closed.\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"console\"], f: typing.Callable[[\"ConsoleMessage\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`.\n\n        The arguments passed into `console.log` and the page are available on the `ConsoleMessage` event handler argument.\n\n        **Usage**\n\n        ```py\n        def print_args(msg):\n            for arg in msg.args:\n                print(arg.json_value())\n\n        context.on(\\\"console\\\", print_args)\n        page.evaluate(\\\"console.log('hello', 5, { foo: 'bar' })\\\")\n        ```\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"dialog\"], f: typing.Callable[[\"Dialog\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**\n        either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will\n        [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog,\n        and actions like click will never finish.\n\n        **Usage**\n\n        ```python\n        context.on(\\\"dialog\\\", lambda dialog: dialog.accept())\n        ```\n\n        **NOTE** When no `page.on('dialog')` or `browser_context.on('dialog')` listeners are present, all dialogs are\n        automatically dismissed.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"page\"], f: typing.Callable[[\"Page\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event\n        will also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a\n        specific page.\n\n        The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a\n        popup with `window.open('http://example.com')`, this event will fire when the network request to\n        \\\"http://example.com\\\" is done and its response has started loading in the popup. If you would like to route/listen\n        to this network request, use `browser_context.route()` and `browser_context.on('request')` respectively\n        instead of similar methods on the `Page`.\n\n        ```py\n        with context.expect_page() as page_info:\n            page.get_by_text(\\\"open new page\\\").click(),\n        page = page_info.value\n        print(page.evaluate(\\\"location.href\\\"))\n        ```\n\n        **NOTE** Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not\n        need it in most cases).\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"weberror\"], f: typing.Callable[[\"WebError\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular\n        page, use `page.on('page_error')` instead.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"request\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To\n        only listen for requests from a particular page, use `page.on('request')`.\n\n        In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.\n        \"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"requestfailed\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page,\n        use `page.on('request_failed')`.\n\n        **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request\n        will complete with `browser_context.on('request_finished')` event and not with\n        `browser_context.on('request_failed')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"requestfinished\"], f: typing.Callable[[\"Request\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when a request finishes successfully after downloading the response body. For a successful response, the\n        sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a\n        particular page, use `page.on('request_finished')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"response\"], f: typing.Callable[[\"Response\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when [response] status and headers are received for a request. For a successful response, the sequence of\n        events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use\n        `page.on('response')`.\"\"\"\n\n    @typing.overload\n    def once(\n        self, event: Literal[\"serviceworker\"], f: typing.Callable[[\"Worker\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        Emitted when new service worker is created in the context.\"\"\"\n\n    def once(self, event: str, f: typing.Callable[..., None]) -> None:\n        return super().once(event=event, f=f)\n\n    @property\n    def pages(self) -> typing.List[\"Page\"]:\n        \"\"\"BrowserContext.pages\n\n        Returns all open pages in the context.\n\n        Returns\n        -------\n        List[Page]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.pages)\n\n    @property\n    def browser(self) -> typing.Optional[\"Browser\"]:\n        \"\"\"BrowserContext.browser\n\n        Gets the browser instance that owns the context. Returns `null` if the context is created outside of normal\n        browser, e.g. Android or Electron.\n\n        Returns\n        -------\n        Union[Browser, None]\n        \"\"\"\n        return mapping.from_impl_nullable(self._impl_obj.browser)\n\n    @property\n    def background_pages(self) -> typing.List[\"Page\"]:\n        \"\"\"BrowserContext.background_pages\n\n        Returns an empty list.\n\n        Returns\n        -------\n        List[Page]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.background_pages)\n\n    @property\n    def service_workers(self) -> typing.List[\"Worker\"]:\n        \"\"\"BrowserContext.service_workers\n\n        **NOTE** Service workers are only supported on Chromium-based browsers.\n\n        All existing service workers in the context.\n\n        Returns\n        -------\n        List[Worker]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.service_workers)\n\n    @property\n    def tracing(self) -> \"Tracing\":\n        \"\"\"BrowserContext.tracing\n\n        Returns\n        -------\n        Tracing\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.tracing)\n\n    @property\n    def request(self) -> \"APIRequestContext\":\n        \"\"\"BrowserContext.request\n\n        API testing helper associated with this context. Requests made with this API will use context cookies.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    @property\n    def clock(self) -> \"Clock\":\n        \"\"\"BrowserContext.clock\n\n        Playwright has ability to mock clock and passage of time.\n\n        Returns\n        -------\n        Clock\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.clock)\n\n    def set_default_navigation_timeout(self, timeout: float) -> None:\n        \"\"\"BrowserContext.set_default_navigation_timeout\n\n        This setting will change the default maximum navigation time for the following methods and related shortcuts:\n        - `page.go_back()`\n        - `page.go_forward()`\n        - `page.goto()`\n        - `page.reload()`\n        - `page.set_content()`\n        - `page.expect_navigation()`\n\n        **NOTE** `page.set_default_navigation_timeout()` and `page.set_default_timeout()` take priority over\n        `browser_context.set_default_navigation_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum navigation time in milliseconds\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_navigation_timeout(timeout=timeout)\n        )\n\n    def set_default_timeout(self, timeout: float) -> None:\n        \"\"\"BrowserContext.set_default_timeout\n\n        This setting will change the default maximum time for all the methods accepting `timeout` option.\n\n        **NOTE** `page.set_default_navigation_timeout()`, `page.set_default_timeout()` and\n        `browser_context.set_default_navigation_timeout()` take priority over\n        `browser_context.set_default_timeout()`.\n\n        Parameters\n        ----------\n        timeout : float\n            Maximum time in milliseconds. Pass `0` to disable timeout.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._impl_obj.set_default_timeout(timeout=timeout)\n        )\n\n    def new_page(self) -> \"Page\":\n        \"\"\"BrowserContext.new_page\n\n        Creates a new page in the browser context.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n\n        return mapping.from_impl(self._sync(self._impl_obj.new_page()))\n\n    def cookies(\n        self, urls: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None\n    ) -> typing.List[Cookie]:\n        \"\"\"BrowserContext.cookies\n\n        If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those\n        URLs are returned.\n\n        Parameters\n        ----------\n        urls : Union[Sequence[str], str, None]\n            Optional list of URLs.\n\n        Returns\n        -------\n        List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"], partitionKey: Union[str, None]}]\n        \"\"\"\n\n        return mapping.from_impl_list(\n            self._sync(self._impl_obj.cookies(urls=mapping.to_impl(urls)))\n        )\n\n    def add_cookies(self, cookies: typing.Sequence[SetCookieParam]) -> None:\n        \"\"\"BrowserContext.add_cookies\n\n        Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies\n        can be obtained via `browser_context.cookies()`.\n\n        **Usage**\n\n        ```py\n        browser_context.add_cookies([cookie_object1, cookie_object2])\n        ```\n\n        Parameters\n        ----------\n        cookies : Sequence[{name: str, value: str, url: Union[str, None], domain: Union[str, None], path: Union[str, None], expires: Union[float, None], httpOnly: Union[bool, None], secure: Union[bool, None], sameSite: Union[\"Lax\", \"None\", \"Strict\", None], partitionKey: Union[str, None]}]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.add_cookies(cookies=mapping.to_impl(cookies)))\n        )\n\n    def clear_cookies(\n        self,\n        *,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        domain: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        path: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.clear_cookies\n\n        Removes cookies from context. Accepts optional filter.\n\n        **Usage**\n\n        ```py\n        context.clear_cookies()\n        context.clear_cookies(name=\\\"session-id\\\")\n        context.clear_cookies(domain=\\\"my-origin.com\\\")\n        context.clear_cookies(path=\\\"/api/v1\\\")\n        context.clear_cookies(name=\\\"session-id\\\", domain=\\\"my-origin.com\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str, None]\n            Only removes cookies with the given name.\n        domain : Union[Pattern[str], str, None]\n            Only removes cookies with the given domain.\n        path : Union[Pattern[str], str, None]\n            Only removes cookies with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.clear_cookies(name=name, domain=domain, path=path)\n            )\n        )\n\n    def grant_permissions(\n        self, permissions: typing.Sequence[str], *, origin: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"BrowserContext.grant_permissions\n\n        Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if\n        specified.\n\n        Parameters\n        ----------\n        permissions : Sequence[str]\n            A list of permissions to grant.\n\n            **NOTE** Supported permissions differ between browsers, and even between different versions of the same browser.\n            Any permission may stop working after an update.\n\n            Here are some permissions that may be supported by some browsers:\n            - `'accelerometer'`\n            - `'ambient-light-sensor'`\n            - `'background-sync'`\n            - `'camera'`\n            - `'clipboard-read'`\n            - `'clipboard-write'`\n            - `'geolocation'`\n            - `'gyroscope'`\n            - `'local-fonts'`\n            - `'local-network-access'`\n            - `'magnetometer'`\n            - `'microphone'`\n            - `'midi-sysex'` (system-exclusive midi)\n            - `'midi'`\n            - `'notifications'`\n            - `'payment-handler'`\n            - `'storage-access'`\n        origin : Union[str, None]\n            The [origin] to grant permissions to, e.g. \"https://example.com\".\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.grant_permissions(\n                    permissions=mapping.to_impl(permissions), origin=origin\n                )\n            )\n        )\n\n    def clear_permissions(self) -> None:\n        \"\"\"BrowserContext.clear_permissions\n\n        Clears all permission overrides for the browser context.\n\n        **Usage**\n\n        ```py\n        context = browser.new_context()\n        context.grant_permissions([\\\"clipboard-read\\\"])\n        # do stuff ..\n        context.clear_permissions()\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.clear_permissions()))\n\n    def set_geolocation(self, geolocation: typing.Optional[Geolocation] = None) -> None:\n        \"\"\"BrowserContext.set_geolocation\n\n        Sets the context's geolocation. Passing `null` or `undefined` emulates position unavailable.\n\n        **Usage**\n\n        ```py\n        browser_context.set_geolocation({\\\"latitude\\\": 59.95, \\\"longitude\\\": 30.31667})\n        ```\n\n        **NOTE** Consider using `browser_context.grant_permissions()` to grant permissions for the browser context\n        pages to read its geolocation.\n\n        Parameters\n        ----------\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.set_geolocation(geolocation=geolocation))\n        )\n\n    def set_extra_http_headers(self, headers: typing.Dict[str, str]) -> None:\n        \"\"\"BrowserContext.set_extra_http_headers\n\n        The extra HTTP headers will be sent with every request initiated by any page in the context. These headers are\n        merged with page-specific extra HTTP headers set with `page.set_extra_http_headers()`. If page overrides a\n        particular header, page-specific header value will be used instead of the browser context header value.\n\n        **NOTE** `browser_context.set_extra_http_headers()` does not guarantee the order of headers in the outgoing\n        requests.\n\n        Parameters\n        ----------\n        headers : Dict[str, str]\n            An object containing additional HTTP headers to be sent with every request. All header values must be strings.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_extra_http_headers(headers=mapping.to_impl(headers))\n            )\n        )\n\n    def set_offline(self, offline: bool) -> None:\n        \"\"\"BrowserContext.set_offline\n\n        Parameters\n        ----------\n        offline : bool\n            Whether to emulate network being offline for the browser context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.set_offline(offline=offline))\n        )\n\n    def add_init_script(\n        self,\n        script: typing.Optional[str] = None,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.add_init_script\n\n        Adds a script which would be evaluated in one of the following scenarios:\n        - Whenever a page is created in the browser context or is navigated.\n        - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is\n          evaluated in the context of the newly attached frame.\n\n        The script is evaluated after the document was created but before any of its scripts were run. This is useful to\n        amend the JavaScript environment, e.g. to seed `Math.random`.\n\n        **Usage**\n\n        An example of overriding `Math.random` before the page loads:\n\n        ```py\n        # in your playwright script, assuming the preload.js file is in same directory.\n        browser_context.add_init_script(path=\\\"preload.js\\\")\n        ```\n\n        **NOTE** The order of evaluation of multiple scripts installed via `browser_context.add_init_script()` and\n        `page.add_init_script()` is not defined.\n\n        Parameters\n        ----------\n        script : Union[str, None]\n            Script to be evaluated in all pages in the browser context. Optional.\n        path : Union[pathlib.Path, str, None]\n            Path to the JavaScript file. If `path` is a relative path, then it is resolved relative to the current working\n            directory. Optional.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.add_init_script(script=script, path=path))\n        )\n\n    def expose_binding(\n        self,\n        name: str,\n        callback: typing.Callable,\n        *,\n        handle: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"BrowserContext.expose_binding\n\n        The method adds a function called `name` on the `window` object of every frame in every page in the context. When\n        called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n        If the `callback` returns a [Promise], it will be awaited.\n\n        The first argument of the `callback` function contains information about the caller: `{ browserContext:\n        BrowserContext, page: Page, frame: Frame }`.\n\n        See `page.expose_binding()` for page-only version.\n\n        **Usage**\n\n        An example of exposing page URL to all frames in all pages in the context:\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch(headless=False)\n            context = browser.new_context()\n            context.expose_binding(\\\"pageURL\\\", lambda source: source[\\\"page\\\"].url)\n            page = context.new_page()\n            page.set_content(\\\"\\\"\\\"\n            <script>\n              async function onClick() {\n                document.querySelector('div').textContent = await window.pageURL();\n              }\n            </script>\n            <button onclick=\\\"onClick()\\\">Click me</button>\n            <div></div>\n            \\\"\\\"\\\")\n            page.get_by_role(\\\"button\\\").click()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        handle : Union[bool, None]\n            Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is\n            supported. When passing by value, multiple arguments are supported.\n            Deprecated: This option will be removed in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.expose_binding(\n                    name=name, callback=self._wrap_handler(callback), handle=handle\n                )\n            )\n        )\n\n    def expose_function(self, name: str, callback: typing.Callable) -> None:\n        \"\"\"BrowserContext.expose_function\n\n        The method adds a function called `name` on the `window` object of every frame in every page in the context. When\n        called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.\n\n        If the `callback` returns a [Promise], it will be awaited.\n\n        See `page.expose_function()` for page-only version.\n\n        **Usage**\n\n        An example of adding a `sha256` function to all pages in the context:\n\n        ```py\n        import hashlib\n        from playwright.sync_api import sync_playwright\n\n        def sha256(text: str) -> str:\n            m = hashlib.sha256()\n            m.update(bytes(text, \\\"utf8\\\"))\n            return m.hexdigest()\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            browser = webkit.launch(headless=False)\n            context = browser.new_context()\n            context.expose_function(\\\"sha256\\\", sha256)\n            page = context.new_page()\n            page.set_content(\\\"\\\"\\\"\n                <script>\n                  async function onClick() {\n                    document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');\n                  }\n                </script>\n                <button onclick=\\\"onClick()\\\">Click me</button>\n                <div></div>\n            \\\"\\\"\\\")\n            page.get_by_role(\\\"button\\\").click()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Name of the function on the window object.\n        callback : Callable\n            Callback function that will be called in the Playwright's context.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.expose_function(\n                    name=name, callback=self._wrap_handler(callback)\n                )\n            )\n        )\n\n    def route(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Union[\n            typing.Callable[[\"Route\"], typing.Any],\n            typing.Callable[[\"Route\", \"Request\"], typing.Any],\n        ],\n        *,\n        times: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"BrowserContext.route\n\n        Routing provides the capability to modify network requests that are made by any page in the browser context. Once\n        route is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.\n\n        **NOTE** `browser_context.route()` will not intercept requests intercepted by Service Worker. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        **Usage**\n\n        An example of a naive handler that aborts all image requests:\n\n        ```py\n        context = browser.new_context()\n        page = context.new_page()\n        context.route(\\\"**/*.{png,jpg,jpeg}\\\", lambda route: route.abort())\n        page.goto(\\\"https://example.com\\\")\n        browser.close()\n        ```\n\n        or the same snippet using a regex pattern instead:\n\n        ```py\n        context = browser.new_context()\n        page = context.new_page()\n        context.route(re.compile(r\\\"(\\\\.png$)|(\\\\.jpg$)\\\"), lambda route: route.abort())\n        page = await context.new_page()\n        page = context.new_page()\n        page.goto(\\\"https://example.com\\\")\n        browser.close()\n        ```\n\n        It is possible to examine the request to decide the route action. For example, mocking all requests that contain\n        some post data, and leaving all other requests as is:\n\n        ```py\n        def handle_route(route: Route):\n          if (\\\"my-string\\\" in route.request.post_data):\n            route.fulfill(body=\\\"mocked-data\\\")\n          else:\n            route.continue_()\n        context.route(\\\"/api/**\\\", handle_route)\n        ```\n\n        Page routes (set up with `page.route()`) take precedence over browser context routes when request matches\n        both handlers.\n\n        To remove a route with its handler you can use `browser_context.unroute()`.\n\n        **NOTE** Enabling routing disables http cache.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If `baseURL` is set in\n            the context options and the provided URL is a string that does not start with `*`, it is resolved using the\n            [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\n            handler function to route the request.\n        times : Union[int, None]\n            How often a route should be used. By default it will be used every time.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route(\n                    url=self._wrap_handler(url),\n                    handler=self._wrap_handler(handler),\n                    times=times,\n                )\n            )\n        )\n\n    def unroute(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Optional[\n            typing.Union[\n                typing.Callable[[\"Route\"], typing.Any],\n                typing.Callable[[\"Route\", \"Request\"], typing.Any],\n            ]\n        ] = None,\n    ) -> None:\n        \"\"\"BrowserContext.unroute\n\n        Removes a route created with `browser_context.route()`. When `handler` is not specified, removes all routes\n        for the `url`.\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with\n            `browser_context.route()`.\n        handler : Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\n            Optional handler function used to register a routing with `browser_context.route()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.unroute(\n                    url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n                )\n            )\n        )\n\n    def route_web_socket(\n        self,\n        url: typing.Union[str, typing.Pattern[str], typing.Callable[[str], bool]],\n        handler: typing.Callable[[\"WebSocketRoute\"], typing.Any],\n    ) -> None:\n        \"\"\"BrowserContext.route_web_socket\n\n        This method allows to modify websocket connections that are made by any page in the browser context.\n\n        Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this\n        method before creating any pages.\n\n        **Usage**\n\n        Below is an example of a simple handler that blocks some websocket messages. See `WebSocketRoute` for more details\n        and examples.\n\n        ```py\n        def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):\n          if message == \\\"to-be-blocked\\\":\n            return\n          ws.send(message)\n\n        def handler(ws: WebSocketRoute):\n          ws.route_send(lambda message: message_handler(ws, message))\n          ws.connect()\n\n        context.route_web_socket(\\\"/ws\\\", handler)\n        ```\n\n        Parameters\n        ----------\n        url : Union[Callable[[str], bool], Pattern[str], str]\n            Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the\n            `baseURL` context option.\n        handler : Callable[[WebSocketRoute], Any]\n            Handler function to route the WebSocket.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route_web_socket(\n                    url=self._wrap_handler(url), handler=self._wrap_handler(handler)\n                )\n            )\n        )\n\n    def unroute_all(\n        self,\n        *,\n        behavior: typing.Optional[Literal[\"default\", \"ignoreErrors\", \"wait\"]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.unroute_all\n\n        Removes all routes created with `browser_context.route()` and `browser_context.route_from_har()`.\n\n        Parameters\n        ----------\n        behavior : Union[\"default\", \"ignoreErrors\", \"wait\", None]\n            Specifies whether to wait for already running handlers and what to do if they throw errors:\n            - `'default'` - do not wait for current handler calls (if any) to finish, if unrouted handler throws, it may\n              result in unhandled error\n            - `'wait'` - wait for current handler calls (if any) to finish\n            - `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers\n              after unrouting are silently caught\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.unroute_all(behavior=behavior))\n        )\n\n    def route_from_har(\n        self,\n        har: typing.Union[pathlib.Path, str],\n        *,\n        url: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        not_found: typing.Optional[Literal[\"abort\", \"fallback\"]] = None,\n        update: typing.Optional[bool] = None,\n        update_content: typing.Optional[Literal[\"attach\", \"embed\"]] = None,\n        update_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n    ) -> None:\n        \"\"\"BrowserContext.route_from_har\n\n        If specified the network requests that are made in the context will be served from the HAR file. Read more about\n        [Replaying from HAR](https://playwright.dev/python/docs/mock#replaying-from-har).\n\n        Playwright will not serve requests intercepted by Service Worker from the HAR file. See\n        [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when\n        using request interception by setting `serviceWorkers` to `'block'`.\n\n        Parameters\n        ----------\n        har : Union[pathlib.Path, str]\n            Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a\n            relative path, then it is resolved relative to the current working directory.\n        url : Union[Pattern[str], str, None]\n            A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the\n            pattern will be served from the HAR file. If not specified, all requests are served from the HAR file.\n        not_found : Union[\"abort\", \"fallback\", None]\n            - If set to 'abort' any request not found in the HAR file will be aborted.\n            - If set to 'fallback' falls through to the next route handler in the handler chain.\n\n            Defaults to abort.\n        update : Union[bool, None]\n            If specified, updates the given HAR with the actual network information instead of serving from file. The file is\n            written to disk when `browser_context.close()` is called.\n        update_content : Union[\"attach\", \"embed\", None]\n            Optional setting to control resource content management. If `attach` is specified, resources are persisted as\n            separate files or entries in the ZIP archive. If `embed` is specified, content is stored inline the HAR file.\n        update_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to\n            `minimal`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.route_from_har(\n                    har=har,\n                    url=url,\n                    notFound=not_found,\n                    update=update,\n                    updateContent=update_content,\n                    updateMode=update_mode,\n                )\n            )\n        )\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager:\n        \"\"\"BrowserContext.expect_event\n\n        Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy\n        value. Will throw an error if the context closes before the event is fired. Returns the event data value.\n\n        **Usage**\n\n        ```py\n        with context.expect_event(\\\"page\\\") as event_info:\n            page.get_by_role(\\\"button\\\").click()\n        page = event_info.value\n        ```\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one would pass into `browserContext.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_event(\n                event=event, predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def close(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"BrowserContext.close\n\n        Closes the browser context. All the pages that belong to the browser context will be closed.\n\n        **NOTE** The default browser context cannot be closed.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the context closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.close(reason=reason)))\n\n    def storage_state(\n        self,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        indexed_db: typing.Optional[bool] = None,\n    ) -> StorageState:\n        \"\"\"BrowserContext.storage_state\n\n        Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB\n        snapshot.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current\n            working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.\n        indexed_db : Union[bool, None]\n            Set to `true` to include [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) in the storage\n            state snapshot. If your application uses IndexedDB to store authentication tokens, like Firebase Authentication,\n            enable this.\n\n        Returns\n        -------\n        {cookies: List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: List[{origin: str, localStorage: List[{name: str, value: str}]}]}\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(self._impl_obj.storage_state(path=path, indexedDB=indexed_db))\n        )\n\n    def wait_for_event(\n        self,\n        event: str,\n        predicate: typing.Optional[typing.Callable] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"BrowserContext.wait_for_event\n\n        **NOTE** In most cases, you should use `browser_context.expect_event()`.\n\n        Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function\n        and waits for `predicate(event)` to return a truthy value. Will throw an error if the browser context is closed\n        before the `event` is fired.\n\n        Parameters\n        ----------\n        event : str\n            Event name, same one typically passed into `*.on(event)`.\n        predicate : Union[Callable, None]\n            Receives the event data and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.wait_for_event(\n                    event=event,\n                    predicate=self._wrap_handler(predicate),\n                    timeout=timeout,\n                )\n            )\n        )\n\n    def expect_console_message(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"ConsoleMessage\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"ConsoleMessage\"]:\n        \"\"\"BrowserContext.expect_console_message\n\n        Performs action and waits for a `ConsoleMessage` to be logged by in the pages in the context. If predicate is\n        provided, it passes `ConsoleMessage` value into the `predicate` function and waits for `predicate(message)` to\n        return a truthy value. Will throw an error if the page is closed before the `browser_context.on('console')` event\n        is fired.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[ConsoleMessage], bool], None]\n            Receives the `ConsoleMessage` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[ConsoleMessage]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_console_message(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def expect_page(\n        self,\n        predicate: typing.Optional[typing.Callable[[\"Page\"], bool]] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> EventContextManager[\"Page\"]:\n        \"\"\"BrowserContext.expect_page\n\n        Performs action and waits for a new `Page` to be created in the context. If predicate is provided, it passes `Page`\n        value into the `predicate` function and waits for `predicate(event)` to return a truthy value. Will throw an error\n        if the context closes before new `Page` is created.\n\n        Parameters\n        ----------\n        predicate : Union[Callable[[Page], bool], None]\n            Receives the `Page` object and resolves to truthy value when the waiting should resolve.\n        timeout : Union[float, None]\n            Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The\n            default value can be changed by using the `browser_context.set_default_timeout()`.\n\n        Returns\n        -------\n        EventContextManager[Page]\n        \"\"\"\n        return EventContextManager(\n            self,\n            self._impl_obj.expect_page(\n                predicate=self._wrap_handler(predicate), timeout=timeout\n            ).future,\n        )\n\n    def new_cdp_session(self, page: typing.Union[\"Page\", \"Frame\"]) -> \"CDPSession\":\n        \"\"\"BrowserContext.new_cdp_session\n\n        **NOTE** CDP sessions are only supported on Chromium-based browsers.\n\n        Returns the newly created session.\n\n        Parameters\n        ----------\n        page : Union[Frame, Page]\n            Target to create new session for. For backwards-compatibility, this parameter is named `page`, but it can be a\n            `Page` or `Frame` type.\n\n        Returns\n        -------\n        CDPSession\n        \"\"\"\n\n        return mapping.from_impl(self._sync(self._impl_obj.new_cdp_session(page=page)))\n\n\nmapping.register(BrowserContextImpl, BrowserContext)\n\n\nclass CDPSession(SyncBase):\n\n    def send(\n        self, method: str, params: typing.Optional[typing.Dict] = None\n    ) -> typing.Dict:\n        \"\"\"CDPSession.send\n\n        Parameters\n        ----------\n        method : str\n            Protocol method name.\n        params : Union[Dict, None]\n            Optional method parameters.\n\n        Returns\n        -------\n        Dict\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.send(method=method, params=mapping.to_impl(params))\n            )\n        )\n\n    def detach(self) -> None:\n        \"\"\"CDPSession.detach\n\n        Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be\n        used to send messages.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.detach()))\n\n\nmapping.register(CDPSessionImpl, CDPSession)\n\n\nclass Browser(SyncContextManager):\n\n    def on(\n        self, event: Literal[\"disconnected\"], f: typing.Callable[[\"Browser\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when Browser gets disconnected from the browser application. This might happen because of one of the\n        following:\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n        return super().on(event=event, f=f)\n\n    def once(\n        self, event: Literal[\"disconnected\"], f: typing.Callable[[\"Browser\"], \"None\"]\n    ) -> None:\n        \"\"\"\n        Emitted when Browser gets disconnected from the browser application. This might happen because of one of the\n        following:\n        - Browser application is closed or crashed.\n        - The `browser.close()` method was called.\"\"\"\n        return super().once(event=event, f=f)\n\n    @property\n    def contexts(self) -> typing.List[\"BrowserContext\"]:\n        \"\"\"Browser.contexts\n\n        Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts.\n\n        **Usage**\n\n        ```py\n        browser = pw.webkit.launch()\n        print(len(browser.contexts)) # prints `0`\n        context = browser.new_context()\n        print(len(browser.contexts)) # prints `1`\n        ```\n\n        Returns\n        -------\n        List[BrowserContext]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.contexts)\n\n    @property\n    def browser_type(self) -> \"BrowserType\":\n        \"\"\"Browser.browser_type\n\n        Get the browser type (chromium, firefox or webkit) that the browser belongs to.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.browser_type)\n\n    @property\n    def version(self) -> str:\n        \"\"\"Browser.version\n\n        Returns the browser version.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.version)\n\n    def is_connected(self) -> bool:\n        \"\"\"Browser.is_connected\n\n        Indicates that the browser is connected.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._impl_obj.is_connected())\n\n    def new_context(\n        self,\n        *,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        default_browser_type: typing.Optional[str] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"BrowserContext\":\n        \"\"\"Browser.new_context\n\n        Creates a new browser context. It won't share cookies/cache with other browser contexts.\n\n        **NOTE** If directly using this method to create `BrowserContext`s, it is best practice to explicitly close the\n        returned context via `browser_context.close()` when your code is done with the `BrowserContext`, and before\n        calling `browser.close()`. This will ensure the `context` is closed gracefully and any artifacts—like HARs\n        and videos—are fully flushed and saved.\n\n        **Usage**\n\n        ```py\n        browser = playwright.firefox.launch() # or \\\"chromium\\\" or \\\"webkit\\\".\n        # create a new incognito browser context.\n        context = browser.new_context()\n        # create a new page in a pristine context.\n        page = context.new_page()\n        page.goto(\\\"https://example.com\\\")\n\n        # gracefully close up everything\n        context.close()\n        browser.close()\n        ```\n\n        Parameters\n        ----------\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings to use with this context. Defaults to none.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Learn more about [storage state and auth](../auth.md).\n\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()`.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.new_context(\n                    viewport=viewport,\n                    screen=screen,\n                    noViewport=no_viewport,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    javaScriptEnabled=java_script_enabled,\n                    bypassCSP=bypass_csp,\n                    userAgent=user_agent,\n                    locale=locale,\n                    timezoneId=timezone_id,\n                    geolocation=geolocation,\n                    permissions=mapping.to_impl(permissions),\n                    extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                    offline=offline,\n                    httpCredentials=http_credentials,\n                    deviceScaleFactor=device_scale_factor,\n                    isMobile=is_mobile,\n                    hasTouch=has_touch,\n                    colorScheme=color_scheme,\n                    reducedMotion=reduced_motion,\n                    forcedColors=forced_colors,\n                    contrast=contrast,\n                    acceptDownloads=accept_downloads,\n                    defaultBrowserType=default_browser_type,\n                    proxy=proxy,\n                    recordHarPath=record_har_path,\n                    recordHarOmitContent=record_har_omit_content,\n                    recordVideoDir=record_video_dir,\n                    recordVideoSize=record_video_size,\n                    storageState=storage_state,\n                    baseURL=base_url,\n                    strictSelectors=strict_selectors,\n                    serviceWorkers=service_workers,\n                    recordHarUrlFilter=record_har_url_filter,\n                    recordHarMode=record_har_mode,\n                    recordHarContent=record_har_content,\n                    clientCertificates=client_certificates,\n                )\n            )\n        )\n\n    def new_page(\n        self,\n        *,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        default_browser_type: typing.Optional[str] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"Page\":\n        \"\"\"Browser.new_page\n\n        Creates a new page in a new browser context. Closing this page will close the context as well.\n\n        This is a convenience API that should only be used for the single-page scenarios and short snippets. Production\n        code and testing frameworks should explicitly create `browser.new_context()` followed by the\n        `browser_context.new_page()` to control their exact life times.\n\n        Parameters\n        ----------\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings to use with this context. Defaults to none.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Learn more about [storage state and auth](../auth.md).\n\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()`.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        Page\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.new_page(\n                    viewport=viewport,\n                    screen=screen,\n                    noViewport=no_viewport,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    javaScriptEnabled=java_script_enabled,\n                    bypassCSP=bypass_csp,\n                    userAgent=user_agent,\n                    locale=locale,\n                    timezoneId=timezone_id,\n                    geolocation=geolocation,\n                    permissions=mapping.to_impl(permissions),\n                    extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                    offline=offline,\n                    httpCredentials=http_credentials,\n                    deviceScaleFactor=device_scale_factor,\n                    isMobile=is_mobile,\n                    hasTouch=has_touch,\n                    colorScheme=color_scheme,\n                    forcedColors=forced_colors,\n                    contrast=contrast,\n                    reducedMotion=reduced_motion,\n                    acceptDownloads=accept_downloads,\n                    defaultBrowserType=default_browser_type,\n                    proxy=proxy,\n                    recordHarPath=record_har_path,\n                    recordHarOmitContent=record_har_omit_content,\n                    recordVideoDir=record_video_dir,\n                    recordVideoSize=record_video_size,\n                    storageState=storage_state,\n                    baseURL=base_url,\n                    strictSelectors=strict_selectors,\n                    serviceWorkers=service_workers,\n                    recordHarUrlFilter=record_har_url_filter,\n                    recordHarMode=record_har_mode,\n                    recordHarContent=record_har_content,\n                    clientCertificates=client_certificates,\n                )\n            )\n        )\n\n    def close(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"Browser.close\n\n        In case this browser is obtained using `browser_type.launch()`, closes the browser and all of its pages (if\n        any were opened).\n\n        In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from\n        the browser server.\n\n        **NOTE** This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close\n        events, call `browser_context.close()` on any `BrowserContext` instances you explicitly created earlier\n        using `browser.new_context()` **before** calling `browser.close()`.\n\n        The `Browser` object itself is considered to be disposed and cannot be used anymore.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the browser closure.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.close(reason=reason)))\n\n    def new_browser_cdp_session(self) -> \"CDPSession\":\n        \"\"\"Browser.new_browser_cdp_session\n\n        **NOTE** CDP Sessions are only supported on Chromium-based browsers.\n\n        Returns the newly created browser session.\n\n        Returns\n        -------\n        CDPSession\n        \"\"\"\n\n        return mapping.from_impl(self._sync(self._impl_obj.new_browser_cdp_session()))\n\n    def start_tracing(\n        self,\n        *,\n        page: typing.Optional[\"Page\"] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        screenshots: typing.Optional[bool] = None,\n        categories: typing.Optional[typing.Sequence[str]] = None,\n    ) -> None:\n        \"\"\"Browser.start_tracing\n\n        **NOTE** This API controls\n        [Chromium Tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool) which is a low-level\n        chromium-specific debugging tool. API to control [Playwright Tracing](https://playwright.dev/python/docs/trace-viewer) could be found\n        [here](https://playwright.dev/python/docs/api/class-tracing).\n\n        You can use `browser.start_tracing()` and `browser.stop_tracing()` to create a trace file that can be\n        opened in Chrome DevTools performance panel.\n\n        **Usage**\n\n        ```py\n        browser.start_tracing(page, path=\\\"trace.json\\\")\n        page.goto(\\\"https://www.google.com\\\")\n        browser.stop_tracing()\n        ```\n\n        Parameters\n        ----------\n        page : Union[Page, None]\n            Optional, if specified, tracing includes screenshots of the given page.\n        path : Union[pathlib.Path, str, None]\n            A path to write the trace file to.\n        screenshots : Union[bool, None]\n            captures screenshots in the trace.\n        categories : Union[Sequence[str], None]\n            specify custom categories to use instead of default.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.start_tracing(\n                    page=page._impl_obj if page else None,\n                    path=path,\n                    screenshots=screenshots,\n                    categories=mapping.to_impl(categories),\n                )\n            )\n        )\n\n    def stop_tracing(self) -> bytes:\n        \"\"\"Browser.stop_tracing\n\n        **NOTE** This API controls\n        [Chromium Tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool) which is a low-level\n        chromium-specific debugging tool. API to control [Playwright Tracing](https://playwright.dev/python/docs/trace-viewer) could be found\n        [here](https://playwright.dev/python/docs/api/class-tracing).\n\n        Returns the buffer with trace data.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.stop_tracing()))\n\n\nmapping.register(BrowserImpl, Browser)\n\n\nclass BrowserType(SyncBase):\n\n    @property\n    def name(self) -> str:\n        \"\"\"BrowserType.name\n\n        Returns browser name. For example: `'chromium'`, `'webkit'` or `'firefox'`.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.name)\n\n    @property\n    def executable_path(self) -> str:\n        \"\"\"BrowserType.executable_path\n\n        A path where Playwright expects to find a bundled browser executable.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.executable_path)\n\n    def launch(\n        self,\n        *,\n        executable_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        channel: typing.Optional[str] = None,\n        args: typing.Optional[typing.Sequence[str]] = None,\n        ignore_default_args: typing.Optional[\n            typing.Union[bool, typing.Sequence[str]]\n        ] = None,\n        handle_sigint: typing.Optional[bool] = None,\n        handle_sigterm: typing.Optional[bool] = None,\n        handle_sighup: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        env: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        headless: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        downloads_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        slow_mo: typing.Optional[float] = None,\n        traces_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        chromium_sandbox: typing.Optional[bool] = None,\n        firefox_user_prefs: typing.Optional[\n            typing.Dict[str, typing.Union[str, float, bool]]\n        ] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.launch\n\n        Returns the browser instance.\n\n        **Usage**\n\n        You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments:\n\n        ```py\n        browser = playwright.chromium.launch( # or \\\"firefox\\\" or \\\"webkit\\\".\n            ignore_default_args=[\\\"--mute-audio\\\"]\n        )\n        ```\n\n        > **Chromium-only** Playwright can also be used to control the Google Chrome or Microsoft Edge browsers, but it\n        works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other\n        version. Use `executablePath` option with extreme caution.\n        >\n        > If Google Chrome (rather than Chromium) is preferred, a\n        [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or\n        [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested.\n        >\n        > Stock browsers like Google Chrome and Microsoft Edge are suitable for tests that require proprietary media codecs\n        for video playback. See\n        [this article](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for\n        other differences between Chromium and Chrome.\n        [This article](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md)\n        describes some differences for Linux users.\n\n        Parameters\n        ----------\n        executable_path : Union[pathlib.Path, str, None]\n            Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is\n            resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium,\n            Firefox or WebKit, use at your own risk.\n        channel : Union[str, None]\n            Browser distribution channel.\n\n            Use \"chromium\" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).\n\n            Use \"chrome\", \"chrome-beta\", \"chrome-dev\", \"chrome-canary\", \"msedge\", \"msedge-beta\", \"msedge-dev\", or\n            \"msedge-canary\" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).\n        args : Union[Sequence[str], None]\n            **NOTE** Use custom browser args at your own risk, as some of them may break Playwright functionality.\n\n            Additional arguments to pass to the browser instance. The list of Chromium flags can be found\n            [here](https://peter.sh/experiments/chromium-command-line-switches/).\n        ignore_default_args : Union[Sequence[str], bool, None]\n            If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is\n            given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.\n        handle_sigint : Union[bool, None]\n            Close the browser process on Ctrl-C. Defaults to `true`.\n        handle_sigterm : Union[bool, None]\n            Close the browser process on SIGTERM. Defaults to `true`.\n        handle_sighup : Union[bool, None]\n            Close the browser process on SIGHUP. Defaults to `true`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0`\n            to disable timeout.\n        env : Union[Dict[str, Union[bool, float, str]], None]\n            Specify environment variables that will be visible to the browser. Defaults to `process.env`.\n        headless : Union[bool, None]\n            Whether to run browser in headless mode. More details for\n            [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and\n            [Firefox](https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/). Defaults to `true`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        downloads_path : Union[pathlib.Path, str, None]\n            If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and\n            is deleted when browser is closed. In either case, the downloads are deleted when the browser context they were\n            created in is closed.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on.\n        traces_dir : Union[pathlib.Path, str, None]\n            If specified, traces are saved into this directory.\n        chromium_sandbox : Union[bool, None]\n            Enable Chromium sandboxing. Defaults to `false`.\n        firefox_user_prefs : Union[Dict[str, Union[bool, float, str]], None]\n            Firefox user preferences. Learn more about the Firefox user preferences at\n            [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).\n\n            You can also provide a path to a custom [`policies.json` file](https://mozilla.github.io/policy-templates/) via\n            `PLAYWRIGHT_FIREFOX_POLICIES_JSON` environment variable.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.launch(\n                    executablePath=executable_path,\n                    channel=channel,\n                    args=mapping.to_impl(args),\n                    ignoreDefaultArgs=mapping.to_impl(ignore_default_args),\n                    handleSIGINT=handle_sigint,\n                    handleSIGTERM=handle_sigterm,\n                    handleSIGHUP=handle_sighup,\n                    timeout=timeout,\n                    env=mapping.to_impl(env),\n                    headless=headless,\n                    proxy=proxy,\n                    downloadsPath=downloads_path,\n                    slowMo=slow_mo,\n                    tracesDir=traces_dir,\n                    chromiumSandbox=chromium_sandbox,\n                    firefoxUserPrefs=mapping.to_impl(firefox_user_prefs),\n                )\n            )\n        )\n\n    def launch_persistent_context(\n        self,\n        user_data_dir: typing.Union[str, pathlib.Path],\n        *,\n        channel: typing.Optional[str] = None,\n        executable_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        args: typing.Optional[typing.Sequence[str]] = None,\n        ignore_default_args: typing.Optional[\n            typing.Union[bool, typing.Sequence[str]]\n        ] = None,\n        handle_sigint: typing.Optional[bool] = None,\n        handle_sigterm: typing.Optional[bool] = None,\n        handle_sighup: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        env: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        headless: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        downloads_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        slow_mo: typing.Optional[float] = None,\n        viewport: typing.Optional[ViewportSize] = None,\n        screen: typing.Optional[ViewportSize] = None,\n        no_viewport: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        java_script_enabled: typing.Optional[bool] = None,\n        bypass_csp: typing.Optional[bool] = None,\n        user_agent: typing.Optional[str] = None,\n        locale: typing.Optional[str] = None,\n        timezone_id: typing.Optional[str] = None,\n        geolocation: typing.Optional[Geolocation] = None,\n        permissions: typing.Optional[typing.Sequence[str]] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        offline: typing.Optional[bool] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        device_scale_factor: typing.Optional[float] = None,\n        is_mobile: typing.Optional[bool] = None,\n        has_touch: typing.Optional[bool] = None,\n        color_scheme: typing.Optional[\n            Literal[\"dark\", \"light\", \"no-preference\", \"null\"]\n        ] = None,\n        reduced_motion: typing.Optional[\n            Literal[\"no-preference\", \"null\", \"reduce\"]\n        ] = None,\n        forced_colors: typing.Optional[Literal[\"active\", \"none\", \"null\"]] = None,\n        contrast: typing.Optional[Literal[\"more\", \"no-preference\", \"null\"]] = None,\n        accept_downloads: typing.Optional[bool] = None,\n        traces_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        chromium_sandbox: typing.Optional[bool] = None,\n        firefox_user_prefs: typing.Optional[\n            typing.Dict[str, typing.Union[str, float, bool]]\n        ] = None,\n        record_har_path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_har_omit_content: typing.Optional[bool] = None,\n        record_video_dir: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        record_video_size: typing.Optional[ViewportSize] = None,\n        base_url: typing.Optional[str] = None,\n        strict_selectors: typing.Optional[bool] = None,\n        service_workers: typing.Optional[Literal[\"allow\", \"block\"]] = None,\n        record_har_url_filter: typing.Optional[\n            typing.Union[typing.Pattern[str], str]\n        ] = None,\n        record_har_mode: typing.Optional[Literal[\"full\", \"minimal\"]] = None,\n        record_har_content: typing.Optional[Literal[\"attach\", \"embed\", \"omit\"]] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n    ) -> \"BrowserContext\":\n        \"\"\"BrowserType.launch_persistent_context\n\n        Returns the persistent browser context instance.\n\n        Launches browser that uses persistent storage located at `userDataDir` and returns the only context. Closing this\n        context will automatically close the browser.\n\n        Parameters\n        ----------\n        user_data_dir : Union[pathlib.Path, str]\n            Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty\n            string to create a temporary directory.\n\n            More details for\n            [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and\n            [Firefox](https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile). Chromium's user data directory is the\n            **parent** directory of the \"Profile Path\" seen at `chrome://version`.\n\n            Note that browsers do not allow launching multiple instances with the same User Data Directory.\n\n            **NOTE** Chromium/Chrome: Due to recent Chrome policy changes, automating the default Chrome user profile is not\n            supported. Pointing `userDataDir` to Chrome's main \"User Data\" directory (the profile used for your regular\n            browsing) may result in pages not loading or the browser exiting. Create and use a separate directory (for example,\n            an empty folder) as your automation profile instead. See https://developer.chrome.com/blog/remote-debugging-port\n            for details.\n\n        channel : Union[str, None]\n            Browser distribution channel.\n\n            Use \"chromium\" to [opt in to new headless mode](../browsers.md#chromium-new-headless-mode).\n\n            Use \"chrome\", \"chrome-beta\", \"chrome-dev\", \"chrome-canary\", \"msedge\", \"msedge-beta\", \"msedge-dev\", or\n            \"msedge-canary\" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).\n        executable_path : Union[pathlib.Path, str, None]\n            Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is\n            resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium,\n            Firefox or WebKit, use at your own risk.\n        args : Union[Sequence[str], None]\n            **NOTE** Use custom browser args at your own risk, as some of them may break Playwright functionality.\n\n            Additional arguments to pass to the browser instance. The list of Chromium flags can be found\n            [here](https://peter.sh/experiments/chromium-command-line-switches/).\n        ignore_default_args : Union[Sequence[str], bool, None]\n            If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is\n            given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.\n        handle_sigint : Union[bool, None]\n            Close the browser process on Ctrl-C. Defaults to `true`.\n        handle_sigterm : Union[bool, None]\n            Close the browser process on SIGTERM. Defaults to `true`.\n        handle_sighup : Union[bool, None]\n            Close the browser process on SIGHUP. Defaults to `true`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0`\n            to disable timeout.\n        env : Union[Dict[str, Union[bool, float, str]], None]\n            Specify environment variables that will be visible to the browser. Defaults to `process.env`.\n        headless : Union[bool, None]\n            Whether to run browser in headless mode. More details for\n            [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and\n            [Firefox](https://hacks.mozilla.org/2017/12/using-headless-mode-in-firefox/). Defaults to `true`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        downloads_path : Union[pathlib.Path, str, None]\n            If specified, accepted downloads are downloaded into this directory. Otherwise, temporary directory is created and\n            is deleted when browser is closed. In either case, the downloads are deleted when the browser context they were\n            created in is closed.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on.\n        viewport : Union[{width: int, height: int}, None]\n            Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `no_viewport` disables the fixed\n            viewport. Learn more about [viewport emulation](../emulation.md#viewport).\n        screen : Union[{width: int, height: int}, None]\n            Emulates consistent window screen size available inside web page via `window.screen`. Is only used when the\n            `viewport` is set.\n        no_viewport : Union[bool, None]\n            Does not enforce fixed viewport, allows resizing window in the headed mode.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        java_script_enabled : Union[bool, None]\n            Whether or not to enable JavaScript in the context. Defaults to `true`. Learn more about\n            [disabling JavaScript](../emulation.md#javascript-enabled).\n        bypass_csp : Union[bool, None]\n            Toggles bypassing page's Content-Security-Policy. Defaults to `false`.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        locale : Union[str, None]\n            Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,\n            `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default\n            locale. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).\n        timezone_id : Union[str, None]\n            Changes the timezone of the context. See\n            [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)\n            for a list of supported timezone IDs. Defaults to the system timezone.\n        geolocation : Union[{latitude: float, longitude: float, accuracy: Union[float, None]}, None]\n        permissions : Union[Sequence[str], None]\n            A list of permissions to grant to all pages in this context. See `browser_context.grant_permissions()` for\n            more details. Defaults to none.\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        offline : Union[bool, None]\n            Whether to emulate network being offline. Defaults to `false`. Learn more about\n            [network emulation](../emulation.md#offline).\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        device_scale_factor : Union[float, None]\n            Specify device scale factor (can be thought of as dpr). Defaults to `1`. Learn more about\n            [emulating devices with device scale factor](../emulation.md#devices).\n        is_mobile : Union[bool, None]\n            Whether the `meta viewport` tag is taken into account and touch events are enabled. isMobile is a part of device,\n            so you don't actually need to set it manually. Defaults to `false` and is not supported in Firefox. Learn more\n            about [mobile emulation](../emulation.md#ismobile).\n        has_touch : Union[bool, None]\n            Specifies if viewport supports touch events. Defaults to false. Learn more about\n            [mobile emulation](../emulation.md#devices).\n        color_scheme : Union[\"dark\", \"light\", \"no-preference\", \"null\", None]\n            Emulates [prefers-colors-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n            media feature, supported values are `'light'` and `'dark'`. See `page.emulate_media()` for more details.\n            Passing `'null'` resets emulation to system defaults. Defaults to `'light'`.\n        reduced_motion : Union[\"no-preference\", \"null\", \"reduce\", None]\n            Emulates `'prefers-reduced-motion'` media feature, supported values are `'reduce'`, `'no-preference'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        forced_colors : Union[\"active\", \"none\", \"null\", None]\n            Emulates `'forced-colors'` media feature, supported values are `'active'`, `'none'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'none'`.\n        contrast : Union[\"more\", \"no-preference\", \"null\", None]\n            Emulates `'prefers-contrast'` media feature, supported values are `'no-preference'`, `'more'`. See\n            `page.emulate_media()` for more details. Passing `'null'` resets emulation to system defaults. Defaults to\n            `'no-preference'`.\n        accept_downloads : Union[bool, None]\n            Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted.\n        traces_dir : Union[pathlib.Path, str, None]\n            If specified, traces are saved into this directory.\n        chromium_sandbox : Union[bool, None]\n            Enable Chromium sandboxing. Defaults to `false`.\n        firefox_user_prefs : Union[Dict[str, Union[bool, float, str]], None]\n            Firefox user preferences. Learn more about the Firefox user preferences at\n            [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).\n\n            You can also provide a path to a custom [`policies.json` file](https://mozilla.github.io/policy-templates/) via\n            `PLAYWRIGHT_FIREFOX_POLICIES_JSON` environment variable.\n        record_har_path : Union[pathlib.Path, str, None]\n            Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into the specified HAR file\n            on the filesystem. If not specified, the HAR is not recorded. Make sure to call `browser_context.close()`\n            for the HAR to be saved.\n        record_har_omit_content : Union[bool, None]\n            Optional setting to control whether to omit request content from the HAR. Defaults to `false`.\n        record_video_dir : Union[pathlib.Path, str, None]\n            Enables video recording for all pages into the specified directory. If not specified videos are not recorded. Make\n            sure to call `browser_context.close()` for videos to be saved.\n        record_video_size : Union[{width: int, height: int}, None]\n            Dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit into\n            800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page\n            will be scaled down if necessary to fit the specified size.\n        base_url : Union[str, None]\n            When using `page.goto()`, `page.route()`, `page.wait_for_url()`,\n            `page.expect_request()`, or `page.expect_response()` it takes the base URL in consideration by\n            using the [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the\n            corresponding URL. Unset by default. Examples:\n            - baseURL: `http://localhost:3000` and navigating to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and navigating to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        strict_selectors : Union[bool, None]\n            If set to true, enables strict selectors mode for this context. In the strict selectors mode all operations on\n            selectors that imply single target DOM element will throw when more than one element matches the selector. This\n            option does not affect any Locator APIs (Locators are always strict). Defaults to `false`. See `Locator` to learn\n            more about the strict mode.\n        service_workers : Union[\"allow\", \"block\", None]\n            Whether to allow sites to register Service workers. Defaults to `'allow'`.\n            - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be\n              registered.\n            - `'block'`: Playwright will block all registration of Service Workers.\n        record_har_url_filter : Union[Pattern[str], str, None]\n        record_har_mode : Union[\"full\", \"minimal\", None]\n            When set to `minimal`, only record information necessary for routing from HAR. This omits sizes, timing, page,\n            cookies, security and other types of HAR information that are not used when replaying from HAR. Defaults to `full`.\n        record_har_content : Union[\"attach\", \"embed\", \"omit\", None]\n            Optional setting to control resource content management. If `omit` is specified, content is not persisted. If\n            `attach` is specified, resources are persisted as separate files and all of these files are archived along with the\n            HAR file. Defaults to `embed`, which stores content inline the HAR file as per HAR specification.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n\n        Returns\n        -------\n        BrowserContext\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.launch_persistent_context(\n                    userDataDir=user_data_dir,\n                    channel=channel,\n                    executablePath=executable_path,\n                    args=mapping.to_impl(args),\n                    ignoreDefaultArgs=mapping.to_impl(ignore_default_args),\n                    handleSIGINT=handle_sigint,\n                    handleSIGTERM=handle_sigterm,\n                    handleSIGHUP=handle_sighup,\n                    timeout=timeout,\n                    env=mapping.to_impl(env),\n                    headless=headless,\n                    proxy=proxy,\n                    downloadsPath=downloads_path,\n                    slowMo=slow_mo,\n                    viewport=viewport,\n                    screen=screen,\n                    noViewport=no_viewport,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    javaScriptEnabled=java_script_enabled,\n                    bypassCSP=bypass_csp,\n                    userAgent=user_agent,\n                    locale=locale,\n                    timezoneId=timezone_id,\n                    geolocation=geolocation,\n                    permissions=mapping.to_impl(permissions),\n                    extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                    offline=offline,\n                    httpCredentials=http_credentials,\n                    deviceScaleFactor=device_scale_factor,\n                    isMobile=is_mobile,\n                    hasTouch=has_touch,\n                    colorScheme=color_scheme,\n                    reducedMotion=reduced_motion,\n                    forcedColors=forced_colors,\n                    contrast=contrast,\n                    acceptDownloads=accept_downloads,\n                    tracesDir=traces_dir,\n                    chromiumSandbox=chromium_sandbox,\n                    firefoxUserPrefs=mapping.to_impl(firefox_user_prefs),\n                    recordHarPath=record_har_path,\n                    recordHarOmitContent=record_har_omit_content,\n                    recordVideoDir=record_video_dir,\n                    recordVideoSize=record_video_size,\n                    baseURL=base_url,\n                    strictSelectors=strict_selectors,\n                    serviceWorkers=service_workers,\n                    recordHarUrlFilter=record_har_url_filter,\n                    recordHarMode=record_har_mode,\n                    recordHarContent=record_har_content,\n                    clientCertificates=client_certificates,\n                )\n            )\n        )\n\n    def connect_over_cdp(\n        self,\n        endpoint_url: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        slow_mo: typing.Optional[float] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        is_local: typing.Optional[bool] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.connect_over_cdp\n\n        This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.\n\n        The default browser context is accessible via `browser.contexts()`.\n\n        **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers.\n\n        **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via\n        `browser_type.connect()`. If you are experiencing issues or attempting to use advanced functionality, you\n        probably want to use `browser_type.connect()`.\n\n        **Usage**\n\n        ```py\n        browser = playwright.chromium.connect_over_cdp(\\\"http://localhost:9222\\\")\n        default_context = browser.contexts[0]\n        page = default_context.pages[0]\n        ```\n\n        Parameters\n        ----------\n        endpoint_url : str\n            A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or\n            `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass\n            `0` to disable timeout.\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on. Defaults to 0.\n        headers : Union[Dict[str, str], None]\n            Additional HTTP headers to be sent with connect request. Optional.\n        is_local : Union[bool, None]\n            Tells Playwright that it runs on the same host as the CDP server. It will enable certain optimizations that rely\n            upon the file system being the same between Playwright and the Browser.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.connect_over_cdp(\n                    endpointURL=endpoint_url,\n                    timeout=timeout,\n                    slowMo=slow_mo,\n                    headers=mapping.to_impl(headers),\n                    isLocal=is_local,\n                )\n            )\n        )\n\n    def connect(\n        self,\n        ws_endpoint: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        slow_mo: typing.Optional[float] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        expose_network: typing.Optional[str] = None,\n    ) -> \"Browser\":\n        \"\"\"BrowserType.connect\n\n        This method attaches Playwright to an existing browser instance created via `BrowserType.launchServer` in Node.js.\n\n        **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of\n        Playwright that launches the browser (1.2.3 → is compatible with 1.2.x).\n\n        Parameters\n        ----------\n        ws_endpoint : str\n            A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the connection to be established. Defaults to `0` (no timeout).\n        slow_mo : Union[float, None]\n            Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going\n            on. Defaults to 0.\n        headers : Union[Dict[str, str], None]\n            Additional HTTP headers to be sent with web socket connect request. Optional.\n        expose_network : Union[str, None]\n            This option exposes network available on the connecting client to the browser being connected to. Consists of a\n            list of rules separated by comma.\n\n            Available rules:\n            1. Hostname pattern, for example: `example.com`, `*.org:99`, `x.*.y.com`, `*foo.org`.\n            1. IP literal, for example: `127.0.0.1`, `0.0.0.0:99`, `[::1]`, `[0:0::1]:99`.\n            1. `<loopback>` that matches local loopback interfaces: `localhost`, `*.localhost`, `127.0.0.1`, `[::1]`.\n\n            Some common examples:\n            1. `\"*\"` to expose all network.\n            1. `\"<loopback>\"` to expose localhost network.\n            1. `\"*.test.internal-domain,*.staging.internal-domain,<loopback>\"` to expose test/staging deployments and\n               localhost.\n\n        Returns\n        -------\n        Browser\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.connect(\n                    wsEndpoint=ws_endpoint,\n                    timeout=timeout,\n                    slowMo=slow_mo,\n                    headers=mapping.to_impl(headers),\n                    exposeNetwork=expose_network,\n                )\n            )\n        )\n\n\nmapping.register(BrowserTypeImpl, BrowserType)\n\n\nclass Playwright(SyncBase):\n\n    @property\n    def devices(self) -> typing.Dict:\n        \"\"\"Playwright.devices\n\n        Returns a dictionary of devices to be used with `browser.new_context()` or `browser.new_page()`.\n\n        ```py\n        from playwright.sync_api import sync_playwright, Playwright\n\n        def run(playwright: Playwright):\n            webkit = playwright.webkit\n            iphone = playwright.devices[\\\"iPhone 6\\\"]\n            browser = webkit.launch()\n            context = browser.new_context(**iphone)\n            page = context.new_page()\n            page.goto(\\\"http://example.com\\\")\n            # other actions...\n            browser.close()\n\n        with sync_playwright() as playwright:\n            run(playwright)\n        ```\n\n        Returns\n        -------\n        Dict\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.devices)\n\n    @property\n    def selectors(self) -> \"Selectors\":\n        \"\"\"Playwright.selectors\n\n        Selectors can be used to install custom selector engines. See [extensibility](https://playwright.dev/python/docs/extensibility) for more\n        information.\n\n        Returns\n        -------\n        Selectors\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.selectors)\n\n    @property\n    def chromium(self) -> \"BrowserType\":\n        \"\"\"Playwright.chromium\n\n        This object can be used to launch or connect to Chromium, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.chromium)\n\n    @property\n    def firefox(self) -> \"BrowserType\":\n        \"\"\"Playwright.firefox\n\n        This object can be used to launch or connect to Firefox, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.firefox)\n\n    @property\n    def webkit(self) -> \"BrowserType\":\n        \"\"\"Playwright.webkit\n\n        This object can be used to launch or connect to WebKit, returning instances of `Browser`.\n\n        Returns\n        -------\n        BrowserType\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.webkit)\n\n    @property\n    def request(self) -> \"APIRequest\":\n        \"\"\"Playwright.request\n\n        Exposes API that can be used for the Web API testing.\n\n        Returns\n        -------\n        APIRequest\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.request)\n\n    def __getitem__(self, value: str) -> \"BrowserType\":\n\n        return mapping.from_impl(self._impl_obj.__getitem__(value=value))\n\n    def stop(self) -> None:\n        \"\"\"Playwright.stop\n\n        Terminates this instance of Playwright in case it was created bypassing the Python context manager. This is useful\n        in REPL applications.\n\n        ```py\n        from playwright.sync_api import sync_playwright\n\n        playwright = sync_playwright().start()\n\n        browser = playwright.chromium.launch()\n        page = browser.new_page()\n        page.goto(\\\"https://playwright.dev/\\\")\n        page.screenshot(path=\\\"example.png\\\")\n        browser.close()\n\n        playwright.stop()\n        ```\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.stop()))\n\n\nmapping.register(PlaywrightImpl, Playwright)\n\n\nclass Tracing(SyncBase):\n\n    def start(\n        self,\n        *,\n        name: typing.Optional[str] = None,\n        title: typing.Optional[str] = None,\n        snapshots: typing.Optional[bool] = None,\n        screenshots: typing.Optional[bool] = None,\n        sources: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Tracing.start\n\n        Start tracing.\n\n        **NOTE** You probably want to\n        [enable tracing in your config file](https://playwright.dev/docs/api/class-testoptions#test-options-trace) instead\n        of using `Tracing.start`.\n\n        The `context.tracing` API captures browser operations and network activity, but it doesn't record test assertions\n        (like `expect` calls). We recommend\n        [enabling tracing through Playwright Test configuration](https://playwright.dev/docs/api/class-testoptions#test-options-trace),\n        which includes those assertions and provides a more complete trace for debugging test failures.\n\n        **Usage**\n\n        ```py\n        context.tracing.start(screenshots=True, snapshots=True)\n        page = context.new_page()\n        page.goto(\\\"https://playwright.dev\\\")\n        context.tracing.stop(path = \\\"trace.zip\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[str, None]\n            If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the\n            `tracesDir` directory specified in `browser_type.launch()`. To specify the final trace zip file name, you\n            need to pass `path` option to `tracing.stop()` instead.\n        title : Union[str, None]\n            Trace name to be shown in the Trace Viewer.\n        snapshots : Union[bool, None]\n            If this option is true tracing will\n            - capture DOM snapshot on every action\n            - record network activity\n        screenshots : Union[bool, None]\n            Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview.\n        sources : Union[bool, None]\n            Whether to include source files for trace actions.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.start(\n                    name=name,\n                    title=title,\n                    snapshots=snapshots,\n                    screenshots=screenshots,\n                    sources=sources,\n                )\n            )\n        )\n\n    def start_chunk(\n        self, *, title: typing.Optional[str] = None, name: typing.Optional[str] = None\n    ) -> None:\n        \"\"\"Tracing.start_chunk\n\n        Start a new trace chunk. If you'd like to record multiple traces on the same `BrowserContext`, use\n        `tracing.start()` once, and then create multiple trace chunks with `tracing.start_chunk()` and\n        `tracing.stop_chunk()`.\n\n        **Usage**\n\n        ```py\n        context.tracing.start(screenshots=True, snapshots=True)\n        page = context.new_page()\n        page.goto(\\\"https://playwright.dev\\\")\n\n        context.tracing.start_chunk()\n        page.get_by_text(\\\"Get Started\\\").click()\n        # Everything between start_chunk and stop_chunk will be recorded in the trace.\n        context.tracing.stop_chunk(path = \\\"trace1.zip\\\")\n\n        context.tracing.start_chunk()\n        page.goto(\\\"http://example.com\\\")\n        # Save a second trace file with different actions.\n        context.tracing.stop_chunk(path = \\\"trace2.zip\\\")\n        ```\n\n        Parameters\n        ----------\n        title : Union[str, None]\n            Trace name to be shown in the Trace Viewer.\n        name : Union[str, None]\n            If specified, intermediate trace files are going to be saved into the files with the given name prefix inside the\n            `tracesDir` directory specified in `browser_type.launch()`. To specify the final trace zip file name, you\n            need to pass `path` option to `tracing.stop_chunk()` instead.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.start_chunk(title=title, name=name))\n        )\n\n    def stop_chunk(\n        self, *, path: typing.Optional[typing.Union[pathlib.Path, str]] = None\n    ) -> None:\n        \"\"\"Tracing.stop_chunk\n\n        Stop the trace chunk. See `tracing.start_chunk()` for more details about multiple trace chunks.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            Export trace collected since the last `tracing.start_chunk()` call into the file with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.stop_chunk(path=path)))\n\n    def stop(\n        self, *, path: typing.Optional[typing.Union[pathlib.Path, str]] = None\n    ) -> None:\n        \"\"\"Tracing.stop\n\n        Stop tracing.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            Export trace into the file with the given path.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.stop(path=path)))\n\n    def group(\n        self, name: str, *, location: typing.Optional[TracingGroupLocation] = None\n    ) -> None:\n        \"\"\"Tracing.group\n\n        **NOTE** Use `test.step` instead when available.\n\n        Creates a new group within the trace, assigning any subsequent API calls to this group, until\n        `tracing.group_end()` is called. Groups can be nested and will be visible in the trace viewer.\n\n        **Usage**\n\n        ```py\n        # All actions between group and group_end\n        # will be shown in the trace viewer as a group.\n        await page.context.tracing.group(\\\"Open Playwright.dev > API\\\")\n        await page.goto(\\\"https://playwright.dev/\\\")\n        await page.get_by_role(\\\"link\\\", name=\\\"API\\\").click()\n        await page.context.tracing.group_end()\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Group name shown in the trace viewer.\n        location : Union[{file: str, line: Union[int, None], column: Union[int, None]}, None]\n            Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the\n            `tracing.group()` call.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.group(name=name, location=location))\n        )\n\n    def group_end(self) -> None:\n        \"\"\"Tracing.group_end\n\n        Closes the last group created by `tracing.group()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.group_end()))\n\n\nmapping.register(TracingImpl, Tracing)\n\n\nclass Locator(SyncBase):\n\n    @property\n    def page(self) -> \"Page\":\n        \"\"\"Locator.page\n\n        A page this locator belongs to.\n\n        Returns\n        -------\n        Page\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.page)\n\n    @property\n    def first(self) -> \"Locator\":\n        \"\"\"Locator.first\n\n        Returns locator to the first matching element.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.first)\n\n    @property\n    def last(self) -> \"Locator\":\n        \"\"\"Locator.last\n\n        Returns locator to the last matching element.\n\n        **Usage**\n\n        ```py\n        banana = page.get_by_role(\\\"listitem\\\").last\n        ```\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.last)\n\n    @property\n    def content_frame(self) -> \"FrameLocator\":\n        \"\"\"Locator.content_frame\n\n        Returns a `FrameLocator` object pointing to the same `iframe` as this locator.\n\n        Useful when you have a `Locator` object obtained somewhere, and later on would like to interact with the content\n        inside the frame.\n\n        For a reverse operation, use `frame_locator.owner()`.\n\n        **Usage**\n\n        ```py\n        locator = page.locator(\\\"iframe[name=\\\\\\\"embedded\\\\\\\"]\\\")\n        # ...\n        frame_locator = locator.content_frame\n        frame_locator.get_by_role(\\\"button\\\").click()\n        ```\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n        return mapping.from_impl(self._impl_obj.content_frame)\n\n    @property\n    def description(self) -> typing.Optional[str]:\n        \"\"\"Locator.description\n\n        Returns locator description previously set with `locator.describe()`. Returns `null` if no custom\n        description has been set.\n\n        **Usage**\n\n        ```py\n        button = page.get_by_role(\\\"button\\\").describe(\\\"Subscribe button\\\")\n        print(button.description())  # \\\"Subscribe button\\\"\n\n        input = page.get_by_role(\\\"textbox\\\")\n        print(input.description())  # None\n        ```\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.description)\n\n    def bounding_box(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[FloatRect]:\n        \"\"\"Locator.bounding_box\n\n        This method returns the bounding box of the element matching the locator, or `null` if the element is not visible.\n        The bounding box is calculated relative to the main frame viewport - which is usually the same as the browser\n        window.\n\n        **Details**\n\n        Scrolling affects the returned bounding box, similarly to\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n        That means `x` and/or `y` may be negative.\n\n        Elements from child frames return the bounding box relative to the main frame, unlike the\n        [Element.getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).\n\n        Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the\n        following snippet should click the center of the element.\n\n        **Usage**\n\n        ```py\n        box = page.get_by_role(\\\"button\\\").bounding_box()\n        page.mouse.click(box[\\\"x\\\"] + box[\\\"width\\\"] / 2, box[\\\"y\\\"] + box[\\\"height\\\"] / 2)\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[{x: float, y: float, width: float, height: float}, None]\n        \"\"\"\n\n        return mapping.from_impl_nullable(\n            self._sync(self._impl_obj.bounding_box(timeout=timeout))\n        )\n\n    def check(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.check\n\n        Ensure that checkbox or radio element is checked.\n\n        **Details**\n\n        Performs the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           checked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"checkbox\\\").check()\n        ```\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.check(\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def click(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        click_count: typing.Optional[int] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.click\n\n        Click an element.\n\n        **Details**\n\n        This method clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element, or the specified `position`.\n        1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **Usage**\n\n        Click a button:\n\n        ```py\n        page.get_by_role(\\\"button\\\").click()\n        ```\n\n        Shift-right-click at a specific position on a canvas:\n\n        ```py\n        page.locator(\\\"canvas\\\").click(\n            button=\\\"right\\\", modifiers=[\\\"Shift\\\"], position={\\\"x\\\": 23, \\\"y\\\": 32}\n        )\n        ```\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        click_count : Union[int, None]\n            defaults to 1. See [UIEvent.detail].\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.click(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    clickCount=click_count,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def dblclick(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        delay: typing.Optional[float] = None,\n        button: typing.Optional[Literal[\"left\", \"middle\", \"right\"]] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.dblclick\n\n        Double-click an element.\n\n        **Details**\n\n        This method double clicks the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to double click in the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `element.dblclick()` dispatches two `click` events and a single `dblclick` event.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        delay : Union[float, None]\n            Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.\n        button : Union[\"left\", \"middle\", \"right\", None]\n            Defaults to `left`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor\n            position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dblclick(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    delay=delay,\n                    button=button,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                    steps=steps,\n                )\n            )\n        )\n\n    def dispatch_event(\n        self,\n        type: str,\n        event_init: typing.Optional[typing.Dict] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Locator.dispatch_event\n\n        Programmatically dispatch an event on the matching element.\n\n        **Usage**\n\n        ```py\n        locator.dispatch_event(\\\"click\\\")\n        ```\n\n        **Details**\n\n        The snippet above dispatches the `click` event on the element. Regardless of the visibility state of the element,\n        `click` is dispatched. This is equivalent to calling\n        [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).\n\n        Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit`\n        properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.\n\n        Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:\n        - [DeviceMotionEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent)\n        - [DeviceOrientationEvent](https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent)\n        - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)\n        - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)\n        - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)\n        - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)\n        - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)\n        - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)\n        - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)\n        - [WheelEvent](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent)\n\n        You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:\n\n        ```py\n        data_transfer = page.evaluate_handle(\\\"new DataTransfer()\\\")\n        locator.dispatch_event(\\\"#source\\\", \\\"dragstart\\\", {\\\"dataTransfer\\\": data_transfer})\n        ```\n\n        Parameters\n        ----------\n        type : str\n            DOM event type: `\"click\"`, `\"dragstart\"`, etc.\n        event_init : Union[Dict, None]\n            Optional event-specific initialization properties.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.dispatch_event(\n                    type=type, eventInit=mapping.to_impl(event_init), timeout=timeout\n                )\n            )\n        )\n\n    def evaluate(\n        self,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> typing.Any:\n        \"\"\"Locator.evaluate\n\n        Execute JavaScript code in the page, taking the matching element as an argument.\n\n        **Details**\n\n        Returns the return value of `expression`, called with the matching element as a first argument, and `arg` as a\n        second argument.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        **Usage**\n\n        Passing argument to `expression`:\n\n        ```py\n        result = page.get_by_testid(\\\"myId\\\").evaluate(\\\"(element, [x, y]) => element.textContent + ' ' + x * y\\\", [7, 8])\n        print(result) # prints \\\"myId text 56\\\"\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,\n            evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate(\n                    expression=expression, arg=mapping.to_impl(arg), timeout=timeout\n                )\n            )\n        )\n\n    def evaluate_all(\n        self, expression: str, arg: typing.Optional[typing.Any] = None\n    ) -> typing.Any:\n        \"\"\"Locator.evaluate_all\n\n        Execute JavaScript code in the page, taking all matching elements as an argument.\n\n        **Details**\n\n        Returns the return value of `expression`, called with an array of all matching elements as a first argument, and\n        `arg` as a second argument.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        **Usage**\n\n        ```py\n        locator = page.locator(\\\"div\\\")\n        more_than_ten = locator.evaluate_all(\\\"(divs, min) => divs.length > min\\\", 10)\n        ```\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.evaluate_all(\n                    expression=expression, arg=mapping.to_impl(arg)\n                )\n            )\n        )\n\n    def evaluate_handle(\n        self,\n        expression: str,\n        arg: typing.Optional[typing.Any] = None,\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> \"JSHandle\":\n        \"\"\"Locator.evaluate_handle\n\n        Execute JavaScript code in the page, taking the matching element as an argument, and return a `JSHandle` with the\n        result.\n\n        **Details**\n\n        Returns the return value of `expression` as a`JSHandle`, called with the matching element as a first argument, and\n        `arg` as a second argument.\n\n        The only difference between `locator.evaluate()` and `locator.evaluate_handle()` is that\n        `locator.evaluate_handle()` returns `JSHandle`.\n\n        If `expression` returns a [Promise], this method will wait for the promise to resolve and return its value.\n\n        If `expression` throws or rejects, this method throws.\n\n        See `page.evaluate_handle()` for more details.\n\n        Parameters\n        ----------\n        expression : str\n            JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the\n            function is automatically invoked.\n        arg : Union[Any, None]\n            Optional argument to pass to `expression`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the locator before evaluating. Note that after locator is resolved,\n            evaluation itself is not limited by the timeout. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n\n        Returns\n        -------\n        JSHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.evaluate_handle(\n                    expression=expression, arg=mapping.to_impl(arg), timeout=timeout\n                )\n            )\n        )\n\n    def fill(\n        self,\n        value: str,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.fill\n\n        Set a value to the input field.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"textbox\\\").fill(\\\"example value\\\")\n        ```\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, fills it and triggers an\n        `input` event after filling. Note that you can pass an empty string to clear the input field.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled\n        instead.\n\n        To send fine-grained keyboard events, use `locator.press_sequentially()`.\n\n        Parameters\n        ----------\n        value : str\n            Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.fill(\n                    value=value, timeout=timeout, noWaitAfter=no_wait_after, force=force\n                )\n            )\n        )\n\n    def clear(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.clear\n\n        Clear the input field.\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, focuses the element, clears it and triggers an\n        `input` event after clearing.\n\n        If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an\n        error. However, if the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be cleared\n        instead.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"textbox\\\").clear()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.clear(\n                    timeout=timeout, noWaitAfter=no_wait_after, force=force\n                )\n            )\n        )\n\n    def locator(\n        self,\n        selector_or_locator: typing.Union[str, \"Locator\"],\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.locator\n\n        The method finds an element matching the specified selector in the locator's subtree. It also accepts filter\n        options, similar to `locator.filter()` method.\n\n        [Learn more about locators](https://playwright.dev/python/docs/locators).\n\n        Parameters\n        ----------\n        selector_or_locator : Union[Locator, str]\n            A selector or locator to use when resolving DOM element.\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.locator(\n                selectorOrLocator=selector_or_locator,\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n            )\n        )\n\n    def get_by_alt_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_alt_text\n\n        Allows locating elements by their alt text.\n\n        **Usage**\n\n        For example, this method will find the image by alt text \\\"Playwright logo\\\":\n\n        ```html\n        <img alt='Playwright logo'>\n        ```\n\n        ```py\n        page.get_by_alt_text(\\\"Playwright logo\\\").click()\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_alt_text(text=text, exact=exact))\n\n    def get_by_label(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_label\n\n        Allows locating input elements by the text of the associated `<label>` or `aria-labelledby` element, or by the\n        `aria-label` attribute.\n\n        **Usage**\n\n        For example, this method will find inputs by label \\\"Username\\\" and \\\"Password\\\" in the following DOM:\n\n        ```html\n        <input aria-label=\\\"Username\\\">\n        <label for=\\\"password-input\\\">Password:</label>\n        <input id=\\\"password-input\\\">\n        ```\n\n        ```py\n        page.get_by_label(\\\"Username\\\").fill(\\\"john\\\")\n        page.get_by_label(\\\"Password\\\").fill(\\\"secret\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_label(text=text, exact=exact))\n\n    def get_by_placeholder(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_placeholder\n\n        Allows locating input elements by the placeholder text.\n\n        **Usage**\n\n        For example, consider the following DOM structure.\n\n        ```html\n        <input type=\\\"email\\\" placeholder=\\\"name@example.com\\\" />\n        ```\n\n        You can fill the input after locating it by the placeholder text:\n\n        ```py\n        page.get_by_placeholder(\\\"name@example.com\\\").fill(\\\"playwright@microsoft.com\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_placeholder(text=text, exact=exact)\n        )\n\n    def get_by_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        checked: typing.Optional[bool] = None,\n        disabled: typing.Optional[bool] = None,\n        expanded: typing.Optional[bool] = None,\n        include_hidden: typing.Optional[bool] = None,\n        level: typing.Optional[int] = None,\n        name: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        pressed: typing.Optional[bool] = None,\n        selected: typing.Optional[bool] = None,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_role\n\n        Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),\n        [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <h3>Sign up</h3>\n        <label>\n          <input type=\\\"checkbox\\\" /> Subscribe\n        </label>\n        <br/>\n        <button>Submit</button>\n        ```\n\n        You can locate each element by it's implicit role:\n\n        ```py\n        expect(page.get_by_role(\\\"heading\\\", name=\\\"Sign up\\\")).to_be_visible()\n\n        page.get_by_role(\\\"checkbox\\\", name=\\\"Subscribe\\\").check()\n\n        page.get_by_role(\\\"button\\\", name=re.compile(\\\"submit\\\", re.IGNORECASE)).click()\n        ```\n\n        **Details**\n\n        Role selector **does not replace** accessibility audits and conformance tests, but rather gives early feedback\n        about the ARIA guidelines.\n\n        Many html elements have an implicitly [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings)\n        that is recognized by the role selector. You can find all the\n        [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not recommend**\n        duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        checked : Union[bool, None]\n            An attribute that is usually set by `aria-checked` or native `<input type=checkbox>` controls.\n\n            Learn more about [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).\n        disabled : Union[bool, None]\n            An attribute that is usually set by `aria-disabled` or `disabled`.\n\n            **NOTE** Unlike most other attributes, `disabled` is inherited through the DOM hierarchy. Learn more about\n            [`aria-disabled`](https://www.w3.org/TR/wai-aria-1.2/#aria-disabled).\n\n        expanded : Union[bool, None]\n            An attribute that is usually set by `aria-expanded`.\n\n            Learn more about [`aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).\n        include_hidden : Union[bool, None]\n            Option that controls whether hidden elements are matched. By default, only non-hidden elements, as\n            [defined by ARIA](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), are matched by role selector.\n\n            Learn more about [`aria-hidden`](https://www.w3.org/TR/wai-aria-1.2/#aria-hidden).\n        level : Union[int, None]\n            A number attribute that is usually present for roles `heading`, `listitem`, `row`, `treeitem`, with default values\n            for `<h1>-<h6>` elements.\n\n            Learn more about [`aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).\n        name : Union[Pattern[str], str, None]\n            Option to match the [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). By default, matching is\n            case-insensitive and searches for a substring, use `exact` to control this behavior.\n\n            Learn more about [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n        pressed : Union[bool, None]\n            An attribute that is usually set by `aria-pressed`.\n\n            Learn more about [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).\n        selected : Union[bool, None]\n            An attribute that is usually set by `aria-selected`.\n\n            Learn more about [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).\n        exact : Union[bool, None]\n            Whether `name` is matched exactly: case-sensitive and whole-string. Defaults to false. Ignored when `name` is a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.get_by_role(\n                role=role,\n                checked=checked,\n                disabled=disabled,\n                expanded=expanded,\n                includeHidden=include_hidden,\n                level=level,\n                name=name,\n                pressed=pressed,\n                selected=selected,\n                exact=exact,\n            )\n        )\n\n    def get_by_test_id(\n        self, test_id: typing.Union[str, typing.Pattern[str]]\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_test_id\n\n        Locate element by the test id.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <button data-testid=\\\"directions\\\">Itinéraire</button>\n        ```\n\n        You can locate the element by it's test id:\n\n        ```py\n        page.get_by_test_id(\\\"directions\\\").click()\n        ```\n\n        **Details**\n\n        By default, the `data-testid` attribute is used as a test id. Use `selectors.set_test_id_attribute()` to\n        configure a different test id attribute if necessary.\n\n        Parameters\n        ----------\n        test_id : Union[Pattern[str], str]\n            Id to locate the element by.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_test_id(testId=test_id))\n\n    def get_by_text(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_text\n\n        Allows locating elements that contain given text.\n\n        See also `locator.filter()` that allows to match by another criteria, like an accessible role, and then\n        filter by the text content.\n\n        **Usage**\n\n        Consider the following DOM structure:\n\n        ```html\n        <div>Hello <span>world</span></div>\n        <div>Hello</div>\n        ```\n\n        You can locate by text substring, exact string, or a regular expression:\n\n        ```py\n        # Matches <span>\n        page.get_by_text(\\\"world\\\")\n\n        # Matches first <div>\n        page.get_by_text(\\\"Hello world\\\")\n\n        # Matches second <div>\n        page.get_by_text(\\\"Hello\\\", exact=True)\n\n        # Matches both <div>s\n        page.get_by_text(re.compile(\\\"Hello\\\"))\n\n        # Matches second <div>\n        page.get_by_text(re.compile(\\\"^hello$\\\", re.IGNORECASE))\n        ```\n\n        **Details**\n\n        Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into\n        one, turns line breaks into spaces and ignores leading and trailing whitespace.\n\n        Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For\n        example, locating by text `\\\"Log in\\\"` matches `<input type=button value=\\\"Log in\\\">`.\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_text(text=text, exact=exact))\n\n    def get_by_title(\n        self,\n        text: typing.Union[str, typing.Pattern[str]],\n        *,\n        exact: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.get_by_title\n\n        Allows locating elements by their title attribute.\n\n        **Usage**\n\n        Consider the following DOM structure.\n\n        ```html\n        <span title='Issues count'>25 issues</span>\n        ```\n\n        You can check the issues count after locating it by the title text:\n\n        ```py\n        expect(page.get_by_title(\\\"Issues count\\\")).to_have_text(\\\"25 issues\\\")\n        ```\n\n        Parameters\n        ----------\n        text : Union[Pattern[str], str]\n            Text to locate the element for.\n        exact : Union[bool, None]\n            Whether to find an exact match: case-sensitive and whole-string. Default to false. Ignored when locating by a\n            regular expression. Note that exact match still trims whitespace.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.get_by_title(text=text, exact=exact))\n\n    def frame_locator(self, selector: str) -> \"FrameLocator\":\n        \"\"\"Locator.frame_locator\n\n        When working with iframes, you can create a frame locator that will enter the iframe and allow locating elements in\n        that iframe:\n\n        **Usage**\n\n        ```py\n        locator = page.frame_locator(\\\"iframe\\\").get_by_text(\\\"Submit\\\")\n        locator.click()\n        ```\n\n        Parameters\n        ----------\n        selector : str\n            A selector to use when resolving DOM element.\n\n        Returns\n        -------\n        FrameLocator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.frame_locator(selector=selector))\n\n    def element_handle(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> \"ElementHandle\":\n        \"\"\"Locator.element_handle\n\n        Resolves given locator to the first matching DOM element. If there are no matching elements, waits for one. If\n        multiple elements match the locator, throws.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        ElementHandle\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(self._impl_obj.element_handle(timeout=timeout))\n        )\n\n    def element_handles(self) -> typing.List[\"ElementHandle\"]:\n        \"\"\"Locator.element_handles\n\n        Resolves given locator to all matching DOM elements. If there are no matching elements, returns an empty list.\n\n        Returns\n        -------\n        List[ElementHandle]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.element_handles()))\n\n    def nth(self, index: int) -> \"Locator\":\n        \"\"\"Locator.nth\n\n        Returns locator to the n-th matching element. It's zero based, `nth(0)` selects the first element.\n\n        **Usage**\n\n        ```py\n        banana = page.get_by_role(\\\"listitem\\\").nth(2)\n        ```\n\n        Parameters\n        ----------\n        index : int\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.nth(index=index))\n\n    def describe(self, description: str) -> \"Locator\":\n        \"\"\"Locator.describe\n\n        Describes the locator, description is used in the trace viewer and reports. Returns the locator pointing to the\n        same element.\n\n        **Usage**\n\n        ```py\n        button = page.get_by_test_id(\\\"btn-sub\\\").describe(\\\"Subscribe button\\\")\n        button.click()\n        ```\n\n        Parameters\n        ----------\n        description : str\n            Locator description.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.describe(description=description))\n\n    def filter(\n        self,\n        *,\n        has_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has_not_text: typing.Optional[typing.Union[typing.Pattern[str], str]] = None,\n        has: typing.Optional[\"Locator\"] = None,\n        has_not: typing.Optional[\"Locator\"] = None,\n        visible: typing.Optional[bool] = None,\n    ) -> \"Locator\":\n        \"\"\"Locator.filter\n\n        This method narrows existing locator according to the options, for example filters by text. It can be chained to\n        filter multiple times.\n\n        **Usage**\n\n        ```py\n        row_locator = page.locator(\\\"tr\\\")\n        # ...\n        row_locator.filter(has_text=\\\"text in column 1\\\").filter(\n            has=page.get_by_role(\\\"button\\\", name=\\\"column 2 button\\\")\n        ).screenshot()\n        ```\n\n        Parameters\n        ----------\n        has_text : Union[Pattern[str], str, None]\n            Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. When\n            passed a [string], matching is case-insensitive and searches for a substring. For example, `\"Playwright\"` matches\n            `<article><div>Playwright</div></article>`.\n        has_not_text : Union[Pattern[str], str, None]\n            Matches elements that do not contain specified text somewhere inside, possibly in a child or a descendant element.\n            When passed a [string], matching is case-insensitive and searches for a substring.\n        has : Union[Locator, None]\n            Narrows down the results of the method to those which contain elements matching this relative locator. For example,\n            `article` that has `text=Playwright` matches `<article><div>Playwright</div></article>`.\n\n            Inner locator **must be relative** to the outer locator and is queried starting with the outer locator match, not\n            the document root. For example, you can find `content` that has `div` in\n            `<article><content><div>Playwright</div></content></article>`. However, looking for `content` that has `article\n            div` will fail, because the inner locator must be relative and should not use any elements outside the `content`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        has_not : Union[Locator, None]\n            Matches elements that do not contain an element that matches an inner locator. Inner locator is queried against the\n            outer one. For example, `article` that does not have `div` matches `<article><span>Playwright</span></article>`.\n\n            Note that outer and inner locators must belong to the same frame. Inner locator must not contain `FrameLocator`s.\n        visible : Union[bool, None]\n            Only matches visible or invisible elements.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(\n            self._impl_obj.filter(\n                hasText=has_text,\n                hasNotText=has_not_text,\n                has=has._impl_obj if has else None,\n                hasNot=has_not._impl_obj if has_not else None,\n                visible=visible,\n            )\n        )\n\n    def or_(self, locator: \"Locator\") -> \"Locator\":\n        \"\"\"Locator.or_\n\n        Creates a locator matching all elements that match one or both of the two locators.\n\n        Note that when both locators match something, the resulting locator will have multiple matches, potentially causing\n        a [locator strictness](https://playwright.dev/python/docs/locators#strictness) violation.\n\n        **Usage**\n\n        Consider a scenario where you'd like to click on a \\\"New email\\\" button, but sometimes a security settings dialog\n        shows up instead. In this case, you can wait for either a \\\"New email\\\" button, or a dialog and act accordingly.\n\n        **NOTE** If both \\\"New email\\\" button and security dialog appear on screen, the \\\"or\\\" locator will match both of them,\n        possibly throwing the [\\\"strict mode violation\\\" error](https://playwright.dev/python/docs/locators#strictness). In this case, you can use\n        `locator.first()` to only match one of them.\n\n        ```py\n        new_email = page.get_by_role(\\\"button\\\", name=\\\"New\\\")\n        dialog = page.get_by_text(\\\"Confirm security settings\\\")\n        expect(new_email.or_(dialog).first).to_be_visible()\n        if (dialog.is_visible()):\n          page.get_by_role(\\\"button\\\", name=\\\"Dismiss\\\").click()\n        new_email.click()\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Alternative locator to match.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.or_(locator=locator._impl_obj))\n\n    def and_(self, locator: \"Locator\") -> \"Locator\":\n        \"\"\"Locator.and_\n\n        Creates a locator that matches both this locator and the argument locator.\n\n        **Usage**\n\n        The following example finds a button with a specific title.\n\n        ```py\n        button = page.get_by_role(\\\"button\\\").and_(page.get_by_title(\\\"Subscribe\\\"))\n        ```\n\n        Parameters\n        ----------\n        locator : Locator\n            Additional locator to match.\n\n        Returns\n        -------\n        Locator\n        \"\"\"\n\n        return mapping.from_impl(self._impl_obj.and_(locator=locator._impl_obj))\n\n    def focus(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"Locator.focus\n\n        Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the matching element.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.focus(timeout=timeout))\n        )\n\n    def blur(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"Locator.blur\n\n        Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) on the element.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.blur(timeout=timeout)))\n\n    def all(self) -> typing.List[\"Locator\"]:\n        \"\"\"Locator.all\n\n        When the locator points to a list of elements, this returns an array of locators, pointing to their respective\n        elements.\n\n        **NOTE** `locator.all()` does not wait for elements to match the locator, and instead immediately returns\n        whatever is present in the page.\n\n        When the list of elements changes dynamically, `locator.all()` will produce unpredictable and flaky\n        results.\n\n        When the list of elements is stable, but loaded dynamically, wait for the full list to finish loading before\n        calling `locator.all()`.\n\n        **Usage**\n\n        ```py\n        for li in page.get_by_role('listitem').all():\n          li.click();\n        ```\n\n        Returns\n        -------\n        List[Locator]\n        \"\"\"\n\n        return mapping.from_impl_list(self._sync(self._impl_obj.all()))\n\n    def count(self) -> int:\n        \"\"\"Locator.count\n\n        Returns the number of elements matching the locator.\n\n        **NOTE** If you need to assert the number of elements on the page, prefer `locator_assertions.to_have_count()`\n        to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        count = page.get_by_role(\\\"listitem\\\").count()\n        ```\n\n        Returns\n        -------\n        int\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.count()))\n\n    def drag_to(\n        self,\n        target: \"Locator\",\n        *,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        trial: typing.Optional[bool] = None,\n        source_position: typing.Optional[Position] = None,\n        target_position: typing.Optional[Position] = None,\n        steps: typing.Optional[int] = None,\n    ) -> None:\n        \"\"\"Locator.drag_to\n\n        Drag the source element towards the target element and drop it.\n\n        **Details**\n\n        This method drags the locator to another target locator or target position. It will first move to the source\n        element, perform a `mousedown`, then move to the target element or position and perform a `mouseup`.\n\n        **Usage**\n\n        ```py\n        source = page.locator(\\\"#source\\\")\n        target = page.locator(\\\"#target\\\")\n\n        source.drag_to(target)\n        # or specify exact positions relative to the top-left corners of the elements:\n        source.drag_to(\n          target,\n          source_position={\\\"x\\\": 34, \\\"y\\\": 7},\n          target_position={\\\"x\\\": 10, \\\"y\\\": 20}\n        )\n        ```\n\n        Parameters\n        ----------\n        target : Locator\n            Locator of the element to drag to.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        source_position : Union[{x: float, y: float}, None]\n            Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        target_position : Union[{x: float, y: float}, None]\n            Drops on the target element at this point relative to the top-left corner of the element's padding box. If not\n            specified, some visible point of the element is used.\n        steps : Union[int, None]\n            Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between the `mousedown` and `mouseup`\n            of the drag. When set to 1, emits a single `mousemove` event at the destination location.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.drag_to(\n                    target=target._impl_obj,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    timeout=timeout,\n                    trial=trial,\n                    sourcePosition=source_position,\n                    targetPosition=target_position,\n                    steps=steps,\n                )\n            )\n        )\n\n    def get_attribute(\n        self, name: str, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[str]:\n        \"\"\"Locator.get_attribute\n\n        Returns the matching element's attribute value.\n\n        **NOTE** If you need to assert an element's attribute, prefer `locator_assertions.to_have_attribute()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name to get the value for.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.get_attribute(name=name, timeout=timeout))\n        )\n\n    def hover(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.hover\n\n        Hover over the matching element.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"link\\\").hover()\n        ```\n\n        **Details**\n\n        This method hovers over the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to hover over the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.hover(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                    trial=trial,\n                )\n            )\n        )\n\n    def inner_html(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.inner_html\n\n        Returns the [`element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML).\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.inner_html(timeout=timeout))\n        )\n\n    def inner_text(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.inner_text\n\n        Returns the [`element.innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText).\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` with\n        `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.inner_text(timeout=timeout))\n        )\n\n    def input_value(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.input_value\n\n        Returns the value for the matching `<input>` or `<textarea>` or `<select>` element.\n\n        **NOTE** If you need to assert input value, prefer `locator_assertions.to_have_value()` to avoid flakiness.\n        See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        value = page.get_by_role(\\\"textbox\\\").input_value()\n        ```\n\n        **Details**\n\n        Throws elements that are not an input, textarea or a select. However, if the element is inside the `<label>`\n        element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), returns the value of the\n        control.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.input_value(timeout=timeout))\n        )\n\n    def is_checked(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_checked\n\n        Returns whether the element is checked. Throws if the element is not a checkbox or radio input.\n\n        **NOTE** If you need to assert that checkbox is checked, prefer `locator_assertions.to_be_checked()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        checked = page.get_by_role(\\\"checkbox\\\").is_checked()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_checked(timeout=timeout))\n        )\n\n    def is_disabled(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_disabled\n\n        Returns whether the element is disabled, the opposite of [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        **NOTE** If you need to assert that an element is disabled, prefer `locator_assertions.to_be_disabled()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        disabled = page.get_by_role(\\\"button\\\").is_disabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_disabled(timeout=timeout))\n        )\n\n    def is_editable(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_editable\n\n        Returns whether the element is [editable](https://playwright.dev/python/docs/actionability#editable). If the target element is not an `<input>`,\n        `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method\n        throws an error.\n\n        **NOTE** If you need to assert that an element is editable, prefer `locator_assertions.to_be_editable()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        editable = page.get_by_role(\\\"textbox\\\").is_editable()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_editable(timeout=timeout))\n        )\n\n    def is_enabled(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_enabled\n\n        Returns whether the element is [enabled](https://playwright.dev/python/docs/actionability#enabled).\n\n        **NOTE** If you need to assert that an element is enabled, prefer `locator_assertions.to_be_enabled()` to\n        avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        enabled = page.get_by_role(\\\"button\\\").is_enabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_enabled(timeout=timeout))\n        )\n\n    def is_hidden(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_hidden\n\n        Returns whether the element is hidden, the opposite of [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        **NOTE** If you need to assert that element is hidden, prefer `locator_assertions.to_be_hidden()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        hidden = page.get_by_role(\\\"button\\\").is_hidden()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `locator.is_hidden()` does not wait for the element to become hidden and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_hidden(timeout=timeout))\n        )\n\n    def is_visible(self, *, timeout: typing.Optional[float] = None) -> bool:\n        \"\"\"Locator.is_visible\n\n        Returns whether the element is [visible](https://playwright.dev/python/docs/actionability#visible).\n\n        **NOTE** If you need to assert that element is visible, prefer `locator_assertions.to_be_visible()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        visible = page.get_by_role(\\\"button\\\").is_visible()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Deprecated: This option is ignored. `locator.is_visible()` does not wait for the element to become visible and returns immediately.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.is_visible(timeout=timeout))\n        )\n\n    def press(\n        self,\n        key: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.press\n\n        Focuses the matching element and presses a combination of the keys.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"textbox\\\").press(\\\"Backspace\\\")\n        ```\n\n        **Details**\n\n        Focuses the element, and then uses `keyboard.down()` and `keyboard.up()`.\n\n        `key` can specify the intended\n        [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character\n        to generate the text for. A superset of the `key` values can be found\n        [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:\n\n        `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,\n        `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,\n        etc.\n\n        Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`,\n        `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.\n\n        Holding down `Shift` will type the text that corresponds to the `key` in the upper case.\n\n        If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective\n        texts.\n\n        Shortcuts such as `key: \\\"Control+o\\\"`, `key: \\\"Control++` or `key: \\\"Control+Shift+T\\\"` are supported as well. When\n        specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.\n\n        Parameters\n        ----------\n        key : str\n            Name of the key to press or a character to generate, such as `ArrowLeft` or `a`.\n        delay : Union[float, None]\n            Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You\n            can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as\n            navigating to inaccessible pages. Defaults to `false`.\n            Deprecated: This option will default to `true` in the future.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.press(\n                    key=key, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n                )\n            )\n        )\n\n    def screenshot(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        type: typing.Optional[Literal[\"jpeg\", \"png\"]] = None,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        quality: typing.Optional[int] = None,\n        omit_background: typing.Optional[bool] = None,\n        animations: typing.Optional[Literal[\"allow\", \"disabled\"]] = None,\n        caret: typing.Optional[Literal[\"hide\", \"initial\"]] = None,\n        scale: typing.Optional[Literal[\"css\", \"device\"]] = None,\n        mask: typing.Optional[typing.Sequence[\"Locator\"]] = None,\n        mask_color: typing.Optional[str] = None,\n        style: typing.Optional[str] = None,\n    ) -> bytes:\n        \"\"\"Locator.screenshot\n\n        Take a screenshot of the element matching the locator.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"link\\\").screenshot()\n        ```\n\n        Disable animations and save screenshot to a file:\n\n        ```py\n        page.get_by_role(\\\"link\\\").screenshot(animations=\\\"disabled\\\", path=\\\"link.png\\\")\n        ```\n\n        **Details**\n\n        This method captures a screenshot of the page, clipped to the size and position of a particular element matching\n        the locator. If the element is covered by other elements, it will not be actually visible on the screenshot. If the\n        element is a scrollable container, only the currently scrolled content will be visible on the screenshot.\n\n        This method waits for the [actionability](https://playwright.dev/python/docs/actionability) checks, then scrolls element into view before taking\n        a screenshot. If the element is detached from DOM, the method throws an error.\n\n        Returns the buffer with the captured screenshot.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        type : Union[\"jpeg\", \"png\", None]\n            Specify screenshot type, defaults to `png`.\n        path : Union[pathlib.Path, str, None]\n            The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a\n            relative path, then it is resolved relative to the current working directory. If no path is provided, the image\n            won't be saved to the disk.\n        quality : Union[int, None]\n            The quality of the image, between 0-100. Not applicable to `png` images.\n        omit_background : Union[bool, None]\n            Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.\n            Defaults to `false`.\n        animations : Union[\"allow\", \"disabled\", None]\n            When set to `\"disabled\"`, stops CSS animations, CSS transitions and Web Animations. Animations get different\n            treatment depending on their duration:\n            - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.\n            - infinite animations are canceled to initial state, and then played over after the screenshot.\n\n            Defaults to `\"allow\"` that leaves animations untouched.\n        caret : Union[\"hide\", \"initial\", None]\n            When set to `\"hide\"`, screenshot will hide text caret. When set to `\"initial\"`, text caret behavior will not be\n            changed.  Defaults to `\"hide\"`.\n        scale : Union[\"css\", \"device\", None]\n            When set to `\"css\"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this\n            will keep screenshots small. Using `\"device\"` option will produce a single pixel per each device pixel, so\n            screenshots of high-dpi devices will be twice as large or even larger.\n\n            Defaults to `\"device\"`.\n        mask : Union[Sequence[Locator], None]\n            Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink\n            box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box. The mask is also applied to\n            invisible elements, see [Matching only visible elements](../locators.md#matching-only-visible-elements) to disable\n            that.\n        mask_color : Union[str, None]\n            Specify the color of the overlay box for masked elements, in\n            [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.\n        style : Union[str, None]\n            Text of the stylesheet to apply while making the screenshot. This is where you can hide dynamic elements, make\n            elements invisible or change their properties to help you creating repeatable screenshots. This stylesheet pierces\n            the Shadow DOM and applies to the inner frames.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.screenshot(\n                    timeout=timeout,\n                    type=type,\n                    path=path,\n                    quality=quality,\n                    omitBackground=omit_background,\n                    animations=animations,\n                    caret=caret,\n                    scale=scale,\n                    mask=mapping.to_impl(mask),\n                    maskColor=mask_color,\n                    style=style,\n                )\n            )\n        )\n\n    def aria_snapshot(self, *, timeout: typing.Optional[float] = None) -> str:\n        \"\"\"Locator.aria_snapshot\n\n        Captures the aria snapshot of the given element. Read more about [aria snapshots](https://playwright.dev/python/docs/aria-snapshots) and\n        `locator_assertions.to_match_aria_snapshot()` for the corresponding assertion.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"link\\\").aria_snapshot()\n        ```\n\n        **Details**\n\n        This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of\n        the element and its children. The snapshot can be used to assert the state of the element in the test, or to\n        compare it to state in the future.\n\n        The ARIA snapshot is represented using [YAML](https://yaml.org/spec/1.2.2/) markup language:\n        - The keys of the objects are the roles and optional accessible names of the elements.\n        - The values are either text content or an array of child elements.\n        - Generic static text can be represented with the `text` key.\n\n        Below is the HTML markup and the respective ARIA snapshot:\n\n        ```html\n        <ul aria-label=\\\"Links\\\">\n          <li><a href=\\\"/\\\">Home</a></li>\n          <li><a href=\\\"/about\\\">About</a></li>\n        <ul>\n        ```\n\n        ```yml\n        - list \\\"Links\\\":\n          - listitem:\n            - link \\\"Home\\\"\n          - listitem:\n            - link \\\"About\\\"\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.aria_snapshot(timeout=timeout))\n        )\n\n    def scroll_into_view_if_needed(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"Locator.scroll_into_view_if_needed\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then tries to scroll element into view, unless\n        it is completely visible as defined by\n        [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)'s `ratio`.\n\n        See [scrolling](https://playwright.dev/python/docs/input#scrolling) for alternative ways to scroll.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.scroll_into_view_if_needed(timeout=timeout))\n        )\n\n    def select_option(\n        self,\n        value: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        *,\n        index: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,\n        label: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,\n        element: typing.Optional[\n            typing.Union[\"ElementHandle\", typing.Sequence[\"ElementHandle\"]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        force: typing.Optional[bool] = None,\n    ) -> typing.List[str]:\n        \"\"\"Locator.select_option\n\n        Selects option or options in `<select>`.\n\n        **Details**\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, waits until all specified options are present in\n        the `<select>` element and selects these options.\n\n        If the target element is not a `<select>` element, this method throws an error. However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used\n        instead.\n\n        Returns the array of option values that have been successfully selected.\n\n        Triggers a `change` and `input` event once all the provided options have been selected.\n\n        **Usage**\n\n        ```html\n        <select multiple>\n          <option value=\\\"red\\\">Red</option>\n          <option value=\\\"green\\\">Green</option>\n          <option value=\\\"blue\\\">Blue</option>\n        </select>\n        ```\n\n        ```py\n        # single selection matching the value or label\n        element.select_option(\\\"blue\\\")\n        # single selection matching the label\n        element.select_option(label=\\\"blue\\\")\n        # multiple selection for blue, red and second option\n        element.select_option(value=[\\\"red\\\", \\\"green\\\", \\\"blue\\\"])\n        ```\n\n        Parameters\n        ----------\n        value : Union[Sequence[str], str, None]\n            Options to select by value. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        index : Union[Sequence[int], int, None]\n            Options to select by index. Optional.\n        label : Union[Sequence[str], str, None]\n            Options to select by label. If the `<select>` has the `multiple` attribute, all given options are selected,\n            otherwise only the first option matching one of the passed options is selected. Optional.\n        element : Union[ElementHandle, Sequence[ElementHandle], None]\n            Option elements to select. Optional.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.select_option(\n                    value=mapping.to_impl(value),\n                    index=mapping.to_impl(index),\n                    label=mapping.to_impl(label),\n                    element=mapping.to_impl(element),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                    force=force,\n                )\n            )\n        )\n\n    def select_text(\n        self,\n        *,\n        force: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"Locator.select_text\n\n        This method waits for [actionability](https://playwright.dev/python/docs/actionability) checks, then focuses the element and selects all its\n        text content.\n\n        If the element is inside the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), focuses and selects text in\n        the control instead.\n\n        Parameters\n        ----------\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.select_text(force=force, timeout=timeout))\n        )\n\n    def set_input_files(\n        self,\n        files: typing.Union[\n            str,\n            pathlib.Path,\n            FilePayload,\n            typing.Sequence[typing.Union[str, pathlib.Path]],\n            typing.Sequence[FilePayload],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.set_input_files\n\n        Upload file or multiple files into `<input type=file>`. For inputs with a `[webkitdirectory]` attribute, only a\n        single directory path is supported.\n\n        **Usage**\n\n        ```py\n        # Select one file\n        page.get_by_label(\\\"Upload file\\\").set_input_files('myfile.pdf')\n\n        # Select multiple files\n        page.get_by_label(\\\"Upload files\\\").set_input_files(['file1.txt', 'file2.txt'])\n\n        # Select a directory\n        page.get_by_label(\\\"Upload directory\\\").set_input_files('mydir')\n\n        # Remove all the selected files\n        page.get_by_label(\\\"Upload file\\\").set_input_files([])\n\n        # Upload buffer from memory\n        page.get_by_label(\\\"Upload file\\\").set_input_files(\n            files=[\n                {\\\"name\\\": \\\"test.txt\\\", \\\"mimeType\\\": \\\"text/plain\\\", \\\"buffer\\\": b\\\"this is a test\\\"}\n            ],\n        )\n        ```\n\n        **Details**\n\n        Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then\n        they are resolved relative to the current working directory. For empty array, clears the selected files.\n\n        This method expects `Locator` to point to an\n        [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). However, if the element is inside\n        the `<label>` element that has an associated\n        [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), targets the control instead.\n\n        Parameters\n        ----------\n        files : Union[Sequence[Union[pathlib.Path, str]], Sequence[{name: str, mimeType: str, buffer: bytes}], pathlib.Path, str, {name: str, mimeType: str, buffer: bytes}]\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_input_files(\n                    files=mapping.to_impl(files),\n                    timeout=timeout,\n                    noWaitAfter=no_wait_after,\n                )\n            )\n        )\n\n    def tap(\n        self,\n        *,\n        modifiers: typing.Optional[\n            typing.Sequence[Literal[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]]\n        ] = None,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.tap\n\n        Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually\n        dispatching touch events, see the [emulating legacy touch events](https://playwright.dev/python/docs/touch-events) page.\n\n        **Details**\n\n        This method taps the element by performing the following steps:\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.touchscreen` to tap the center of the element, or the specified `position`.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        **NOTE** `element.tap()` requires that the `hasTouch` option of the browser context be set to true.\n\n        Parameters\n        ----------\n        modifiers : Union[Sequence[Union[\"Alt\", \"Control\", \"ControlOrMeta\", \"Meta\", \"Shift\"]], None]\n            Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores\n            current modifiers back. If not specified, currently pressed modifiers are used. \"ControlOrMeta\" resolves to\n            \"Control\" on Windows and Linux and to \"Meta\" on macOS.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it. Note that keyboard\n            `modifiers` will be pressed regardless of `trial` to allow testing elements which are only visible when those keys\n            are pressed.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.tap(\n                    modifiers=mapping.to_impl(modifiers),\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def text_content(\n        self, *, timeout: typing.Optional[float] = None\n    ) -> typing.Optional[str]:\n        \"\"\"Locator.text_content\n\n        Returns the [`node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent).\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n\n        Returns\n        -------\n        Union[str, None]\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.text_content(timeout=timeout))\n        )\n\n    def type(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.type\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `locator.press()`.\n\n        **Usage**\n\n        Parameters\n        ----------\n        text : str\n            A text to type into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.type(\n                    text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n                )\n            )\n        )\n\n    def press_sequentially(\n        self,\n        text: str,\n        *,\n        delay: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n        no_wait_after: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.press_sequentially\n\n        **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if\n        there is special keyboard handling on the page.\n\n        Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the\n        text.\n\n        To press a special key, like `Control` or `ArrowDown`, use `locator.press()`.\n\n        **Usage**\n\n        ```py\n        locator.press_sequentially(\\\"hello\\\") # types instantly\n        locator.press_sequentially(\\\"world\\\", delay=100) # types slower, like a user\n        ```\n\n        An example of typing into a text field and then submitting the form:\n\n        ```py\n        locator = page.get_by_label(\\\"Password\\\")\n        locator.press_sequentially(\\\"my password\\\")\n        locator.press(\\\"Enter\\\")\n        ```\n\n        Parameters\n        ----------\n        text : str\n            String of characters to sequentially press into a focused element.\n        delay : Union[float, None]\n            Time to wait between key presses in milliseconds. Defaults to 0.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.press_sequentially(\n                    text=text, delay=delay, timeout=timeout, noWaitAfter=no_wait_after\n                )\n            )\n        )\n\n    def uncheck(\n        self,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.uncheck\n\n        Ensure that checkbox or radio element is unchecked.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"checkbox\\\").uncheck()\n        ```\n\n        **Details**\n\n        This method unchecks the element by performing the following steps:\n        1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already\n           unchecked, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the element, unless `force` option is set.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now unchecked. If not, this method throws.\n\n        If the element is detached from the DOM at any moment during the action, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.uncheck(\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def all_inner_texts(self) -> typing.List[str]:\n        \"\"\"Locator.all_inner_texts\n\n        Returns an array of `node.innerText` values for all matching nodes.\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` with\n        `useInnerText` option to avoid flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        texts = page.get_by_role(\\\"link\\\").all_inner_texts()\n        ```\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.all_inner_texts()))\n\n    def all_text_contents(self) -> typing.List[str]:\n        \"\"\"Locator.all_text_contents\n\n        Returns an array of `node.textContent` values for all matching nodes.\n\n        **NOTE** If you need to assert text on the page, prefer `locator_assertions.to_have_text()` to avoid\n        flakiness. See [assertions guide](https://playwright.dev/python/docs/test-assertions) for more details.\n\n        **Usage**\n\n        ```py\n        texts = page.get_by_role(\\\"link\\\").all_text_contents()\n        ```\n\n        Returns\n        -------\n        List[str]\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.all_text_contents()))\n\n    def wait_for(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        state: typing.Optional[\n            Literal[\"attached\", \"detached\", \"hidden\", \"visible\"]\n        ] = None,\n    ) -> None:\n        \"\"\"Locator.wait_for\n\n        Returns when element specified by locator satisfies the `state` option.\n\n        If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to\n        `timeout` milliseconds until the condition is met.\n\n        **Usage**\n\n        ```py\n        order_sent = page.locator(\\\"#order-sent\\\")\n        order_sent.wait_for()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        state : Union[\"attached\", \"detached\", \"hidden\", \"visible\", None]\n            Defaults to `'visible'`. Can be either:\n            - `'attached'` - wait for element to be present in DOM.\n            - `'detached'` - wait for element to not be present in DOM.\n            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element\n              without any content or with `display:none` has an empty bounding box and is not considered visible.\n            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or\n              `visibility:hidden`. This is opposite to the `'visible'` option.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.wait_for(timeout=timeout, state=state))\n        )\n\n    def set_checked(\n        self,\n        checked: bool,\n        *,\n        position: typing.Optional[Position] = None,\n        timeout: typing.Optional[float] = None,\n        force: typing.Optional[bool] = None,\n        no_wait_after: typing.Optional[bool] = None,\n        trial: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"Locator.set_checked\n\n        Set the state of a checkbox or a radio element.\n\n        **Usage**\n\n        ```py\n        page.get_by_role(\\\"checkbox\\\").set_checked(True)\n        ```\n\n        **Details**\n\n        This method checks or unchecks an element by performing the following steps:\n        1. Ensure that matched element is a checkbox or a radio input. If not, this method throws.\n        1. If the element already has the right checked state, this method returns immediately.\n        1. Wait for [actionability](https://playwright.dev/python/docs/actionability) checks on the matched element, unless `force` option is set. If\n           the element is detached during the checks, the whole action is retried.\n        1. Scroll the element into view if needed.\n        1. Use `page.mouse` to click in the center of the element.\n        1. Ensure that the element is now checked or unchecked. If not, this method throws.\n\n        When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`.\n        Passing zero timeout disables this.\n\n        Parameters\n        ----------\n        checked : bool\n            Whether to check or uncheck the checkbox.\n        position : Union[{x: float, y: float}, None]\n            A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of\n            the element.\n        timeout : Union[float, None]\n            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can\n            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.\n        force : Union[bool, None]\n            Whether to bypass the [actionability](../actionability.md) checks. Defaults to `false`.\n        no_wait_after : Union[bool, None]\n            This option has no effect.\n            Deprecated: This option has no effect.\n        trial : Union[bool, None]\n            When set, this method only performs the [actionability](../actionability.md) checks and skips the action. Defaults\n            to `false`. Useful to wait until the element is ready for the action without performing it.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.set_checked(\n                    checked=checked,\n                    position=position,\n                    timeout=timeout,\n                    force=force,\n                    noWaitAfter=no_wait_after,\n                    trial=trial,\n                )\n            )\n        )\n\n    def highlight(self) -> None:\n        \"\"\"Locator.highlight\n\n        Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses\n        `locator.highlight()`.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.highlight()))\n\n\nmapping.register(LocatorImpl, Locator)\n\n\nclass APIResponse(SyncBase):\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"APIResponse.ok\n\n        Contains a boolean stating whether the response was successful (status in the range 200-299) or not.\n\n        Returns\n        -------\n        bool\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.ok)\n\n    @property\n    def url(self) -> str:\n        \"\"\"APIResponse.url\n\n        Contains the URL of the response.\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.url)\n\n    @property\n    def status(self) -> int:\n        \"\"\"APIResponse.status\n\n        Contains the status code of the response (e.g., 200 for a success).\n\n        Returns\n        -------\n        int\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status)\n\n    @property\n    def status_text(self) -> str:\n        \"\"\"APIResponse.status_text\n\n        Contains the status text of the response (e.g. usually an \\\"OK\\\" for a success).\n\n        Returns\n        -------\n        str\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.status_text)\n\n    @property\n    def headers(self) -> typing.Dict[str, str]:\n        \"\"\"APIResponse.headers\n\n        An object with all the response HTTP headers associated with this response.\n\n        Returns\n        -------\n        Dict[str, str]\n        \"\"\"\n        return mapping.from_maybe_impl(self._impl_obj.headers)\n\n    @property\n    def headers_array(self) -> typing.List[NameValue]:\n        \"\"\"APIResponse.headers_array\n\n        An array with all the response HTTP headers associated with this response. Header names are not lower-cased.\n        Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times.\n\n        Returns\n        -------\n        List[{name: str, value: str}]\n        \"\"\"\n        return mapping.from_impl_list(self._impl_obj.headers_array)\n\n    def body(self) -> bytes:\n        \"\"\"APIResponse.body\n\n        Returns the buffer with response body.\n\n        Returns\n        -------\n        bytes\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.body()))\n\n    def text(self) -> str:\n        \"\"\"APIResponse.text\n\n        Returns the text representation of response body.\n\n        Returns\n        -------\n        str\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.text()))\n\n    def json(self) -> typing.Any:\n        \"\"\"APIResponse.json\n\n        Returns the JSON representation of response body.\n\n        This method will throw if the response body is not parsable via `JSON.parse`.\n\n        Returns\n        -------\n        Any\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.json()))\n\n    def dispose(self) -> None:\n        \"\"\"APIResponse.dispose\n\n        Disposes the body of this response. If not called then the body will stay in memory until the context closes.\n        \"\"\"\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.dispose()))\n\n\nmapping.register(APIResponseImpl, APIResponse)\n\n\nclass APIRequestContext(SyncBase):\n\n    def dispose(self, *, reason: typing.Optional[str] = None) -> None:\n        \"\"\"APIRequestContext.dispose\n\n        All responses returned by `a_pi_request_context.get()` and similar methods are stored in the memory, so that\n        you can later call `a_pi_response.body()`.This method discards all its resources, calling any method on\n        disposed `APIRequestContext` will throw an exception.\n\n        Parameters\n        ----------\n        reason : Union[str, None]\n            The reason to be reported to the operations interrupted by the context disposal.\n        \"\"\"\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.dispose(reason=reason))\n        )\n\n    def delete(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.delete\n\n        Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.delete(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def head(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.head\n\n        Sends HTTP(S) [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.head(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def get(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.get\n\n        Sends HTTP(S) [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        **Usage**\n\n        Request parameters can be configured with `params` option, they will be serialized into the URL search parameters:\n\n        ```python\n        query_params = {\n          \\\"isbn\\\": \\\"1234\\\",\n          \\\"page\\\": \\\"23\\\"\n        }\n        api_request_context.get(\\\"https://example.com/api/getText\\\", params=query_params)\n        ```\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.get(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def patch(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.patch\n\n        Sends HTTP(S) [PATCH](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.patch(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def put(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.put\n\n        Sends HTTP(S) [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.put(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def post(\n        self,\n        url: str,\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.post\n\n        Sends HTTP(S) [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request and returns its\n        response. The method will populate request cookies from the context and update context cookies from the response.\n        The method will automatically follow redirects.\n\n        **Usage**\n\n        JSON objects can be passed directly to the request:\n\n        To send form data to the server use `form` option. Its value will be encoded into the request body with\n        `application/x-www-form-urlencoded` encoding (see below how to use `multipart/form-data` form encoding to send\n        files):\n\n        ```python\n        formData = {\n            \\\"title\\\": \\\"Book Title\\\",\n            \\\"body\\\": \\\"John Doe\\\",\n        }\n        api_request_context.post(\\\"https://example.com/api/findBook\\\", form=formData)\n        ```\n\n        The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data`\n        encoding. Use `FormData` to construct request body and pass it to the request as `multipart` parameter:\n\n        ```python\n        api_request_context.post(\n          \\\"https://example.com/api/uploadScript'\\\",\n          multipart={\n            \\\"fileField\\\": {\n              \\\"name\\\": \\\"f.js\\\",\n              \\\"mimeType\\\": \\\"text/javascript\\\",\n              \\\"buffer\\\": b\\\"console.log(2022);\\\",\n            },\n          })\n        ```\n\n        Parameters\n        ----------\n        url : str\n            Target URL.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.post(\n                    url=url,\n                    params=mapping.to_impl(params),\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def fetch(\n        self,\n        url_or_request: typing.Union[str, \"Request\"],\n        *,\n        params: typing.Optional[\n            typing.Union[typing.Dict[str, typing.Union[str, float, bool]], str]\n        ] = None,\n        method: typing.Optional[str] = None,\n        headers: typing.Optional[typing.Dict[str, str]] = None,\n        data: typing.Optional[typing.Union[typing.Any, str, bytes]] = None,\n        form: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,\n        multipart: typing.Optional[\n            typing.Dict[str, typing.Union[bytes, bool, float, str, FilePayload]]\n        ] = None,\n        timeout: typing.Optional[float] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n        max_retries: typing.Optional[int] = None,\n    ) -> \"APIResponse\":\n        \"\"\"APIRequestContext.fetch\n\n        Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and\n        update context cookies from the response. The method will automatically follow redirects.\n\n        **Usage**\n\n        JSON objects can be passed directly to the request:\n\n        The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data`\n        encoding, by specifiying the `multipart` parameter:\n\n        ```python\n        api_request_context.fetch(\n          \\\"https://example.com/api/uploadScript\\\",  method=\\\"post\\\",\n          multipart={\n            \\\"fileField\\\": {\n              \\\"name\\\": \\\"f.js\\\",\n              \\\"mimeType\\\": \\\"text/javascript\\\",\n              \\\"buffer\\\": b\\\"console.log(2022);\\\",\n            },\n          })\n        ```\n\n        Parameters\n        ----------\n        url_or_request : Union[Request, str]\n            Target URL or Request to get all parameters from.\n        params : Union[Dict[str, Union[bool, float, str]], str, None]\n            Query parameters to be sent with the URL.\n        method : Union[str, None]\n            If set changes the fetch method (e.g. [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) or\n            [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)). If not specified, GET method is used.\n        headers : Union[Dict[str, str], None]\n            Allows to set HTTP headers. These headers will apply to the fetched request as well as any redirects initiated by\n            it.\n        data : Union[Any, bytes, str, None]\n            Allows to set post data of the request. If the data parameter is an object, it will be serialized to json string\n            and `content-type` header will be set to `application/json` if not explicitly set. Otherwise the `content-type`\n            header will be set to `application/octet-stream` if not explicitly set.\n        form : Union[Dict[str, Union[bool, float, str]], None]\n            Provides an object that will be serialized as html form using `application/x-www-form-urlencoded` encoding and sent\n            as this request body. If this parameter is specified `content-type` header will be set to\n            `application/x-www-form-urlencoded` unless explicitly provided.\n        multipart : Union[Dict[str, Union[bool, bytes, float, str, {name: str, mimeType: str, buffer: bytes}]], None]\n            Provides an object that will be serialized as html form using `multipart/form-data` encoding and sent as this\n            request body. If this parameter is specified `content-type` header will be set to `multipart/form-data` unless\n            explicitly provided. File values can be passed as file-like object containing file name, mime-type and its content.\n        timeout : Union[float, None]\n            Request timeout in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects.\n        max_retries : Union[int, None]\n            Maximum number of times network errors should be retried. Currently only `ECONNRESET` error is retried. Does not\n            retry based on HTTP response codes. An error will be thrown if the limit is exceeded. Defaults to `0` - no retries.\n\n        Returns\n        -------\n        APIResponse\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.fetch(\n                    urlOrRequest=url_or_request,\n                    params=mapping.to_impl(params),\n                    method=method,\n                    headers=mapping.to_impl(headers),\n                    data=mapping.to_impl(data),\n                    form=mapping.to_impl(form),\n                    multipart=mapping.to_impl(multipart),\n                    timeout=timeout,\n                    failOnStatusCode=fail_on_status_code,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    maxRedirects=max_redirects,\n                    maxRetries=max_retries,\n                )\n            )\n        )\n\n    def storage_state(\n        self,\n        *,\n        path: typing.Optional[typing.Union[pathlib.Path, str]] = None,\n        indexed_db: typing.Optional[bool] = None,\n    ) -> StorageState:\n        \"\"\"APIRequestContext.storage_state\n\n        Returns storage state for this request context, contains current cookies and local storage snapshot if it was\n        passed to the constructor.\n\n        Parameters\n        ----------\n        path : Union[pathlib.Path, str, None]\n            The file path to save the storage state to. If `path` is a relative path, then it is resolved relative to current\n            working directory. If no path is provided, storage state is still returned, but won't be saved to the disk.\n        indexed_db : Union[bool, None]\n            Set to `true` to include IndexedDB in the storage state snapshot.\n\n        Returns\n        -------\n        {cookies: List[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: List[{origin: str, localStorage: List[{name: str, value: str}]}]}\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(self._impl_obj.storage_state(path=path, indexedDB=indexed_db))\n        )\n\n\nmapping.register(APIRequestContextImpl, APIRequestContext)\n\n\nclass APIRequest(SyncBase):\n\n    def new_context(\n        self,\n        *,\n        base_url: typing.Optional[str] = None,\n        extra_http_headers: typing.Optional[typing.Dict[str, str]] = None,\n        http_credentials: typing.Optional[HttpCredentials] = None,\n        ignore_https_errors: typing.Optional[bool] = None,\n        proxy: typing.Optional[ProxySettings] = None,\n        user_agent: typing.Optional[str] = None,\n        timeout: typing.Optional[float] = None,\n        storage_state: typing.Optional[\n            typing.Union[StorageState, str, pathlib.Path]\n        ] = None,\n        client_certificates: typing.Optional[typing.List[ClientCertificate]] = None,\n        fail_on_status_code: typing.Optional[bool] = None,\n        max_redirects: typing.Optional[int] = None,\n    ) -> \"APIRequestContext\":\n        \"\"\"APIRequest.new_context\n\n        Creates new instances of `APIRequestContext`.\n\n        Parameters\n        ----------\n        base_url : Union[str, None]\n            Methods like `a_pi_request_context.get()` take the base URL into consideration by using the\n            [`URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor for building the corresponding URL.\n            Examples:\n            - baseURL: `http://localhost:3000` and sending request to `/bar.html` results in `http://localhost:3000/bar.html`\n            - baseURL: `http://localhost:3000/foo/` and sending request to `./bar.html` results in\n              `http://localhost:3000/foo/bar.html`\n            - baseURL: `http://localhost:3000/foo` (without trailing slash) and navigating to `./bar.html` results in\n              `http://localhost:3000/bar.html`\n        extra_http_headers : Union[Dict[str, str], None]\n            An object containing additional HTTP headers to be sent with every request. Defaults to none.\n        http_credentials : Union[{username: str, password: str, origin: Union[str, None], send: Union[\"always\", \"unauthorized\", None]}, None]\n            Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no\n            origin is specified, the username and password are sent to any servers upon unauthorized responses.\n        ignore_https_errors : Union[bool, None]\n            Whether to ignore HTTPS errors when sending network requests. Defaults to `false`.\n        proxy : Union[{server: str, bypass: Union[str, None], username: Union[str, None], password: Union[str, None]}, None]\n            Network proxy settings.\n        user_agent : Union[str, None]\n            Specific user agent to use in this context.\n        timeout : Union[float, None]\n            Maximum time in milliseconds to wait for the response. Defaults to `30000` (30 seconds). Pass `0` to disable\n            timeout.\n        storage_state : Union[pathlib.Path, str, {cookies: Sequence[{name: str, value: str, domain: str, path: str, expires: float, httpOnly: bool, secure: bool, sameSite: Union[\"Lax\", \"None\", \"Strict\"]}], origins: Sequence[{origin: str, localStorage: Sequence[{name: str, value: str}]}]}, None]\n            Populates context with given storage state. This option can be used to initialize context with logged-in\n            information obtained via `browser_context.storage_state()` or `a_pi_request_context.storage_state()`.\n            Either a path to the file with saved storage, or the value returned by one of\n            `browser_context.storage_state()` or `a_pi_request_context.storage_state()` methods.\n        client_certificates : Union[Sequence[{origin: str, certPath: Union[pathlib.Path, str, None], cert: Union[bytes, None], keyPath: Union[pathlib.Path, str, None], key: Union[bytes, None], pfxPath: Union[pathlib.Path, str, None], pfx: Union[bytes, None], passphrase: Union[str, None]}], None]\n            TLS Client Authentication allows the server to request a client certificate and verify it.\n\n            **Details**\n\n            An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,\n            a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,\n            `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided\n            with an exact match to the request origin that the certificate is valid for.\n\n            Client certificate authentication is only active when at least one client certificate is provided. If you want to\n            reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that\n            does not match any of the domains you plan to visit.\n\n            **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it\n            work by replacing `localhost` with `local.playwright`.\n\n        fail_on_status_code : Union[bool, None]\n            Whether to throw on response codes other than 2xx and 3xx. By default response object is returned for all status\n            codes.\n        max_redirects : Union[int, None]\n            Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is\n            exceeded. Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request\n            individually.\n\n        Returns\n        -------\n        APIRequestContext\n        \"\"\"\n\n        return mapping.from_impl(\n            self._sync(\n                self._impl_obj.new_context(\n                    baseURL=base_url,\n                    extraHTTPHeaders=mapping.to_impl(extra_http_headers),\n                    httpCredentials=http_credentials,\n                    ignoreHTTPSErrors=ignore_https_errors,\n                    proxy=proxy,\n                    userAgent=user_agent,\n                    timeout=timeout,\n                    storageState=storage_state,\n                    clientCertificates=client_certificates,\n                    failOnStatusCode=fail_on_status_code,\n                    maxRedirects=max_redirects,\n                )\n            )\n        )\n\n\nmapping.register(APIRequestImpl, APIRequest)\n\n\nclass PageAssertions(SyncBase):\n\n    def to_have_title(\n        self,\n        title_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"PageAssertions.to_have_title\n\n        Ensures the page has the given title.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        # ...\n        expect(page).to_have_title(re.compile(r\\\".*checkout\\\"))\n        ```\n\n        Parameters\n        ----------\n        title_or_reg_exp : Union[Pattern[str], str]\n            Expected title or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_title(\n                    titleOrRegExp=title_or_reg_exp, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_title(\n        self,\n        title_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"PageAssertions.not_to_have_title\n\n        The opposite of `page_assertions.to_have_title()`.\n\n        Parameters\n        ----------\n        title_or_reg_exp : Union[Pattern[str], str]\n            Expected title or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_title(\n                    titleOrRegExp=title_or_reg_exp, timeout=timeout\n                )\n            )\n        )\n\n    def to_have_url(\n        self,\n        url_or_reg_exp: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"PageAssertions.to_have_url\n\n        Ensures the page is navigated to the given URL.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        # ...\n        expect(page).to_have_url(re.compile(\\\".*checkout\\\"))\n        ```\n\n        Parameters\n        ----------\n        url_or_reg_exp : Union[Pattern[str], str]\n            Expected URL string or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression parameter if specified. A provided predicate ignores this flag.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_url(\n                    urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case\n                )\n            )\n        )\n\n    def not_to_have_url(\n        self,\n        url_or_reg_exp: typing.Union[typing.Pattern[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"PageAssertions.not_to_have_url\n\n        The opposite of `page_assertions.to_have_url()`.\n\n        Parameters\n        ----------\n        url_or_reg_exp : Union[Pattern[str], str]\n            Expected URL string or RegExp.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_url(\n                    urlOrRegExp=url_or_reg_exp, timeout=timeout, ignoreCase=ignore_case\n                )\n            )\n        )\n\n\nmapping.register(PageAssertionsImpl, PageAssertions)\n\n\nclass LocatorAssertions(SyncBase):\n\n    def to_contain_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_contain_text\n\n        Ensures the `Locator` points to an element that contains the given text. All nested elements will be considered\n        when computing the text content of the element. You can use regular expressions for the value as well.\n\n        **Details**\n\n        When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual\n        text and in the expected string before matching. When regular expression is used, the actual text is matched as is.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        locator = page.locator('.title')\n        expect(locator).to_contain_text(\\\"substring\\\")\n        expect(locator).to_contain_text(re.compile(r\\\"\\\\d messages\\\"))\n        ```\n\n        If you pass an array as an expected value, the expectations are:\n        1. Locator resolves to a list of elements.\n        1. Elements from a **subset** of this list contain text from the expected array, respectively.\n        1. The matching subset of elements has the same order as the expected array.\n        1. Each text value from the expected array is matched by some element from the list.\n\n        For example, consider the following list:\n\n        ```html\n        <ul>\n          <li>Item Text 1</li>\n          <li>Item Text 2</li>\n          <li>Item Text 3</li>\n        </ul>\n        ```\n\n        Let's see how we can use the assertion:\n\n        ```py\n        from playwright.sync_api import expect\n\n        # ✓ Contains the right items in the right order\n        expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Text 1\\\", \\\"Text 3\\\"])\n\n        # ✖ Wrong order\n        expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Text 3\\\", \\\"Text 2\\\"])\n\n        # ✖ No item contains this text\n        expect(page.locator(\\\"ul > li\\\")).to_contain_text([\\\"Some 33\\\"])\n\n        # ✖ Locator points to the outer list element, not to the list items\n        expect(page.locator(\\\"ul\\\")).to_contain_text([\\\"Text 3\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected substring or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_contain_text(\n                    expected=mapping.to_impl(expected),\n                    useInnerText=use_inner_text,\n                    timeout=timeout,\n                    ignoreCase=ignore_case,\n                )\n            )\n        )\n\n    def not_to_contain_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_contain_text\n\n        The opposite of `locator_assertions.to_contain_text()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected substring or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_contain_text(\n                    expected=mapping.to_impl(expected),\n                    useInnerText=use_inner_text,\n                    timeout=timeout,\n                    ignoreCase=ignore_case,\n                )\n            )\n        )\n\n    def to_have_attribute(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_attribute\n\n        Ensures the `Locator` points to an element with given attribute.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"input\\\")\n        expect(locator).to_have_attribute(\\\"type\\\", \\\"text\\\")\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Attribute name.\n        value : Union[Pattern[str], str]\n            Expected attribute value.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_attribute(\n                    name=name, value=value, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_attribute(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_attribute\n\n        The opposite of `locator_assertions.to_have_attribute()`.\n\n        Parameters\n        ----------\n        name : str\n            Attribute name.\n        value : Union[Pattern[str], str]\n            Expected attribute value.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_attribute(\n                    name=name, value=value, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def to_have_class(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_class\n\n        Ensures the `Locator` points to an element with given CSS classes. When a string is provided, it must fully match\n        the element's `class` attribute. To match individual classes use `locator_assertions.to_contain_class()`.\n\n        **Usage**\n\n        ```html\n        <div class='middle selected row' id='component'></div>\n        ```\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"#component\\\")\n        expect(locator).to_have_class(\\\"middle selected row\\\")\n        expect(locator).to_have_class(re.compile(r\\\"(^|\\\\\\\\s)selected(\\\\\\\\s|$)\\\"))\n        ```\n\n        When an array is passed, the method asserts that the list of elements located matches the corresponding list of\n        expected class values. Each element's class attribute is matched against the corresponding string or regular\n        expression in the array:\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\".list > .component\\\")\n        expect(locator).to_have_class([\\\"component\\\", \\\"component selected\\\", \\\"component\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_class(\n                    expected=mapping.to_impl(expected), timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_class(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_class\n\n        The opposite of `locator_assertions.to_have_class()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_class(\n                    expected=mapping.to_impl(expected), timeout=timeout\n                )\n            )\n        )\n\n    def to_contain_class(\n        self,\n        expected: typing.Union[typing.Sequence[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_contain_class\n\n        Ensures the `Locator` points to an element with given CSS classes. All classes from the asserted value, separated\n        by spaces, must be present in the\n        [Element.classList](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) in any order.\n\n        **Usage**\n\n        ```html\n        <div class='middle selected row' id='component'></div>\n        ```\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"#component\\\")\n        expect(locator).to_contain_class(\\\"middle selected row\\\")\n        expect(locator).to_contain_class(\\\"selected\\\")\n        expect(locator).to_contain_class(\\\"row middle\\\")\n        ```\n\n        When an array is passed, the method asserts that the list of elements located matches the corresponding list of\n        expected class lists. Each element's class attribute is matched against the corresponding class in the array:\n\n        ```html\n        <div class='list'>\n          <div class='component inactive'></div>\n          <div class='component active'></div>\n          <div class='component inactive'></div>\n        </div>\n        ```\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\".list > .component\\\")\n        await expect(locator).to_contain_class([\\\"inactive\\\", \\\"active\\\", \\\"inactive\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Sequence[str], str]\n            A string containing expected class names, separated by spaces, or a list of such strings to assert multiple\n            elements.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_contain_class(\n                    expected=mapping.to_impl(expected), timeout=timeout\n                )\n            )\n        )\n\n    def not_to_contain_class(\n        self,\n        expected: typing.Union[typing.Sequence[str], str],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_contain_class\n\n        The opposite of `locator_assertions.to_contain_class()`.\n\n        Parameters\n        ----------\n        expected : Union[Sequence[str], str]\n            Expected class or RegExp or a list of those.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_contain_class(\n                    expected=mapping.to_impl(expected), timeout=timeout\n                )\n            )\n        )\n\n    def to_have_count(\n        self, count: int, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_count\n\n        Ensures the `Locator` resolves to an exact number of DOM nodes.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"list > .component\\\")\n        expect(locator).to_have_count(3)\n        ```\n\n        Parameters\n        ----------\n        count : int\n            Expected count.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_have_count(count=count, timeout=timeout))\n        )\n\n    def not_to_have_count(\n        self, count: int, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_count\n\n        The opposite of `locator_assertions.to_have_count()`.\n\n        Parameters\n        ----------\n        count : int\n            Expected count.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_have_count(count=count, timeout=timeout))\n        )\n\n    def to_have_css(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_css\n\n        Ensures the `Locator` resolves to an element with the given computed CSS style.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_role(\\\"button\\\")\n        expect(locator).to_have_css(\\\"display\\\", \\\"flex\\\")\n        ```\n\n        Parameters\n        ----------\n        name : str\n            CSS property name.\n        value : Union[Pattern[str], str]\n            CSS property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_css(name=name, value=value, timeout=timeout)\n            )\n        )\n\n    def not_to_have_css(\n        self,\n        name: str,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_css\n\n        The opposite of `locator_assertions.to_have_css()`.\n\n        Parameters\n        ----------\n        name : str\n            CSS property name.\n        value : Union[Pattern[str], str]\n            CSS property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_css(name=name, value=value, timeout=timeout)\n            )\n        )\n\n    def to_have_id(\n        self,\n        id: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_id\n\n        Ensures the `Locator` points to an element with the given DOM Node ID.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        expect(locator).to_have_id(\\\"lastname\\\")\n        ```\n\n        Parameters\n        ----------\n        id : Union[Pattern[str], str]\n            Element id.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_have_id(id=id, timeout=timeout))\n        )\n\n    def not_to_have_id(\n        self,\n        id: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_id\n\n        The opposite of `locator_assertions.to_have_id()`.\n\n        Parameters\n        ----------\n        id : Union[Pattern[str], str]\n            Element id.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_have_id(id=id, timeout=timeout))\n        )\n\n    def to_have_js_property(\n        self, name: str, value: typing.Any, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_js_property\n\n        Ensures the `Locator` points to an element with given JavaScript property. Note that this property can be of a\n        primitive type as well as a plain serializable JavaScript object.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\".component\\\")\n        expect(locator).to_have_js_property(\\\"loaded\\\", True)\n        ```\n\n        Parameters\n        ----------\n        name : str\n            Property name.\n        value : Any\n            Property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_js_property(\n                    name=name, value=mapping.to_impl(value), timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_js_property(\n        self, name: str, value: typing.Any, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_js_property\n\n        The opposite of `locator_assertions.to_have_js_property()`.\n\n        Parameters\n        ----------\n        name : str\n            Property name.\n        value : Any\n            Property value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_js_property(\n                    name=name, value=mapping.to_impl(value), timeout=timeout\n                )\n            )\n        )\n\n    def to_have_value(\n        self,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_value\n\n        Ensures the `Locator` points to an element with the given input value. You can use regular expressions for the\n        value as well.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"input[type=number]\\\")\n        expect(locator).to_have_value(re.compile(r\\\"[0-9]\\\"))\n        ```\n\n        Parameters\n        ----------\n        value : Union[Pattern[str], str]\n            Expected value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_have_value(value=value, timeout=timeout))\n        )\n\n    def not_to_have_value(\n        self,\n        value: typing.Union[str, typing.Pattern[str]],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_value\n\n        The opposite of `locator_assertions.to_have_value()`.\n\n        Parameters\n        ----------\n        value : Union[Pattern[str], str]\n            Expected value.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_have_value(value=value, timeout=timeout))\n        )\n\n    def to_have_values(\n        self,\n        values: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_values\n\n        Ensures the `Locator` points to multi-select/combobox (i.e. a `select` with the `multiple` attribute) and the\n        specified values are selected.\n\n        **Usage**\n\n        For example, given the following element:\n\n        ```html\n        <select id=\\\"favorite-colors\\\" multiple>\n          <option value=\\\"R\\\">Red</option>\n          <option value=\\\"G\\\">Green</option>\n          <option value=\\\"B\\\">Blue</option>\n        </select>\n        ```\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"id=favorite-colors\\\")\n        locator.select_option([\\\"R\\\", \\\"G\\\"])\n        expect(locator).to_have_values([re.compile(r\\\"R\\\"), re.compile(r\\\"G\\\")])\n        ```\n\n        Parameters\n        ----------\n        values : Union[Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str]]\n            Expected options currently selected.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_values(\n                    values=mapping.to_impl(values), timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_values(\n        self,\n        values: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_values\n\n        The opposite of `locator_assertions.to_have_values()`.\n\n        Parameters\n        ----------\n        values : Union[Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str]]\n            Expected options currently selected.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_values(\n                    values=mapping.to_impl(values), timeout=timeout\n                )\n            )\n        )\n\n    def to_have_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_text\n\n        Ensures the `Locator` points to an element with the given text. All nested elements will be considered when\n        computing the text content of the element. You can use regular expressions for the value as well.\n\n        **Details**\n\n        When `expected` parameter is a string, Playwright will normalize whitespaces and line breaks both in the actual\n        text and in the expected string before matching. When regular expression is used, the actual text is matched as is.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\".title\\\")\n        expect(locator).to_have_text(re.compile(r\\\"Welcome, Test User\\\"))\n        expect(locator).to_have_text(re.compile(r\\\"Welcome, .*\\\"))\n        ```\n\n        If you pass an array as an expected value, the expectations are:\n        1. Locator resolves to a list of elements.\n        1. The number of elements equals the number of expected values in the array.\n        1. Elements from the list have text matching expected array values, one by one, in order.\n\n        For example, consider the following list:\n\n        ```html\n        <ul>\n          <li>Text 1</li>\n          <li>Text 2</li>\n          <li>Text 3</li>\n        </ul>\n        ```\n\n        Let's see how we can use the assertion:\n\n        ```py\n        from playwright.sync_api import expect\n\n        # ✓ Has the right items in the right order\n        expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text 3\\\"])\n\n        # ✖ Wrong order\n        expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 3\\\", \\\"Text 2\\\", \\\"Text 1\\\"])\n\n        # ✖ Last item does not match\n        expect(page.locator(\\\"ul > li\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text\\\"])\n\n        # ✖ Locator points to the outer list element, not to the list items\n        expect(page.locator(\\\"ul\\\")).to_have_text([\\\"Text 1\\\", \\\"Text 2\\\", \\\"Text 3\\\"])\n        ```\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected string or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_text(\n                    expected=mapping.to_impl(expected),\n                    useInnerText=use_inner_text,\n                    timeout=timeout,\n                    ignoreCase=ignore_case,\n                )\n            )\n        )\n\n    def not_to_have_text(\n        self,\n        expected: typing.Union[\n            typing.Sequence[str],\n            typing.Sequence[typing.Pattern[str]],\n            typing.Sequence[typing.Union[typing.Pattern[str], str]],\n            typing.Pattern[str],\n            str,\n        ],\n        *,\n        use_inner_text: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n        ignore_case: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_text\n\n        The opposite of `locator_assertions.to_have_text()`.\n\n        Parameters\n        ----------\n        expected : Union[Pattern[str], Sequence[Pattern[str]], Sequence[Union[Pattern[str], str]], Sequence[str], str]\n            Expected string or RegExp or a list of those.\n        use_inner_text : Union[bool, None]\n            Whether to use `element.innerText` instead of `element.textContent` when retrieving DOM node text.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_text(\n                    expected=mapping.to_impl(expected),\n                    useInnerText=use_inner_text,\n                    timeout=timeout,\n                    ignoreCase=ignore_case,\n                )\n            )\n        )\n\n    def to_be_attached(\n        self,\n        *,\n        attached: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_attached\n\n        Ensures that `Locator` points to an element that is\n        [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.\n\n        **Usage**\n\n        ```py\n        expect(page.get_by_text(\\\"Hidden text\\\")).to_be_attached()\n        ```\n\n        Parameters\n        ----------\n        attached : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_be_attached(attached=attached, timeout=timeout)\n            )\n        )\n\n    def to_be_checked(\n        self,\n        *,\n        timeout: typing.Optional[float] = None,\n        checked: typing.Optional[bool] = None,\n        indeterminate: typing.Optional[bool] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_checked\n\n        Ensures the `Locator` points to a checked input.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_label(\\\"Subscribe to newsletter\\\")\n        expect(locator).to_be_checked()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        checked : Union[bool, None]\n            Provides state to assert for. Asserts for input to be checked by default. This option can't be used when\n            `indeterminate` is set to true.\n        indeterminate : Union[bool, None]\n            Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons.\n            This option can't be true when `checked` is provided.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_be_checked(\n                    timeout=timeout, checked=checked, indeterminate=indeterminate\n                )\n            )\n        )\n\n    def not_to_be_attached(\n        self,\n        *,\n        attached: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_attached\n\n        The opposite of `locator_assertions.to_be_attached()`.\n\n        Parameters\n        ----------\n        attached : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_be_attached(attached=attached, timeout=timeout)\n            )\n        )\n\n    def not_to_be_checked(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_checked\n\n        The opposite of `locator_assertions.to_be_checked()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_be_checked(timeout=timeout))\n        )\n\n    def to_be_disabled(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_disabled\n\n        Ensures the `Locator` points to a disabled element. Element is disabled if it has \\\"disabled\\\" attribute or is\n        disabled via\n        ['aria-disabled'](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled). Note\n        that only native control elements such as HTML `button`, `input`, `select`, `textarea`, `option`, `optgroup` can be\n        disabled by setting \\\"disabled\\\" attribute. \\\"disabled\\\" attribute on other elements is ignored by the browser.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"button.submit\\\")\n        expect(locator).to_be_disabled()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_disabled(timeout=timeout))\n        )\n\n    def not_to_be_disabled(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_disabled\n\n        The opposite of `locator_assertions.to_be_disabled()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_be_disabled(timeout=timeout))\n        )\n\n    def to_be_editable(\n        self,\n        *,\n        editable: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_editable\n\n        Ensures the `Locator` points to an editable element.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        expect(locator).to_be_editable()\n        ```\n\n        Parameters\n        ----------\n        editable : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_be_editable(editable=editable, timeout=timeout)\n            )\n        )\n\n    def not_to_be_editable(\n        self,\n        *,\n        editable: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_editable\n\n        The opposite of `locator_assertions.to_be_editable()`.\n\n        Parameters\n        ----------\n        editable : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_be_editable(editable=editable, timeout=timeout)\n            )\n        )\n\n    def to_be_empty(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_empty\n\n        Ensures the `Locator` points to an empty editable element or to a DOM node that has no text.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"div.warning\\\")\n        expect(locator).to_be_empty()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_empty(timeout=timeout))\n        )\n\n    def not_to_be_empty(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_empty\n\n        The opposite of `locator_assertions.to_be_empty()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_be_empty(timeout=timeout))\n        )\n\n    def to_be_enabled(\n        self,\n        *,\n        enabled: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_enabled\n\n        Ensures the `Locator` points to an enabled element.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator(\\\"button.submit\\\")\n        expect(locator).to_be_enabled()\n        ```\n\n        Parameters\n        ----------\n        enabled : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_enabled(enabled=enabled, timeout=timeout))\n        )\n\n    def not_to_be_enabled(\n        self,\n        *,\n        enabled: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_enabled\n\n        The opposite of `locator_assertions.to_be_enabled()`.\n\n        Parameters\n        ----------\n        enabled : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_be_enabled(enabled=enabled, timeout=timeout)\n            )\n        )\n\n    def to_be_hidden(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_hidden\n\n        Ensures that `Locator` either does not resolve to any DOM node, or resolves to a\n        [non-visible](https://playwright.dev/python/docs/actionability#visible) one.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.locator('.my-element')\n        expect(locator).to_be_hidden()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_hidden(timeout=timeout))\n        )\n\n    def not_to_be_hidden(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_hidden\n\n        The opposite of `locator_assertions.to_be_hidden()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_be_hidden(timeout=timeout))\n        )\n\n    def to_be_visible(\n        self,\n        *,\n        visible: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_visible\n\n        Ensures that `Locator` points to an attached and [visible](https://playwright.dev/python/docs/actionability#visible) DOM node.\n\n        To check that at least one element from the list is visible, use `locator.first()`.\n\n        **Usage**\n\n        ```py\n        # A specific element is visible.\n        expect(page.get_by_text(\\\"Welcome\\\")).to_be_visible()\n\n        # At least one item in the list is visible.\n        expect(page.get_by_test_id(\\\"todo-item\\\").first).to_be_visible()\n\n        # At least one of the two elements is visible, possibly both.\n        expect(\n            page.get_by_role(\\\"button\\\", name=\\\"Sign in\\\")\n            .or_(page.get_by_role(\\\"button\\\", name=\\\"Sign up\\\"))\n            .first\n        ).to_be_visible()\n        ```\n\n        Parameters\n        ----------\n        visible : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_visible(visible=visible, timeout=timeout))\n        )\n\n    def not_to_be_visible(\n        self,\n        *,\n        visible: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_visible\n\n        The opposite of `locator_assertions.to_be_visible()`.\n\n        Parameters\n        ----------\n        visible : Union[bool, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_be_visible(visible=visible, timeout=timeout)\n            )\n        )\n\n    def to_be_focused(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.to_be_focused\n\n        Ensures the `Locator` points to a focused DOM node.\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_role(\\\"textbox\\\")\n        expect(locator).to_be_focused()\n        ```\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_focused(timeout=timeout))\n        )\n\n    def not_to_be_focused(self, *, timeout: typing.Optional[float] = None) -> None:\n        \"\"\"LocatorAssertions.not_to_be_focused\n\n        The opposite of `locator_assertions.to_be_focused()`.\n\n        Parameters\n        ----------\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_be_focused(timeout=timeout))\n        )\n\n    def to_be_in_viewport(\n        self,\n        *,\n        ratio: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_be_in_viewport\n\n        Ensures the `Locator` points to an element that intersects viewport, according to the\n        [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n\n        **Usage**\n\n        ```py\n        from playwright.sync_api import expect\n\n        locator = page.get_by_role(\\\"button\\\")\n        # Make sure at least some part of element intersects viewport.\n        expect(locator).to_be_in_viewport()\n        # Make sure element is fully outside of viewport.\n        expect(locator).not_to_be_in_viewport()\n        # Make sure that at least half of the element intersects viewport.\n        expect(locator).to_be_in_viewport(ratio=0.5)\n        ```\n\n        Parameters\n        ----------\n        ratio : Union[float, None]\n            The minimal ratio of the element to intersect viewport. If equals to `0`, then element should intersect viewport at\n            any positive ratio. Defaults to `0`.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_be_in_viewport(ratio=ratio, timeout=timeout))\n        )\n\n    def not_to_be_in_viewport(\n        self,\n        *,\n        ratio: typing.Optional[float] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_be_in_viewport\n\n        The opposite of `locator_assertions.to_be_in_viewport()`.\n\n        Parameters\n        ----------\n        ratio : Union[float, None]\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_be_in_viewport(ratio=ratio, timeout=timeout)\n            )\n        )\n\n    def to_have_accessible_description(\n        self,\n        description: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_description\n\n        Ensures the `Locator` points to an element with a given\n        [accessible description](https://w3c.github.io/accname/#dfn-accessible-description).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        expect(locator).to_have_accessible_description(\\\"Save results to disk\\\")\n        ```\n\n        Parameters\n        ----------\n        description : Union[Pattern[str], str]\n            Expected accessible description.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_accessible_description(\n                    description=description, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_accessible_description(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_description\n\n        The opposite of `locator_assertions.to_have_accessible_description()`.\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible description.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_accessible_description(\n                    name=name, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def to_have_accessible_name(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_name\n\n        Ensures the `Locator` points to an element with a given\n        [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        expect(locator).to_have_accessible_name(\\\"Save to disk\\\")\n        ```\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible name.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_accessible_name(\n                    name=name, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_accessible_name(\n        self,\n        name: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_name\n\n        The opposite of `locator_assertions.to_have_accessible_name()`.\n\n        Parameters\n        ----------\n        name : Union[Pattern[str], str]\n            Expected accessible name.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_accessible_name(\n                    name=name, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def to_have_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_role\n\n        Ensures the `Locator` points to an element with a given [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles).\n\n        Note that role is matched as a string, disregarding the ARIA role hierarchy. For example, asserting  a superclass\n        role `\\\"checkbox\\\"` on an element with a subclass role `\\\"switch\\\"` will fail.\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"save-button\\\")\n        expect(locator).to_have_role(\\\"button\\\")\n        ```\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.to_have_role(role=role, timeout=timeout))\n        )\n\n    def to_have_accessible_error_message(\n        self,\n        error_message: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.to_have_accessible_error_message\n\n        Ensures the `Locator` points to an element with a given\n        [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).\n\n        **Usage**\n\n        ```py\n        locator = page.get_by_test_id(\\\"username-input\\\")\n        expect(locator).to_have_accessible_error_message(\\\"Username is required.\\\")\n        ```\n\n        Parameters\n        ----------\n        error_message : Union[Pattern[str], str]\n            Expected accessible error message.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_have_accessible_error_message(\n                    errorMessage=error_message, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_accessible_error_message(\n        self,\n        error_message: typing.Union[str, typing.Pattern[str]],\n        *,\n        ignore_case: typing.Optional[bool] = None,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_accessible_error_message\n\n        The opposite of `locator_assertions.to_have_accessible_error_message()`.\n\n        Parameters\n        ----------\n        error_message : Union[Pattern[str], str]\n            Expected accessible error message.\n        ignore_case : Union[bool, None]\n            Whether to perform case-insensitive match. `ignoreCase` option takes precedence over the corresponding regular\n            expression flag if specified.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_have_accessible_error_message(\n                    errorMessage=error_message, ignoreCase=ignore_case, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_have_role(\n        self,\n        role: Literal[\n            \"alert\",\n            \"alertdialog\",\n            \"application\",\n            \"article\",\n            \"banner\",\n            \"blockquote\",\n            \"button\",\n            \"caption\",\n            \"cell\",\n            \"checkbox\",\n            \"code\",\n            \"columnheader\",\n            \"combobox\",\n            \"complementary\",\n            \"contentinfo\",\n            \"definition\",\n            \"deletion\",\n            \"dialog\",\n            \"directory\",\n            \"document\",\n            \"emphasis\",\n            \"feed\",\n            \"figure\",\n            \"form\",\n            \"generic\",\n            \"grid\",\n            \"gridcell\",\n            \"group\",\n            \"heading\",\n            \"img\",\n            \"insertion\",\n            \"link\",\n            \"list\",\n            \"listbox\",\n            \"listitem\",\n            \"log\",\n            \"main\",\n            \"marquee\",\n            \"math\",\n            \"menu\",\n            \"menubar\",\n            \"menuitem\",\n            \"menuitemcheckbox\",\n            \"menuitemradio\",\n            \"meter\",\n            \"navigation\",\n            \"none\",\n            \"note\",\n            \"option\",\n            \"paragraph\",\n            \"presentation\",\n            \"progressbar\",\n            \"radio\",\n            \"radiogroup\",\n            \"region\",\n            \"row\",\n            \"rowgroup\",\n            \"rowheader\",\n            \"scrollbar\",\n            \"search\",\n            \"searchbox\",\n            \"separator\",\n            \"slider\",\n            \"spinbutton\",\n            \"status\",\n            \"strong\",\n            \"subscript\",\n            \"superscript\",\n            \"switch\",\n            \"tab\",\n            \"table\",\n            \"tablist\",\n            \"tabpanel\",\n            \"term\",\n            \"textbox\",\n            \"time\",\n            \"timer\",\n            \"toolbar\",\n            \"tooltip\",\n            \"tree\",\n            \"treegrid\",\n            \"treeitem\",\n        ],\n        *,\n        timeout: typing.Optional[float] = None,\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_have_role\n\n        The opposite of `locator_assertions.to_have_role()`.\n\n        Parameters\n        ----------\n        role : Union[\"alert\", \"alertdialog\", \"application\", \"article\", \"banner\", \"blockquote\", \"button\", \"caption\", \"cell\", \"checkbox\", \"code\", \"columnheader\", \"combobox\", \"complementary\", \"contentinfo\", \"definition\", \"deletion\", \"dialog\", \"directory\", \"document\", \"emphasis\", \"feed\", \"figure\", \"form\", \"generic\", \"grid\", \"gridcell\", \"group\", \"heading\", \"img\", \"insertion\", \"link\", \"list\", \"listbox\", \"listitem\", \"log\", \"main\", \"marquee\", \"math\", \"menu\", \"menubar\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"meter\", \"navigation\", \"none\", \"note\", \"option\", \"paragraph\", \"presentation\", \"progressbar\", \"radio\", \"radiogroup\", \"region\", \"row\", \"rowgroup\", \"rowheader\", \"scrollbar\", \"search\", \"searchbox\", \"separator\", \"slider\", \"spinbutton\", \"status\", \"strong\", \"subscript\", \"superscript\", \"switch\", \"tab\", \"table\", \"tablist\", \"tabpanel\", \"term\", \"textbox\", \"time\", \"timer\", \"toolbar\", \"tooltip\", \"tree\", \"treegrid\", \"treeitem\"]\n            Required aria role.\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(self._impl_obj.not_to_have_role(role=role, timeout=timeout))\n        )\n\n    def to_match_aria_snapshot(\n        self, expected: str, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.to_match_aria_snapshot\n\n        Asserts that the target element matches the given [accessibility snapshot](https://playwright.dev/python/docs/aria-snapshots).\n\n        **Usage**\n\n        ```py\n        page.goto(\\\"https://demo.playwright.dev/todomvc/\\\")\n        expect(page.locator('body')).to_match_aria_snapshot('''\n          - heading \\\"todos\\\"\n          - textbox \\\"What needs to be done?\\\"\n        ''')\n        ```\n\n        Parameters\n        ----------\n        expected : str\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.to_match_aria_snapshot(\n                    expected=expected, timeout=timeout\n                )\n            )\n        )\n\n    def not_to_match_aria_snapshot(\n        self, expected: str, *, timeout: typing.Optional[float] = None\n    ) -> None:\n        \"\"\"LocatorAssertions.not_to_match_aria_snapshot\n\n        The opposite of `locator_assertions.to_match_aria_snapshot()`.\n\n        Parameters\n        ----------\n        expected : str\n        timeout : Union[float, None]\n            Time to retry the assertion for in milliseconds. Defaults to `5000`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(\n            self._sync(\n                self._impl_obj.not_to_match_aria_snapshot(\n                    expected=expected, timeout=timeout\n                )\n            )\n        )\n\n\nmapping.register(LocatorAssertionsImpl, LocatorAssertions)\n\n\nclass APIResponseAssertions(SyncBase):\n\n    def to_be_ok(self) -> None:\n        \"\"\"APIResponseAssertions.to_be_ok\n\n        Ensures the response status code is within `200..299` range.\n\n        **Usage**\n\n        ```py\n        import re\n        from playwright.sync_api import expect\n\n        # ...\n        expect(response).to_be_ok()\n        ```\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.to_be_ok()))\n\n    def not_to_be_ok(self) -> None:\n        \"\"\"APIResponseAssertions.not_to_be_ok\n\n        The opposite of `a_pi_response_assertions.to_be_ok()`.\n        \"\"\"\n        __tracebackhide__ = True\n\n        return mapping.from_maybe_impl(self._sync(self._impl_obj.not_to_be_ok()))\n\n\nmapping.register(APIResponseAssertionsImpl, APIResponseAssertions)\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\r\nrequires = [\"setuptools==80.9.0\", \"setuptools-scm==8.3.1\", \"wheel==0.45.1\", \"auditwheel==6.2.0\"]\r\nbuild-backend = \"setuptools.build_meta\"\r\n\r\n[project]\r\nname = \"playwright\"\r\ndescription = \"A high-level API to automate web browsers\"\r\nauthors = [\r\n    {name = \"Microsoft Corporation\"}\r\n]\r\nreadme = \"README.md\"\r\nlicense = \"Apache-2.0\"\r\ndynamic = [\"version\"]\r\nrequires-python = \">=3.9\"\r\n# Please when changing dependencies run the following commands to update requirements.txt:\r\n# - pip install uv==0.5.4\r\n# - uv pip compile pyproject.toml -o requirements.txt\r\ndependencies = [\r\n    \"pyee>=13,<14\",\r\n    \"greenlet>=3.1.1,<4.0.0\"\r\n]\r\nclassifiers = [\r\n    \"Topic :: Software Development :: Testing\",\r\n    \"Topic :: Internet :: WWW/HTTP :: Browsers\",\r\n    \"Intended Audience :: Developers\",\r\n    \"Programming Language :: Python :: 3\",\r\n    \"Programming Language :: Python :: 3.9\",\r\n    \"Programming Language :: Python :: 3.10\",\r\n    \"Programming Language :: Python :: 3.11\",\r\n    \"Programming Language :: Python :: 3.12\",\r\n    \"Programming Language :: Python :: 3.13\",\r\n    \"Operating System :: OS Independent\",\r\n]\r\n\r\n[project.urls]\r\nhomepage = \"https://github.com/Microsoft/playwright-python\"\r\n\"Release notes\" = \"https://github.com/microsoft/playwright-python/releases\"\r\n\r\n[project.scripts]\r\nplaywright = \"playwright.__main__:main\"\r\n\r\n[project.entry-points.pyinstaller40]\r\nhook-dirs = \"playwright._impl.__pyinstaller:get_hook_dirs\"\r\n\r\n[tool.setuptools]\r\npackages = [\r\n    \"playwright\",\r\n    \"playwright.async_api\",\r\n    \"playwright.sync_api\",\r\n    \"playwright._impl\",\r\n    \"playwright._impl.__pyinstaller\",\r\n]\r\ninclude-package-data = true\r\n\r\n[tool.setuptools_scm]\r\nversion_file = \"playwright/_repo_version.py\"\r\n\r\n[tool.pytest.ini_options]\r\naddopts = \"-Wall -rsx -vv -s\"\r\nmarkers = [\r\n    \"skip_browser\",\r\n    \"only_browser\",\r\n    \"skip_platform\",\r\n    \"only_platform\"\r\n]\r\njunit_family = \"xunit2\"\r\nasyncio_mode = \"auto\"\r\nasyncio_default_fixture_loop_scope = \"session\"\r\nasyncio_default_test_loop_scope = \"session\"\r\n\r\n[tool.mypy]\r\nignore_missing_imports = true\r\npython_version = \"3.9\"\r\nwarn_unused_ignores = false\r\nwarn_redundant_casts = true\r\nwarn_unused_configs = true\r\ncheck_untyped_defs = true\r\ndisallow_untyped_defs = true\r\nno_implicit_optional = false\r\nexclude = [\r\n    \"build/\",\r\n    \"env/\",\r\n]\r\n\r\n[tool.isort]\r\nprofile = \"black\"\r\n\r\n[tool.pyright]\r\ninclude = [\"playwright\", \"tests\", \"scripts\"]\r\nexclude = [\"**/node_modules\", \"**/__pycache__\", \"**/.*\", \"./build\"]\r\npythonVersion = \"3.9\"\r\nreportMissingImports = false\r\nreportTypedDictNotRequiredAccess = false\r\nreportCallInDefaultInitializer = true\r\nreportOptionalSubscript = false\r\nreportUnboundVariable = false\r\nstrictParameterNoneValue = false\r\nreportIncompatibleVariableOverride = false\r\n"
  },
  {
    "path": "requirements.txt",
    "content": "# This file was autogenerated by uv via the following command:\n#    uv pip compile pyproject.toml -o requirements.txt\ngreenlet==3.2.4\n    # via playwright (pyproject.toml)\npyee==13.0.0\n    # via playwright (pyproject.toml)\ntyping-extensions==4.14.1\n    # via pyee\n"
  },
  {
    "path": "scripts/documentation_provider.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport pathlib\nimport re\nimport subprocess\nfrom sys import stderr\nfrom typing import Any, Dict, List, Set, Union, get_args, get_origin, get_type_hints\nfrom urllib.parse import urljoin\n\nfrom playwright._impl._helper import to_snake_case\n\nenum_regex = r\"^\\\"[^\\\"]+\\\"(?:\\|\\\"[^\\\"]+\\\")+$\"\nunion_regex = r\"^[^\\|]+(?:\\|[^\\|]+)+$\"\n\n\nclass DocumentationProvider:\n    def __init__(self, is_async: bool) -> None:\n        self.is_async = is_async\n        self.api: Any = {}\n        self.links: Dict[str, str] = {}\n        self.printed_entries: List[str] = []\n        process_output = subprocess.run(\n            [\"python\", \"-m\", \"playwright\", \"print-api-json\"],\n            check=True,\n            capture_output=True,\n        )\n        self.api = json.loads(process_output.stdout)\n        self.errors: Set[str] = set()\n        self._patch_case()\n\n    def _patch_case(self) -> None:\n        self.classes = {}\n        for clazz in self.api:\n            if not works_for_python(clazz):\n                continue\n            members = {}\n            self.classes[clazz[\"name\"]] = clazz\n            events = []\n            for member in clazz[\"members\"]:\n                if not works_for_python(member):\n                    continue\n                member_name = member[\"name\"]\n                new_name = name_or_alias(member)\n                self._add_link(member[\"kind\"], clazz[\"name\"], member_name, new_name)\n\n                if member[\"kind\"] == \"event\":\n                    events.append(member)\n                else:\n                    new_name = to_snake_case(new_name)\n                    member[\"name\"] = new_name\n                    members[new_name] = member\n                apply_type_or_override(member)\n\n                if \"args\" in member:\n                    args = {}\n                    for arg in member[\"args\"]:\n                        if not works_for_python(arg):\n                            continue\n                        if arg[\"name\"] == \"options\":\n                            for option in arg[\"type\"][\"properties\"]:\n                                if not works_for_python(option):\n                                    continue\n                                option = self_or_override(option)\n                                option_name = to_snake_case(name_or_alias(option))\n                                option[\"name\"] = option_name\n                                option[\"required\"] = False\n                                args[option_name] = option\n                        else:\n                            arg = self_or_override(arg)\n                            arg_name = to_snake_case(name_or_alias(arg))\n                            arg[\"name\"] = arg_name\n                            args[arg_name] = arg\n\n                    member[\"args\"] = args\n\n            clazz[\"members\"] = members\n            clazz[\"events\"] = events\n\n    def _add_link(self, kind: str, clazz: str, member: str, alias: str) -> None:\n        match = re.match(r\"(JS|CDP|[A-Z])([^.]+)\", clazz)\n        if not match:\n            raise Exception(\"Invalid class \" + clazz)\n        var_name = to_snake_case(f\"{match.group(1).lower()}{match.group(2)}\")\n        new_name = to_snake_case(alias)\n        if kind == \"event\":\n            new_name = new_name.lower()\n            self.links[f\"[`event: {clazz}.{member}`]\"] = (\n                f\"`{var_name}.on('{new_name}')`\"\n            )\n        elif kind == \"property\":\n            self.links[f\"[`property: {clazz}.{member}`]\"] = f\"`{var_name}.{new_name}`\"\n        else:\n            self.links[f\"[`method: {clazz}.{member}`]\"] = f\"`{var_name}.{new_name}()`\"\n\n    def print_entry(\n        self,\n        class_name: str,\n        method_name: str,\n        signature: Dict[str, Any] = None,\n        is_property: bool = False,\n    ) -> None:\n        if class_name in [\"BindingCall\"] or method_name in [\n            \"pid\",\n        ]:\n            return\n        original_method_name = method_name\n        self.printed_entries.append(f\"{class_name}.{method_name}\")\n        clazz = self.classes[class_name]\n        method = clazz[\"members\"].get(method_name)\n        if not method and \"extends\" in clazz:\n            superclass = self.classes.get(clazz[\"extends\"])\n            if superclass:\n                method = superclass[\"members\"].get(method_name)\n        fqname = f\"{class_name}.{method_name}\"\n\n        if not method:\n            self.errors.add(f\"Method not documented: {fqname}\")\n            return\n\n        doc_is_property = (\n            not method.get(\"async\") and not len(method[\"args\"]) and \"type\" in method\n        )\n        if (\n            method[\"name\"].startswith(\"is_\")\n            or method[\"name\"].startswith(\"as_\")\n            or method[\"name\"] == \"connect_to_server\"\n        ):\n            doc_is_property = False\n        if doc_is_property != is_property:\n            self.errors.add(f\"Method vs property mismatch: {fqname}\")\n            return\n\n        indent = \" \" * 8\n        print(f'{indent}\"\"\"{class_name}.{to_snake_case(original_method_name)}')\n        if method.get(\"comment\"):\n            print(f\"{indent}{self.beautify_method_comment(method['comment'], indent)}\")\n        signature_no_return = {**signature} if signature else None\n        if signature_no_return and \"return\" in signature_no_return:\n            del signature_no_return[\"return\"]\n\n        # Collect a list of all names, flatten options.\n        args = method[\"args\"]\n        if signature and signature_no_return:\n            print(\"\")\n            print(\"        Parameters\")\n            print(\"        ----------\")\n            for [name, value] in signature.items():\n                name = to_snake_case(name)\n                if name == \"return\":\n                    continue\n                original_name = name\n                doc_value = args.get(name)\n                if name in args:\n                    del args[name]\n                if not doc_value:\n                    self.errors.add(f\"Parameter not documented: {fqname}({name}=)\")\n                else:\n                    code_type = self.serialize_python_type(value, \"in\")\n\n                    print(f\"{indent}{to_snake_case(original_name)} : {code_type}\")\n                    if doc_value.get(\"comment\"):\n                        print(\n                            f\"{indent}    {self.indent_paragraph(self.render_links(doc_value['comment']), f'{indent}    ')}\"\n                        )\n                    if doc_value.get(\"deprecated\"):\n                        print(\n                            f\"{indent}    Deprecated: {self.render_links(doc_value['deprecated'])}\"\n                        )\n                    self.compare_types(code_type, doc_value, f\"{fqname}({name}=)\", \"in\")\n        if (\n            signature\n            and \"return\" in signature\n            and str(signature[\"return\"]) != \"<class 'NoneType'>\"\n        ):\n            value = signature[\"return\"]\n            doc_value = method\n            self.compare_types(value, doc_value, f\"{fqname}(return=)\", \"out\")\n            print(\"\")\n            print(\"        Returns\")\n            print(\"        -------\")\n            print(f\"        {self.serialize_python_type(value, 'out')}\")\n        print(f'{indent}\"\"\"')\n\n        for name in args:\n            if args[name].get(\"deprecated\"):\n                continue\n            self.errors.add(\n                f\"Parameter not implemented: {class_name}.{method_name}({name}=)\"\n            )\n\n    def print_events(self, class_name: str) -> None:\n        clazz = self.classes[class_name]\n        events = clazz[\"events\"]\n        if events:\n            doc = []\n            for event_type in [\"on\", \"once\"]:\n                for event in events:\n                    return_type = (\n                        \"typing.Union[typing.Awaitable[None], None]\"\n                        if self.is_async\n                        else \"None\"\n                    )\n                    func_arg = self.serialize_doc_type(event[\"type\"], \"\")\n                    if func_arg.startswith(\"{\"):\n                        func_arg = \"typing.Dict\"\n                    if \"Union[\" in func_arg:\n                        func_arg = func_arg.replace(\"Union[\", \"typing.Union[\")\n                    if len(events) > 1:\n                        doc.append(\"    @typing.overload\")\n                    impl = \"\"\n                    if len(events) == 1:\n                        impl = f\"        return super().{event_type}(event=event,f=f)\"\n                    doc.append(\n                        f\"    def {event_type}(self, event: Literal['{event['name'].lower()}'], f: typing.Callable[['{func_arg}'], '{return_type}']) -> None:\"\n                    )\n                    doc.append(\n                        f'        \"\"\"{self.beautify_method_comment(event[\"comment\"], \" \" * 8)}\"\"\"'\n                    )\n                    doc.append(impl)\n                if len(events) > 1:\n                    doc.append(\n                        f\"    def {event_type}(self, event: str, f: typing.Callable[...,{return_type}]) -> None:\"\n                    )\n                    doc.append(f\"        return super().{event_type}(event=event,f=f)\")\n            print(\"\\n\".join(doc))\n\n    def indent_paragraph(self, p: str, indent: str) -> str:\n        lines = p.split(\"\\n\")\n        result = [lines[0]]\n        for line in lines[1:]:\n            result.append(indent + line)\n        return \"\\n\".join(result)\n\n    def beautify_method_comment(self, comment: str, indent: str) -> str:\n        comment = self.filter_out_redudant_python_code_snippets(comment)\n        comment = comment.replace(\"\\\\\", \"\\\\\\\\\")\n        comment = comment.replace('\"', '\\\\\"')\n        lines = comment.split(\"\\n\")\n        result = []\n        skip_example = False\n        last_was_blank = True\n        for line in lines:\n            if not line.strip():\n                last_was_blank = True\n                continue\n            match = re.match(r\"\\s*```(.+)\", line)\n            if match:\n                lang = match[1]\n                if lang in [\"html\", \"yml\", \"sh\", \"py\", \"python\"]:\n                    skip_example = False\n                elif lang == \"python \" + (\"async\" if self.is_async else \"sync\"):\n                    skip_example = False\n                    line = \"```py\"\n                else:\n                    skip_example = True\n            if not skip_example:\n                if last_was_blank:\n                    last_was_blank = False\n                    result.append(\"\")\n                result.append(self.render_links(line))\n            if skip_example and line.strip() == \"```\":\n                skip_example = False\n        comment = self.indent_paragraph(\"\\n\".join(result), indent)\n        return self.resolve_playwright_dev_links(comment)\n\n    def filter_out_redudant_python_code_snippets(self, comment: str) -> str:\n        groups = []\n        current_group = []\n        lines = comment.split(\"\\n\")\n        start_pos = None\n        for i in range(len(lines)):\n            line = lines[i].strip()\n            if line.startswith(\"```py\"):\n                start_pos = i\n            elif line == \"```\" and start_pos is not None:\n                current_group.append((start_pos, i))\n                start_pos = None\n            elif (\n                (line.startswith(\"```\") or i == len(lines) - 1)\n                and start_pos is None\n                and len(current_group) == 2\n            ):\n                groups.append(current_group)\n                current_group = []\n        groups.reverse()\n        for first_pos, second_pos in groups:\n            # flake8: noqa: E203\n            second_snippet_is_async = \"await\" in lines[second_pos[0] : second_pos[1]]\n            if second_snippet_is_async == self.is_async:\n                # flake8: noqa: E203\n                del lines[first_pos[0] : first_pos[1] + 1]\n            else:\n                # flake8: noqa: E203\n                del lines[second_pos[0] : second_pos[1] + 1]\n        return \"\\n\".join(lines)\n\n    def resolve_playwright_dev_links(self, comment: str) -> str:\n        def replace_callback(m: re.Match) -> str:\n            link_text = m.group(1)\n            link_href = m.group(2)\n            resolved = urljoin(\n                \"https://playwright.dev/python/docs/api/\", link_href.replace(\".md\", \"\")\n            )\n            return f\"[{link_text}]({resolved})\"\n\n        # matches against internal markdown links which start with '.'/'..'\n        # e.g. [Playwright](./class-foobar.md)\n        return re.sub(r\"\\[([^\\]]+)\\]\\((\\.[^\\)]+)\\)\", replace_callback, comment)\n\n    def render_links(self, comment: str) -> str:\n        for [old, new] in self.links.items():\n            comment = comment.replace(old, new)\n        return comment\n\n    def make_optional(self, text: str) -> str:\n        if text.startswith(\"Union[\"):\n            if text.endswith(\"None]\"):\n                return text\n            return text[:-1] + \", None]\"\n        return f\"Union[{text}, None]\"\n\n    def compare_types(\n        self, value: Any, doc_value: Any, fqname: str, direction: str\n    ) -> None:\n        if \"(arg=)\" in fqname or \"(pageFunction=)\" in fqname:\n            return\n        code_type = self.serialize_python_type(value, direction)\n        doc_type = self.serialize_doc_type(doc_value[\"type\"], direction)\n        if not doc_value[\"required\"]:\n            doc_type = self.make_optional(doc_type)\n\n        if doc_type != code_type:\n            self.errors.add(\n                f\"Parameter type mismatch in {fqname}: documented as {doc_type}, code has {code_type}\"\n            )\n\n    def serialize_python_type(self, value: Any, direction: str) -> str:\n        str_value = str(value)\n        if isinstance(value, list):\n            return f\"[{', '.join(list(map(lambda a: self.serialize_python_type(a, direction), value)))}]\"\n        if str_value == \"<class 'playwright._impl._errors.Error'>\":\n            return \"Error\"\n        if str_value == \"<class 'NoneType'>\":\n            return \"None\"\n        if str_value == \"<class 'datetime.datetime'>\":\n            return \"datetime.datetime\"\n        match = re.match(r\"^<class '((?:pathlib\\.)?\\w+)'>$\", str_value)\n        if match:\n            return match.group(1)\n        if str_value == str(pathlib.Path):\n            return \"pathlib.Path\"\n        match = re.match(\n            r\"playwright._impl._event_context_manager.EventContextManagerImpl\\[playwright._impl.[^.]+.(.*)\\]\",\n            str_value,\n        )\n        if match:\n            return \"EventContextManager[\" + match.group(1) + \"]\"\n        match = re.match(r\"^<class 'playwright\\._impl\\.[\\w_]+\\.([^']+)'>$\", str_value)\n        if match and \"_api_structures\" not in str_value and \"_errors\" not in str_value:\n            if match.group(1) == \"EventContextManagerImpl\":\n                return \"EventContextManager\"\n            return match.group(1)\n\n        match = re.match(r\"^typing\\.(\\w+)$\", str_value)\n        if match:\n            return match.group(1)\n\n        origin = get_origin(value)\n        args = get_args(value)\n        hints = None\n        try:\n            hints = get_type_hints(value)\n        except Exception:\n            pass\n        if hints:\n            signature: List[str] = []\n            for [name, value] in hints.items():\n                signature.append(\n                    f\"{name}: {self.serialize_python_type(value, direction)}\"\n                )\n            return f\"{{{', '.join(signature)}}}\"\n        if origin == Union:\n            args = get_args(value)\n            if len(args) == 2 and str(args[1]) == \"<class 'NoneType'>\":\n                return self.make_optional(\n                    self.serialize_python_type(args[0], direction)\n                )\n            ll = list(map(lambda a: self.serialize_python_type(a, direction), args))\n            ll.sort(key=lambda item: \"}\" if item == \"None\" else item)\n            return f\"Union[{', '.join(ll)}]\"\n        if str(origin) == \"<class 'dict'>\":\n            args = get_args(value)\n            return f\"Dict[{', '.join(list(map(lambda a: self.serialize_python_type(a, direction), args)))}]\"\n        if str(origin) == \"<class 'collections.abc.Sequence'>\":\n            args = get_args(value)\n            return f\"Sequence[{', '.join(list(map(lambda a: self.serialize_python_type(a, direction), args)))}]\"\n        if str(origin) == \"<class 'list'>\":\n            args = get_args(value)\n            list_type = \"Sequence\" if direction == \"in\" else \"List\"\n            return f\"{list_type}[{', '.join(list(map(lambda a: self.serialize_python_type(a, direction), args)))}]\"\n        if str(origin) == \"<class 'collections.abc.Callable'>\":\n            args = get_args(value)\n            return f\"Callable[{', '.join(list(map(lambda a: self.serialize_python_type(a, direction), args)))}]\"\n        if str(origin) == \"<class 're.Pattern'>\":\n            return \"Pattern[str]\"\n        if str(origin) == \"typing.Literal\":\n            args = get_args(value)\n            if len(args) == 1:\n                return '\"' + self.serialize_python_type(args[0], direction) + '\"'\n            body = \", \".join(\n                list(\n                    map(\n                        lambda a: '\"' + self.serialize_python_type(a, direction) + '\"',\n                        args,\n                    )\n                )\n            )\n            return f\"Union[{body}]\"\n        return str_value\n\n    def serialize_doc_type(self, type: Any, direction: str) -> str:\n        result = self.inner_serialize_doc_type(type, direction)\n        return result\n\n    def inner_serialize_doc_type(self, type: Any, direction: str) -> str:\n        if type[\"name\"] == \"Promise\":\n            type = type[\"templates\"][0]\n\n        if \"union\" in type:\n            ll = [self.serialize_doc_type(t, direction) for t in type[\"union\"]]\n            ll.sort(key=lambda item: \"}\" if item == \"None\" else item)\n            for i in range(len(ll)):\n                if ll[i].startswith(\"Union[\"):\n                    ll[i] = ll[i][6:-1]\n            return f\"Union[{', '.join(ll)}]\"\n\n        type_name = type[\"name\"]\n        if type_name == \"path\":\n            if direction == \"in\":\n                return \"Union[pathlib.Path, str]\"\n            else:\n                return \"pathlib.Path\"\n\n        if type_name == \"function\" and \"args\" not in type:\n            return \"Callable\"\n\n        if type_name == \"function\":\n            return_type = \"Any\"\n            if type.get(\"returnType\"):\n                return_type = self.serialize_doc_type(type[\"returnType\"], direction)\n            return f\"Callable[[{', '.join(self.serialize_doc_type(t, direction) for t in type['args'])}], {return_type}]\"\n\n        if \"templates\" in type:\n            base = type_name\n            if type_name == \"Array\":\n                base = \"Sequence\" if direction == \"in\" else \"List\"\n            if type_name == \"Object\" or type_name == \"Map\":\n                base = \"Dict\"\n            return f\"{base}[{', '.join(self.serialize_doc_type(t, direction) for t in type['templates'])}]\"\n\n        if type_name == \"Object\" and \"properties\" in type:\n            items = []\n            for p in type[\"properties\"]:\n                items.append(\n                    (p[\"name\"])\n                    + \": \"\n                    + (\n                        self.serialize_doc_type(p[\"type\"], direction)\n                        if p[\"required\"]\n                        else self.make_optional(\n                            self.serialize_doc_type(p[\"type\"], direction)\n                        )\n                    )\n                )\n            return f\"{{{', '.join(items)}}}\"\n        if type_name == \"boolean\":\n            return \"bool\"\n        if type_name == \"long\":\n            return \"int\"\n        if type_name.lower() == \"string\":\n            return \"str\"\n        if type_name == \"any\" or type_name == \"unknown\" or type_name == \"Serializable\":\n            return \"Any\"\n        if type_name == \"Object\":\n            return \"Dict\"\n        if type_name == \"Function\":\n            return \"Callable\"\n        if type_name == \"Buffer\" or type_name == \"ReadStream\":\n            return \"bytes\"\n        if type_name == \"Date\":\n            return \"datetime.datetime\"\n        if type_name == \"URL\":\n            return \"str\"\n        if type_name == \"RegExp\":\n            return \"Pattern[str]\"\n        if type_name == \"null\":\n            return \"None\"\n        if type_name == \"EvaluationArgument\":\n            return \"Dict\"\n        return type[\"name\"]\n\n    def print_remainder(self) -> None:\n        for [class_name, clazz] in self.classes.items():\n            for [member_name, member] in clazz[\"members\"].items():\n                if member.get(\"deprecated\"):\n                    continue\n                if class_name in [\"Error\"]:\n                    continue\n                entry = f\"{class_name}.{member_name}\"\n                if entry not in self.printed_entries:\n                    self.errors.add(f\"Method not implemented: {entry}\")\n\n        with open(\"scripts/expected_api_mismatch.txt\") as f:\n            for line in f.readlines():\n                sline = line.strip()\n                if not len(sline) or sline.startswith(\"#\"):\n                    continue\n                if sline in self.errors:\n                    self.errors.remove(sline)\n                else:\n                    print(\"No longer there: \" + sline, file=stderr)\n\n        if len(self.errors) > 0:\n            for error in self.errors:\n                print(error, file=stderr)\n            exit(1)\n\n\ndef works_for_python(item: Any) -> bool:\n    return not item[\"langs\"].get(\"only\") or \"python\" in item[\"langs\"][\"only\"]\n\n\ndef name_or_alias(item: Any) -> str:\n    alias = (\n        item[\"langs\"].get(\"aliases\").get(\"python\")\n        if item[\"langs\"].get(\"aliases\")\n        else None\n    )\n    return alias or item[\"name\"]\n\n\ndef self_or_override(item: Any) -> Any:\n    override = (\n        item[\"langs\"].get(\"overrides\").get(\"python\")\n        if item[\"langs\"].get(\"overrides\")\n        else None\n    )\n    return override or item\n\n\ndef apply_type_or_override(member: Any) -> Any:\n    if member[\"langs\"].get(\"types\") and member[\"langs\"][\"types\"].get(\"python\"):\n        member[\"type\"] = member[\"langs\"][\"types\"][\"python\"]\n"
  },
  {
    "path": "scripts/example_async.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\n\nfrom playwright.async_api import async_playwright\n\n\nasync def main() -> None:\n    async with async_playwright() as p:\n        for browser_type in [p.chromium, p.firefox, p.webkit]:\n            browser = await browser_type.launch()\n            page = await browser.new_page()\n            assert await page.evaluate(\"() => 11 * 11\") == 121\n            await browser.close()\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n"
  },
  {
    "path": "scripts/example_sync.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import sync_playwright\n\n\ndef main() -> None:\n    with sync_playwright() as p:\n        for browser_type in [p.chromium, p.firefox, p.webkit]:\n            browser = browser_type.launch()\n            page = browser.new_page()\n            assert page.evaluate(\"() => 11 * 11\") == 121\n            browser.close()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/expected_api_mismatch.txt",
    "content": "# Playwright Python API\n\n# Hidden property\nParameter not documented: Browser.new_context(default_browser_type=)\nParameter not documented: Browser.new_page(default_browser_type=)\n\n# One vs two arguments in the callback, Python explicitly unions.\nParameter type mismatch in BrowserContext.route(handler=): documented as Callable[[Route, Request], Union[Any, Any]], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\nParameter type mismatch in BrowserContext.unroute(handler=): documented as Union[Callable[[Route, Request], Union[Any, Any]], None], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\nParameter type mismatch in Page.route(handler=): documented as Callable[[Route, Request], Union[Any, Any]], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any]]\nParameter type mismatch in Page.unroute(handler=): documented as Union[Callable[[Route, Request], Union[Any, Any]], None], code has Union[Callable[[Route, Request], Any], Callable[[Route], Any], None]\n\n# One vs two arguments in the callback, Python explicitly unions.\nParameter type mismatch in Page.add_locator_handler(handler=): documented as Callable[[Locator], Any], code has Union[Callable[[Locator], Any], Callable[[], Any]]\n\nParameter type mismatch in BrowserContext.route_web_socket(handler=): documented as Callable[[WebSocketRoute], Union[Any, Any]], code has Callable[[WebSocketRoute], Any]\nParameter type mismatch in Page.route_web_socket(handler=): documented as Callable[[WebSocketRoute], Union[Any, Any]], code has Callable[[WebSocketRoute], Any]\nParameter type mismatch in WebSocketRoute.on_close(handler=): documented as Callable[[Union[int, undefined]], Union[Any, Any]], code has Callable[[Union[int, None], Union[str, None]], Any]\nParameter type mismatch in WebSocketRoute.on_message(handler=): documented as Callable[[str], Union[Any, Any]], code has Callable[[Union[bytes, str]], Any]\n"
  },
  {
    "path": "scripts/generate_api.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nimport sys\nfrom types import FunctionType\nfrom typing import Any, Dict, List, Match, Optional, Union, cast, get_args, get_origin\nfrom typing import get_type_hints as typing_get_type_hints\n\nfrom playwright._impl._assertions import (\n    APIResponseAssertions,\n    LocatorAssertions,\n    PageAssertions,\n)\nfrom playwright._impl._browser import Browser\nfrom playwright._impl._browser_context import BrowserContext\nfrom playwright._impl._browser_type import BrowserType\nfrom playwright._impl._cdp_session import CDPSession\nfrom playwright._impl._clock import Clock\nfrom playwright._impl._console_message import ConsoleMessage\nfrom playwright._impl._dialog import Dialog\nfrom playwright._impl._download import Download\nfrom playwright._impl._element_handle import ElementHandle\nfrom playwright._impl._fetch import APIRequest, APIRequestContext, APIResponse\nfrom playwright._impl._file_chooser import FileChooser\nfrom playwright._impl._frame import Frame\nfrom playwright._impl._helper import Error, to_snake_case\nfrom playwright._impl._input import Keyboard, Mouse, Touchscreen\nfrom playwright._impl._js_handle import JSHandle, Serializable\nfrom playwright._impl._locator import FrameLocator, Locator\nfrom playwright._impl._network import (\n    Request,\n    Response,\n    Route,\n    WebSocket,\n    WebSocketRoute,\n)\nfrom playwright._impl._page import Page, Worker\nfrom playwright._impl._playwright import Playwright\nfrom playwright._impl._selectors import Selectors\nfrom playwright._impl._tracing import Tracing\nfrom playwright._impl._video import Video\nfrom playwright._impl._web_error import WebError\n\n\ndef process_type(value: Any, param: bool = False) -> str:\n    value = str(value)\n    value = re.sub(\"pathlib._local.Path\", \"pathlib.Path\", value)\n    value = re.sub(r\"<class '([^']+)'>\", r\"\\1\", value)\n    value = re.sub(r\"NoneType\", \"None\", value)\n    value = re.sub(r\"playwright\\._impl\\._api_structures.([\\w]+)\", r\"\\1\", value)\n    value = re.sub(r\"playwright\\._impl\\.[\\w]+\\.([\\w]+)\", r'\"\\1\"', value)\n    value = re.sub(r\"typing.Literal\", \"Literal\", value)\n    if param:\n        value = re.sub(r\"^typing.Union\\[([^,]+), None\\]$\", r\"\\1 = None\", value)\n        value = re.sub(\n            r\"typing.Union\\[(Literal\\[[^\\]]+\\]), None\\]\", r\"\\1 = None\", value\n        )\n        value = re.sub(\n            r\"^typing.Union\\[(.+), None\\]$\", r\"typing.Union[\\1] = None\", value\n        )\n        value = re.sub(\n            r\"^typing.Optional\\[(.+)\\]$\", r\"typing.Optional[\\1] = None\", value\n        )\n        if not re.match(r\"typing.Optional\\[.*\\] = None\", value):\n            value = re.sub(r\"(.*) = None\", r\"typing.Optional[\\1] = None\", value)\n    return value\n\n\npositional_exceptions = [\n    r\"abort\\.errorCode\",\n    r\"accept\\.promptText\",\n    r\"add_init_script\\.script\",\n    r\"cookies\\.urls\",\n    r\"dispatch_event\\.eventInit\",\n    r\"eval.*\\.arg\",\n    r\"expect_.*\\.predicate\",\n    r\"evaluate_handle\\.arg\",\n    r\"frame.*\\.name\",\n    r\"register\\.script\",\n    r\"select_option\\.value\",\n    r\"send\\.params\",\n    r\"set_geolocation\\.geolocation\",\n    r\"wait_for_.*\\.predicate\",\n    r\"wait_for_load_state\\.state\",\n    r\"unroute\\.handler\",\n]\n\n\ndef is_positional_exception(key: str) -> bool:\n    for pattern in positional_exceptions:\n        if re.match(pattern, key):\n            return True\n    return False\n\n\ndef signature(func: FunctionType, indent: int) -> str:\n    hints = get_type_hints(func, globals())\n    tokens = [\"self\"]\n    split = \",\\n\" + \" \" * indent\n\n    saw_optional = False\n    for [name, value] in hints.items():\n        if name == \"return\":\n            continue\n        positional_exception = is_positional_exception(f\"{func.__name__}.{name}\")\n        if saw_optional and positional_exception:\n            raise Exception(\n                \"Positional exception is not first in the list \"\n                + f\"{func.__name__}.{name}\"\n            )\n        processed = process_type(value, True)\n        if (\n            not positional_exception\n            and not saw_optional\n            and processed.startswith(\"typing.Optional\")\n        ):\n            saw_optional = True\n            tokens.append(\"*\")\n        tokens.append(f\"{to_snake_case(name)}: {processed}\")\n    return split.join(tokens)\n\n\ndef arguments(func: FunctionType, indent: int) -> str:\n    hints = get_type_hints(func, globals())\n    tokens = []\n    split = \",\\n\" + \" \" * indent\n    for [name, value] in hints.items():\n        value_str = str(value)\n        if name == \"return\":\n            continue\n        assert (\n            \"_\" not in name\n        ), f\"Underscore in impl classes is not allowed, use camel case, func={func}, name={name}\"\n        if \"Callable\" in value_str:\n            tokens.append(f\"{name}=self._wrap_handler({to_snake_case(name)})\")\n        elif (\n            \"typing.Any\" in value_str\n            or \"typing.Dict\" in value_str\n            or \"typing.Sequence\" in value_str\n            or \"Handle\" in value_str\n        ):\n            tokens.append(f\"{name}=mapping.to_impl({to_snake_case(name)})\")\n        elif (\n            re.match(r\"<class 'playwright\\._impl\\.[\\w]+\\.[\\w]+\", value_str)\n            and \"_api_structures\" not in value_str\n        ):\n            tokens.append(f\"{name}={to_snake_case(name)}._impl_obj\")\n        elif (\n            re.match(r\"typing\\.Optional\\[playwright\\._impl\\.[\\w]+\\.[\\w]+\\]\", value_str)\n            and \"_api_structures\" not in value_str\n        ):\n            tokens.append(\n                f\"{name}={to_snake_case(name)}._impl_obj if {to_snake_case(name)} else None\"\n            )\n        else:\n            tokens.append(f\"{name}={to_snake_case(name)}\")\n    return split.join(tokens)\n\n\ndef return_type(func: FunctionType) -> str:\n    value = get_type_hints(func, globals())[\"return\"]\n    return process_type(value)\n\n\ndef short_name(t: Any) -> str:\n    match = cast(\n        Match[str], re.compile(r\"playwright\\._impl\\.[^.]+\\.([^']+)\").search(str(t))\n    )\n    return match.group(1)\n\n\ndef return_value(value: Any) -> List[str]:\n    value_str = str(value)\n    if \"playwright\" not in value_str:\n        return [\"mapping.from_maybe_impl(\", \")\"]\n    if (\n        get_origin(value) == Union\n        and len(get_args(value)) == 2\n        and str(get_args(value)[1]) == \"<class 'NoneType'>\"\n    ):\n        return [\"mapping.from_impl_nullable(\", \")\"]\n    if str(get_origin(value)) in [\n        \"<class 'list'>\",\n        \"<class 'collections.abc.Sequence'>\",\n    ]:\n        return [\"mapping.from_impl_list(\", \")\"]\n    if str(get_origin(value)) == \"<class 'dict'>\":\n        return [\"mapping.from_impl_dict(\", \")\"]\n    return [\"mapping.from_impl(\", \")\"]\n\n\nheader = \"\"\"\n# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport typing\nimport pathlib\nimport datetime\n\nfrom typing import Literal\n\n\nfrom playwright._impl._api_structures import Cookie, SetCookieParam, FloatRect, FilePayload, Geolocation, HttpCredentials, PdfMargins, Position, ProxySettings, ResourceTiming, SourceLocation, StorageState, ClientCertificate, ViewportSize, RemoteAddr, SecurityDetails, RequestSizes, NameValue, TracingGroupLocation\nfrom playwright._impl._browser import Browser as BrowserImpl\nfrom playwright._impl._browser_context import BrowserContext as BrowserContextImpl\nfrom playwright._impl._browser_type import BrowserType as BrowserTypeImpl\nfrom playwright._impl._clock import Clock as ClockImpl\nfrom playwright._impl._cdp_session import CDPSession as CDPSessionImpl\nfrom playwright._impl._console_message import ConsoleMessage as ConsoleMessageImpl\nfrom playwright._impl._dialog import Dialog as DialogImpl\nfrom playwright._impl._download import Download as DownloadImpl\nfrom playwright._impl._element_handle import ElementHandle as ElementHandleImpl\nfrom playwright._impl._file_chooser import FileChooser as FileChooserImpl\nfrom playwright._impl._frame import Frame as FrameImpl\nfrom playwright._impl._input import Keyboard as KeyboardImpl, Mouse as MouseImpl, Touchscreen as TouchscreenImpl\nfrom playwright._impl._js_handle import JSHandle as JSHandleImpl\nfrom playwright._impl._network import Request as RequestImpl, Response as ResponseImpl, Route as RouteImpl, WebSocket as WebSocketImpl, WebSocketRoute as WebSocketRouteImpl\nfrom playwright._impl._page import Page as PageImpl, Worker as WorkerImpl\nfrom playwright._impl._web_error import WebError as WebErrorImpl\nfrom playwright._impl._playwright import Playwright as PlaywrightImpl\nfrom playwright._impl._selectors import Selectors as SelectorsImpl\nfrom playwright._impl._video import Video as VideoImpl\nfrom playwright._impl._tracing import Tracing as TracingImpl\nfrom playwright._impl._locator import Locator as LocatorImpl, FrameLocator as FrameLocatorImpl\nfrom playwright._impl._errors import Error\nfrom playwright._impl._fetch import APIRequest as APIRequestImpl, APIResponse as APIResponseImpl, APIRequestContext as APIRequestContextImpl\nfrom playwright._impl._assertions import PageAssertions as PageAssertionsImpl, LocatorAssertions as LocatorAssertionsImpl, APIResponseAssertions as APIResponseAssertionsImpl\n\"\"\"\n\n\ngenerated_types = [\n    Request,\n    Response,\n    Route,\n    WebSocket,\n    WebSocketRoute,\n    Keyboard,\n    Mouse,\n    Touchscreen,\n    JSHandle,\n    ElementHandle,\n    FileChooser,\n    Frame,\n    FrameLocator,\n    Worker,\n    Selectors,\n    Clock,\n    ConsoleMessage,\n    Dialog,\n    Download,\n    Video,\n    Page,\n    WebError,\n    BrowserContext,\n    CDPSession,\n    Browser,\n    BrowserType,\n    Playwright,\n    Tracing,\n    Locator,\n    APIResponse,\n    APIRequestContext,\n    APIRequest,\n    PageAssertions,\n    LocatorAssertions,\n    APIResponseAssertions,\n]\n\nall_types = generated_types + [\n    Error,\n]\n\napi_globals = globals()\nassert Serializable\n\n\n# Python 3.11+ does not treat default args with None as Optional anymore, this wrapper will still wrap them.\n# https://github.com/python/cpython/issues/90353\ndef get_type_hints(func: Any, globalns: Any) -> Dict[str, Any]:\n    original_value = typing_get_type_hints(func, globalns)\n    if sys.version_info < (3, 11):\n        return original_value\n    for key, value in _get_defaults(func).items():\n        if value is None and original_value[key] is not Optional:\n            original_value[key] = Optional[original_value[key]]\n    return original_value\n\n\ndef _get_defaults(func: Any) -> Dict[str, Any]:\n    \"\"\"Internal helper to extract the default arguments, by name.\"\"\"\n    try:\n        code = func.__code__\n    except AttributeError:\n        # Some built-in functions don't have __code__, __defaults__, etc.\n        return {}\n    pos_count = code.co_argcount\n    arg_names = code.co_varnames\n    arg_names = arg_names[:pos_count]\n    defaults = func.__defaults__ or ()\n    kwdefaults = func.__kwdefaults__\n    res = dict(kwdefaults) if kwdefaults else {}\n    pos_offset = pos_count - len(defaults)\n    for name, value in zip(arg_names[pos_offset:], defaults):\n        assert name not in res\n        res[name] = value\n    return res\n"
  },
  {
    "path": "scripts/generate_async_api.py",
    "content": "#!/usr/bin/env python\n# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nimport re\nfrom types import FunctionType\nfrom typing import Any\n\nfrom documentation_provider import DocumentationProvider\nfrom generate_api import (\n    api_globals,\n    arguments,\n    generated_types,\n    get_type_hints,\n    header,\n    process_type,\n    return_type,\n    return_value,\n    short_name,\n    signature,\n)\n\ndocumentation_provider = DocumentationProvider(True)\n\n\ndef generate(t: Any) -> None:\n    print(\"\")\n    class_name = short_name(t)\n    base_class = t.__bases__[0].__name__\n    if class_name in [\"Page\", \"BrowserContext\", \"Browser\"]:\n        base_sync_class = \"AsyncContextManager\"\n    elif base_class in [\"ChannelOwner\", \"object\", \"AssertionsBase\"]:\n        base_sync_class = \"AsyncBase\"\n    else:\n        base_sync_class = base_class\n    print(f\"class {class_name}({base_sync_class}):\")\n    print(\"\")\n    documentation_provider.print_events(class_name)\n    for [name, type] in get_type_hints(t, api_globals).items():\n        print(\"\")\n        print(\"    @property\")\n        print(f\"    def {name}(self) -> {process_type(type)}:\")\n        documentation_provider.print_entry(class_name, name, {\"return\": type}, True)\n        [prefix, suffix] = return_value(type)\n        prefix = \"        return \" + prefix + f\"self._impl_obj.{name}\"\n        print(f\"{prefix}{suffix}\")\n    for [name, value] in t.__dict__.items():\n        if name.startswith(\"_\"):\n            continue\n        if str(value).startswith(\"<property\"):\n            value = value.fget\n            print(\"\")\n            print(\"    @property\")\n            print(\n                f\"    def {name}({signature(value, len(name) + 9)}) -> {return_type(value)}:\"\n            )\n            documentation_provider.print_entry(\n                class_name, name, get_type_hints(value, api_globals), True\n            )\n            [prefix, suffix] = return_value(\n                get_type_hints(value, api_globals)[\"return\"]\n            )\n            prefix = \"        return \" + prefix + f\"self._impl_obj.{name}\"\n            print(f\"{prefix}{arguments(value, len(prefix))}{suffix}\")\n    for [name, value] in t.__dict__.items():\n        if isinstance(value, FunctionType) and \"remove_listener\" != name:\n            # List of dunder methods to allow without docs\n            allow_without_docs_methods = [\n                \"__getitem__\",\n            ]\n            if name.startswith(\"_\") and name not in allow_without_docs_methods:\n                continue\n            is_async = inspect.iscoroutinefunction(value)\n            return_type_value = return_type(value)\n            return_type_value = re.sub(r\"\\\"([^\\\"]+)Impl\\\"\", r\"\\1\", return_type_value)\n            return_type_value = return_type_value.replace(\n                \"EventContextManager\", \"AsyncEventContextManager\"\n            )\n            print(\"\")\n            async_prefix = \"async \" if is_async else \"\"\n            print(\n                f\"    {async_prefix}def {name}({signature(value, len(name) + 9)}) -> {return_type_value}:\"\n            )\n            # Allow dunder methods without docs\n            if name not in allow_without_docs_methods:\n                documentation_provider.print_entry(\n                    class_name, name, get_type_hints(value, api_globals)\n                )\n            if class_name in [\n                \"LocatorAssertions\",\n                \"PageAssertions\",\n                \"APIResponseAssertions\",\n            ]:\n                print(\"        __tracebackhide__ = True\")\n            if \"expect_\" in name:\n                print(\"\")\n                print(\n                    f\"        return AsyncEventContextManager(self._impl_obj.{name}({arguments(value, 12)}).future)\"\n                )\n            else:\n                [prefix, suffix] = return_value(\n                    get_type_hints(value, api_globals)[\"return\"]\n                )\n                if is_async:\n                    prefix += \"await \"\n                prefix = prefix + f\"self._impl_obj.{name}(\"\n                suffix = \")\" + suffix\n                print(\n                    f\"\"\"\n        return {prefix}{arguments(value, len(prefix))}{suffix}\"\"\"\n                )\n    print(\"\")\n    print(f\"mapping.register({class_name}Impl, {class_name})\")\n\n\ndef main() -> None:\n    print(header)\n    print(\n        \"from playwright._impl._async_base import AsyncEventContextManager, AsyncBase, AsyncContextManager, mapping\"\n    )\n\n    for t in generated_types:\n        generate(t)\n    documentation_provider.print_remainder()\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    main()\n"
  },
  {
    "path": "scripts/generate_sync_api.py",
    "content": "#!/usr/bin/env python\n# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nimport re\nimport sys\nfrom types import FunctionType\nfrom typing import Any\n\nfrom documentation_provider import DocumentationProvider\nfrom generate_api import (\n    api_globals,\n    arguments,\n    generated_types,\n    get_type_hints,\n    header,\n    process_type,\n    return_type,\n    return_value,\n    short_name,\n    signature,\n)\n\ndocumentation_provider = DocumentationProvider(False)\n\n\ndef generate(t: Any) -> None:\n    print(\"\")\n    class_name = short_name(t)\n    base_class = t.__bases__[0].__name__\n    if class_name in [\"Page\", \"BrowserContext\", \"Browser\"]:\n        base_sync_class = \"SyncContextManager\"\n    elif base_class in [\"ChannelOwner\", \"object\", \"AssertionsBase\"]:\n        base_sync_class = \"SyncBase\"\n    else:\n        base_sync_class = base_class\n    print(f\"class {class_name}({base_sync_class}):\")\n    print(\"\")\n    documentation_provider.print_events(class_name)\n    for [name, type] in get_type_hints(t, api_globals).items():\n        print(\"\")\n        print(\"    @property\")\n        print(f\"    def {name}(self) -> {process_type(type)}:\")\n        documentation_provider.print_entry(class_name, name, {\"return\": type}, True)\n        [prefix, suffix] = return_value(type)\n        prefix = \"        return \" + prefix + f\"self._impl_obj.{name}\"\n        print(f\"{prefix}{suffix}\")\n    for [name, value] in t.__dict__.items():\n        if name.startswith(\"_\"):\n            continue\n        if str(value).startswith(\"<property\"):\n            value = value.fget\n            print(\"\")\n            print(\"    @property\")\n            print(\n                f\"    def {name}({signature(value, len(name) + 9)}) -> {return_type(value)}:\"\n            )\n            documentation_provider.print_entry(\n                class_name, name, get_type_hints(value, api_globals), True\n            )\n            [prefix, suffix] = return_value(\n                get_type_hints(value, api_globals)[\"return\"]\n            )\n            prefix = \"        return \" + prefix + f\"self._impl_obj.{name}\"\n            print(f\"{prefix}{arguments(value, len(prefix))}{suffix}\")\n    for [name, value] in t.__dict__.items():\n        if isinstance(value, FunctionType) and \"remove_listener\" != name:\n            # List of dunder methods to allow without docs\n            allow_without_docs_methods = [\n                \"__getitem__\",\n            ]\n            if name.startswith(\"_\") and name not in allow_without_docs_methods:\n                continue\n            is_async = inspect.iscoroutinefunction(value)\n            return_type_value = return_type(value)\n            return_type_value = re.sub(r\"\\\"([^\\\"]+)Impl\\\"\", r\"\\1\", return_type_value)\n            print(\"\")\n            print(\n                f\"    def {name}({signature(value, len(name) + 9)}) -> {return_type_value}:\"\n            )\n            # Allow dunder methods without docs\n            if name not in allow_without_docs_methods:\n                documentation_provider.print_entry(\n                    class_name, name, get_type_hints(value, api_globals)\n                )\n            if class_name in [\n                \"LocatorAssertions\",\n                \"PageAssertions\",\n                \"APIResponseAssertions\",\n            ]:\n                print(\"        __tracebackhide__ = True\")\n            if \"expect_\" in name:\n                print(\n                    f\"        return EventContextManager(self, self._impl_obj.{name}({arguments(value, 12)}).future)\"\n                )\n            else:\n                [prefix, suffix] = return_value(\n                    get_type_hints(value, api_globals)[\"return\"]\n                )\n                if is_async:\n                    prefix += f\"self._sync(self._impl_obj.{name}(\"\n                    suffix = \"))\" + suffix\n                else:\n                    prefix += f\"self._impl_obj.{name}(\"\n                    suffix = \")\" + suffix\n\n                print(\n                    f\"\"\"\n        return {prefix}{arguments(value, len(prefix))}{suffix}\"\"\"\n                )\n    print(\"\")\n    print(f\"mapping.register({class_name}Impl, {class_name})\")\n\n\ndef main() -> None:\n    assert sys.version_info >= (3, 9)\n\n    print(header)\n    print(\n        \"from playwright._impl._sync_base import EventContextManager, SyncBase, SyncContextManager, mapping\"\n    )\n\n    for t in generated_types:\n        generate(t)\n    documentation_provider.print_remainder()\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    main()\n"
  },
  {
    "path": "scripts/update_api.sh",
    "content": "#!/bin/bash\n\nfunction update_api {\n    echo \"Generating $1\"\n    file_name=\"$1\"\n    generate_script=\"$2\"\n    git checkout HEAD -- \"$file_name\"\n\n    if PYTHONIOENCODING=utf-8 python \"$generate_script\" > .x; then\n        mv .x \"$file_name\"\n        pre-commit run --files $file_name\n        echo \"Regenerated APIs\"\n    else\n        echo \"Exited due to errors\"\n        exit 1\n    fi\n}\n\nupdate_api \"playwright/sync_api/_generated.py\" \"scripts/generate_sync_api.py\"\nupdate_api \"playwright/async_api/_generated.py\" \"scripts/generate_async_api.py\"\n\nplaywright install\n\npython scripts/update_versions.py\n"
  },
  {
    "path": "scripts/update_versions.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport re\nfrom pathlib import Path\n\nfrom playwright.sync_api import sync_playwright\n\n\ndef main() -> None:\n    with sync_playwright() as p:\n        readme = Path(\"README.md\").resolve()\n        text = readme.read_text(encoding=\"utf-8\")\n        for browser_type in [p.chromium, p.firefox, p.webkit]:\n            rx = re.compile(\n                r\"<!-- GEN:\"\n                + browser_type.name\n                + r\"-version -->([^<]+)<!-- GEN:stop -->\"\n            )\n            browser = browser_type.launch()\n            text = rx.sub(\n                f\"<!-- GEN:{browser_type.name}-version -->{browser.version}<!-- GEN:stop -->\",\n                text,\n            )\n            browser.close()\n        readme.write_text(text, encoding=\"utf-8\")\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "setup.cfg",
    "content": "[flake8]\nignore =\n    E501\n    W503\n    E302\n    # Conflicts with black https://github.com/PyCQA/flake8/issues/1921\n    E704\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport glob\nimport os\nimport platform\nimport shutil\nimport subprocess\nimport sys\nimport zipfile\nfrom typing import Dict\n\ndriver_version = \"1.58.0\"\n\nbase_wheel_bundles = [\n    {\n        \"wheel\": \"macosx_10_13_x86_64.whl\",\n        \"machine\": \"x86_64\",\n        \"platform\": \"darwin\",\n        \"zip_name\": \"mac\",\n    },\n    {\n        \"wheel\": \"macosx_11_0_universal2.whl\",\n        \"machine\": \"x86_64\",\n        \"platform\": \"darwin\",\n        \"zip_name\": \"mac\",\n    },\n    {\n        \"wheel\": \"macosx_11_0_arm64.whl\",\n        \"machine\": \"arm64\",\n        \"platform\": \"darwin\",\n        \"zip_name\": \"mac-arm64\",\n    },\n    {\n        \"wheel\": \"manylinux1_x86_64.whl\",\n        \"machine\": \"x86_64\",\n        \"platform\": \"linux\",\n        \"zip_name\": \"linux\",\n    },\n    {\n        \"wheel\": \"manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\n        \"machine\": \"aarch64\",\n        \"platform\": \"linux\",\n        \"zip_name\": \"linux-arm64\",\n    },\n    {\n        \"wheel\": \"win32.whl\",\n        \"machine\": \"i386\",\n        \"platform\": \"win32\",\n        \"zip_name\": \"win32_x64\",\n    },\n    {\n        \"wheel\": \"win_amd64.whl\",\n        \"machine\": \"amd64\",\n        \"platform\": \"win32\",\n        \"zip_name\": \"win32_x64\",\n    },\n    {\n        \"wheel\": \"win_arm64.whl\",\n        \"machine\": \"arm64\",\n        \"platform\": \"win32\",\n        \"zip_name\": \"win32_arm64\",\n    },\n]\n\nif len(sys.argv) == 2 and sys.argv[1] == \"--list-wheels\":\n    for bundle in base_wheel_bundles:\n        print(bundle[\"wheel\"])\n    exit(0)\n\nfrom setuptools import setup  # noqa: E402\n\ntry:\n    from auditwheel.wheeltools import InWheel\nexcept ImportError:\n    InWheel = None\nfrom wheel.bdist_wheel import bdist_wheel as BDistWheelCommand  # noqa: E402\n\n\ndef extractall(zip: zipfile.ZipFile, path: str) -> None:\n    for name in zip.namelist():\n        member = zip.getinfo(name)\n        extracted_path = zip.extract(member, path)\n        attr = member.external_attr >> 16\n        if attr != 0:\n            os.chmod(extracted_path, attr)\n\n\ndef download_driver(zip_name: str) -> None:\n    zip_file = f\"playwright-{driver_version}-{zip_name}.zip\"\n    destination_path = \"driver/\" + zip_file\n    if os.path.exists(destination_path):\n        return\n    url = \"https://cdn.playwright.dev/builds/driver/\"\n    if (\n        \"-alpha\" in driver_version\n        or \"-beta\" in driver_version\n        or \"-next\" in driver_version\n    ):\n        url = url + \"next/\"\n    url = url + zip_file\n    temp_destination_path = destination_path + \".tmp\"\n    print(f\"Fetching {url}\")\n    # Don't replace this with urllib - Python won't have certificates to do SSL on all platforms.\n    subprocess.check_call([\"curl\", url, \"-o\", temp_destination_path])\n    os.rename(temp_destination_path, destination_path)\n\n\nclass PlaywrightBDistWheelCommand(BDistWheelCommand):\n    def run(self) -> None:\n        super().run()\n        os.makedirs(\"driver\", exist_ok=True)\n        os.makedirs(\"playwright/driver\", exist_ok=True)\n        self._download_and_extract_local_driver()\n\n        wheel = None\n        if os.getenv(\"PLAYWRIGHT_TARGET_WHEEL\", None):\n            wheel = list(\n                filter(\n                    lambda wheel: wheel[\"wheel\"]\n                    == os.getenv(\"PLAYWRIGHT_TARGET_WHEEL\"),\n                    base_wheel_bundles,\n                )\n            )[0]\n        else:\n            wheel = list(\n                filter(\n                    lambda wheel: wheel[\"platform\"] == sys.platform\n                    and wheel[\"machine\"] == platform.machine().lower(),\n                    base_wheel_bundles,\n                )\n            )[0]\n        assert wheel\n        self._build_wheel(wheel)\n\n    def _build_wheel(\n        self,\n        wheel_bundle: Dict[str, str],\n    ) -> None:\n        assert self.dist_dir\n        base_wheel_location: str = glob.glob(os.path.join(self.dist_dir, \"*.whl\"))[0]\n        without_platform = base_wheel_location[:-7]\n        download_driver(wheel_bundle[\"zip_name\"])\n        zip_file = f\"driver/playwright-{driver_version}-{wheel_bundle['zip_name']}.zip\"\n        with zipfile.ZipFile(zip_file, \"r\") as zip:\n            extractall(zip, f\"driver/{wheel_bundle['zip_name']}\")\n        wheel_location = without_platform + wheel_bundle[\"wheel\"]\n        shutil.copy(base_wheel_location, wheel_location)\n        with zipfile.ZipFile(\n            wheel_location, mode=\"a\", compression=zipfile.ZIP_DEFLATED\n        ) as zip:\n            driver_root = os.path.abspath(f\"driver/{wheel_bundle['zip_name']}\")\n            for dir_path, _, files in os.walk(driver_root):\n                for file in files:\n                    from_path = os.path.join(dir_path, file)\n                    to_path = os.path.relpath(from_path, driver_root)\n                    zip.write(from_path, f\"playwright/driver/{to_path}\")\n            zip.writestr(\n                \"playwright/driver/README.md\",\n                f\"{wheel_bundle['wheel']} driver package\",\n            )\n        os.remove(base_wheel_location)\n        for whlfile in glob.glob(os.path.join(self.dist_dir, \"*.whl\")):\n            os.makedirs(\"wheelhouse\", exist_ok=True)\n            if InWheel:\n                wheelhouse_whl = os.path.join(\"wheelhouse\", os.path.basename(whlfile))\n                shutil.move(whlfile, wheelhouse_whl)\n                with InWheel(in_wheel=wheelhouse_whl, out_wheel=whlfile):\n                    print(f\"Updating RECORD file of {whlfile}\")\n        print(\"Copying new wheels\")\n        shutil.rmtree(\"wheelhouse\")\n\n    def _download_and_extract_local_driver(\n        self,\n    ) -> None:\n        zip_names_for_current_system = set(\n            map(\n                lambda wheel: wheel[\"zip_name\"],\n                filter(\n                    lambda wheel: wheel[\"machine\"] == platform.machine().lower()\n                    and wheel[\"platform\"] == sys.platform,\n                    base_wheel_bundles,\n                ),\n            )\n        )\n        assert len(zip_names_for_current_system) == 1\n        zip_name = zip_names_for_current_system.pop()\n        download_driver(zip_name)\n        zip_file = f\"driver/playwright-{driver_version}-{zip_name}.zip\"\n        with zipfile.ZipFile(zip_file, \"r\") as zip:\n            extractall(zip, \"playwright/driver\")\n\n\nsetup(\n    cmdclass={\"bdist_wheel\": PlaywrightBDistWheelCommand},\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/assets/beforeunload.html",
    "content": "<div>beforeunload demo.</div>\n<script>\nwindow.addEventListener('beforeunload', event => {\n  // Chromium & WebKit way.\n  event.returnValue = 'Leave?';\n  // Firefox way.\n  event.preventDefault();\n});\n</script>\n"
  },
  {
    "path": "tests/assets/client-certificates/README.md",
    "content": "# Client Certificate test-certificates\n\nRegenerate all certificates by running:\n\n```\nbash generate.sh\n```\n"
  },
  {
    "path": "tests/assets/client-certificates/client/localhost/localhost.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIEizCCAnMCAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEAuxTIfOTWJzsLSYT/RXu6eUYe95oFA0gMM6pdQAws\nU08ufU8S68UzBIAb6zdwDqM6LD5FqLrwaTsUW73dKefb003EgA19L3sZTHSwnSv9\ng9+rnKw/8EA+1thFo7h3EfdWkO/nuxnPfEnHu5oxMk/mCu+hdjfgh01mxc00PIO/\nwioxZtuhzKNIUu+qQAj70Dwbkzy3Zb1QAnj+02AUYnrVTYhusmP4pjrfYgAWU1x4\nWN+XTSI27LnQHE8t8OxuICFwqEEPcJg4pCFNQgmO0gcRDbd4U7JgQ0xMXZ9na9k0\nf1GOgtjKsz5IIDrAwLnrIOEXiIYlNaQ/v28BrlWytaOHGFKmDxAbGXHz1WuftmR1\nvYQivC5fkXU3+QKWoGPZaLIhA7/ZOMSVgoo+Rv+mS6Mze/6LphatUMcI1oYX4ZBY\nFrsVsAeAO/FPZDcwVct0bdPCRRFBQpgl6DxMyItHMexNHwURi4YHwhYiBBe+Njc1\ntTh6dniA11MUVC5WmKME7CxgXxHVXE4MYcf/w1x4sOnxHneb1bCa8jOPELRc6byR\n8mk23fbBP42EPkrgpCtLhbHgskLKobLFav43xaIiYFn744U7rbINI+5RfJ8BH5Kw\nCB5RlAsymPxwDNcPXyyyeHEEUAQmtnJPrGPSGMN/QODgCWRXEvY556NGx0D5riu7\nMXkCAwEAAaAyMDAGCSqGSIb3DQEJDjEjMCEwHwYDVR0RBBgwFoIJbG9jYWxob3N0\nggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggIBAEYqpx28FFRHqXfyumTkkFb/\nhrlUD9qKUYSdjpsPLFRUYiLofNlYhCtEFEuHyLkNHqLO5Z6E7yOpm/HvdXY0eBQy\ntK0verggKXy8MnpLVtsFOf4DgenTwFRG3NbNk2/KynwiAwJ0iwOdDfAmwrzYFLeR\nuXysPE0lGJu277akuo/PDg3QEY44iDJgI4rqtY25sR8J23M9Roa5qN4bdaXl6l10\nUJhdugTG90gRFmmUVLLRa21A3j4m7pvWHu7zYJN7ylS/NQ6ZEnpNno7Ie6MLzrm6\nh8MOSoxtmrXi0e3aYf7PC3u3YLAjyUzHicgMdXk8SjtUXi6l3U5hU+6WBeOKKmwa\nvi+FUpRfnutkWpinNqikmMw0Sqy+bpXwbwgm2oD6driiMneBCT37gW/IyUPWVxzg\ndVgeQIt3i8SMwnGUrzfVo/gqjq4qFBpzS3h9jPPrMYQN2LQdmyo13R9UH26cLGE1\ncgVmqUKa11kX8353wfA36JuW5yGv2yaK4V1kwFISznMvk5AGdOHONdZOrN4sLIIY\n7Npqp5AO1KdoYcNVptCA4n6mN/30fTmA/W1ajsKcfIn5RlmvdgpooKoEgaU/X9RL\nd2ydIpijWbQqm+nF9skpO4jIFPfuQj6mMXCPWtsvbpHCoo0iMHHWfVFcpvsXGYN2\n6ji5z3opVapp1m5uTmcA\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/localhost/localhost.ext",
    "content": "subjectAltName=DNS:localhost,DNS:127.0.0.1\n"
  },
  {
    "path": "tests/assets/client-certificates/client/localhost/localhost.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC7FMh85NYnOwtJ\nhP9Fe7p5Rh73mgUDSAwzql1ADCxTTy59TxLrxTMEgBvrN3AOozosPkWouvBpOxRb\nvd0p59vTTcSADX0vexlMdLCdK/2D36ucrD/wQD7W2EWjuHcR91aQ7+e7Gc98Sce7\nmjEyT+YK76F2N+CHTWbFzTQ8g7/CKjFm26HMo0hS76pACPvQPBuTPLdlvVACeP7T\nYBRietVNiG6yY/imOt9iABZTXHhY35dNIjbsudAcTy3w7G4gIXCoQQ9wmDikIU1C\nCY7SBxENt3hTsmBDTExdn2dr2TR/UY6C2MqzPkggOsDAuesg4ReIhiU1pD+/bwGu\nVbK1o4cYUqYPEBsZcfPVa5+2ZHW9hCK8Ll+RdTf5ApagY9losiEDv9k4xJWCij5G\n/6ZLozN7/oumFq1QxwjWhhfhkFgWuxWwB4A78U9kNzBVy3Rt08JFEUFCmCXoPEzI\ni0cx7E0fBRGLhgfCFiIEF742NzW1OHp2eIDXUxRULlaYowTsLGBfEdVcTgxhx//D\nXHiw6fEed5vVsJryM48QtFzpvJHyaTbd9sE/jYQ+SuCkK0uFseCyQsqhssVq/jfF\noiJgWfvjhTutsg0j7lF8nwEfkrAIHlGUCzKY/HAM1w9fLLJ4cQRQBCa2ck+sY9IY\nw39A4OAJZFcS9jnno0bHQPmuK7sxeQIDAQABAoICAA5XaYcpg8E+JX9dUrRg58qk\nNXuFsxytSUIsrTlbtYotZ8LzbN/mHiMaLwm5Fj4JBUye+XgV3Jg0jzr5MxsjSxbH\nv2iRoCcjqKzTxTZHSQfy/ZTlH4GrayXNLol+eqJF87zopzsQn3dHsKgRCfRxa5Er\nDZWicvPsWxSOxpJdBzY7Rc48yAqH+eNhvAtspOExumtvHCAQgzGtVNufYfCque9X\npiTGxSj5GmbI2u1JCXDGszKWjN9Y3ztMVplBhq+v4JMFacmX4b+zTdjiIrC3GfeT\nOQYxhm+iSbhjn+oEnKGl/ubI98EF5UGTP3OGzR+YIdW1cuTJ0pk6SUa0Cx8hihmR\nrnUgxbXjR/sB383ES4kYVT+tm/a8PIvPd1gu4j28sNRXGgVZ8GU76Y2ZITm3eoDr\n7PzVWsTe+vbxBXenlcmzO9rj91yK0eDBaJnwAIPxHGFxTkQkx3T2nnWStkHrvT6h\n15fHITgKWE+K2FMCIJQj5gIT3qGnZxZDLIgmyowVV9ZXuI2XchehD+L7nVsMhsby\nyIITglz1miYgy7zVL0oolIXuE0eQ4xAZlu1D5wjAQy/VzyB7OYY6bTErC0EmXjNS\n3QAYsI+Kn7YgjNJJNuJ5cC2S8nHNJ2uTJ54KWeA5s1Ry8B5kJrMGZxCT6Uk3POcG\nRy5BxOczToKIgXeqm4IBAoIBAQDcvNBNXANexHn4r3yXgmL9IH4QXHAJ3h7tO6Ng\nzZh4K5VmoxSJ82CWQkJrs827F1kE2T9Mq6YPvXrh5VlKZjR+II2ZG7KqT00SULDy\nkSgG8OU/2+DWhyoQ6nt1RhXKDz0s4SjHwkeKwBFexTHf5ed+TILPfnPJIUX89gxV\n4EKL+EsqOsT6CMS7gruUuaA29VGVDneBw9KxKD9IdNXDrbot0DhidIYW1QkCed9g\ntnb2QvJrQmTkupUDI+HdWqOfpcBHwf9FIEI1q6cJtpVYSuaZfZX7hbvSHsTt9rwt\nb6gkiuPBy6qoXP1LWHc093tSzXEk1CWy0F4cCPi4r7UWqmWtAoIBAQDY95LGRUTn\nRKRSl2C5uZnsME5G/6XkqVcGhP5TOnT3/li/NLESZDaF+4iEhEh4M1oWVqShq4ps\nS98baYebriZDMmBm8L1YM63N+fhiI3q1+TF26f7zk6XPFQr88WIli58JmrK02EXG\n4XG/GBtzqfsnueBnwBGz5iE8phXqWyPs1/3qGyK+BHnUqnr3BAZpg8aw2PEjMzoC\nChfLMzSJZRGDNp7sslkMJ9H9CXoMZsG5YJkQp7aSBD1SI5rJ2q7JC3PmOR913CgU\n0DDhxyJhD77aTEwrZOByaHq8F8KxZ8cl+YRl/tnWu7Gwa2pfjnC9DCk1ARCuDcSg\nOW7JTtsd3zx9AoIBAH17uM7BaAkPmGcPG7zlmnBbcE7MvcReSSaDqLT3K53k6OGY\nA60Idff1YtznMiUReMGQ3rMvQQ/hn2Gbh88Lmvu4dcZ8QG0g96dZx72dVyva9ff/\nfyl1XSyQn+5jES/0ycohlZU5lIID/dvqLhgiEh9yT0q1kAzepXLQTOLkwe/gDprL\nHf8lzPDruMcrXzDe9KnPt5BFShj70D3YbUz4DcbNf8A4jaGdKaoGrj3EfIwyMq1W\n6RQ+HUfTtiqnxCyVhWFFn2Aknn70Pdj/upaevciz4/dAZy1j4H+GrCMIPoXHjwI0\nTae4dSXH/LxXk/vWXmOZVnT4jwdQ8lPLTx67b2ECggEAK5t20IrTknflXwQ12J5J\nJYN/+B0hxpeSeij4xNmW8NEaHTQF8uBZZQxtH9VGi4ImtR6s8CF+LM4DBYtsSgny\nfsb9QTNZmwSoBiIbnf3rh++R1YiqSWJ/jON51eTeCRXK3S9Og7KEM7jUF8hMnC6p\n4A4n4DJmXHYAcCQhe3zd95hh3E+f5/kWU3wAQu14LHTj1l+D98MwAYDtz1V3VbYO\nkwTDZGdkJmFKf0UMVrnAbfXQTdyngSmA+aVWUwO05Yt7u+X3QMUC+UvuxzIy4rc7\ncLytAnu/8L63DF7qLqXhDOzdg3J5bgNDb2Xnd1U1q4lqLtEL/S+fOWTRs3w55gMc\nMQKCAQA0tO3yakIOlizE7qlJye2n2Y/q0nSx1iFlKqEe1ja0O7/t+UzfklBiaDhA\nNAqYOiUsDsL/vf76KQ0uE/v0No/36SKvBuQxozdNEGlZHf7CnsrIg1w9llrJgkYA\n1FoSHgHCbC84+dUoonjYg9vx3Lsszw5O1bmwqTBaGSSglIr3f+a3zxtQUquxNEPS\nadvAKAdvXsNECj2cMkv3mxSokGQtd/s/rpvxJ8zi5nq5ig80Gfe/6GJpwJnC+sLr\n+tn38FNfk5pKXk8jBXjyLtMNDb2iJbiTa1iX1P76Ae2jtexSd2gELNu7oWbotb/O\nDjCwHSmcSUmAmx7IXfoyRQuKLeRw\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/localhost/localhost.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFKDCCAxCgAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRIwEAYDVQQDDAlsb2Nh\nbGhvc3QxIDAeBgNVBAoMF0NsaWVudCBDZXJ0aWZpY2F0ZSBEZW1vMB4XDTI1MDcy\nMTE1NTYxOVoXDTM1MDcxOTE1NTYxOVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIC\nIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuxTIfOTWJzsLSYT/RXu6eUYe\n95oFA0gMM6pdQAwsU08ufU8S68UzBIAb6zdwDqM6LD5FqLrwaTsUW73dKefb003E\ngA19L3sZTHSwnSv9g9+rnKw/8EA+1thFo7h3EfdWkO/nuxnPfEnHu5oxMk/mCu+h\ndjfgh01mxc00PIO/wioxZtuhzKNIUu+qQAj70Dwbkzy3Zb1QAnj+02AUYnrVTYhu\nsmP4pjrfYgAWU1x4WN+XTSI27LnQHE8t8OxuICFwqEEPcJg4pCFNQgmO0gcRDbd4\nU7JgQ0xMXZ9na9k0f1GOgtjKsz5IIDrAwLnrIOEXiIYlNaQ/v28BrlWytaOHGFKm\nDxAbGXHz1WuftmR1vYQivC5fkXU3+QKWoGPZaLIhA7/ZOMSVgoo+Rv+mS6Mze/6L\nphatUMcI1oYX4ZBYFrsVsAeAO/FPZDcwVct0bdPCRRFBQpgl6DxMyItHMexNHwUR\ni4YHwhYiBBe+Njc1tTh6dniA11MUVC5WmKME7CxgXxHVXE4MYcf/w1x4sOnxHneb\n1bCa8jOPELRc6byR8mk23fbBP42EPkrgpCtLhbHgskLKobLFav43xaIiYFn744U7\nrbINI+5RfJ8BH5KwCB5RlAsymPxwDNcPXyyyeHEEUAQmtnJPrGPSGMN/QODgCWRX\nEvY556NGx0D5riu7MXkCAwEAAaNjMGEwHwYDVR0RBBgwFoIJbG9jYWxob3N0ggkx\nMjcuMC4wLjEwHQYDVR0OBBYEFIE6kbIR0PlzaJuZg52JqXuFFQHBMB8GA1UdIwQY\nMBaAFPHaEfjJoIftSkHTb8mwme27LtifMA0GCSqGSIb3DQEBCwUAA4ICAQB7E57H\n19qUD4DjLxJGAVpDphD8mZg9aR7bUd6laXZ12VQnn3+OrF+6JjJ2TIr8ssRkkzc6\nrMhRqob+DxeB94JqlFQDmwP37wJXnuTtuGj71NHQal15b5ZB28fFdwWgUlYECWyg\nsYeMK5HMnNDziniRnjPoKU6f8urmUW2G8N2SOufnRar/StY/8u24IFh/vDTVJKWB\na22pVk/PIx6JcErfMx0OJdoIUZd/C9DT/a7t+Pt+t3EPVNM2fcjs9aMcg6tlU6Tp\nYwHMzkFQYmcrp2QEhwy23z2E+wnLbeHTULDsHVOLudUGDcSgkW48bPNTOtZTysQ+\nP4mJf+Ju2HC/EwuVrOa14uuay9c+hC1Q1gsGbLZB8NE6jOX5QYN2a7iiUEdCdufo\nwWIXEC+2dJiEji3ehwJp0q4jK15X0IOIfxLH4PholORH659DdCfpm7YAjoaPE9nr\n28MMd9Lj6zWmfyeb5p0IUDpkreL2huxjXHJkmSm4dcxDxLqsP6KSPlhrPvjAVUqw\n5M/jUarDAHaPLNlwqcqWODb891FIyRmc6YHtSRGujMyfJ8LRS2f9rHeyM3m2jMHf\n8EahOgyQ8nvSs/a42VS97IHkGl6qufK7ZUq/VMhQRb+KbaIDN5TLtNwEQ1I40Bm9\nUOoSX/uP1PstOZvf9gnYto8sf75B+PxO692uYw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/self-signed/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEyzCCArOgAwIBAgIUUo60oaPj20QM6oeGSn+2CT5j7GYwDQYJKoZIhvcNAQEL\nBQAwDjEMMAoGA1UEAwwDQm9iMB4XDTI1MDcyMTE1NTYyMFoXDTM1MDcxOTE1NTYy\nMFowDjEMMAoGA1UEAwwDQm9iMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC\nAgEAzQMXYOZz3ILrbF9qpDng2pw2wJf1UFopehwaYyu7riWJ9+ADrhSnCSFBM3Sc\nMBc/8dIR6etWwci8QwJ/MtvIU0yx4llq+53G+19Bc1teC6q/b4QCRDIcTGxOZoR+\njfYZVjPODEyJ5y5MZIo34ZP4bu+JnpT4W7+uojm3jOoyNPqXMcc70uAhfSKG+Jfr\nwZKteA4T5VKFytVcWgh4v03z8zTYeW3kD4lCsongBz6yu2dn3D6XMROnxiPwi+IR\nQqX1pwnJ0UA3CTeOHEw1jA3koxWIIg44PWaPaCj9Udrhf4ew00XLPWVZP8T5rVf8\nyUfecWQCR7FueJFqoLhPMMFi17rYmGZUvw3/YkXBjay4Q9e+G2WS3Xk8u+I1sCuV\nBJNBRv9DqtMC9D/N9NI8GkLrXwZmk82SXG+cQ0TSkNUHYI/03YKoqsn5H8PsG7Tc\n+Y2Ca6TaCWims7lvOg7U0E6lu2h5NGcdWHFPJ9qfe+xho/yfYYwGqEKanGAu1kd5\nSbIaX6/YiM5/Pp/96MeRNrB9kLzDnZTNuGtdCawVFgbkWmfX2Z6/a0d6SvZGDzBx\nxTVZRB0my01E1FP53MS8YRH98HUjGEwNRVq2e1W+aXldKppgZ85GZD3l2YTeuI0i\nPJCDUzQiaWzFtWc8s13YQ1HLCOyOXF7QqMyNCiLGb4xQ56kCAwEAAaMhMB8wHQYD\nVR0OBBYEFMqdCDxkZm8HxNI4MLLveAVTdH7UMA0GCSqGSIb3DQEBCwUAA4ICAQAB\nMKd3WEJ8zI51FuyeTcMq6L1zk2vmKTFg6T7HZJhNZoD1AYvvsKJ8mrqeSwhxqjlE\n0H2FGLY+Z8Fw3+TE1QuvTuz3gwRI+yzBEyqi/fEGCGrhOVcWaXGgLCtWG+BA4Su8\nHenK97/n3OnXUnBozRPZMH02IaLrOiEGbpaabXKCCabpm5U1oGq437e3SeAeIL7U\nWxuHhHBx0yo9j7ACaCL6mz8xpk8NaRZpPy22MTlrPKwbOK2eYf3Jy5fHa/f6edTs\nKqZewI7t4oe/OqKdjyTgGYkjTE3Xcmo2T/fmcAeEP3HJX267kCzBi5J3McwonWxD\nN8zz9qKSf5YGQy140eEOTjjESwlPz6zfrTW92YdCIr63k9UCDL2HGQTRSxB6g4BQ\nloVzKS9/BKhulGqvSGvEoj6D+qG/PgFlBtJoE71X+vSIxvdbnOVmOi/l+NGNuP1Z\nnwnDtZWp4BKshhSKvqeOI+EyNMQ4FL20S4w8T+LR873jKrbd2MEuAsJiygWh+/ZJ\nhaHTEhFxvH/a4i8gb/SGZlFB6oyPJ+XM5kZo4fcp7PnzxhYrIaEpPq+AR/3657hm\nAajXpS5lTCkNJc85QeHHj/0geDsOvfK4XUj2lgaJ0gXpgsoxnSCSq6Xox6ZqAVON\n0ra1KkGBTQH+5DxJ2Gp1UBaucrLYZTfXuJ8fPYeeNQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/self-signed/csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIEUzCCAjsCAQAwDjEMMAoGA1UEAwwDQm9iMIICIjANBgkqhkiG9w0BAQEFAAOC\nAg8AMIICCgKCAgEAzQMXYOZz3ILrbF9qpDng2pw2wJf1UFopehwaYyu7riWJ9+AD\nrhSnCSFBM3ScMBc/8dIR6etWwci8QwJ/MtvIU0yx4llq+53G+19Bc1teC6q/b4QC\nRDIcTGxOZoR+jfYZVjPODEyJ5y5MZIo34ZP4bu+JnpT4W7+uojm3jOoyNPqXMcc7\n0uAhfSKG+JfrwZKteA4T5VKFytVcWgh4v03z8zTYeW3kD4lCsongBz6yu2dn3D6X\nMROnxiPwi+IRQqX1pwnJ0UA3CTeOHEw1jA3koxWIIg44PWaPaCj9Udrhf4ew00XL\nPWVZP8T5rVf8yUfecWQCR7FueJFqoLhPMMFi17rYmGZUvw3/YkXBjay4Q9e+G2WS\n3Xk8u+I1sCuVBJNBRv9DqtMC9D/N9NI8GkLrXwZmk82SXG+cQ0TSkNUHYI/03YKo\nqsn5H8PsG7Tc+Y2Ca6TaCWims7lvOg7U0E6lu2h5NGcdWHFPJ9qfe+xho/yfYYwG\nqEKanGAu1kd5SbIaX6/YiM5/Pp/96MeRNrB9kLzDnZTNuGtdCawVFgbkWmfX2Z6/\na0d6SvZGDzBxxTVZRB0my01E1FP53MS8YRH98HUjGEwNRVq2e1W+aXldKppgZ85G\nZD3l2YTeuI0iPJCDUzQiaWzFtWc8s13YQ1HLCOyOXF7QqMyNCiLGb4xQ56kCAwEA\nAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAn/ZI7IkBUEfhZHefwtF+QHCyxSEKvqwHq\nfSqKVdarBPz8Ik8m3icj8R/DcS3y5jgzx3x8bXQoDpgsAQgeb825NRv2wAQAGoH1\n8vh204lTyjqzrgtK7eQeQDc7fjeigIkxQsAK9zk4BaFUWp0wEC0RLVAgvlQTl7vu\nn1jSSrhK8tvGy62/cIxZfwD0bAMHlW4m1A4fUuSGWQX2KldgA8tnmT6wx0If/nKb\nVB68AMbyMHUeb32v9wEvx2nHlwMjqNFeg7vYyJXOfBdDILUl+OTBoQY1X+jSx5iM\ntxTzmA8Hcgx0Fq+BnbQuZCLqFpNWEfenAtQtaAFuJwMiKCf6kgbqkDVShJkmt+vC\nj3dcsVMZDsdMk4qRpiJhaTQOYmsMGCj4uoDpFGjwPoUwlDkjYgHAAsm9uCkshc+m\nWZO7I6Z3Tbi3XskJvAMc3dTWjtc6nApEtr/mn8LcETfOp7RRSfjllj6ijWUrVwUy\nBpzU9C/zLTkhFX0DVDCIV+jEefF8JPfzSKLgXyRbInTz1/6/sKXtswXW0NjzqLMI\nC9ggMBhOiDv9KJn3G/mY4CqIfo9KMzF+++4t+wdXTir8DWNlMUAn1vlBwxZAgKCM\nGonVExBU0VIGCpyTRLkesEHnPMgybP6gLzP3++54x288OS5JwuPPtkDcsBHUjTq8\nHxTJvUul/Q==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/self-signed/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDNAxdg5nPcguts\nX2qkOeDanDbAl/VQWil6HBpjK7uuJYn34AOuFKcJIUEzdJwwFz/x0hHp61bByLxD\nAn8y28hTTLHiWWr7ncb7X0FzW14Lqr9vhAJEMhxMbE5mhH6N9hlWM84MTInnLkxk\nijfhk/hu74melPhbv66iObeM6jI0+pcxxzvS4CF9Iob4l+vBkq14DhPlUoXK1Vxa\nCHi/TfPzNNh5beQPiUKyieAHPrK7Z2fcPpcxE6fGI/CL4hFCpfWnCcnRQDcJN44c\nTDWMDeSjFYgiDjg9Zo9oKP1R2uF/h7DTRcs9ZVk/xPmtV/zJR95xZAJHsW54kWqg\nuE8wwWLXutiYZlS/Df9iRcGNrLhD174bZZLdeTy74jWwK5UEk0FG/0Oq0wL0P830\n0jwaQutfBmaTzZJcb5xDRNKQ1Qdgj/Tdgqiqyfkfw+wbtNz5jYJrpNoJaKazuW86\nDtTQTqW7aHk0Zx1YcU8n2p977GGj/J9hjAaoQpqcYC7WR3lJshpfr9iIzn8+n/3o\nx5E2sH2QvMOdlM24a10JrBUWBuRaZ9fZnr9rR3pK9kYPMHHFNVlEHSbLTUTUU/nc\nxLxhEf3wdSMYTA1FWrZ7Vb5peV0qmmBnzkZkPeXZhN64jSI8kINTNCJpbMW1Zzyz\nXdhDUcsI7I5cXtCozI0KIsZvjFDnqQIDAQABAoICAAN6aFLBqijNNFEM/95MKJVQ\n5eln0pbDxtUeZbC1yNv8IU56J5nUGh7gqG5m7bDvrgssXxcuwdStEwuYft+2JJyM\nLi7qyTK+YyY34CCExfBQ++k4jkDJsFr4Ee7xk8OVD6o7nATvpf3M9mkUwryyIdqA\n+B7fhGSrGHuCWuu6O/KT502GBazu1kadF7jfO/XXZxfEtl/zQdeWfdf9sY2+VPOU\n+5C41XARijcE+Y7p6IafKx8MlUxU+ulUygOXiOcucV/dfcXt7tkaTxAKF3T6Nd0x\n8/Ku9tOM2kVAP8b8HYwIOW7mLdvrbKOVNA61sdFY5axbD+JXP2pufiZ+pgJL36FF\nSDQIW5M3aH7CSa1i3i4MP49jWomhTNwseVrXsDuGCKVqgIR5LZwpS4VOHLAILkCh\ncIEDnoMS9YPuQdENIIxKyZGGHaeJ+LRb4w+szvtmu55Kp+N7AubPfoypPrx7a8LN\n8/0/w731DS6nTICYXXzzGoB3cefb3nsBNaH1+edffPTZOYlFZ9ElFjIs/xvWCSy4\nqYwQ1cW4DslIiVD62wm8Df2yr/5J6znfU01RXQ4GWfmDNFBdYsQO/8JEy6UZEvCy\ntFZ1gseD9K69O4XZSEKRKIvv8+1Y/CwD0ppIOYCIycTKn87GXFmsjbuj8tghmHp4\nTUi3EUvrw8mQMi5QBa5BAoIBAQD8JzdNy4ietoT8UUWqBT+ZSURPWeQN1esbncYU\nb9viBIznnjjFFr5JdYa5k3rxM+bTRq47NRt+r0HOvyJWUFJcI6tbgmGtW+rmM2kB\nhq6ekTJleLz+/cjNSjWD14avNORPz/ozZMlcz52NEl31pdniuDbueeNgyh+CkJtH\nBS8s8mMVZ+3NtafZ1ilGn/RP+n21C1J8Kcxd/1srtfcpybzAQiSYul2DlKVPGvqO\nXpLyt42/cyc7a3MyXtms2XhJ62fDK24Qptp6KJNTzqdtY+KP/iOW0SUgxf+JpC87\nW2NJW7tqyyaebn7KO1lGs9y03KzwaLZvy2RaBfjQnxS8uXzpAoIBAQDQI8QHNtr5\nnHYSzLZJMhJkP641wQWo4ODfkWxtEMqTyOXrVw3L8HMdA03Nmf9jnCv5awYYA3j0\nPmSL3PdM36d3VsAyyxMBN4HrH2Z94oTnoKmDfXjB3prhPYgO6aosSvE8rw1N055o\n757p9vAA5w9apBBLNdcm3cjUm2ZKeocL4wnFjOW63CtcFEouE4R7C32rauEu9Bdg\ndAXciBmOrHtihJUQrpMfyfN2fVSLbO4SoFy7ZHq5YKFk4MzNIp/cENwRIqdcLvOz\no++RSbwptRtkd/HZCFSh/4gEPRLe/k5gGErS9ZqpeSMfV++IdBMqC0Sx3UpyXuue\nFOhIvnLpJlzBAoIBAGjcDh2mBLyr/oXHbocUA6zFUUkGgtZWHZ2wcQ1Sr0hAyDAS\nFl2v5ZY677oA4OGpydYW0KICpdp7G4zU43ytjnKOytYVVHV5gigVPRfLYJbEnwaf\nvUj1VSo6MCMR4ArAnimqvcvdn/eex1BBUR20yPWF0iI+QhagN5ZeeJSCTWoNqrLe\nM4CWiKUIcMXUAw+3hctiV/0WjMySQuHcnFqecIYre3igF/9+M3jAKW5HWijhuGrj\ngm8tcgyCcVd2YJWs9cuuJel62eRvN0Vk7S+KmE91SmuPsjb84BXnV1UB3jpFkZ0J\nupesL8H+CFRku+Xi13Bqu2OmW6csUJrBbShGovECggEBAKGk9SupJXyvT1+gTn0f\n/vqOHiyvAEc8hkf6t5sobDtDzZPs4tEcpznEBBuF2rqwYdJtlKj3oWsGPa4FaKXy\nGCvtWozX+6V5R1Oj6kQftJnyw1NUEYF28Q+2asEyJTAK77jyNkHX9HGIjwEi/xek\nWt9JBUJzyOjtW3gKS/HRoKnRpBghKZTqQl5bf5SzIbMxpGKJOeLuPG1zDc5MgJS2\nTYigcOgovCf2/jZqdUtmyKn8kqgSC+GGMzGWCFfT6RTOnypLoHBOIoPD8F0ER7aY\naXKoWFH2T0wUmLy59brrA1FL7GhTx86QPn+sGmH9y5hecfY0ZwnVv+TgVdmQ1stN\nOMECggEBAOXDX319Dmo0ydAPYyngK4/slOetGaJmz8looU8a2R2+Ko/VMTZDmJhf\nP0vS74g3U7sukRjmYzUY1mPj27CDURvk1ENam9KPOQ59ws/TaHaJ7tobjUXVQ93/\nOREkrlCuqbEqJJzQ01mCWIbmDnGvJwD87rW6YwmI9Rs+kjZxNLj+IW4CqpY36q9A\nHwaUZLXc2q0W1CqLmFYF5HotvSFIAYHWuClEmM2NI9+0VItarBz6AwCnVXwKbJLC\nirXlllX+63uloTDR5W1ymy2hTUrhE1jgh9DR4106QSVDiEWqme/BAWmUoyuN/zms\nv3/WVVAXEcIowL3T4jzJ0RLdf1qE8Bc=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/trusted/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFAzCCAuugAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRIwEAYDVQQDDAlsb2Nh\nbGhvc3QxIDAeBgNVBAoMF0NsaWVudCBDZXJ0aWZpY2F0ZSBEZW1vMB4XDTI1MDcy\nMTE1NTYxOFoXDTM1MDcxOTE1NTYxOFowEDEOMAwGA1UEAwwFQWxpY2UwggIiMA0G\nCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC4ggn74iAHLgOWOiOvB2CPe+Hr7W6S\nTYJLZOoqPdh7mv7QGm8cYxfD+26p13aEaW4/qn45losWdEPPy2ZiVIF+kcOP0R4A\nqsB0w9UHT4WSzCWtDqs8ywDMJ6tHge0++8S1bTpdutn/m8DPnKtkD9RQUzLFmGDO\n+mB08Xu+egTzJvURbHXRJ27E+CXUXLEHbAJd8EKjJiQYXhcj4lzUXOUg+xkpPAGe\n1dgQ6BDkv3xq/81S9NTIT5YriHEm6egi9AFJLZbbZtCpRQm1MDMq7zfD7oL6ECLG\nrJ/aaxLEM49gMdw2SscYHotVxX2OMAKgN/ytB18L/mIQ4pOWp3HJQ+nks9Zu1V8c\ng7Pz5pWwjqoncbyUaBi3vGsitAot3cyLXbN9hH8zQB8QqGyNLvqQnCJOBlYxOPA8\nM8NQGDThWKspcix0LhkmnYsWWt3+KEGEpFRcJCEthm/DAd1GImP7/dg1/btT/zOs\nIIkIoCyBwznuR4M4XoNTbIBsiTnO/6PjOdTrjLVX7vMueTjdbCgK3VeOuwetbZ7a\nZ/hvMJCjJ4wuM3l2ZyfEQlP4gJLJS3Fw2PRsySZJl9JyEzwTqYVosDF5SF8uw0HI\ncFaliZCqBbgYfOiQzjIX/GiHZdor1ZrhAbO/MpSxuxGzxF7Em4n+4p816NnC9S2J\nbHRetkM9P1adHwIDAQABo0IwQDAdBgNVHQ4EFgQUXhGloOgfXb0tY7HZZGil/d11\nbcwwHwYDVR0jBBgwFoAU8doR+Mmgh+1KQdNvybCZ7bsu2J8wDQYJKoZIhvcNAQEL\nBQADggIBADNVu8YodDjB0474ztGSjV8WO0x984WQpGG6VPIt9xnnswNM8aOZVMNC\nAS12BjgviutSBUbL40xYNlytnqP0KtlgZSNpPVTGMAOoGttbgV7w46+hyBgV4hYs\nPsYrxMU1/4hTW/aGnNXpWJ4VwOESknSqUudzfy1mNVaEuPoL97SCvPrlKPU6El1a\nWmove/QTKsbsjWaahqE59uClQ6CBcWbxpN5CLyIVM4c/UrsoXdwRewgXl19YFRzR\nl6LXBPid4UPQqdE6XIxgsNpoTDwAyxSRMPydlulJD5sSTaXHB7h63DsT9mEydjR3\njPjhNZL5ADtDes3/UhfpXDbb8MudXdbdsHNswEl5ewAOdRXAuoaJuQ2TOnhz+cO0\noaq9X3YaWjl8SwdvZrLIHLG3jEAQYEXDR+dEtT6vs7HmReMrYhGGoCEVrjSZAaeO\n9bQoSe0m8xbuSV19e5A+LZv3bcPcVOe91x/wnjVb98KRQcSQXCBE3yT5Oav/OLKZ\nTrdRkKe1yyPMHJiicn3pffdXuG6mIJC8HgYhvJEX/wkWf1BiyRd44dD1cgI1Ttt1\npQoapJCmjA0XHtr15o4TW6hmnrmOOYTOVCCug8h1bWscBedMgdR5Qm+umbUiuC3N\nN6mxD49Sc40NCeDboX87rDiObhOXAAFsiE9o39z/tPToVa3TsJsA\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/trusted/csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIEVTCCAj0CAQAwEDEOMAwGA1UEAwwFQWxpY2UwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQC4ggn74iAHLgOWOiOvB2CPe+Hr7W6STYJLZOoqPdh7mv7Q\nGm8cYxfD+26p13aEaW4/qn45losWdEPPy2ZiVIF+kcOP0R4AqsB0w9UHT4WSzCWt\nDqs8ywDMJ6tHge0++8S1bTpdutn/m8DPnKtkD9RQUzLFmGDO+mB08Xu+egTzJvUR\nbHXRJ27E+CXUXLEHbAJd8EKjJiQYXhcj4lzUXOUg+xkpPAGe1dgQ6BDkv3xq/81S\n9NTIT5YriHEm6egi9AFJLZbbZtCpRQm1MDMq7zfD7oL6ECLGrJ/aaxLEM49gMdw2\nSscYHotVxX2OMAKgN/ytB18L/mIQ4pOWp3HJQ+nks9Zu1V8cg7Pz5pWwjqoncbyU\naBi3vGsitAot3cyLXbN9hH8zQB8QqGyNLvqQnCJOBlYxOPA8M8NQGDThWKspcix0\nLhkmnYsWWt3+KEGEpFRcJCEthm/DAd1GImP7/dg1/btT/zOsIIkIoCyBwznuR4M4\nXoNTbIBsiTnO/6PjOdTrjLVX7vMueTjdbCgK3VeOuwetbZ7aZ/hvMJCjJ4wuM3l2\nZyfEQlP4gJLJS3Fw2PRsySZJl9JyEzwTqYVosDF5SF8uw0HIcFaliZCqBbgYfOiQ\nzjIX/GiHZdor1ZrhAbO/MpSxuxGzxF7Em4n+4p816NnC9S2JbHRetkM9P1adHwID\nAQABoAAwDQYJKoZIhvcNAQELBQADggIBACpScaAoLAs9DuTcI5Y4dbHf7LwF4Zxx\nUgPNzE1HB1Pr6NaHRiOWrlCtDEB5UwHrr0oZuRTqSEGg3Pe5Z2QtPG/sdFgfm4BP\n29o6qo0CXiEVBwU7/K0/lL2/0a7uSbD0Tkw9d6Bgik7/Z5rzXZIi2vtUcrOtHskP\nC+Z9w3vH9a+RnUeo52mCnRi4SaiSEnD5jvhmgaI9iF+k5pYBiMrKRj5W/F1QCkf4\n7OuSK97xN0eG/I4Oxgzi/qt51ySCYZbqoh7dIpwi/a4UsK8kdzDDI1M3J7bU07cO\nCJRfr0EETqCQw/gAKoag3tRFNvWQB6Z9G3Ev5jeaCLpcc32NlpN5xH3VW8A0Zb81\ndn5BXkPSxjwJaD0a3cLFkfgrasoe7ZMmHrVpQDw+9USuGCYXMPzNZLEeTFbrRkUn\nsqi30e28E1H69zVWj+OKzCWEH/azVlfaoVbwM+njUJDe5V09KvFtI7aZYmvLxbUX\n4ifoRUVoKedyKnueVmoIG57lF2VzeEhX5YjCngxIg+YuE99HkMQAZSlS6uJcVM92\ntsC/+pYECBk8ukenbxmKXROl3u4p2M1iCSL/8EOVROuyjnuzCXJZOpNptdpX4ZgL\nkHP1erq7/U8ZU8HviUsfMoisagx8dA8uj/4fk0jfNxOJqlZL9eJhpgBfYHqmAz3h\nm+PQVw96eeoK\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/assets/client-certificates/client/trusted/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC4ggn74iAHLgOW\nOiOvB2CPe+Hr7W6STYJLZOoqPdh7mv7QGm8cYxfD+26p13aEaW4/qn45losWdEPP\ny2ZiVIF+kcOP0R4AqsB0w9UHT4WSzCWtDqs8ywDMJ6tHge0++8S1bTpdutn/m8DP\nnKtkD9RQUzLFmGDO+mB08Xu+egTzJvURbHXRJ27E+CXUXLEHbAJd8EKjJiQYXhcj\n4lzUXOUg+xkpPAGe1dgQ6BDkv3xq/81S9NTIT5YriHEm6egi9AFJLZbbZtCpRQm1\nMDMq7zfD7oL6ECLGrJ/aaxLEM49gMdw2SscYHotVxX2OMAKgN/ytB18L/mIQ4pOW\np3HJQ+nks9Zu1V8cg7Pz5pWwjqoncbyUaBi3vGsitAot3cyLXbN9hH8zQB8QqGyN\nLvqQnCJOBlYxOPA8M8NQGDThWKspcix0LhkmnYsWWt3+KEGEpFRcJCEthm/DAd1G\nImP7/dg1/btT/zOsIIkIoCyBwznuR4M4XoNTbIBsiTnO/6PjOdTrjLVX7vMueTjd\nbCgK3VeOuwetbZ7aZ/hvMJCjJ4wuM3l2ZyfEQlP4gJLJS3Fw2PRsySZJl9JyEzwT\nqYVosDF5SF8uw0HIcFaliZCqBbgYfOiQzjIX/GiHZdor1ZrhAbO/MpSxuxGzxF7E\nm4n+4p816NnC9S2JbHRetkM9P1adHwIDAQABAoICACBW8qcKoHCBuTE4qY6BLYSY\nwyWWLT5JhZ/vZTfYNTydEzKon3cLS1wXkvMECAr3a9KO8KbpYyGhaU1fqmdrxnLH\n2842ahrV0vvkY09vucrcK3Jk0tDKCC7AeT4EYPAcMwNVzNgm6xTpWOdK36OfPqiB\nnLGTnsxIiGWW+giN3JY96tCOASySy9CMah0JziGt5dBPT27HPaZjv4yTnY+/ZI3e\nVS+sC+CqPL/h3SwrAATFJ1j1/uHJSVoCBUs7zmtp91u7OOjl4Yb5ydTPSPiqi0y1\nXpG0CFRoZ3BiOhzXqLbEpoOBodnxaJy1C+fDNIKerZQqaZdxlAC/pfzPBpuvYqxe\nRRPV85+AXZ5nosSoqjPprKDfDrLfwnEAJIXtZjDQvJ1mA49Tbtx30rzk3u36BU7v\n4JXBTcCiaxhPw4MlPzCYKXXL8B02m8vC/RYZO29YHytERbAAMd4uUfGU0lOKWi6W\nCEHXYTjbqSDuytpuxT9InLVxEC+u+h9CxAi3FpawLmu5ELW5qA5yMHRoyYOCKUDO\nEsjV/qDSo6T1kkYZ0qj4Ya9DeVWgMRekn9TWZmzeYMzDnZtAp1OscmcETFH28xo0\niuxQiStWEZaGdxD5njF+0GHtlECxMmPx03IH9bCxt6GU1aFCdfvhzqtovze4OCI2\nSJOxeJRAoom/LcCBQgCBAoIBAQDZygQguNUxwkaEpwsUhAstwse9SwY2fisNQTbU\nLj1rbvCDlOADrthIBsb9jpsUyqow9UbeB+mq13xrFz7BfmQ0Bf2+3P8mV0IxwmvE\nmg8Wbi8pkYoet4yzSc2f8yHCvAsejFFWNU7/7JB0gpeRxXJTjdgoTiLEop5cus8r\nSmBzCphzOMM8iUV/ByOgIAEOU3bo2GuSRbEEJ0kA1bd/l12uT7tApT+0umo/OPRR\nyYEIexuIa7fean7ZHf31GbQdJm9vgoi+ar0mZg9yHSiqlOnghWVheEeNVGH+XgFt\n4qQkbTW8Ie0Am88uT+3fbxIZHbJ4GkPnKwc+klN5p2efxmlfAoIBAQDY4TOoj2zf\nUvu8E1KboCiGjfHvvm3UhYVVUVqoctHQihSOQSThmKCGv025K4iSvxfC77XfuJv8\nZC14TT176VvSZVrV4vZDod572/co5WyEGqaVfqwqjBmCWAODT+IgmIZrDfDRLIkC\n4cQObd8DkzzdP8msqp731UlrEZvOkh9XW4rwBmFXeBqgDKDgM7Zge4Clu6lWABxT\nBnGv00OjqRGd6IH1+tDTm0w0qNkXzZ3UDAuwVEHBjMJnPr7t9LEi5e3AluspjoF9\nIe/KtQQ6D6guYtdRZismg3CGlnGwcf/yjS1YEVTqyybY52M9N3aHDVlPesn1BWO7\nUKQgHbPQq6RBAoIBAQCQv+n6bZ6VEdCYvgVpP1HGulzS/RhGA5lNl/h/EbSUwQlu\nCvbQu9bYGFkNkUiVixWOsJbHX274s3voGW0GYaDryseZoXyb2QcP126VHufEOrtx\n319zhv8m8niORKQ9r4mcZhpxN8En6+0e4uUmZ5rS2cW/FB+bnZGvhCHJXge4rmQg\nwKtSgtID2ZTeCidphCPWInFsqJE8d3fX7DOnw8zp2+hS0QIEdpnDJ3GLImh2YIwu\nIZn1Y8anO33c95Z0gWUzMgj8tii9arv9Vk//ADZpmX+GRtEXp+vxij1c8XOzGjrK\nram969DJsSoihMn8k3ZYyOw0qq6H8e01QARpdw/1AoIBAE/9Bxt1And/WJ7uFXqW\nYDv4IDIG7uUB9cIYxjH4Xw/lzV0GA788ln/8EINp3e4Zkn7wAAkqQkWdAPQssK+B\nyr7XaOAX3DHngnH2F7s6moJCfgwG8yKiF0pugaUtkj3pYzIaqyXKoiGw+KlFtonQ\nBROo0g3fw8+uF2zoyqkuVWbXuW97Ou2Su2cqIS9vgyUkh7cYdoTkd43bg5SQe5Lh\n6UBvH3eEcP6KeVm2qJLR4BLz+l+nQ7VJ3+1KRArpQ2eWm9B7GPJzv6hSGumNR6jO\nW334MGeyIdoLgjXxSK8F7JsdnIqtob8S/BnlhUFvskRvFPBuXgwDV9wfCtlZexdM\nJsECggEAJy5r3YTLiNFqXiwIyZ+GFQYlyhYAH75tP6yaF5Sr6FaDDaGTQxtMgp+V\nUAOZIOHQAWpjQtgnjJAYLyDgkhVWJqQILBC3V1GHj6Kr+3avyV4cGuqP7wxsS2kt\nvUY9emtg0dil/8l6IL3DzjXhRI8+00FZSvcueQYVgv+fv1zPx3T/RuUp8x4E0lq1\nYmlLjgi1EznRu1YW1IczsGwBQDpghKnf2E0tUn9wUr1Kb8k0uOVdzUZETvJjWYeD\n+GwoR5lW/BhSIwE4zeK66RY18n7GOIS7y7Yvn4onr3j3geqIKAEjQrlBkQlPkhfd\n21eLmxOuln98Q3na7ftpAWQdyjcE6Q==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/assets/client-certificates/generate.sh",
    "content": "#!/bin/bash\n\n# Client Certificate test-certificates\n\ncd \"$(dirname \"$0\")\"\n\n## Server\n\nopenssl req \\\n\t-x509 \\\n\t-newkey rsa:4096 \\\n\t-keyout server/server_key.pem \\\n\t-out server/server_cert.pem \\\n\t-nodes \\\n\t-days 3650 \\\n\t-subj \"/CN=localhost/O=Client\\ Certificate\\ Demo\" \\\n\t-addext \"subjectAltName=DNS:localhost,DNS:local.playwright\"\n\n## Trusted client-certificate (server signed/valid)\n\nmkdir -p client/trusted\n# generate server-signed (valid) certificate\nopenssl req \\\n\t-newkey rsa:4096 \\\n\t-keyout client/trusted/key.pem \\\n\t-out client/trusted/csr.pem \\\n\t-nodes \\\n\t-days 3650 \\\n\t-subj \"/CN=Alice\"\n\n# sign with server_cert.pem\nopenssl x509 \\\n\t-req \\\n\t-in client/trusted/csr.pem \\\n\t-CA server/server_cert.pem \\\n\t-CAkey server/server_key.pem \\\n\t-out client/trusted/cert.pem \\\n\t-set_serial 01 \\\n\t-days 3650\n# create pfx\nopenssl pkcs12 -export -out client/trusted/cert.pfx -inkey client/trusted/key.pem -in client/trusted/cert.pem -passout pass:secure\n\n## Trusted certificate for localhost (server signed/valid)\n\nmkdir -p client/localhost\n\n# generate server-signed (valid) certificate\nopenssl req \\\n\t-newkey rsa:4096 \\\n\t-keyout client/localhost/localhost.key \\\n\t-out client/localhost/localhost.csr \\\n\t-nodes \\\n\t-days 3650 \\\n\t-subj \"/CN=localhost\" \\\n  -addext \"subjectAltName=DNS:localhost,DNS:127.0.0.1\"\n\n# put extensions\necho \"subjectAltName=DNS:localhost,DNS:127.0.0.1\" > client/localhost/localhost.ext\n\n# sign with server_cert.pem\nopenssl x509 \\\n\t-req \\\n\t-in client/localhost/localhost.csr \\\n\t-CA server/server_cert.pem \\\n\t-CAkey server/server_key.pem \\\n\t-set_serial 01 \\\n\t-out client/localhost/localhost.pem \\\n\t-days 3650 \\\n  -extfile client/localhost/localhost.ext\n\n## Self-signed certificate (invalid)\n\nmkdir -p client/self-signed\nopenssl req \\\n\t-newkey rsa:4096 \\\n\t-keyout client/self-signed/key.pem \\\n\t-out client/self-signed/csr.pem \\\n\t-nodes \\\n\t-days 3650 \\\n\t-subj \"/CN=Bob\"\n\n# sign with self-signed/key.pem\nopenssl x509 \\\n\t-req \\\n\t-in client/self-signed/csr.pem \\\n\t-signkey client/self-signed/key.pem \\\n\t-out client/self-signed/cert.pem \\\n\t-days 3650\n"
  },
  {
    "path": "tests/assets/client-certificates/server/server_cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIFdTCCA12gAwIBAgIUFhAlW/DnHoHOFg2CXKBAvwYN4BIwDQYJKoZIhvcNAQEL\nBQAwNjESMBAGA1UEAwwJbG9jYWxob3N0MSAwHgYDVQQKDBdDbGllbnQgQ2VydGlm\naWNhdGUgRGVtbzAeFw0yNTA3MjExNTU2MThaFw0zNTA3MTkxNTU2MThaMDYxEjAQ\nBgNVBAMMCWxvY2FsaG9zdDEgMB4GA1UECgwXQ2xpZW50IENlcnRpZmljYXRlIERl\nbW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYDbBlM8ILtq2xKLFU\nZwU4n7+0VHsO0skCfyNwpSZndbArjJUd/ZgFyCy5RK5Cg23KtXSMgkU6QlXOWvIr\nWJ/1wAkH9tuef/JDo9NJ00jBeua7HjdudNAsz6WwXSZC+a6DhA7nVHNwuyqq1SyH\ng5tuSP294EwfbEhXhaDyfXyBa4PaEmyjD2+O1NHSZ1AuUhblcUelKkZbykMvwCb7\ngJVXxlm/SN0K4vOVq80dBknr785562hDFGoXFX9hd2uPCQKbWGdMca+MmRWwNM2B\nCuxVoUl/Ooqmr7AdZ62lhpcqCd+todWC5GzrxwZjcIwt5P7MDvd+Ktmc8ePcZhp3\nL3ddlxXNgfi+c9OnZaz1QVgEn1YlDAmHvQtj9H+v2mmawyyRycjmhIpV/eaXaJvW\nA2wq+O2OERl056IjjzcXHrD6DOk7IjlTwQ87AMU4mNrCzW1LLd428OxQdGv2I4/b\nnq+0snOJOrTk65upybJYMGKUuTRqfEQgv5d/923VUISC69uTq+0zVFxG3pyMJR2D\nO+6xinBkp0Gk+ft9L4aaBAYXyUmi+PRSfdTfUk3yQkYefC55QTDDRK2rb7JiQqzr\nxhJ2vBar0TqXShQKKlMaOWHd+1U2ZjuyzboDDPeACoAmUVY2IHs+P8Dem0fwuKNd\nTF4jZkVQIWWLfTzzghOelmy9mwIDAQABo3sweTAdBgNVHQ4EFgQU8doR+Mmgh+1K\nQdNvybCZ7bsu2J8wHwYDVR0jBBgwFoAU8doR+Mmgh+1KQdNvybCZ7bsu2J8wDwYD\nVR0TAQH/BAUwAwEB/zAmBgNVHREEHzAdgglsb2NhbGhvc3SCEGxvY2FsLnBsYXl3\ncmlnaHQwDQYJKoZIhvcNAQELBQADggIBAH1/6Sp4dW96yvwi9ptgVRYfRSRWfYYy\n2nU6kJ1DPOW7hTPf2wLf6Z2KqiJXn8tECHfM4pnPSgDhtZHDDHAu9l7Diqzk/NIl\nAblRs++4vSKdnCx1uh3EFLjIMmHa/cRUzfuR+oGfri3v7jgTBV6UJJQ0pMqIDk/P\nVEOWWukllru40M8Rwy4F6LmpwIWMtNDxmhtXSbQJ6TZn+isNSoaWgHjQmayrVyXZ\nOcNYuR8M/ECvnufuIOW53+hhwG1X0jEoBdXqqXbTKcGjA+yLHp008AAB5DNQblYm\nFJ4N0v6eYLhOO0u3n0b/ZsqUkZx2h38tlZoAdJNqy+d4TE3WbGRNgNJrTjAOzpPL\ncQ5RNr3dDsUDFPnCoK3LUf9BWerARoDt+bbrM/VqvWH/0uqaU0/vo8IchSyQ9wGv\nSwrWLJU32HQUJ2VQP/m4ADf3X2Ozj+e8bBf7XPPD8KDyL4aR0KuYzrsOnhFlBkHD\nPtNjc7SgiE7AZe1WOEmhBQcr+vI6S8qFTurTntCBkvrFgYBAIIhK2XwFN5sFtByr\nGCB67NQMp37g2qFNYi8EmKQvPLpAcC3Je5PvHFSj2y9Z14FclGoxjU50SlH9/A5I\niHGcIVjCv9NhNDFAwakF/sTnrpT/FIS7tm9GwoJGWCdYg4pl72WYCW6hJBG+XY7G\nt3jt9TP8623D\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/assets/client-certificates/server/server_key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDYDbBlM8ILtq2x\nKLFUZwU4n7+0VHsO0skCfyNwpSZndbArjJUd/ZgFyCy5RK5Cg23KtXSMgkU6QlXO\nWvIrWJ/1wAkH9tuef/JDo9NJ00jBeua7HjdudNAsz6WwXSZC+a6DhA7nVHNwuyqq\n1SyHg5tuSP294EwfbEhXhaDyfXyBa4PaEmyjD2+O1NHSZ1AuUhblcUelKkZbykMv\nwCb7gJVXxlm/SN0K4vOVq80dBknr785562hDFGoXFX9hd2uPCQKbWGdMca+MmRWw\nNM2BCuxVoUl/Ooqmr7AdZ62lhpcqCd+todWC5GzrxwZjcIwt5P7MDvd+Ktmc8ePc\nZhp3L3ddlxXNgfi+c9OnZaz1QVgEn1YlDAmHvQtj9H+v2mmawyyRycjmhIpV/eaX\naJvWA2wq+O2OERl056IjjzcXHrD6DOk7IjlTwQ87AMU4mNrCzW1LLd428OxQdGv2\nI4/bnq+0snOJOrTk65upybJYMGKUuTRqfEQgv5d/923VUISC69uTq+0zVFxG3pyM\nJR2DO+6xinBkp0Gk+ft9L4aaBAYXyUmi+PRSfdTfUk3yQkYefC55QTDDRK2rb7Ji\nQqzrxhJ2vBar0TqXShQKKlMaOWHd+1U2ZjuyzboDDPeACoAmUVY2IHs+P8Dem0fw\nuKNdTF4jZkVQIWWLfTzzghOelmy9mwIDAQABAoICADF+KUt1qN0QEwgDX2QLWYnY\nJo1D0RDbPorg3xh97KdEsX+4a6x8HGguq/ghAJ5iBzOpj7JkYUFwUsG72cAORE6C\nmE8HwNW1T6UpEUzXJtKTuelhiac3AT1SsA0PuaUcF1svVE6v7OYFKkgKH3JHtsJz\n3BS0HhwQrR3HkdAa6PuoyoKZN+O+tHqOzCYb3qVNzsruwU/XuFhspCl7JjL1CMEb\nwhFsup400UIXIhylBSgUPkN1puO++HKjTRPhzHTuxncZsEg1vtZBd1NvNSh7fRo8\noV6Q5ZQ7qOeDiabihxxtOJ1I9mVOuJjmddMvxBz7WVcbkpyHamRmkSE7DpMA/6G4\nI6MI7pF1jEkPjvZRVfC9irpd+uFtlmCgjTloFUtGIS9wPmEzbtHsAPWvWQAgbv/A\nBDw9SumA/cPEjjhD0jAdPczTxLZk86yr9UZou5WLmuCZJD3zozdZ/7HM5OvhGf7z\nvXqpIwxulU8uOxB7qVh2FTkArP1CxMPNASsaTzSIVmT4+G2NBaakHT3lCS+47XK/\nDJXc97w38cN/4WCxf5vRbeytS+ruDVWU0Ez0mqzYEo0xyWNcyjlWJfmamgI9Vfml\nwdhdYq/CQrrXi5VoYkTKfJl+Bp1HC+rvLjTWLH8tiWty+DHxmkwTedjPulukrBGp\nUw9zMan3IxXsX8BanP2RAoIBAQDw4QNiFKYnCKhawyqgDrbtWtQjUPWP3DkSPSfE\niWEkIwvH2tJpmmkvt+bSzXLLWv0tO2oTn88zmtIanptGbijjPZqhHuB1f6Di+h3A\nZEFye586WlhCqZCw5RDCXw0Wj5ZQREHazO3txSOHvJxtbwW1Sqf6clGJs6Sq/Mk0\npw9Cl5jtLELPL3dwQrtRjVpfK1WWh14E5XSFjHRXvWGMWdje1EN+Lf5V27a7W7SD\nelYqhn5NOKU237UB7cnXsvTndf8zsyx4BmBNO2BirU2B/MikK/LMR3BbJkp3rhMI\neLkgKsN8kf7L4ZABcX1iUr0WxDfrPLxUsuvzb1FAgMpLIsSpAoIBAQDlnblRmqDz\nqsZdZJE2E6PQ7ELCz3b4g6ftMVOyYhptFZqDfku7T1MEdedeHr66pfh1BXgDSYwg\nhnGx8tPmaAZ+ZxghczhADLw1mUXiZ36xIlWVRaVatCCUquVent0PPytUlkPvy37Z\nqIXBAmQNIUorZMVJzeN45073KmZFZfAUXqSx8N0ObBh34oVDx7xgVSQvQyEssR8e\nVsvWWxKY6zrkFeFtonb6A5EDS8R9rmCdGqH0FCGQRo0pP4bT2iXxp40KLeJdZdmy\nnHOEHWtif7/hbaR6w7DLzmWbY5VHKzIFfVkoeoOuQPxYs2ds1mz3ywYKbXXNDcwD\nVMk6mtkqKxajAoIBAArgLfXstr/GbUuDylXltC6tTiy2CBBRwiXnqvb9uOwXxP1m\nDOAFv8AOzpYv/oHd/tZe+2AddA6BbAEVri8U5DW2X1fs+/dyJsJ4xoUcQbQ4jqzk\nzV1dKJJEFWihQAcHvqKrIkoNvKRipUMIqgtq2tgfocv2A2ZzPPkXZsJA1LiN/bKf\nr/iIzRy9dpWtCyqG21trizwvW/53o/0eKNxcZiVRciatTvFzdSGqd1EEYgWTgvpb\nl2IN4a9PnDBn/RTCSB5+dYCJ0SlLiAOMjZZT4n8/GLxOcW08Ilqa+nMEeF9Sbvcd\n5GIyMf1OsXmSAMWZYGj3mg088thP61w9NGUGEdkCggEBAMpwSFa98XFi+wiUBcKb\nhi5IXoPKzaVEzeS9PIFlJM9P4K5VxwcZZKPmH1pH2PhOI8NoUurzCOwUHGE7Kb9V\nr4P5+LhlEQ7HK5hFzetSO8yH7NRyVtqlPKRWF2tYvKUYmGc3JCZiTzAu99228ebx\nlqazbY0oTIjnxiL76rb8rLIIz0NijEKO4vOvbrbXfimgZwqUMMdqUXk6JPSTzs2r\ndnxpHhq+xg6e3lb9kfsMpnlcZbT/mqfMy9+19nUJO7LWee6jjZOynEBw1xd/qJFq\n+A0T0ZO6vECzc7mQDqh0WOGmJdkeSsJy4QiDA4hddCzzfhvrbZSfuWKmedOFejlH\nS+kCggEAXxTOwID/U/8d6DnLLQzX1d9S5VPiKS+Jminvl6LzmGBojn0K1+nQXLNT\nc+EIURlHNuK3aR6dR/iSXiffjHzVAeu0FOSg3wTONowmyTB8LQamIt5Gx5vR3ptq\n4hhv2SSgagpJfSiBKzt3D1/Ls+GPIiRhEMNh6RTTvEK90neNNE5SLfPHFsBzAVmA\nVdlaM/mpudP5KCB4LQAGSjzEWYZpJuhBdoNd5guxb3044FcLA7quVQcANWKnXZxh\n7iHegXE37s7suS8tVscfNAXKccBBGsigba+knnhRQ0M9hlKZLlAAFD+qMHpkTYxJ\ndvQgA6dBjtYg5cQMHNbnfT3j3WzyQg==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/assets/client.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nfrom pathlib import Path\n\nfrom playwright.sync_api import Playwright, sync_playwright\n\n\ndef main(playwright: Playwright, browser_name: str) -> None:\n    browser = playwright[browser_name].launch()\n    page = browser.new_page()\n    page.goto(\"data:text/html,Foobar\")\n    here = Path(__file__).parent.resolve()\n    page.screenshot(path=here / f\"{browser_name}.png\")\n    page.close()\n    browser.close()\n\n\nif __name__ == \"__main__\":\n    browser_name = sys.argv[1]\n    with sync_playwright() as p:\n        main(p, browser_name)\n"
  },
  {
    "path": "tests/assets/consolelog.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>console.log test</title>\n  </head>\n  <body>\n    <script>\n      console.log('yellow')\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/csp.html",
    "content": "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'\">\n"
  },
  {
    "path": "tests/assets/dom.html",
    "content": "<div id=\"outer\" name=\"value\"><div id=\"inner\">Text,\nmore text</div></div><input id=\"check\" type=checkbox checked foo=\"bar&quot;\">\n<input id=\"input\"></input>\n<textarea id=\"textarea\"></textarea>\n"
  },
  {
    "path": "tests/assets/download-blob.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Blob Download Example</title>\n  </head>\n  <body>\n    <script>\n      const download = (data, filename) => {\n      const a = document.createElement(\"a\");\n      a.style = \"display: none\";\n      document.body.appendChild(a);\n      a.style = \"display: none\";\n\n      const blob = new Blob([data], { type: \"octet/stream\" });\n      const url = window.URL.createObjectURL(blob);\n      a.href = url;\n      a.download = filename;\n      a.click();\n      window.URL.revokeObjectURL(url);\n      document.body.removeChild(a);\n    };\n\n    const downloadIt = () => {\n      download(\"Hello world\", \"example.txt\");\n    }\n    </script>\n    <a onclick=\"javascript:downloadIt();\">Download</a>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/drag-n-drop.html",
    "content": "<style>\ndiv:not(.mouse-helper) {\n  margin: 0em;\n  padding: 2em;\n}\n#source {\n  color: blue;\n  border: 1px solid black;\n}\n#target {\n  border: 1px solid black;\n}\n</style>\n\n<script>\n\nfunction dragstart_handler(ev) {\n  ev.currentTarget.style.border = \"dashed\";\n  ev.dataTransfer.setData(\"text/plain\", ev.target.id);\n}\n\nfunction dragover_handler(ev) {\n  ev.preventDefault();\n}\n\nfunction drop_handler(ev) {\n  console.log(\"Drop\");\n  ev.preventDefault();\n  var data = ev.dataTransfer.getData(\"text\");\n  ev.target.appendChild(document.getElementById(data));\n}\n</script>\n\n<body>\n  <div>\n    <p id=\"source\" ondragstart=\"dragstart_handler(event);\" draggable=\"true\">\n      Select this element, drag it to the Drop Zone and then release the selection to move the element.</p>\n  </div>\n  <div id=\"target\" ondrop=\"drop_handler(event);\" ondragover=\"dragover_handler(event);\">Drop Zone</div>\n</body>\n"
  },
  {
    "path": "tests/assets/dummy_bad_browser_executable.js",
    "content": "#!/usr/bin/env node\n\nprocess.exit(1);\n"
  },
  {
    "path": "tests/assets/empty.html",
    "content": ""
  },
  {
    "path": "tests/assets/error.html",
    "content": "<!DOCTYPE html> <script>\nconsole.error('Not a JS error');\na();\n\nfunction a() {\n    b();\n}\n\nfunction b() {\n    c();\n}\n\nfunction c() {\n    throw new Error('Fancy error!');\n}\n//# sourceURL=myscript.js\n</script>\n"
  },
  {
    "path": "tests/assets/es6/.eslintrc",
    "content": "{\n    \"parserOptions\": {\n        \"sourceType\": \"module\"\n    }\n}\n"
  },
  {
    "path": "tests/assets/es6/es6import.js",
    "content": "import num from './es6module.js';\nwindow.__es6injected = num;\n"
  },
  {
    "path": "tests/assets/es6/es6module.js",
    "content": "export default 42;\n"
  },
  {
    "path": "tests/assets/es6/es6pathimport.js",
    "content": "import num from './es6/es6module.js';\nwindow.__es6injected = num;\n"
  },
  {
    "path": "tests/assets/extension-mv3-simple/content-script.js",
    "content": "console.log('hey from the content-script');\nself.thisIsTheContentScript = true;\n"
  },
  {
    "path": "tests/assets/extension-mv3-simple/index.js",
    "content": "// Mock script for background extension\nglobalThis.MAGIC = 42;\n"
  },
  {
    "path": "tests/assets/extension-mv3-simple/manifest.json",
    "content": "{\n  \"name\": \"Simple extension\",\n  \"version\": \"0.1\",\n  \"background\": {\n    \"service_worker\": \"index.js\"\n  },\n  \"content_scripts\": [{\n    \"matches\": [\"<all_urls>\"],\n    \"css\": [],\n    \"js\": [\"content-script.js\"]\n  }],\n  \"permissions\": [\"background\", \"activeTab\"],\n  \"manifest_version\": 3\n}\n"
  },
  {
    "path": "tests/assets/extension-mv3-with-logging/background.js",
    "content": "console.log(\"Service worker script loaded\");\n\nchrome.runtime.onInstalled.addListener(() => {\n  console.log(\"Extension installed\");\n});\n"
  },
  {
    "path": "tests/assets/extension-mv3-with-logging/content.js",
    "content": "console.log(\"Test console log from a third-party execution context\");\n"
  },
  {
    "path": "tests/assets/extension-mv3-with-logging/manifest.json",
    "content": "{\n    \"manifest_version\": 3,\n    \"name\": \"Console Log Extension\",\n    \"version\": \"1.0\",\n    \"background\": {\n      \"service_worker\": \"background.js\"\n    },\n    \"permissions\": [\n      \"tabs\"\n    ],\n    \"content_scripts\": [\n      {\n        \"matches\": [\"<all_urls>\"],\n        \"js\": [\"content.js\"]\n      }\n    ]\n  }\n"
  },
  {
    "path": "tests/assets/file-to-upload-2.txt",
    "content": "contents of the file\n"
  },
  {
    "path": "tests/assets/file-to-upload.txt",
    "content": "contents of the file\n"
  },
  {
    "path": "tests/assets/frames/child-redirect.html",
    "content": "<iframe src='./redirect-my-parent.html'></iframe>\n"
  },
  {
    "path": "tests/assets/frames/frame.html",
    "content": "<link rel='stylesheet' href='./style.css'>\n<script src='./script.js' type='text/javascript'></script>\n<style>\nbody {\n  height: 100px;\n  margin: 8px;\n  border: 0;\n  background-color: #555;\n\n}\ndiv {\n  line-height: 18px;\n}\n</style>\n<div>Hi, I'm frame</div>\n"
  },
  {
    "path": "tests/assets/frames/frameset.html",
    "content": "<frameset>\n  <frameset>\n    <frame src='./frame.html'></frame>\n    <frame src='about:blank'></frame>\n  </frameset>\n  <frame src='/empty.html'></frame>\n  <frame></frame>\n</frameset>\n"
  },
  {
    "path": "tests/assets/frames/nested-frames.html",
    "content": "<style>\nbody {\n    display: flex;\n    height: 500px;\n    margin: 8px;\n}\n\nbody iframe {\n    flex-grow: 1;\n    flex-shrink: 1;\n    border: 0;\n    background-color: green;\n}\n\n::-webkit-scrollbar{\n    display: none;\n}\n</style>\n<script>\nasync function attachFrame(frameId, url) {\n    var frame = document.createElement('iframe');\n    frame.src = url;\n    frame.id = frameId;\n    document.body.appendChild(frame);\n    await new Promise(x => frame.onload = x);\n    return 'kazakh';\n}\n</script>\n<iframe src='./two-frames.html' name='2frames'></iframe>\n<iframe src='./frame.html' name='aframe'></iframe>\n"
  },
  {
    "path": "tests/assets/frames/one-frame.html",
    "content": "<iframe src='./frame.html'></iframe>\n"
  },
  {
    "path": "tests/assets/frames/redirect-my-parent.html",
    "content": "<script>\n  window.parent.location = './one-frame.html';\n</script>\n"
  },
  {
    "path": "tests/assets/frames/script.js",
    "content": "console.log('Cheers!');\n"
  },
  {
    "path": "tests/assets/frames/style.css",
    "content": "div {\n    color: blue;\n}\n"
  },
  {
    "path": "tests/assets/frames/two-frames.html",
    "content": "<style>\nbody {\n    display: flex;\n    flex-direction: column;\n    height: 400px;\n    margin: 8px;\n}\n\nbody iframe {\n    flex-grow: 1;\n    flex-shrink: 1;\n    border: 0;\n}\n</style>\n<iframe src='./frame.html' name='uno'></iframe>\n<iframe src='./frame.html' name='dos'></iframe>\n"
  },
  {
    "path": "tests/assets/geolocation.html",
    "content": "<script>\n\twindow.geolocationPromise = new Promise(resolve => {\n\t\tnavigator.geolocation.getCurrentPosition(position => {\n\t\t\tresolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n\t\t});\n  });\n</script>\n"
  },
  {
    "path": "tests/assets/global-var.html",
    "content": "<script>\n  var globalVar = 123;\n</script>\n"
  },
  {
    "path": "tests/assets/grid.html",
    "content": "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n    function generatePalette(amount) {\n        var result = [];\n        var hueStep = 360 / amount;\n        for (var i = 0; i < amount; ++i)\n            result.push('hsl(' + (hueStep * i) + ', 100%, 90%)');\n        return result;\n    }\n\n    var palette = generatePalette(100);\n    for (var i = 0; i < 200; ++i) {\n        var box = document.createElement('div');\n        box.classList.add('box');\n        box.style.setProperty('background-color', palette[i % palette.length]);\n        var x = i;\n        do {\n            var digit = x % 10;\n            x = (x / 10)|0;\n            var img = document.createElement('img');\n            img.src = `./digits/${digit}.png`;\n            box.insertBefore(img, box.firstChild);\n        } while (x);\n        document.body.appendChild(box);\n    }\n});\n</script>\n\n<style>\n\nbody {\n    margin: 0;\n    padding: 0;\n}\n\n.box {\n    font-family: arial;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    margin: 0;\n    padding: 0;\n    width: 50px;\n    height: 50px;\n    box-sizing: border-box;\n    border: 1px solid darkgray;\n}\n\n::-webkit-scrollbar {\n    display: none;\n}\n</style>\n"
  },
  {
    "path": "tests/assets/har-fulfill.har",
    "content": "{\n  \"log\": {\n    \"version\": \"1.2\",\n    \"creator\": {\n      \"name\": \"Playwright\",\n      \"version\": \"1.23.0-next\"\n    },\n    \"browser\": {\n      \"name\": \"chromium\",\n      \"version\": \"103.0.5060.33\"\n    },\n    \"pages\": [\n      {\n        \"startedDateTime\": \"2022-06-10T04:27:32.125Z\",\n        \"id\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"title\": \"Hey\",\n        \"pageTimings\": {\n          \"onContentLoad\": 70,\n          \"onLoad\": 70\n        }\n      }\n    ],\n    \"entries\": [\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572145.898,\n        \"startedDateTime\": \"2022-06-10T04:27:32.146Z\",\n        \"time\": 8.286,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://no.playwright/\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\"\n            },\n            {\n              \"name\": \"Upgrade-Insecure-Requests\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 326,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"OK\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"content-length\",\n              \"value\": \"111\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/html\"\n            }\n          ],\n          \"content\": {\n            \"size\": 111,\n            \"mimeType\": \"text/html\",\n            \"compression\": 0,\n            \"text\": \"<title>Hey</title><link rel='stylesheet' href='./style.css'><script src='./script.js'></script><div>hello</div>\"\n          },\n          \"headersSize\": 65,\n          \"bodySize\": 170,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 170\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 8.286,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      },\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572174.683,\n        \"startedDateTime\": \"2022-06-10T04:27:32.172Z\",\n        \"time\": 7.132,\n        \"request\": {\n          \"method\": \"POST\",\n          \"url\": \"http://no.playwright/style.css\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"text/css,*/*;q=0.1\"\n            },\n            {\n              \"name\": \"Referer\",\n              \"value\": \"http://no.playwright/\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 220,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"OK\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"content-length\",\n              \"value\": \"24\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/css\"\n            }\n          ],\n          \"content\": {\n            \"size\": 24,\n            \"mimeType\": \"text/css\",\n            \"compression\": 0,\n            \"text\": \"body { background:cyan }\"\n          },\n          \"headersSize\": 63,\n          \"bodySize\": 81,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 81\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 8.132,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      },\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572174.683,\n        \"startedDateTime\": \"2022-06-10T04:27:32.174Z\",\n        \"time\": 8.132,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://no.playwright/style.css\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"text/css,*/*;q=0.1\"\n            },\n            {\n              \"name\": \"Referer\",\n              \"value\": \"http://no.playwright/\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 220,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"OK\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"content-length\",\n              \"value\": \"24\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/css\"\n            }\n          ],\n          \"content\": {\n            \"size\": 24,\n            \"mimeType\": \"text/css\",\n            \"compression\": 0,\n            \"text\": \"body { background: red }\"\n          },\n          \"headersSize\": 63,\n          \"bodySize\": 81,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 81\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 8.132,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      },\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572175.042,\n        \"startedDateTime\": \"2022-06-10T04:27:32.175Z\",\n        \"time\": 15.997,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://no.playwright/script.js\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"*/*\"\n            },\n            {\n              \"name\": \"Referer\",\n              \"value\": \"http://no.playwright/\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 205,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 301,\n          \"statusText\": \"Moved Permanently\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"location\",\n              \"value\": \"http://no.playwright/script2.js\"\n            }\n          ],\n          \"content\": {\n            \"size\": -1,\n            \"mimeType\": \"x-unknown\",\n            \"compression\": 0\n          },\n          \"headersSize\": 77,\n          \"bodySize\": 0,\n          \"redirectURL\": \"http://no.playwright/script2.js\",\n          \"_transferSize\": 77\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 7.673,\n          \"receive\": 8.324\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      },\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572181.822,\n        \"startedDateTime\": \"2022-06-10T04:27:32.182Z\",\n        \"time\": 6.735,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://no.playwright/script2.js\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"*/*\"\n            },\n            {\n              \"name\": \"Referer\",\n              \"value\": \"http://no.playwright/\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 206,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"OK\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"content-length\",\n              \"value\": \"18\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/javascript\"\n            }\n          ],\n          \"content\": {\n            \"size\": 18,\n            \"mimeType\": \"text/javascript\",\n            \"compression\": 0,\n            \"text\": \"window.value='foo'\"\n          },\n          \"headersSize\": 70,\n          \"bodySize\": 82,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 82\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 6.735,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "tests/assets/har-redirect.har",
    "content": "{\n  \"log\": {\n    \"version\": \"1.2\",\n    \"creator\": {\n      \"name\": \"Playwright\",\n      \"version\": \"1.23.0-next\"\n    },\n    \"browser\": {\n      \"name\": \"chromium\",\n      \"version\": \"103.0.5060.42\"\n    },\n    \"pages\": [\n      {\n        \"startedDateTime\": \"2022-06-16T21:41:23.901Z\",\n        \"id\": \"page@8f314969edc000996eb5c2ab22f0e6b3\",\n        \"title\": \"Microsoft\",\n        \"pageTimings\": {\n          \"onContentLoad\": 8363,\n          \"onLoad\": 8896\n        }\n      }\n    ],\n    \"entries\": [\n      {\n        \"_frameref\": \"frame@3767e074ecde4cb8372abba2f6f9bb4f\",\n        \"_monotonicTime\": 110928357.437,\n        \"startedDateTime\": \"2022-06-16T21:41:23.951Z\",\n        \"time\": 93.99,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"https://theverge.com/\",\n          \"httpVersion\": \"HTTP/2.0\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \":authority\",\n              \"value\": \"theverge.com\"\n            },\n            {\n              \"name\": \":method\",\n              \"value\": \"GET\"\n            },\n            {\n              \"name\": \":path\",\n              \"value\": \"/\"\n            },\n            {\n              \"name\": \":scheme\",\n              \"value\": \"https\"\n            },\n            {\n              \"name\": \"accept\",\n              \"value\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\"\n            },\n            {\n              \"name\": \"accept-encoding\",\n              \"value\": \"gzip, deflate, br\"\n            },\n            {\n              \"name\": \"accept-language\",\n              \"value\": \"en-US,en;q=0.9\"\n            },\n            {\n              \"name\": \"sec-ch-ua\",\n              \"value\": \"\\\"Chromium\\\";v=\\\"103\\\", \\\".Not/A)Brand\\\";v=\\\"99\\\"\"\n            },\n            {\n              \"name\": \"sec-ch-ua-mobile\",\n              \"value\": \"?0\"\n            },\n            {\n              \"name\": \"sec-ch-ua-platform\",\n              \"value\": \"\\\"Linux\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"value\": \"document\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"value\": \"navigate\"\n            },\n            {\n              \"name\": \"sec-fetch-site\",\n              \"value\": \"none\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"value\": \"?1\"\n            },\n            {\n              \"name\": \"upgrade-insecure-requests\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"user-agent\",\n              \"value\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 644,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 301,\n          \"statusText\": \"\",\n          \"httpVersion\": \"HTTP/2.0\",\n          \"cookies\": [\n            {\n              \"name\": \"vmidv1\",\n              \"value\": \"9faf31ab-1415-4b90-b367-24b670205f41\",\n              \"expires\": \"2027-06-15T21:41:24.000Z\",\n              \"domain\": \"theverge.com\",\n              \"path\": \"/\",\n              \"sameSite\": \"Lax\",\n              \"secure\": true\n            }\n          ],\n          \"headers\": [\n            {\n              \"name\": \"accept-ranges\",\n              \"value\": \"bytes\"\n            },\n            {\n              \"name\": \"content-length\",\n              \"value\": \"0\"\n            },\n            {\n              \"name\": \"date\",\n              \"value\": \"Thu, 16 Jun 2022 21:41:24 GMT\"\n            },\n            {\n              \"name\": \"location\",\n              \"value\": \"http://www.theverge.com/\"\n            },\n            {\n              \"name\": \"retry-after\",\n              \"value\": \"0\"\n            },\n            {\n              \"name\": \"server\",\n              \"value\": \"Varnish\"\n            },\n            {\n              \"name\": \"set-cookie\",\n              \"value\": \"vmidv1=9faf31ab-1415-4b90-b367-24b670205f41;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=theverge.com;Path=/;SameSite=Lax;Secure\"\n            },\n            {\n              \"name\": \"via\",\n              \"value\": \"1.1 varnish\"\n            },\n            {\n              \"name\": \"x-cache\",\n              \"value\": \"HIT\"\n            },\n            {\n              \"name\": \"x-cache-hits\",\n              \"value\": \"0\"\n            },\n            {\n              \"name\": \"x-served-by\",\n              \"value\": \"cache-pao17442-PAO\"\n            },\n            {\n              \"name\": \"x-timer\",\n              \"value\": \"S1655415684.005867,VS0,VE0\"\n            }\n          ],\n          \"content\": {\n            \"size\": -1,\n            \"mimeType\": \"x-unknown\",\n            \"compression\": 0\n          },\n          \"headersSize\": 425,\n          \"bodySize\": 0,\n          \"redirectURL\": \"http://www.theverge.com/\",\n          \"_transferSize\": 425\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": 0,\n          \"connect\": 34.151,\n          \"ssl\": 28.074,\n          \"send\": 0,\n          \"wait\": 27.549,\n          \"receive\": 4.216\n        },\n        \"pageref\": \"page@8f314969edc000996eb5c2ab22f0e6b3\",\n        \"serverIPAddress\": \"151.101.65.52\",\n        \"_serverPort\": 443,\n        \"_securityDetails\": {\n          \"protocol\": \"TLS 1.2\",\n          \"subjectName\": \"*.americanninjawarriornation.com\",\n          \"issuer\": \"GlobalSign Atlas R3 DV TLS CA 2022 Q1\",\n          \"validFrom\": 1644853133,\n          \"validTo\": 1679153932\n        }\n      },\n      {\n        \"_frameref\": \"frame@3767e074ecde4cb8372abba2f6f9bb4f\",\n        \"_monotonicTime\": 110928427.603,\n        \"startedDateTime\": \"2022-06-16T21:41:24.022Z\",\n        \"time\": 44.39499999999999,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://www.theverge.com/\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\"\n            },\n            {\n              \"name\": \"Accept-Encoding\",\n              \"value\": \"gzip, deflate\"\n            },\n            {\n              \"name\": \"Accept-Language\",\n              \"value\": \"en-US,en;q=0.9\"\n            },\n            {\n              \"name\": \"Connection\",\n              \"value\": \"keep-alive\"\n            },\n            {\n              \"name\": \"Host\",\n              \"value\": \"www.theverge.com\"\n            },\n            {\n              \"name\": \"Upgrade-Insecure-Requests\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 423,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 301,\n          \"statusText\": \"Moved Permanently\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [\n            {\n              \"name\": \"_chorus_geoip_continent\",\n              \"value\": \"NA\"\n            },\n            {\n              \"name\": \"vmidv1\",\n              \"value\": \"4e0c1265-10f8-4cb1-a5de-1c3cf70b531c\",\n              \"expires\": \"2027-06-15T21:41:24.000Z\",\n              \"domain\": \"www.theverge.com\",\n              \"path\": \"/\",\n              \"sameSite\": \"Lax\",\n              \"secure\": true\n            }\n          ],\n          \"headers\": [\n            {\n              \"name\": \"Accept-Ranges\",\n              \"value\": \"bytes\"\n            },\n            {\n              \"name\": \"Age\",\n              \"value\": \"2615\"\n            },\n            {\n              \"name\": \"Connection\",\n              \"value\": \"keep-alive\"\n            },\n            {\n              \"name\": \"Content-Length\",\n              \"value\": \"0\"\n            },\n            {\n              \"name\": \"Content-Type\",\n              \"value\": \"text/html\"\n            },\n            {\n              \"name\": \"Date\",\n              \"value\": \"Thu, 16 Jun 2022 21:41:24 GMT\"\n            },\n            {\n              \"name\": \"Location\",\n              \"value\": \"https://www.theverge.com/\"\n            },\n            {\n              \"name\": \"Server\",\n              \"value\": \"nginx\"\n            },\n            {\n              \"name\": \"Set-Cookie\",\n              \"value\": \"_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;\"\n            },\n            {\n              \"name\": \"Set-Cookie\",\n              \"value\": \"vmidv1=4e0c1265-10f8-4cb1-a5de-1c3cf70b531c;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure\"\n            },\n            {\n              \"name\": \"Vary\",\n              \"value\": \"X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Accept-Encoding\"\n            },\n            {\n              \"name\": \"Via\",\n              \"value\": \"1.1 varnish\"\n            },\n            {\n              \"name\": \"X-Cache\",\n              \"value\": \"HIT\"\n            },\n            {\n              \"name\": \"X-Cache-Hits\",\n              \"value\": \"2\"\n            },\n            {\n              \"name\": \"X-Served-By\",\n              \"value\": \"cache-pao17450-PAO\"\n            },\n            {\n              \"name\": \"X-Timer\",\n              \"value\": \"S1655415684.035748,VS0,VE0\"\n            }\n          ],\n          \"content\": {\n            \"size\": -1,\n            \"mimeType\": \"text/html\",\n            \"compression\": 0\n          },\n          \"headersSize\": 731,\n          \"bodySize\": 0,\n          \"redirectURL\": \"https://www.theverge.com/\",\n          \"_transferSize\": 731\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": 2.742,\n          \"connect\": 10.03,\n          \"ssl\": 14.123,\n          \"send\": 0,\n          \"wait\": 15.023,\n          \"receive\": 2.477\n        },\n        \"pageref\": \"page@8f314969edc000996eb5c2ab22f0e6b3\",\n        \"serverIPAddress\": \"151.101.189.52\",\n        \"_serverPort\": 80,\n        \"_securityDetails\": {}\n      },\n      {\n        \"_frameref\": \"frame@3767e074ecde4cb8372abba2f6f9bb4f\",\n        \"_monotonicTime\": 110928455.901,\n        \"startedDateTime\": \"2022-06-16T21:41:24.050Z\",\n        \"time\": 50.29199999999999,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"https://www.theverge.com/\",\n          \"httpVersion\": \"HTTP/2.0\",\n          \"cookies\": [\n            {\n              \"name\": \"vmidv1\",\n              \"value\": \"9faf31ab-1415-4b90-b367-24b670205f41\"\n            },\n            {\n              \"name\": \"_chorus_geoip_continent\",\n              \"value\": \"NA\"\n            }\n          ],\n          \"headers\": [\n            {\n              \"name\": \":authority\",\n              \"value\": \"www.theverge.com\"\n            },\n            {\n              \"name\": \":method\",\n              \"value\": \"GET\"\n            },\n            {\n              \"name\": \":path\",\n              \"value\": \"/\"\n            },\n            {\n              \"name\": \":scheme\",\n              \"value\": \"https\"\n            },\n            {\n              \"name\": \"accept\",\n              \"value\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\"\n            },\n            {\n              \"name\": \"accept-encoding\",\n              \"value\": \"gzip, deflate, br\"\n            },\n            {\n              \"name\": \"accept-language\",\n              \"value\": \"en-US,en;q=0.9\"\n            },\n            {\n              \"name\": \"cookie\",\n              \"value\": \"vmidv1=9faf31ab-1415-4b90-b367-24b670205f41; _chorus_geoip_continent=NA\"\n            },\n            {\n              \"name\": \"sec-ch-ua\",\n              \"value\": \"\\\"Chromium\\\";v=\\\"103\\\", \\\".Not/A)Brand\\\";v=\\\"99\\\"\"\n            },\n            {\n              \"name\": \"sec-ch-ua-mobile\",\n              \"value\": \"?0\"\n            },\n            {\n              \"name\": \"sec-ch-ua-platform\",\n              \"value\": \"\\\"Linux\\\"\"\n            },\n            {\n              \"name\": \"sec-fetch-dest\",\n              \"value\": \"document\"\n            },\n            {\n              \"name\": \"sec-fetch-mode\",\n              \"value\": \"navigate\"\n            },\n            {\n              \"name\": \"sec-fetch-site\",\n              \"value\": \"none\"\n            },\n            {\n              \"name\": \"sec-fetch-user\",\n              \"value\": \"?1\"\n            },\n            {\n              \"name\": \"upgrade-insecure-requests\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"user-agent\",\n              \"value\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 729,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"\",\n          \"httpVersion\": \"HTTP/2.0\",\n          \"cookies\": [\n            {\n              \"name\": \"_chorus_geoip_continent\",\n              \"value\": \"NA\"\n            },\n            {\n              \"name\": \"vmidv1\",\n              \"value\": \"40d8fd14-5ac3-4757-9e9c-efb106e82d3a\",\n              \"expires\": \"2027-06-15T21:41:24.000Z\",\n              \"domain\": \"www.theverge.com\",\n              \"path\": \"/\",\n              \"sameSite\": \"Lax\",\n              \"secure\": true\n            }\n          ],\n          \"headers\": [\n            {\n              \"name\": \"accept-ranges\",\n              \"value\": \"bytes\"\n            },\n            {\n              \"name\": \"age\",\n              \"value\": \"263\"\n            },\n            {\n              \"name\": \"cache-control\",\n              \"value\": \"max-age=0, public, must-revalidate\"\n            },\n            {\n              \"name\": \"content-encoding\",\n              \"value\": \"br\"\n            },\n            {\n              \"name\": \"content-length\",\n              \"value\": \"14\"\n            },\n            {\n              \"name\": \"content-security-policy\",\n              \"value\": \"default-src https: data: 'unsafe-inline' 'unsafe-eval'; child-src https: data: blob:; connect-src https: data: blob: ; font-src https: data:; img-src https: data: blob:; media-src https: data: blob:; object-src https:; script-src https: data: blob: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; block-all-mixed-content; upgrade-insecure-requests\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/html; charset=utf-8\"\n            },\n            {\n              \"name\": \"date\",\n              \"value\": \"Thu, 16 Jun 2022 21:41:24 GMT\"\n            },\n            {\n              \"name\": \"etag\",\n              \"value\": \"W/\\\"d498ef668223d015000070a66a181e85\\\"\"\n            },\n            {\n              \"name\": \"link\",\n              \"value\": \"<https://concertads-configs.vox-cdn.com/sbn/verge/config.json>; rel=preload; as=fetch; crossorigin\"\n            },\n            {\n              \"name\": \"referrer-policy\",\n              \"value\": \"strict-origin-when-cross-origin\"\n            },\n            {\n              \"name\": \"server\",\n              \"value\": \"nginx\"\n            },\n            {\n              \"name\": \"set-cookie\",\n              \"value\": \"_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;\"\n            },\n            {\n              \"name\": \"set-cookie\",\n              \"value\": \"vmidv1=40d8fd14-5ac3-4757-9e9c-efb106e82d3a;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure\"\n            },\n            {\n              \"name\": \"strict-transport-security\",\n              \"value\": \"max-age=31556952; preload\"\n            },\n            {\n              \"name\": \"vary\",\n              \"value\": \"Accept-Encoding, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Origin, X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region\"\n            },\n            {\n              \"name\": \"via\",\n              \"value\": \"1.1 varnish\"\n            },\n            {\n              \"name\": \"x-cache\",\n              \"value\": \"HIT\"\n            },\n            {\n              \"name\": \"x-cache-hits\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"x-content-type-options\",\n              \"value\": \"nosniff\"\n            },\n            {\n              \"name\": \"x-download-options\",\n              \"value\": \"noopen\"\n            },\n            {\n              \"name\": \"x-frame-options\",\n              \"value\": \"SAMEORIGIN\"\n            },\n            {\n              \"name\": \"x-permitted-cross-domain-policies\",\n              \"value\": \"none\"\n            },\n            {\n              \"name\": \"x-request-id\",\n              \"value\": \"97363ad70e272e63641c0bb784fa06a01b848dfd\"\n            },\n            {\n              \"name\": \"x-runtime\",\n              \"value\": \"0.257911\"\n            },\n            {\n              \"name\": \"x-served-by\",\n              \"value\": \"cache-pao17436-PAO\"\n            },\n            {\n              \"name\": \"x-timer\",\n              \"value\": \"S1655415684.075077,VS0,VE1\"\n            },\n            {\n              \"name\": \"x-xss-protection\",\n              \"value\": \"1; mode=block\"\n            }\n          ],\n          \"content\": {\n            \"size\": 14,\n            \"mimeType\": \"text/html\",\n            \"compression\": 0,\n            \"text\": \"<h1>hello</h1>\"\n          },\n          \"headersSize\": 1742,\n          \"bodySize\": 48716,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 48716\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": 0.016,\n          \"connect\": 24.487,\n          \"ssl\": 17.406,\n          \"send\": 0,\n          \"wait\": 8.383,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@8f314969edc000996eb5c2ab22f0e6b3\",\n        \"serverIPAddress\": \"151.101.189.52\",\n        \"_serverPort\": 443,\n        \"_securityDetails\": {\n          \"protocol\": \"TLS 1.2\",\n          \"subjectName\": \"*.americanninjawarriornation.com\",\n          \"issuer\": \"GlobalSign Atlas R3 DV TLS CA 2022 Q1\",\n          \"validFrom\": 1644853133,\n          \"validTo\": 1679153932\n        }\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "tests/assets/har-sha1-main-response.txt",
    "content": "Hello, world"
  },
  {
    "path": "tests/assets/har-sha1.har",
    "content": "{\n  \"log\": {\n    \"version\": \"1.2\",\n    \"creator\": {\n      \"name\": \"Playwright\",\n      \"version\": \"1.23.0-next\"\n    },\n    \"browser\": {\n      \"name\": \"chromium\",\n      \"version\": \"103.0.5060.33\"\n    },\n    \"pages\": [\n      {\n        \"startedDateTime\": \"2022-06-10T04:27:32.125Z\",\n        \"id\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"title\": \"Hey\",\n        \"pageTimings\": {\n          \"onContentLoad\": 70,\n          \"onLoad\": 70\n        }\n      }\n    ],\n    \"entries\": [\n      {\n        \"_frameref\": \"frame@c7467fc0f1f86f09fc3b0d727a3862ea\",\n        \"_monotonicTime\": 270572145.898,\n        \"startedDateTime\": \"2022-06-10T04:27:32.146Z\",\n        \"time\": 8.286,\n        \"request\": {\n          \"method\": \"GET\",\n          \"url\": \"http://no.playwright/\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"Accept\",\n              \"value\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\"\n            },\n            {\n              \"name\": \"Upgrade-Insecure-Requests\",\n              \"value\": \"1\"\n            },\n            {\n              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36\"\n            }\n          ],\n          \"queryString\": [],\n          \"headersSize\": 326,\n          \"bodySize\": 0\n        },\n        \"response\": {\n          \"status\": 200,\n          \"statusText\": \"OK\",\n          \"httpVersion\": \"HTTP/1.1\",\n          \"cookies\": [],\n          \"headers\": [\n            {\n              \"name\": \"content-length\",\n              \"value\": \"12\"\n            },\n            {\n              \"name\": \"content-type\",\n              \"value\": \"text/html\"\n            }\n          ],\n          \"content\": {\n            \"size\": 12,\n            \"mimeType\": \"text/html\",\n            \"compression\": 0,\n            \"_file\": \"har-sha1-main-response.txt\"\n          },\n          \"headersSize\": 64,\n          \"bodySize\": 71,\n          \"redirectURL\": \"\",\n          \"_transferSize\": 71\n        },\n        \"cache\": {\n          \"beforeRequest\": null,\n          \"afterRequest\": null\n        },\n        \"timings\": {\n          \"dns\": -1,\n          \"connect\": -1,\n          \"ssl\": -1,\n          \"send\": 0,\n          \"wait\": 8.286,\n          \"receive\": -1\n        },\n        \"pageref\": \"page@b17b177f1c2e66459db3dcbe44636ffd\",\n        \"_securityDetails\": {}\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "tests/assets/har.html",
    "content": "<title>HAR Page</title>\n<link rel='stylesheet' href='./one-style.css'>\n<div>hello, world!</div>\n"
  },
  {
    "path": "tests/assets/headings.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <title>Headings</title>\n</head>\n\n<body>\n    <h1>Title</h1>\n    <h2>Subtitle</h2>\n    <h3>Subsubtitle</h3>\n    <h2>Subtitle</h2>\n</body>\n\n</html>\n"
  },
  {
    "path": "tests/assets/historyapi.html",
    "content": "<script>\nwindow.addEventListener('DOMContentLoaded', () => {\n  history.pushState({}, '', '#1');\n});\n</script>\n"
  },
  {
    "path": "tests/assets/injectedfile.js",
    "content": "window.__injected = 42;\nwindow.injected = 123;\nwindow.__injectedError = new Error('hi');\n"
  },
  {
    "path": "tests/assets/injectedstyle.css",
    "content": "body {\n  background-color: red;\n}\n"
  },
  {
    "path": "tests/assets/input/animating-button.html",
    "content": "<style>\n  body, html { margin: 0; padding: 0; }\n  @keyframes move {\n    from { marign-left: 0; }\n    to   { margin-left: 100px; }\n  }\n</style>\n<script>\nfunction addButton() {\n  const button = document.createElement('button');\n  button.textContent = 'Click me';\n  button.style.animation = '3s linear move';\n  button.style.animationIterationCount = 'infinite';\n  button.addEventListener('click', () => window.clicked = true);\n  document.body.appendChild(button);\n}\n\nfunction stopButton(remove) {\n  const button = document.querySelector('button');\n  button.style.marginLeft = button.getBoundingClientRect().left + 'px';\n  button.style.animation = '';\n  if (remove)\n    button.remove();\n}\n\nlet x = 0;\nfunction jump() {\n  x += 300;\n  const button = document.querySelector('button');\n  button.style.marginLeft = x + 'px';\n}\n\nfunction startJumping() {\n  x = 0;\n  const moveIt = () => {\n    jump();\n    requestAnimationFrame(moveIt);\n  };\n  setInterval(jump, 0);\n  moveIt();\n}\n</script>\n"
  },
  {
    "path": "tests/assets/input/button.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Button test</title>\n  </head>\n  <body>\n    <script src=\"mouse-helper.js\"></script>\n    <button>Click target</button>\n    <script>\n      window.result = 'Was not clicked';\n      window.offsetX = undefined;\n      window.offsetY = undefined;\n      window.pageX = undefined;\n      window.pageY = undefined;\n      window.shiftKey = undefined;\n      window.pageX = undefined;\n      window.pageY = undefined;\n      window.bubbles = undefined;\n      document.querySelector('button').addEventListener('click', e => {\n        result = 'Clicked';\n        offsetX = e.offsetX;\n        offsetY = e.offsetY;\n        pageX = e.pageX;\n        pageY = e.pageY;\n        shiftKey = e.shiftKey;\n        bubbles = e.bubbles;\n        cancelable = e.cancelable;\n        composed = e.composed;\n      }, false);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/checkbox.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Selection Test</title>\n  </head>\n  <body>\n    <label for=\"agree\">Remember Me</label>\n    <input id=\"agree\" type=\"checkbox\">\n    <script>\n      window.result = {\n        check: null,\n        events: [],\n      };\n\n      let checkbox = document.querySelector('input');\n\n      const events = [\n        'change',\n        'click',\n        'dblclick',\n        'input',\n        'mousedown',\n        'mouseenter',\n        'mouseleave',\n        'mousemove',\n        'mouseout',\n        'mouseover',\n        'mouseup',\n      ];\n\n      for (let event of events) {\n        checkbox.addEventListener(event, () => {\n          if (['change', 'click', 'dblclick', 'input'].includes(event) === true) {\n            result.check = checkbox.checked;\n          }\n\n          result.events.push(event);\n        }, false);\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/fileupload-multi.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>File upload test</title>\n  </head>\n  <body>\n    <form action=\"/upload\" method=\"post\" enctype=\"multipart/form-data\">\n      <input type=\"file\" multiple name=\"file1\">\n      <input type=\"submit\">\n    </form>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/fileupload.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>File upload test</title>\n  </head>\n  <body>\n    <form action=\"/upload\" method=\"post\" enctype=\"multipart/form-data\">\n      <input type=\"file\" name=\"file1\">\n      <input type=\"submit\">\n    </form>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/folderupload.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Folder upload test</title>\n  </head>\n  <body>\n    <form action=\"/upload\" method=\"post\" enctype=\"multipart/form-data\">\n      <input type=\"file\" name=\"file1\" webkitdirectory>\n      <input type=\"submit\">\n    </form>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/handle-locator.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Interstitial test</title>\n  </head>\n  <body>\n    <style>\n      body {\n        position: relative;\n      }\n      #target.removed {\n        display: none;\n      }\n      #target.hidden {\n        visibility: hidden;\n      }\n      #target:hover {\n        background: yellow;\n      }\n      #interstitial {\n        position: absolute;\n        top: 0;\n        left: 0;\n        width: 300px;\n        height: 300px;\n        border: 1px solid black;\n        background: rgba(255, 180, 180);\n        display: none;\n      }\n      #interstitial.visible {\n        display: block;\n      }\n      #close {\n        margin: 50px;\n      }\n    </style>\n    <div><button id=\"target\">Click me</button></div>\n    <div id=\"aside\">A place on the side to hover</div>\n    <div id=\"interstitial\">\n      <div>This interstitial covers the button</div>\n      <button id=\"close\">Close the interstitial</button>\n    </div>\n    <script>\n      const target = document.querySelector('#target');\n      const interstitial = document.querySelector('#interstitial');\n      const close = document.querySelector('#close');\n\n      target.addEventListener('click', () => {\n        window.clicked = (window.clicked ?? 0) + 1;\n      }, false);\n\n      close.addEventListener('click', () => {\n        const closeInterstitial = () => {\n          interstitial.classList.remove('visible');\n          target.classList.remove('hidden');\n          target.classList.remove('removed');\n        };\n\n        if (interstitial.classList.contains('timeout'))\n          setTimeout(closeInterstitial, 3000);\n        else\n          closeInterstitial();\n      });\n\n      let timesToShow = 0;\n      function setupAnnoyingInterstitial(event, times, capture) {\n        timesToShow = times;\n        const listener = () => {\n          timesToShow--;\n          interstitial.classList.add('visible');\n          interstitial.getBoundingClientRect();\n          if (!timesToShow && event !== 'none')\n            target.removeEventListener(event, listener, capture === 'capture');\n        };\n        if (event === 'hide' || event === 'timeout') {\n          target.classList.add('hidden');\n          listener();\n          if (event === 'timeout')\n            interstitial.classList.add('timeout');\n        } else if (event === 'remove') {\n          target.classList.add('removed');\n          listener();\n        } else if (event === 'none') {\n          listener();\n        } else {\n          target.addEventListener(event, listener, capture === 'capture');\n        }\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/keyboard.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Keyboard test</title>\n  </head>\n  <body>\n    <textarea></textarea>\n    <script>\n      window.result = \"\";\n      let textarea = document.querySelector('textarea');\n      textarea.focus();\n      textarea.addEventListener('keydown', event => {\n        log('Keydown:', event.key, event.code, event.which, modifiers(event));\n      });\n      textarea.addEventListener('keypress', event => {\n        log('Keypress:', event.key, event.code, event.which, event.charCode, modifiers(event));\n      });\n      textarea.addEventListener('keyup', event => {\n        log('Keyup:', event.key, event.code, event.which, modifiers(event));\n      });\n      function modifiers(event) {\n        let m = [];\n        if (event.altKey)\n          m.push('Alt')\n        if (event.ctrlKey)\n          m.push('Control');\n        if (event.shiftKey)\n          m.push('Shift')\n        return '[' + m.join(' ') + ']';\n      }\n      function log(...args) {\n        console.log.apply(console, args);\n        result += args.join(' ') + '\\n';\n      }\n      function getResult() {\n        let temp = result.trim();\n        result = \"\";\n        return temp;\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/mouse-helper.js",
    "content": "// This injects a box into the page that moves with the mouse;\n// Useful for debugging\n(function(){\n  const box = document.createElement('div');\n  box.classList.add('mouse-helper');\n  const styleElement = document.createElement('style');\n  styleElement.innerHTML = `\n  .mouse-helper {\n    pointer-events: none;\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 20px;\n    height: 20px;\n    background: rgba(0,0,0,.4);\n    border: 1px solid white;\n    border-radius: 10px;\n    margin-left: -10px;\n    margin-top: -10px;\n    transition: background .2s, border-radius .2s, border-color .2s;\n  }\n  .mouse-helper.button-1 {\n    transition: none;\n    background: rgba(0,0,0,0.9);\n  }\n  .mouse-helper.button-2 {\n    transition: none;\n    border-color: rgba(0,0,255,0.9);\n  }\n  .mouse-helper.button-3 {\n    transition: none;\n    border-radius: 4px;\n  }\n  .mouse-helper.button-4 {\n    transition: none;\n    border-color: rgba(255,0,0,0.9);\n  }\n  .mouse-helper.button-5 {\n    transition: none;\n    border-color: rgba(0,255,0,0.9);\n  }\n  `;\n  document.head.appendChild(styleElement);\n  document.body.appendChild(box);\n  document.addEventListener('mousemove', event => {\n    box.style.left = event.pageX + 'px';\n    box.style.top = event.pageY + 'px';\n    updateButtons(event.buttons);\n  }, true);\n  document.addEventListener('mousedown', event => {\n    updateButtons(event.buttons);\n    box.classList.add('button-' + event.which);\n  }, true);\n  document.addEventListener('mouseup', event => {\n    updateButtons(event.buttons);\n    box.classList.remove('button-' + event.which);\n  }, true);\n  function updateButtons(buttons) {\n    for (let i = 0; i < 5; i++)\n      box.classList.toggle('button-' + i, buttons & (1 << i));\n  }\n})();\n"
  },
  {
    "path": "tests/assets/input/rotatedButton.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Rotated button test</title>\n  </head>\n  <body>\n    <script src=\"mouse-helper.js\"></script>\n    <button onclick=\"clicked();\">Click target</button>\n    <style>\n      button {\n        transform: rotateY(180deg);\n      }\n    </style>\n    <script>\n      window.result = 'Was not clicked';\n      function clicked() {\n        result = 'Clicked';\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/scrollable.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Scrollable test</title>\n  </head>\n  <body>\n    <script src='mouse-helper.js'></script>\n    <script>\n        for (let i = 0; i < 100; i++) {\n            let button = document.createElement('button');\n            button.textContent = i + ': not clicked';\n            button.id = 'button-' + i;\n            button.onclick = () => button.textContent = 'clicked';\n            button.oncontextmenu = event => {\n              event.preventDefault();\n              button.textContent = 'context menu';\n            }\n            document.body.appendChild(button);\n            document.body.appendChild(document.createElement('br'));\n        }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/select.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Selection Test</title>\n  </head>\n  <body>\n    <select>\n      <option value=\"black\">Black</option>\n      <option value=\"blue\">Blue</option>\n      <option value=\"brown\">Brown</option>\n      <option value=\"cyan\">Cyan</option>\n      <option value=\"gray\">Gray</option>\n      <option value=\"green\">Green</option>\n      <option value=\"indigo\">Indigo</option>\n      <option value=\"magenta\">Magenta</option>\n      <option value=\"orange\">Orange</option>\n      <option value=\"pink\">Pink</option>\n      <option value=\"purple\">Purple</option>\n      <option value=\"red\">Red</option>\n      <option value=\"violet\">Violet</option>\n      <option value=\"white\" id=\"whiteOption\">White</option>\n      <option value=\"yellow\">Yellow</option>\n    </select>\n    <script>\n      window.result = {\n        onInput: null,\n        onChange: null,\n        onBubblingChange: null,\n        onBubblingInput: null,\n      };\n\n      let select = document.querySelector('select');\n\n      function makeEmpty() {\n        for (let i = select.options.length - 1; i >= 0; --i) {\n          select.remove(i);\n        }\n      }\n\n      function makeMultiple() {\n        select.setAttribute('multiple', true);\n      }\n\n      select.addEventListener('input', () => {\n        result.onInput = Array.from(select.querySelectorAll('option:checked')).map((option) => {\n          return option.value;\n        });\n      }, false);\n\n      select.addEventListener('change', () => {\n        result.onChange = Array.from(select.querySelectorAll('option:checked')).map((option) => {\n          return option.value;\n        });\n      }, false);\n\n      document.body.addEventListener('input', () => {\n        result.onBubblingInput = Array.from(select.querySelectorAll('option:checked')).map((option) => {\n          return option.value;\n        });\n      }, false);\n\n      document.body.addEventListener('change', () => {\n        result.onBubblingChange = Array.from(select.querySelectorAll('option:checked')).map((option) => {\n          return option.value;\n        });\n      }, false);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/textarea.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Textarea test</title>\n  </head>\n  <body>\n    <textarea spellcheck=\"false\"></textarea>\n    <input></input>\n    <div contenteditable=\"true\"></div>\n    <div class=\"plain\">Plain div</div>\n    <script src='mouse-helper.js'></script>\n    <script>\n      window.result = '';\n      let textarea = document.querySelector('textarea');\n      textarea.addEventListener('input', () => result = textarea.value, false);\n      let input = document.querySelector('input');\n      input.addEventListener('input', () => result = input.value, false);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/input/touches.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Touch test</title>\n  </head>\n  <body>\n    <script src=\"mouse-helper.js\"></script>\n    <button onclick=\"clicked();\">Click target</button>\n    <script>\n      window.result = [];\n      const button = document.querySelector('button');\n      button.style.height = '200px';\n      button.style.width = '200px';\n      button.focus();\n      button.addEventListener('touchstart', event => {\n        log('Touchstart:', ...Array.from(event.changedTouches).map(touch => touch.identifier));\n      });\n      button.addEventListener('touchend', event => {\n        log('Touchend:', ...Array.from(event.changedTouches).map(touch => touch.identifier));\n      });\n      button.addEventListener('touchmove', event => {\n        log('Touchmove:', ...Array.from(event.changedTouches).map(touch => touch.identifier));\n      });\n      function log(...args) {\n        console.log.apply(console, args);\n        result.push(args.join(' '));\n      }\n      function getResult() {\n        let temp = result;\n        result = [];\n        return temp;\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/mobile.html",
    "content": "<meta name = \"viewport\" content = \"initial-scale = 1, user-scalable = no\">\n"
  },
  {
    "path": "tests/assets/networkidle.html",
    "content": "<script src='networkidle.js'></script>\n"
  },
  {
    "path": "tests/assets/networkidle.js",
    "content": "async function main() {\n  window.ws = new WebSocket('ws://localhost:' + window.location.port + '/ws');\n  window.ws.addEventListener('message', message => {});\n\n  fetch('fetch-request-a.js');\n  window.top.fetchSecond = () => {\n    // Do not return the promise here.\n    fetch('fetch-request-b.js');\n  };\n}\n\nmain();\n"
  },
  {
    "path": "tests/assets/offscreenbuttons.html",
    "content": "<style>\n  button {\n    position: absolute;\n    width: 100px;\n    height: 20px;\n    margin: 0;\n  }\n\n  body, html {\n    margin: 0;\n    padding: 0;\n    height: 100%;\n    width: 100%;\n    position: relative;\n  }\n\n  div {\n    position: absolute;\n    left: 0;\n    top: 0;\n    right: 0;\n    bottom: 0;\n  }\n\n  #btn0 { right: 0px; top: 0; }\n  #btn1 { right: -10px; top: 25px; }\n  #btn2 { right: -20px; top: 50px; }\n  #btn3 { right: -30px; top: 75px; }\n  #btn4 { right: -40px; top: 100px; }\n  #btn5 { right: -50px; top: 125px; }\n  #btn6 { right: -60px; top: 150px; }\n  #btn7 { right: -70px; top: 175px; }\n  #btn8 { right: -80px; top: 200px; }\n  #btn9 { right: -90px; top: 225px; }\n  #btn10 { right: -100px; top: 250px; }\n</style>\n<div>\n<button id=btn0>0</button>\n<button id=btn1>1</button>\n<button id=btn2>2</button>\n<button id=btn3>3</button>\n<button id=btn4>4</button>\n<button id=btn5>5</button>\n<button id=btn6>6</button>\n<button id=btn7>7</button>\n<button id=btn8>8</button>\n<button id=btn9>9</button>\n<button id=btn10>10</button>\n</div>\n<script>\nwindow.addEventListener('DOMContentLoaded', () => {\n  for (const button of Array.from(document.querySelectorAll('button')))\n    button.addEventListener('click', () => console.log('button #' + button.textContent + ' clicked'), false);\n}, false);\n</script>\n"
  },
  {
    "path": "tests/assets/one-style.css",
    "content": "body {\n    background-color: pink;\n}\n"
  },
  {
    "path": "tests/assets/one-style.html",
    "content": "<link rel='stylesheet' href='./one-style.css'>\n<div>hello, world!</div>\n"
  },
  {
    "path": "tests/assets/playground.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Playground</title>\n  </head>\n  <body>\n    <button>A button</button>\n    <textarea>A text area</textarea>\n    <div id=\"first\">First div</div>\n    <div id=\"second\">\n        Second div\n        <span class=\"inner\">Inner span</span>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/popup/popup.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Popup</title>\n    <script>\n      window.initialUserAgent = navigator.userAgent;\n    </script>\n  </head>\n  <body>\n    I am a popup\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/popup/window-open.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Popup test</title>\n  </head>\n  <body>\n    <script>\n      window.open('./popup.html');\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/react.html",
    "content": "<head>\n  <script src=\"./react/react@16.13.1.production.min.js\"></script>\n  <script src=\"./react/react-dom@16.13.1.production.min.js\"></script>\n</head>\n<body>\n  <div class='react-root'></div>\n  <script>\n    window.e = React.createElement;\n    window.reactRoot = document.querySelector('.react-root');\n    window.renderComponent = c => ReactDOM.render(c, window.reactRoot);\n\n    window.MyButton = class MyButton extends React.Component {\n      constructor(props) {\n        super(props);\n        this.state = { hovered: false };\n      }\n      render() {\n        return e('button', {\n          disabled: !!this.props.disabled,\n          onClick: () => {\n            window[this.props.name] = true;\n          },\n          onMouseEnter: () => {\n            if (this.props.renameOnHover)\n              this.setState({ hovered: true });\n            if (this.props.onHover)\n              this.props.onHover();\n          },\n        }, this.state.hovered ? 'Hovered' : this.props.name);\n      }\n    };\n  </script>\n</body>\n"
  },
  {
    "path": "tests/assets/sectionselectorengine.js",
    "content": "({\n  create(root, target) {\n  },\n  query(root, selector) {\n    return root.querySelector('section');\n  },\n  queryAll(root, selector) {\n    return Array.from(root.querySelectorAll('section'));\n  }\n})\n"
  },
  {
    "path": "tests/assets/self-request.html",
    "content": "<script>\nvar req = new XMLHttpRequest();\nreq.open('GET', '/self-request.html');\nreq.send(null);\n</script>\n"
  },
  {
    "path": "tests/assets/serviceworkers/empty/sw.html",
    "content": "<script>\n  window.registrationPromise = navigator.serviceWorker.register('sw.js');\n</script>\n"
  },
  {
    "path": "tests/assets/serviceworkers/empty/sw.js",
    "content": ""
  },
  {
    "path": "tests/assets/serviceworkers/fetch/style.css",
    "content": "body {\n    background-color: pink;\n}\n"
  },
  {
    "path": "tests/assets/serviceworkers/fetch/sw.html",
    "content": "<link rel=\"stylesheet\" href=\"./style.css\">\n<script>\n  window.registrationPromise = navigator.serviceWorker.register('sw.js');\n  window.activationPromise = new Promise(resolve => navigator.serviceWorker.oncontrollerchange = resolve);\n</script>\n"
  },
  {
    "path": "tests/assets/serviceworkers/fetch/sw.js",
    "content": "self.addEventListener('fetch', event => {\n  event.respondWith(fetch(event.request));\n});\n\nself.addEventListener('activate', event => {\n  event.waitUntil(clients.claim());\n});\n"
  },
  {
    "path": "tests/assets/serviceworkers/fetchdummy/sw.html",
    "content": "<script>\n  window.registrationPromise = navigator.serviceWorker.register('sw.js');\n  window.activationPromise = new Promise(resolve => navigator.serviceWorker.oncontrollerchange = resolve);\n\n  async function fetchDummy(name) {\n    const response = await fetch(name);\n    if (!response.ok)\n      return 'FAILURE: ' + response.statusText;\n    const text = await response.text();\n    return text;\n  }\n</script>\n"
  },
  {
    "path": "tests/assets/serviceworkers/fetchdummy/sw.js",
    "content": "self.addEventListener('fetch', event => {\n  if (event.request.url.endsWith('.html') || event.request.url.includes('passthrough')) {\n    event.respondWith(fetch(event.request));\n    return;\n  }\n  const slash = event.request.url.lastIndexOf('/');\n  const name = event.request.url.substring(slash + 1);\n  const blob = new Blob([\"responseFromServiceWorker:\" + name], {type : 'text/css'});\n  const response = new Response(blob, { \"status\" : 200 , \"statusText\" : \"OK\" });\n  event.respondWith(response);\n});\n\nself.addEventListener('activate', event => {\n  event.waitUntil(clients.claim());\n});\n"
  },
  {
    "path": "tests/assets/shadow.html",
    "content": "<script>\n\nlet h1 = null;\nlet button = null;\nlet clicked = false;\n\nwindow.addEventListener('DOMContentLoaded', () => {\n  const shadowRoot = document.body.attachShadow({mode: 'open'});\n  h1 = document.createElement('h1');\n  h1.textContent = 'Hellow Shadow DOM v1';\n  button = document.createElement('button');\n  button.textContent = 'Click';\n  button.addEventListener('click', () => clicked = true);\n  shadowRoot.appendChild(h1);\n  shadowRoot.appendChild(button);\n});\n</script>\n"
  },
  {
    "path": "tests/assets/simple.json",
    "content": "{\"foo\": \"bar\"}\n"
  },
  {
    "path": "tests/assets/title.html",
    "content": "<title>Woof-Woof</title>\n"
  },
  {
    "path": "tests/assets/worker/worker.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Worker test</title>\n  </head>\n  <body>\n    <script>\n      var worker = new Worker('worker.js');\n      worker.onmessage = function(message) {\n        console.log(message.data);\n      };\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/assets/worker/worker.js",
    "content": "console.log('hello from the worker');\n\nfunction workerFunction() {\n  return 'worker function result';\n}\n\nself.addEventListener('message', event => {\n  console.log('got this data: ' + event.data);\n});\n\n(async function() {\n  while (true) {\n    self.postMessage(workerFunction.toString());\n    await new Promise(x => setTimeout(x, 100));\n  }\n})();\n"
  },
  {
    "path": "tests/assets/wrappedlink.html",
    "content": "<style>\n:root {\n  font-family: monospace;\n}\n\nbody {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\ndiv {\n  width: 10ch;\n  word-wrap: break-word;\n  border: 1px solid blue;\n  transform: rotate(33deg);\n  line-height: 8ch;\n  padding: 2ch;\n}\n\na {\n  margin-left: 7ch;\n}\n</style>\n<div>\n  <a href='#clicked'>123321</a>\n</div>\n<script>\n  document.querySelector('a').addEventListener('click', () => {\n    window.__clicked = true;\n  });\n</script>\n"
  },
  {
    "path": "tests/async/__init__.py",
    "content": ""
  },
  {
    "path": "tests/async/conftest.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom contextlib import asynccontextmanager\nfrom pathlib import Path\nfrom typing import Any, AsyncGenerator, Awaitable, Callable, Dict, Generator\n\nimport pytest\n\nfrom playwright._impl._driver import compute_driver_executable\nfrom playwright.async_api import (\n    Browser,\n    BrowserContext,\n    BrowserType,\n    FrameLocator,\n    Locator,\n    Page,\n    Playwright,\n    Selectors,\n    async_playwright,\n)\nfrom tests.server import HTTPServer\n\nfrom .utils import Utils\nfrom .utils import utils as utils_object\n\n\n@pytest.fixture\ndef utils() -> Generator[Utils, None, None]:\n    yield utils_object\n\n\n@pytest.fixture(scope=\"session\")\nasync def playwright() -> AsyncGenerator[Playwright, None]:\n    async with async_playwright() as playwright_object:\n        yield playwright_object\n\n\n@pytest.fixture(scope=\"session\")\ndef browser_type(playwright: Playwright, browser_name: str) -> BrowserType:\n    if browser_name == \"chromium\":\n        return playwright.chromium\n    if browser_name == \"firefox\":\n        return playwright.firefox\n    if browser_name == \"webkit\":\n        return playwright.webkit\n    raise Exception(f\"Invalid browser_name: {browser_name}\")\n\n\n@pytest.fixture(scope=\"session\")\nasync def browser_factory(\n    launch_arguments: Dict, browser_type: BrowserType\n) -> AsyncGenerator[Callable[..., Awaitable[Browser]], None]:\n    browsers = []\n\n    async def launch(**kwargs: Any) -> Browser:\n        browser = await browser_type.launch(**launch_arguments, **kwargs)\n        browsers.append(browser)\n        return browser\n\n    yield launch\n    for browser in browsers:\n        await browser.close()\n\n\n@pytest.fixture(scope=\"session\")\nasync def browser(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\",\n) -> AsyncGenerator[Browser, None]:\n    browser = await browser_factory()\n    yield browser\n    await browser.close()\n\n\n@pytest.fixture(scope=\"session\")\ndef browser_version(browser: Browser) -> str:\n    return browser.version\n\n\n@pytest.fixture\nasync def context_factory(\n    browser: Browser,\n) -> AsyncGenerator[\"Callable[..., Awaitable[BrowserContext]]\", None]:\n    contexts = []\n\n    async def launch(**kwargs: Any) -> BrowserContext:\n        context = await browser.new_context(**kwargs)\n        contexts.append(context)\n        return context\n\n    yield launch\n    for context in contexts:\n        await context.close()\n\n\n@pytest.fixture(scope=\"session\")\ndef default_same_site_cookie_value(browser_name: str, is_linux: bool) -> str:\n    if browser_name == \"chromium\":\n        return \"Lax\"\n    if browser_name == \"firefox\":\n        return \"None\"\n    if browser_name == \"webkit\" and is_linux:\n        return \"Lax\"\n    if browser_name == \"webkit\" and not is_linux:\n        return \"None\"\n    raise Exception(f\"Invalid browser_name: {browser_name}\")\n\n\n@pytest.fixture\nasync def context(\n    context_factory: \"Callable[..., asyncio.Future[BrowserContext]]\",\n) -> AsyncGenerator[BrowserContext, None]:\n    context = await context_factory()\n    yield context\n    await context.close()\n\n\n@pytest.fixture\nasync def page(context: BrowserContext) -> AsyncGenerator[Page, None]:\n    page = await context.new_page()\n    yield page\n    await page.close()\n\n\n@pytest.fixture(scope=\"session\")\ndef selectors(playwright: Playwright) -> Selectors:\n    return playwright.selectors\n\n\nclass TraceViewerPage:\n    def __init__(self, page: Page):\n        self.page = page\n\n    @property\n    def actions_tree(self) -> Locator:\n        return self.page.get_by_test_id(\"actions-tree\")\n\n    @property\n    def action_titles(self) -> Locator:\n        return self.page.locator(\".action-title\")\n\n    @property\n    def stack_frames(self) -> Locator:\n        return self.page.get_by_role(\"list\", name=\"Stack Trace\").get_by_role(\"listitem\")\n\n    async def select_action(self, title: str, ordinal: int = 0) -> None:\n        await self.page.locator(\".action-title\", has_text=title).nth(ordinal).click()\n\n    async def select_snapshot(self, name: str) -> None:\n        await self.page.click(\n            f'.snapshot-tab .tabbed-pane-tab-label:has-text(\"{name}\")'\n        )\n\n    async def snapshot_frame(\n        self, action_name: str, ordinal: int = 0, has_subframe: bool = False\n    ) -> FrameLocator:\n        await self.select_action(action_name, ordinal)\n        expected_frames = 4 if has_subframe else 3\n        while len(self.page.frames) < expected_frames:\n            await self.page.wait_for_event(\"frameattached\")\n        return self.page.frame_locator(\"iframe.snapshot-visible[name=snapshot]\")\n\n    async def show_source_tab(self) -> None:\n        await self.page.click(\"text='Source'\")\n\n    async def expand_action(self, title: str, ordinal: int = 0) -> None:\n        await self.actions_tree.locator(\".tree-view-entry\", has_text=title).nth(\n            ordinal\n        ).locator(\".codicon-chevron-right\").click()\n\n\n@pytest.fixture\nasync def show_trace_viewer(browser: Browser) -> AsyncGenerator[Callable, None]:\n    \"\"\"Fixture that provides a function to show trace viewer for a trace file.\"\"\"\n\n    @asynccontextmanager\n    async def _show_trace_viewer(\n        trace_path: Path,\n    ) -> AsyncGenerator[TraceViewerPage, None]:\n        trace_viewer_path = (\n            Path(compute_driver_executable()[0]) / \"../package/lib/vite/traceViewer\"\n        ).resolve()\n\n        server = HTTPServer()\n        server.start(trace_viewer_path)\n        server.set_route(\"/trace.zip\", lambda request: request.serve_file(trace_path))\n\n        page = await browser.new_page()\n\n        try:\n            await page.goto(\n                f\"{server.PREFIX}/index.html?trace={server.PREFIX}/trace.zip\"\n            )\n            yield TraceViewerPage(page)\n        finally:\n            await page.close()\n            server.stop()\n\n    yield _show_trace_viewer\n"
  },
  {
    "path": "tests/async/test_add_init_script.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom playwright.async_api import BrowserContext, Error, Page\n\n\nasync def test_add_init_script_evaluate_before_anything_else_on_the_page(\n    page: Page,\n) -> None:\n    await page.add_init_script(\"window.injected = 123\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_work_with_a_path(page: Page, assetdir: Path) -> None:\n    await page.add_init_script(path=assetdir / \"injectedfile.js\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_work_with_content(page: Page) -> None:\n    await page.add_init_script(\"window.injected = 123\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_throw_without_path_and_content(page: Page) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.add_init_script({\"foo\": \"bar\"})  # type: ignore\n    except Error as e:\n        error = e\n    assert error\n    assert error.message == \"Either path or script parameter must be specified\"\n\n\nasync def test_add_init_script_work_with_browser_context_scripts(\n    page: Page, context: BrowserContext\n) -> None:\n    await context.add_init_script(\"window.temp = 123\")\n    page = await context.new_page()\n    await page.add_init_script(\"window.injected = window.temp\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_work_with_browser_context_scripts_with_a_path(\n    page: Page, context: BrowserContext, assetdir: Path\n) -> None:\n    await context.add_init_script(path=assetdir / \"injectedfile.js\")\n    page = await context.new_page()\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_work_with_browser_context_scripts_for_already_created_pages(\n    page: Page, context: BrowserContext\n) -> None:\n    await context.add_init_script(\"window.temp = 123\")\n    await page.add_init_script(\"window.injected = window.temp\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.result\") == 123\n\n\nasync def test_add_init_script_support_multiple_scripts(page: Page) -> None:\n    await page.add_init_script(\"window.script1 = 1\")\n    await page.add_init_script(\"window.script2 = 2\")\n    await page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert await page.evaluate(\"window.script1\") == 1\n    assert await page.evaluate(\"window.script2\") == 2\n\n\nasync def test_should_work_with_trailing_comments(page: Page) -> None:\n    await page.add_init_script(\"// comment\")\n    await page.add_init_script(\"window.secret = 42;\")\n    await page.goto(\"data:text/html,<html></html>\")\n    assert await page.evaluate(\"secret\") == 42\n"
  },
  {
    "path": "tests/async/test_assertions.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport datetime\nimport re\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error, Page, expect\nfrom tests.server import Server\n\n\nasync def test_assertions_page_to_have_title(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<title>new title</title>\")\n    await expect(page).to_have_title(\"new title\")\n    await expect(page).to_have_title(re.compile(\"new title\"))\n    with pytest.raises(AssertionError):\n        await expect(page).to_have_title(\"not the current title\", timeout=750)\n    with pytest.raises(AssertionError):\n        await expect(page).to_have_title(\n            re.compile(\"not the current title\"), timeout=750\n        )\n    with pytest.raises(AssertionError):\n        await expect(page).not_to_have_title(re.compile(\"new title\"), timeout=750)\n    with pytest.raises(AssertionError):\n        await expect(page).not_to_have_title(\"new title\", timeout=750)\n    await expect(page).not_to_have_title(\"great title\", timeout=750)\n    await page.evaluate(\n        \"\"\"\n        setTimeout(() => {\n            document.title = 'great title';\n        }, 2000);\n    \"\"\"\n    )\n    await expect(page).to_have_title(\"great title\")\n    await expect(page).to_have_title(re.compile(\"great title\"))\n\n\nasync def test_assertions_page_to_have_url(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await expect(page).to_have_url(server.EMPTY_PAGE)\n    await expect(page).to_have_url(re.compile(r\".*/empty\\.html\"))\n    with pytest.raises(AssertionError):\n        await expect(page).to_have_url(\"nooooo\", timeout=750)\n    with pytest.raises(AssertionError):\n        await expect(page).to_have_url(re.compile(\"not-the-url\"), timeout=750)\n    await page.evaluate(\n        \"\"\"\n        setTimeout(() => {\n            window.location = window.location.origin + '/grid.html';\n        }, 2000);\n    \"\"\"\n    )\n    await expect(page).to_have_url(server.PREFIX + \"/grid.html\")\n    await expect(page).not_to_have_url(server.EMPTY_PAGE, timeout=750)\n    with pytest.raises(AssertionError):\n        await expect(page).not_to_have_url(re.compile(r\".*/grid\\.html\"), timeout=750)\n    with pytest.raises(AssertionError):\n        await expect(page).not_to_have_url(server.PREFIX + \"/grid.html\", timeout=750)\n    await expect(page).to_have_url(re.compile(r\".*/grid\\.html\"))\n    await expect(page).not_to_have_url(\"**/empty.html\", timeout=750)\n\n\nasync def test_assertions_page_to_have_url_with_base_url(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=server.PREFIX)\n    await page.goto(\"/empty.html\")\n    await expect(page).to_have_url(\"/empty.html\")\n    await expect(page).to_have_url(re.compile(r\".*/empty\\.html\"))\n    await page.close()\n\n\nasync def test_assertions_page_to_have_url_support_ignore_case(page: Page) -> None:\n    await page.goto(\"data:text/html,<div>A</div>\")\n    await expect(page).to_have_url(\"DATA:teXT/HTml,<div>a</div>\", ignore_case=True)\n\n\nasync def test_assertions_locator_to_contain_text(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div id=foobar>kek</div>\")\n    await expect(page.locator(\"div#foobar\")).to_contain_text(\"kek\")\n    await expect(page.locator(\"div#foobar\")).not_to_contain_text(\"bar\", timeout=100)\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div#foobar\")).to_contain_text(\"bar\", timeout=100)\n\n    await page.set_content(\"<div>Text \\n1</div><div>Text2</div><div>Text3</div>\")\n    await expect(page.locator(\"div\")).to_contain_text([\"ext     1\", re.compile(\"ext3\")])\n\n\nasync def test_assertions_locator_to_contain_text_should_throw_if_arg_is_unsupported_type(\n    page: Page,\n) -> None:\n    with pytest.raises(Error, match=\"value must be a string or regular expression\"):\n        await expect(page.locator(\"div\")).to_contain_text(1)  # type: ignore\n\n\nasync def test_assertions_locator_to_have_attribute(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div id=foobar>kek</div>\")\n    await expect(page.locator(\"div#foobar\")).to_have_attribute(\"id\", \"foobar\")\n    await expect(page.locator(\"div#foobar\")).to_have_attribute(\n        \"id\", re.compile(\"foobar\")\n    )\n    await expect(page.locator(\"div#foobar\")).not_to_have_attribute(\n        \"id\", \"kek\", timeout=100\n    )\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div#foobar\")).to_have_attribute(\n            \"id\", \"koko\", timeout=100\n        )\n\n\nasync def test_assertions_locator_to_have_attribute_ignore_case(\n    page: Page, server: Page\n) -> None:\n    await page.set_content(\"<div id=NoDe>Text content</div>\")\n    locator = page.locator(\"#NoDe\")\n    await expect(locator).to_have_attribute(\"id\", \"node\", ignore_case=True)\n    await expect(locator).not_to_have_attribute(\"id\", \"node\")\n\n\nasync def test_assertions_locator_to_have_class(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div class=foobar>kek</div>\")\n    await expect(page.locator(\"div.foobar\")).to_have_class(\"foobar\")\n    await expect(page.locator(\"div.foobar\")).to_have_class([\"foobar\"])\n    await expect(page.locator(\"div.foobar\")).to_have_class(re.compile(\"foobar\"))\n    await expect(page.locator(\"div.foobar\")).to_have_class([re.compile(\"foobar\")])\n    await expect(page.locator(\"div.foobar\")).not_to_have_class(\"kekstar\", timeout=100)\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div.foobar\")).to_have_class(\"oh-no\", timeout=100)\n\n\nasync def test_assertions_locator_to_contain_class(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div class='foo bar baz'></div>\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_contain_class(\"\")\n    await expect(locator).to_contain_class(\"bar\")\n    await expect(locator).to_contain_class(\"baz bar\")\n    await expect(locator).to_contain_class(\"  bar   foo \")\n    await expect(locator).not_to_contain_class(\n        \"  baz   not-matching \"\n    )  # Strip whitespace and match individual classes\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(locator).to_contain_class(\"does-not-exist\", timeout=100)\n\n    assert excinfo.match(\"Locator expected to contain class 'does-not-exist'\")\n    assert excinfo.match(\"Actual value: foo bar baz\")\n    assert excinfo.match('Expect \"to_contain_class\" with timeout 100ms')\n\n    await page.set_content(\n        '<div class=\"foo\"></div><div class=\"hello bar\"></div><div class=\"baz\"></div>'\n    )\n    await expect(locator).to_contain_class([\"foo\", \"hello\", \"baz\"])\n    await expect(locator).not_to_contain_class([\"not-there\", \"hello\", \"baz\"])\n    await expect(locator).not_to_contain_class([\"foo\", \"hello\"])\n\n\nasync def test_assertions_locator_to_have_count(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div class=foobar>kek</div><div class=foobar>kek</div>\")\n    await expect(page.locator(\"div.foobar\")).to_have_count(2)\n    await expect(page.locator(\"div.foobar\")).not_to_have_count(42, timeout=100)\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div.foobar\")).to_have_count(42, timeout=100)\n\n\nasync def test_assertions_locator_to_have_css(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"<div class=foobar style='color: rgb(234, 74, 90);'>kek</div>\"\n    )\n    await expect(page.locator(\"div.foobar\")).to_have_css(\"color\", \"rgb(234, 74, 90)\")\n    await expect(page.locator(\"div.foobar\")).not_to_have_css(\n        \"color\", \"rgb(42, 42, 42)\", timeout=100\n    )\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div.foobar\")).to_have_css(\n            \"color\", \"rgb(42, 42, 42)\", timeout=100\n        )\n\n\nasync def test_assertions_locator_to_have_id(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div class=foobar id=kek>kek</div>\")\n    await expect(page.locator(\"div.foobar\")).to_have_id(\"kek\")\n    await expect(page.locator(\"div.foobar\")).not_to_have_id(\"top\", timeout=100)\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"div.foobar\")).to_have_id(\"top\", timeout=100)\n\n\nasync def test_assertions_locator_to_have_js_property(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\n        \"div\", \"e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }\"\n    )\n    await expect(page.locator(\"div\")).to_have_js_property(\n        \"foo\",\n        {\n            \"a\": 1,\n            \"b\": \"string\",\n            \"c\": datetime.datetime.fromtimestamp(1627503992000 / 1000),\n        },\n    )\n\n\nasync def test_to_have_js_property_pass_string(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = 'string'\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_js_property(\"foo\", \"string\")\n\n\nasync def test_to_have_js_property_fail_string(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = 'string'\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        await expect(locator).to_have_js_property(\"foo\", \"error\", timeout=500)\n\n\nasync def test_to_have_js_property_pass_number(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = 2021\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_js_property(\"foo\", 2021)\n\n\nasync def test_to_have_js_property_fail_number(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = 2021\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        await expect(locator).to_have_js_property(\"foo\", 1, timeout=500)\n\n\nasync def test_to_have_js_property_pass_boolean(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = true\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_js_property(\"foo\", True)\n\n\nasync def test_to_have_js_property_fail_boolean(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        await expect(locator).to_have_js_property(\"foo\", True, timeout=500)\n\n\nasync def test_to_have_js_property_pass_boolean_2(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_js_property(\"foo\", False)\n\n\nasync def test_to_have_js_property_fail_boolean_2(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        await expect(locator).to_have_js_property(\"foo\", True, timeout=500)\n\n\nasync def test_to_have_js_property_pass_null(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\"div\", \"e => e.foo = null\")\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_js_property(\"foo\", None)\n\n\nasync def test_assertions_locator_to_have_text(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div id=foobar>kek</div>\")\n    await expect(page.locator(\"div#foobar\")).to_have_text(\"kek\")\n    await expect(page.locator(\"div#foobar\")).not_to_have_text(\"top\", timeout=100)\n\n    await page.set_content(\"<div>Text    \\n1</div><div>Text   2a</div>\")\n    # Should only normalize whitespace in the first item.\n    await expect(page.locator(\"div\")).to_have_text(\n        [\"Text  1\", re.compile(r\"Text   \\d+a\")]\n    )\n    # Should work with a tuple\n    await expect(page.locator(\"div\")).to_have_text(\n        (\"Text  1\", re.compile(r\"Text   \\d+a\"))\n    )\n\n\n@pytest.mark.parametrize(\n    \"method\",\n    [\"to_have_text\", \"to_contain_text\"],\n)\nasync def test_ignore_case(page: Page, server: Server, method: str) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div id=target>apple BANANA</div><div>orange</div>\")\n    await getattr(expect(page.locator(\"div#target\")), method)(\"apple BANANA\")\n    await getattr(expect(page.locator(\"div#target\")), method)(\n        \"apple banana\", ignore_case=True\n    )\n    # defaults false\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div#target\")), method)(\n            \"apple banana\", timeout=300\n        )\n    expected_error_msg = method.replace(\"_\", \" \")\n    assert expected_error_msg in str(excinfo.value)\n\n    # Array Variants\n    await getattr(expect(page.locator(\"div\")), method)([\"apple BANANA\", \"orange\"])\n    await getattr(expect(page.locator(\"div\")), method)(\n        [\"apple banana\", \"ORANGE\"], ignore_case=True\n    )\n    # defaults false\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div\")), method)(\n            [\"apple banana\", \"ORANGE\"], timeout=300\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # not variant\n    await getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\"apple banana\")\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n            \"apple banana\", ignore_case=True, timeout=300\n        )\n    assert f\"not {expected_error_msg}\" in str(excinfo)\n\n\n@pytest.mark.parametrize(\n    \"method\",\n    [\"to_have_text\", \"to_contain_text\"],\n)\nasync def test_ignore_case_regex(page: Page, server: Server, method: str) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div id=target>apple BANANA</div><div>orange</div>\")\n    await getattr(expect(page.locator(\"div#target\")), method)(\n        re.compile(\"apple BANANA\")\n    )\n    await getattr(expect(page.locator(\"div#target\")), method)(\n        re.compile(\"apple banana\"), ignore_case=True\n    )\n    # defaults to regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div#target\")), method)(\n            re.compile(\"apple banana\"), timeout=300\n        )\n    expected_error_msg = method.replace(\"_\", \" \")\n    assert expected_error_msg in str(excinfo.value)\n    # overrides regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div#target\")), method)(\n            re.compile(\"apple banana\", re.IGNORECASE), ignore_case=False, timeout=300\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # Array Variants\n    await getattr(expect(page.locator(\"div\")), method)(\n        [re.compile(\"apple BANANA\"), re.compile(\"orange\")]\n    )\n    await getattr(expect(page.locator(\"div\")), method)(\n        [re.compile(\"apple banana\"), re.compile(\"ORANGE\")], ignore_case=True\n    )\n    # defaults regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div\")), method)(\n            [re.compile(\"apple banana\"), re.compile(\"ORANGE\")], timeout=300\n        )\n    assert expected_error_msg in str(excinfo.value)\n    # overrides regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div\")), method)(\n            [\n                re.compile(\"apple banana\", re.IGNORECASE),\n                re.compile(\"ORANGE\", re.IGNORECASE),\n            ],\n            ignore_case=False,\n            timeout=300,\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # not variant\n    await getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n        re.compile(\"apple banana\")\n    )\n    with pytest.raises(AssertionError) as excinfo:\n        await getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n            re.compile(\"apple banana\"), ignore_case=True, timeout=300\n        )\n    assert f\"not {expected_error_msg}\" in str(excinfo)\n\n\nasync def test_assertions_locator_to_have_value(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<input type=text id=foo>\")\n    my_input = page.locator(\"#foo\")\n    await expect(my_input).to_have_value(\"\")\n    await expect(my_input).not_to_have_value(\"bar\", timeout=100)\n    await my_input.fill(\"kektus\")\n    await expect(my_input).to_have_value(\"kektus\")\n\n\nasync def test_to_have_values_works_with_text(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    await locator.select_option([\"R\", \"G\"])\n    await expect(locator).to_have_values([\"R\", \"G\"])\n\n\nasync def test_to_have_values_follows_labels(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <label for=\"colors\">Pick a Color</label>\n        <select id=\"colors\" multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"text=Pick a Color\")\n    await locator.select_option([\"R\", \"G\"])\n    await expect(locator).to_have_values([\"R\", \"G\"])\n\n\nasync def test_to_have_values_exact_match_with_text(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"RR\">Red</option>\n            <option value=\"GG\">Green</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    await locator.select_option([\"RR\", \"GG\"])\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Locator expected to have Values '['R', 'G']'\" in str(excinfo.value)\n    assert \"Actual value: ['RR', 'GG']\" in str(excinfo.value)\n\n\nasync def test_to_have_values_works_with_regex(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    await locator.select_option([\"R\", \"G\"])\n    await expect(locator).to_have_values([re.compile(\"R\"), re.compile(\"G\")])\n\n\nasync def test_to_have_values_fails_when_items_not_selected(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    await locator.select_option([\"B\"])\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Locator expected to have Values '['R', 'G']'\" in str(excinfo.value)\n    assert \"Actual value: ['B']\" in str(excinfo.value)\n\n\nasync def test_to_have_values_fails_when_multiple_not_specified(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    await locator.select_option([\"B\"])\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Error: Not a select element with a multiple attribute\" in str(excinfo.value)\n\n\nasync def test_to_have_values_fails_when_not_a_select_element(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <input type=\"text\">\n    \"\"\"\n    )\n    locator = page.locator(\"input\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Error: Not a select element with a multiple attribute\" in str(excinfo.value)\n\n\nasync def test_assertions_locator_to_be_checked(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    await expect(my_checkbox).not_to_be_checked()\n    with pytest.raises(AssertionError, match=\"Locator expected to be checked\"):\n        await expect(my_checkbox).to_be_checked(timeout=100)\n    await expect(my_checkbox).to_be_checked(timeout=100, checked=False)\n    with pytest.raises(AssertionError):\n        await expect(my_checkbox).to_be_checked(timeout=100, checked=True)\n    await my_checkbox.check()\n    await expect(my_checkbox).to_be_checked(timeout=100, checked=True)\n    with pytest.raises(AssertionError, match=\"Locator expected to be unchecked\"):\n        await expect(my_checkbox).to_be_checked(timeout=100, checked=False)\n    await expect(my_checkbox).to_be_checked()\n\n\nasync def test_assertions_boolean_checked_with_intermediate_true(page: Page) -> None:\n    await page.set_content(\"<input type=checkbox></input>\")\n    await page.locator(\"input\").evaluate(\"e => e.indeterminate = true\")\n    await expect(page.locator(\"input\")).to_be_checked(indeterminate=True)\n\n\nasync def test_assertions_boolean_checked_with_intermediate_true_and_checked(\n    page: Page,\n) -> None:\n    await page.set_content(\"<input type=checkbox></input>\")\n    await page.locator(\"input\").evaluate(\"e => e.indeterminate = true\")\n    with pytest.raises(\n        AssertionError, match=\"Can't assert indeterminate and checked at the same time\"\n    ):\n        await expect(page.locator(\"input\")).to_be_checked(\n            checked=False, indeterminate=True\n        )\n\n\nasync def test_assertions_boolean_fail_with_indeterminate_true(page: Page) -> None:\n    await page.set_content(\"<input type=checkbox></input>\")\n    with pytest.raises(\n        AssertionError, match='Expect \"to_be_checked\" with timeout 1000ms'\n    ):\n        await expect(page.locator(\"input\")).to_be_checked(\n            indeterminate=True, timeout=1000\n        )\n\n\nasync def test_assertions_locator_to_be_disabled_enabled(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    await expect(my_checkbox).not_to_be_disabled()\n    await expect(my_checkbox).to_be_enabled()\n    with pytest.raises(AssertionError):\n        await expect(my_checkbox).to_be_disabled(timeout=100)\n    await my_checkbox.evaluate(\"e => e.disabled = true\")\n    await expect(my_checkbox).to_be_disabled()\n    with pytest.raises(AssertionError, match=\"Locator expected to be enabled\"):\n        await expect(my_checkbox).to_be_enabled(timeout=100)\n\n\nasync def test_assertions_locator_to_be_enabled_with_true(page: Page) -> None:\n    await page.set_content(\"<button>Text</button>\")\n    await expect(page.locator(\"button\")).to_be_enabled(enabled=True)\n\n\nasync def test_assertions_locator_to_be_enabled_with_false_throws_good_exception(\n    page: Page,\n) -> None:\n    await page.set_content(\"<button>Text</button>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be disabled\"):\n        await expect(page.locator(\"button\")).to_be_enabled(enabled=False)\n\n\nasync def test_assertions_locator_to_be_enabled_with_false(page: Page) -> None:\n    await page.set_content(\"<button disabled>Text</button>\")\n    await expect(page.locator(\"button\")).to_be_enabled(enabled=False)\n\n\nasync def test_assertions_locator_to_be_enabled_with_not_and_false(page: Page) -> None:\n    await page.set_content(\"<button>Text</button>\")\n    await expect(page.locator(\"button\")).not_to_be_enabled(enabled=False)\n\n\nasync def test_assertions_locator_to_be_enabled_eventually(page: Page) -> None:\n    await page.set_content(\"<button disabled>Text</button>\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"\n        button => setTimeout(() => {\n            button.removeAttribute('disabled');\n        }, 700);\n    \"\"\",\n    )\n    await expect(page.locator(\"button\")).to_be_enabled()\n\n\nasync def test_assertions_locator_to_be_enabled_eventually_with_not(page: Page) -> None:\n    await page.set_content(\"<button>Text</button>\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"\n        button => setTimeout(() => {\n            button.setAttribute('disabled', '');\n        }, 700);\n    \"\"\",\n    )\n    await expect(page.locator(\"button\")).not_to_be_enabled()\n\n\nasync def test_assertions_locator_to_be_editable(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<input></input>\")\n    await expect(page.locator(\"input\")).to_be_editable()\n\n\nasync def test_assertions_locator_to_be_editable_throws(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button disabled>Text</button>\")\n    with pytest.raises(\n        AssertionError,\n        match=r\"Element is not an <input>, <textarea>, <select> or \\[contenteditable\\] and does not have a role allowing \\[aria-readonly\\]\",\n    ):\n        await expect(page.locator(\"button\")).not_to_be_editable()\n\n\nasync def test_assertions_locator_to_be_editable_with_true(page: Page) -> None:\n    await page.set_content(\"<input></input>\")\n    await expect(page.locator(\"input\")).to_be_editable(editable=True)\n\n\nasync def test_assertions_locator_to_be_editable_with_false(page: Page) -> None:\n    await page.set_content(\"<input readonly></input>\")\n    await expect(page.locator(\"input\")).to_be_editable(editable=False)\n\n\nasync def test_assertions_locator_to_be_editable_with_false_and_throw_good_exception(\n    page: Page,\n) -> None:\n    await page.set_content(\"<input></input>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be readonly\"):\n        await expect(page.locator(\"input\")).to_be_editable(editable=False)\n\n\nasync def test_assertions_locator_to_be_editable_with_not_and_false(page: Page) -> None:\n    await page.set_content(\"<input></input>\")\n    await expect(page.locator(\"input\")).not_to_be_editable(editable=False)\n\n\nasync def test_assertions_locator_to_be_empty(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"<input value=text name=input1></input><input name=input2></input>\"\n    )\n    await expect(page.locator(\"input[name=input1]\")).not_to_be_empty()\n    await expect(page.locator(\"input[name=input2]\")).to_be_empty()\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"input[name=input1]\")).to_be_empty(timeout=100)\n\n\nasync def test_assertions_locator_to_be_focused(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    with pytest.raises(AssertionError):\n        await expect(my_checkbox).to_be_focused(timeout=100)\n    await my_checkbox.focus()\n    await expect(my_checkbox).to_be_focused()\n\n\nasync def test_assertions_locator_to_be_hidden_visible(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div style='width: 50px; height: 50px;'>Something</div>\")\n    my_checkbox = page.locator(\"div\")\n    await expect(my_checkbox).to_be_visible()\n    with pytest.raises(AssertionError):\n        await expect(my_checkbox).to_be_hidden(timeout=100)\n    await my_checkbox.evaluate(\"e => e.style.display = 'none'\")\n    await expect(my_checkbox).to_be_hidden()\n    with pytest.raises(AssertionError, match=\"Locator expected to be visible\"):\n        await expect(my_checkbox).to_be_visible(timeout=100)\n\n\nasync def test_assertions_locator_to_be_visible_with_true(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    await expect(page.locator(\"button\")).to_be_visible(visible=True)\n\n\nasync def test_assertions_locator_to_be_visible_with_false(page: Page) -> None:\n    await page.set_content(\"<button hidden>hello</button>\")\n    await expect(page.locator(\"button\")).to_be_visible(visible=False)\n\n\nasync def test_assertions_locator_to_be_visible_with_false_throws_good_exception(\n    page: Page,\n) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be hidden\"):\n        await expect(page.locator(\"button\")).to_be_visible(visible=False)\n\n\nasync def test_assertions_locator_to_be_visible_with_not_and_false(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    await expect(page.locator(\"button\")).not_to_be_visible(visible=False)\n\n\nasync def test_assertions_locator_to_be_visible_eventually(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    await page.eval_on_selector(\n        \"div\",\n        \"\"\"\n        div => setTimeout(() => {\n            div.innerHTML = '<span>Hello</span>';\n        }, 700);\n    \"\"\",\n    )\n    await expect(page.locator(\"span\")).to_be_visible()\n\n\nasync def test_assertions_locator_to_be_visible_eventually_with_not(page: Page) -> None:\n    await page.set_content(\"<div><span>Hello</span></div>\")\n    await page.eval_on_selector(\n        \"span\",\n        \"\"\"\n        span => setTimeout(() => {\n            span.textContent = '';\n        }, 700);\n    \"\"\",\n    )\n    await expect(page.locator(\"span\")).not_to_be_visible()\n\n\nasync def test_assertions_should_serialize_regexp_correctly(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<div>iGnOrEcAsE</div>\")\n    await expect(page.locator(\"div\")).to_have_text(\n        re.compile(r\"ignorecase\", re.IGNORECASE)\n    )\n    await page.set_content(\n        \"\"\"<div>start\nsome\nlines\nbetween\nend</div>\"\"\"\n    )\n    await expect(page.locator(\"div\")).to_have_text(re.compile(r\"start.*end\", re.DOTALL))\n    await page.set_content(\n        \"\"\"<div>line1\nline2\nline3</div>\"\"\"\n    )\n    await expect(page.locator(\"div\")).to_have_text(re.compile(r\"^line2$\", re.MULTILINE))\n\n\nasync def test_assertions_response_is_ok_pass(page: Page, server: Server) -> None:\n    response = await page.request.get(server.EMPTY_PAGE)\n    await expect(response).to_be_ok()\n\n\nasync def test_assertions_response_is_ok_pass_with_not(\n    page: Page, server: Server\n) -> None:\n    response = await page.request.get(server.PREFIX + \"/unknown\")\n    await expect(response).not_to_be_ok()\n\n\nasync def test_assertions_response_is_ok_fail(page: Page, server: Server) -> None:\n    response = await page.request.get(server.PREFIX + \"/unknown\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/unknown\") in error_message\n    assert \"← 404 Not Found\" in error_message\n\n\nasync def test_should_print_response_with_text_content_type_if_to_be_ok_fails(\n    page: Page, server: Server\n) -> None:\n    server.set_route(\n        \"/text-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.setHeader(\"content-type\", \"text/plain\"),\n            r.write(b\"Text error\"),\n            r.finish(),\n        ),\n    )\n    server.set_route(\n        \"/no-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.write(b\"No content type error\"),\n            r.finish(),\n        ),\n    )\n    server.set_route(\n        \"/binary-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.setHeader(\"content-type\", \"image/bmp\"),\n            r.write(b\"Image content type error\"),\n            r.finish(),\n        ),\n    )\n\n    response = await page.request.get(server.PREFIX + \"/text-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/text-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" in error_message\n    assert \"Text error\" in error_message\n\n    response = await page.request.get(server.PREFIX + \"/no-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/no-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" not in error_message\n    assert \"No content type error\" not in error_message\n\n    response = await page.request.get(server.PREFIX + \"/binary-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/binary-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" not in error_message\n    assert \"Image content type error\" not in error_message\n\n\nasync def test_should_print_users_message_for_page_based_assertion(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<title>new title</title>\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(page, \"Title is not new\").to_have_title(\"old title\", timeout=100)\n    assert \"Title is not new\" in str(excinfo.value)\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(page).to_have_title(\"old title\", timeout=100)\n    assert \"Page title expected to be\" in str(excinfo.value)\n\n\nasync def test_should_print_expected_value_with_custom_message(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<title>new title</title>\")\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(page, \"custom-message\").to_have_title(\"old title\", timeout=100)\n    assert \"custom-message\" in str(excinfo.value)\n    assert \"Expected value: 'old title'\" in str(excinfo.value)\n    with pytest.raises(AssertionError) as excinfo:\n        await expect(page.get_by_text(\"hello\"), \"custom-message\").to_be_visible(\n            timeout=100\n        )\n    assert \"custom-message\" in str(excinfo.value)\n    assert \"Expected value\" not in str(excinfo.value)\n\n\nasync def test_should_be_attached_default(page: Page) -> None:\n    await page.set_content(\"<input></input>\")\n    locator = page.locator(\"input\")\n    await expect(locator).to_be_attached()\n\n\nasync def test_should_be_attached_with_hidden_element(page: Page) -> None:\n    await page.set_content('<button style=\"display:none\">hello</button>')\n    locator = page.locator(\"button\")\n    await expect(locator).to_be_attached()\n\n\nasync def test_should_be_attached_with_not(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"input\")\n    await expect(locator).not_to_be_attached()\n\n\nasync def test_should_be_attached_with_attached_true(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    await expect(locator).to_be_attached(attached=True)\n\n\nasync def test_should_be_attached_with_attached_false(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"input\")\n    await expect(locator).to_be_attached(attached=False)\n\n\nasync def test_should_be_attached_with_attached_false_and_throw_good_error(\n    page: Page,\n) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be detached\"):\n        await expect(locator).to_be_attached(attached=False, timeout=1)\n\n\nasync def test_should_be_attached_with_not_and_attached_false(page: Page) -> None:\n    await page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    await expect(locator).not_to_be_attached(attached=False)\n\n\nasync def test_should_be_attached_eventually(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    locator = page.locator(\"span\")\n    await page.locator(\"div\").evaluate(\n        \"(e) => setTimeout(() => e.innerHTML = '<span>hello</span>', 1000)\"\n    )\n    await expect(locator).to_be_attached()\n\n\nasync def test_should_be_attached_eventually_with_not(page: Page) -> None:\n    await page.set_content(\"<div><span>Hello</span></div>\")\n    locator = page.locator(\"span\")\n    await page.locator(\"div\").evaluate(\n        \"(e) => setTimeout(() => e.textContent = '', 1000)\"\n    )\n    await expect(locator).not_to_be_attached()\n\n\nasync def test_should_be_attached_fail(page: Page) -> None:\n    await page.set_content(\"<button>Hello</button>\")\n    locator = page.locator(\"input\")\n    with pytest.raises(\n        AssertionError, match=\"Locator expected to be attached\"\n    ) as exc_info:\n        await expect(locator).to_be_attached(timeout=1000)\n    assert \"locator resolved to\" not in exc_info.value.args[0]\n\n\nasync def test_should_be_attached_fail_with_not(page: Page) -> None:\n    await page.set_content(\"<input></input>\")\n    locator = page.locator(\"input\")\n    with pytest.raises(AssertionError) as exc_info:\n        await expect(locator).not_to_be_attached(timeout=1000)\n    assert \"locator resolved to <input/>\" in exc_info.value.args[0]\n\n\nasync def test_should_be_attached_with_impossible_timeout(page: Page) -> None:\n    await page.set_content(\"<div id=node>Text content</div>\")\n    await expect(page.locator(\"#node\")).to_be_attached(timeout=1)\n\n\nasync def test_should_be_attached_with_impossible_timeout_not(page: Page) -> None:\n    await page.set_content(\"<div id=node>Text content</div>\")\n    await expect(page.locator(\"no-such-thing\")).not_to_be_attached(timeout=1)\n\n\nasync def test_should_be_attached_with_frame_locator(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    locator = page.frame_locator(\"iframe\").locator(\"input\")\n    task = asyncio.create_task(expect(locator).to_be_attached())\n    await page.wait_for_timeout(1000)\n    assert not task.done()\n    await page.set_content('<iframe srcdoc=\"<input>\"></iframe>')\n    await task\n    assert task.done()\n\n\nasync def test_should_be_attached_over_navigation(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    task = asyncio.create_task(expect(page.locator(\"input\")).to_be_attached())\n    await page.wait_for_timeout(1000)\n    assert not task.done()\n    await page.goto(server.PREFIX + \"/input/checkbox.html\")\n    await task\n    assert task.done()\n\n\nasync def test_should_be_able_to_set_custom_timeout(page: Page) -> None:\n    with pytest.raises(AssertionError) as exc_info:\n        await expect(page.locator(\"#a1\")).to_be_visible(timeout=111)\n    assert 'Expect \"to_be_visible\" with timeout 111ms' in str(exc_info.value)\n\n\nasync def test_should_be_able_to_set_custom_global_timeout(page: Page) -> None:\n    try:\n        expect.set_options(timeout=111)\n        with pytest.raises(AssertionError) as exc_info:\n            await expect(page.locator(\"#a1\")).to_be_visible()\n        assert 'Expect \"to_be_visible\" with timeout 111ms' in str(exc_info.value)\n    finally:\n        expect.set_options(timeout=None)\n\n\nasync def test_to_have_accessible_name(page: Page) -> None:\n    await page.set_content('<div role=\"button\" aria-label=\"Hello\"></div>')\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_accessible_name(\"Hello\")\n    await expect(locator).not_to_have_accessible_name(\"hello\")\n    await expect(locator).to_have_accessible_name(\"hello\", ignore_case=True)\n    await expect(locator).to_have_accessible_name(re.compile(r\"ell\\w\"))\n    await expect(locator).not_to_have_accessible_name(re.compile(r\"hello\"))\n    await expect(locator).to_have_accessible_name(\n        re.compile(r\"hello\"), ignore_case=True\n    )\n\n    await page.set_content(\"<button>foo&nbsp;bar\\nbaz</button>\")\n    await expect(page.locator(\"button\")).to_have_accessible_name(\"foo bar baz\")\n\n\nasync def test_to_have_accessible_error_message(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <form>\n        <input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error-message\" />\n        <div id=\"error-message\">Hello</div>\n        <div id=\"irrelevant-error\">This should not be considered.</div>\n      </form>\n    \"\"\"\n    )\n\n    locator = page.locator('input[role=\"textbox\"]')\n    await expect(locator).to_have_accessible_error_message(\"Hello\")\n    await expect(locator).not_to_have_accessible_error_message(\"hello\")\n    await expect(locator).to_have_accessible_error_message(\"hello\", ignore_case=True)\n    await expect(locator).to_have_accessible_error_message(re.compile(r\"ell\\w\"))\n    await expect(locator).not_to_have_accessible_error_message(re.compile(r\"hello\"))\n    await expect(locator).to_have_accessible_error_message(\n        re.compile(r\"hello\"), ignore_case=True\n    )\n    await expect(locator).not_to_have_accessible_error_message(\n        \"This should not be considered.\"\n    )\n\n\nasync def test_to_have_accessible_error_message_should_handle_multiple_aria_error_message_references(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <form>\n        <input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error1 error2\" />\n        <div id=\"error1\">First error message.</div>\n        <div id=\"error2\">Second error message.</div>\n        <div id=\"irrelevant-error\">This should not be considered.</div>\n      </form>\n    \"\"\"\n    )\n\n    locator = page.locator('input[role=\"textbox\"]')\n\n    await expect(locator).to_have_accessible_error_message(\n        \"First error message. Second error message.\"\n    )\n    await expect(locator).to_have_accessible_error_message(\n        re.compile(r\"first error message.\", re.IGNORECASE)\n    )\n    await expect(locator).to_have_accessible_error_message(\n        re.compile(r\"second error message.\", re.IGNORECASE)\n    )\n    await expect(locator).not_to_have_accessible_error_message(\n        re.compile(r\"This should not be considered.\", re.IGNORECASE)\n    )\n\n\nasync def test_to_have_accessible_description(page: Page) -> None:\n    await page.set_content('<div role=\"button\" aria-description=\"Hello\"></div>')\n    locator = page.locator(\"div\")\n    await expect(locator).to_have_accessible_description(\"Hello\")\n    await expect(locator).not_to_have_accessible_description(\"hello\")\n    await expect(locator).to_have_accessible_description(\"hello\", ignore_case=True)\n    await expect(locator).to_have_accessible_description(re.compile(r\"ell\\w\"))\n    await expect(locator).not_to_have_accessible_description(re.compile(r\"hello\"))\n    await expect(locator).to_have_accessible_description(\n        re.compile(r\"hello\"), ignore_case=True\n    )\n\n    await page.set_content(\n        \"\"\"\n        <div role=\"button\" aria-describedby=\"desc\"></div>\n        <span id=\"desc\">foo&nbsp;bar\\nbaz</span>\n    \"\"\"\n    )\n    await expect(page.locator(\"div\")).to_have_accessible_description(\"foo bar baz\")\n\n\nasync def test_to_have_role(page: Page) -> None:\n    await page.set_content('<div role=\"button\">Button!</div>')\n    await expect(page.locator(\"div\")).to_have_role(\"button\")\n    await expect(page.locator(\"div\")).not_to_have_role(\"checkbox\")\n    with pytest.raises(Error) as excinfo:\n        await expect(page.locator(\"div\")).to_have_role(re.compile(r\"button|checkbox\"))  # type: ignore\n    assert '\"role\" argument in to_have_role must be a string' in str(excinfo.value)\n"
  },
  {
    "path": "tests/async/test_asyncio.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\nimport gc\nimport sys\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import Page, async_playwright\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\nasync def test_should_cancel_underlying_protocol_calls(\n    browser_name: str, launch_arguments: Dict\n) -> None:\n    handler_exception = None\n\n    def exception_handlerdler(loop: asyncio.AbstractEventLoop, context: Dict) -> None:\n        nonlocal handler_exception\n        handler_exception = context[\"exception\"]\n\n    asyncio.get_running_loop().set_exception_handler(exception_handlerdler)\n\n    async with async_playwright() as p:\n        browser = await p[browser_name].launch(**launch_arguments)\n        page = await browser.new_page()\n        task = asyncio.create_task(page.wait_for_selector(\"will-never-find\"))\n        # make sure that the wait_for_selector message was sent to the server (driver)\n        await asyncio.sleep(0.1)\n        task.cancel()\n        with pytest.raises(asyncio.CancelledError):\n            await task\n        await browser.close()\n\n    # The actual 'Future exception was never retrieved' is logged inside the Future destructor (__del__).\n    gc.collect()\n\n    assert handler_exception is None\n\n    asyncio.get_running_loop().set_exception_handler(None)\n\n\nasync def test_async_playwright_stop_multiple_times() -> None:\n    playwright = await async_playwright().start()\n    await playwright.stop()\n    await playwright.stop()\n\n\nasync def test_cancel_pending_protocol_call_on_playwright_stop(server: Server) -> None:\n    server.set_route(\"/hang\", lambda _: None)\n    playwright = await async_playwright().start()\n    api_request_context = await playwright.request.new_context()\n    pending_task = asyncio.create_task(api_request_context.get(server.PREFIX + \"/hang\"))\n    await playwright.stop()\n    with pytest.raises(Exception) as exc_info:\n        await pending_task\n    assert TARGET_CLOSED_ERROR_MESSAGE in str(exc_info.value)\n\n\nasync def test_should_not_throw_with_taskgroup(page: Page) -> None:\n    if sys.version_info < (3, 11):\n        pytest.skip(\"TaskGroup is only available in Python 3.11+\")\n\n    from builtins import ExceptionGroup  # type: ignore\n\n    async def raise_exception() -> None:\n        raise ValueError(\"Something went wrong\")\n\n    with pytest.raises(ExceptionGroup) as exc_info:\n        async with asyncio.TaskGroup() as group:  # type: ignore\n            group.create_task(page.locator(\".this-element-does-not-exist\").inner_text())\n            group.create_task(raise_exception())\n    assert len(exc_info.value.exceptions) == 1\n    assert \"Something went wrong\" in str(exc_info.value.exceptions[0])\n    assert isinstance(exc_info.value.exceptions[0], ValueError)\n    assert await page.evaluate(\"() => 11 * 11\") == 121\n\n\nasync def test_should_return_proper_api_name_on_error(page: Page) -> None:\n    try:\n        await page.evaluate(\"does_not_exist\")\n\n        assert (\n            False\n        ), \"Accessing undefined JavaScript variable should have thrown exception\"\n    except Exception as error:\n        # Each browser returns slightly different error messages, but they should all start with \"Page.evaluate:\", because that was the Playwright method where the error originated\n        assert str(error).startswith(\"Page.evaluate:\")\n"
  },
  {
    "path": "tests/async/test_browser.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserType, Error\n\n\nasync def test_should_create_new_page(browser: Browser) -> None:\n    page1 = await browser.new_page()\n    assert len(browser.contexts) == 1\n\n    page2 = await browser.new_page()\n    assert len(browser.contexts) == 2\n\n    await page1.close()\n    assert len(browser.contexts) == 1\n\n    await page2.close()\n    assert len(browser.contexts) == 0\n\n\nasync def test_should_throw_upon_second_create_new_page(browser: Browser) -> None:\n    page = await browser.new_page()\n    with pytest.raises(Error) as exc:\n        await page.context.new_page()\n    await page.close()\n    assert \"Please use browser.new_context()\" in exc.value.message\n\n\nasync def test_version_should_work(browser: Browser, is_chromium: bool) -> None:\n    version = browser.version\n    if is_chromium:\n        assert re.match(r\"^\\d+\\.\\d+\\.\\d+\\.\\d+$\", version)\n    else:\n        assert re.match(r\"^\\d+\\.\\d+\", version)\n\n\nasync def test_should_return_browser_type(\n    browser: Browser, browser_type: BrowserType\n) -> None:\n    assert browser.browser_type is browser_type\n"
  },
  {
    "path": "tests/async/test_browsercontext.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Any, List\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom playwright.async_api import (\n    Browser,\n    BrowserContext,\n    Error,\n    JSHandle,\n    Page,\n    Playwright,\n)\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\nfrom .utils import Utils\n\n\n@pytest.fixture(scope=\"session\")\ndef fails_on_401(browser_name: str, is_headless_shell: bool) -> bool:\n    return browser_name == \"chromium\" and not is_headless_shell\n\n\nasync def test_page_event_should_create_new_context(browser: Browser) -> None:\n    assert len(browser.contexts) == 0\n    context = await browser.new_context()\n    assert len(browser.contexts) == 1\n    assert context in browser.contexts\n    await context.close()\n    assert len(browser.contexts) == 0\n    assert context.browser == browser\n\n\nasync def test_window_open_should_use_parent_tab_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as page_info:\n        await page.evaluate(\"url => window.open(url)\", server.EMPTY_PAGE)\n    popup = await page_info.value\n    assert popup.context == context\n    await context.close()\n\n\nasync def test_page_event_should_isolate_localStorage_and_cookies(\n    browser: Browser, server: Server\n) -> None:\n    # Create two incognito contexts.\n    context1 = await browser.new_context()\n    context2 = await browser.new_context()\n    assert len(context1.pages) == 0\n    assert len(context2.pages) == 0\n\n    # Create a page in first incognito context.\n    page1 = await context1.new_page()\n    await page1.goto(server.EMPTY_PAGE)\n    await page1.evaluate(\n        \"\"\"() => {\n            localStorage.setItem('name', 'page1')\n            document.cookie = 'name=page1'\n        }\"\"\"\n    )\n\n    assert len(context1.pages) == 1\n    assert len(context2.pages) == 0\n\n    # Create a page in second incognito context.\n    page2 = await context2.new_page()\n    await page2.goto(server.EMPTY_PAGE)\n    await page2.evaluate(\n        \"\"\"() => {\n            localStorage.setItem('name', 'page2')\n            document.cookie = 'name=page2'\n        }\"\"\"\n    )\n\n    assert len(context1.pages) == 1\n    assert len(context2.pages) == 1\n    assert context1.pages[0] == page1\n    assert context2.pages[0] == page2\n\n    # Make sure pages don't share localstorage or cookies.\n    assert await page1.evaluate(\"localStorage.getItem('name')\") == \"page1\"\n    assert await page1.evaluate(\"document.cookie\") == \"name=page1\"\n    assert await page2.evaluate(\"localStorage.getItem('name')\") == \"page2\"\n    assert await page2.evaluate(\"document.cookie\") == \"name=page2\"\n\n    # Cleanup contexts.\n    await asyncio.gather(context1.close(), context2.close())\n    assert browser.contexts == []\n\n\nasync def test_page_event_should_propagate_default_viewport_to_the_page(\n    browser: Browser, utils: Utils\n) -> None:\n    context = await browser.new_context(viewport={\"width\": 456, \"height\": 789})\n    page = await context.new_page()\n    await utils.verify_viewport(page, 456, 789)\n    await context.close()\n\n\nasync def test_page_event_should_respect_device_scale_factor(browser: Browser) -> None:\n    context = await browser.new_context(device_scale_factor=3.5)\n    page = await context.new_page()\n    assert await page.evaluate(\"window.devicePixelRatio\") == 3.5\n    await context.close()\n\n\nasync def test_page_event_should_not_allow_device_scale_factor_with_null_viewport(\n    browser: Browser,\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await browser.new_context(no_viewport=True, device_scale_factor=1)\n    assert (\n        exc_info.value.message\n        == 'Browser.new_context: \"deviceScaleFactor\" option is not supported with null \"viewport\"'\n    )\n\n\nasync def test_page_event_should_not_allow_is_mobile_with_null_viewport(\n    browser: Browser,\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await browser.new_context(no_viewport=True, is_mobile=True)\n    assert (\n        exc_info.value.message\n        == 'Browser.new_context: \"isMobile\" option is not supported with null \"viewport\"'\n    )\n\n\nasync def test_close_should_work_for_empty_context(browser: Browser) -> None:\n    context = await browser.new_context()\n    await context.close()\n\n\nasync def test_close_should_abort_wait_for_event(browser: Browser) -> None:\n    context = await browser.new_context()\n    with pytest.raises(Error) as exc_info:\n        async with context.expect_page():\n            await context.close()\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message\n\n\nasync def test_close_should_be_callable_twice(browser: Browser) -> None:\n    context = await browser.new_context()\n    await asyncio.gather(\n        context.close(),\n        context.close(),\n    )\n    await context.close()\n\n\nasync def test_user_agent_should_work(browser: Browser, server: Server) -> None:\n    async def baseline() -> None:\n        context = await browser.new_context()\n        page = await context.new_page()\n        assert \"Mozilla\" in await page.evaluate(\"navigator.userAgent\")\n        await context.close()\n\n    await baseline()\n\n    async def override() -> None:\n        context = await browser.new_context(user_agent=\"foobar\")\n        page = await context.new_page()\n        [request, _] = await asyncio.gather(\n            server.wait_for_request(\"/empty.html\"),\n            page.goto(server.EMPTY_PAGE),\n        )\n        assert request.getHeader(\"user-agent\") == \"foobar\"\n        await context.close()\n\n    await override()\n\n\nasync def test_user_agent_should_work_for_subframes(\n    browser: Browser, server: Server, utils: Utils\n) -> None:\n    context = await browser.new_context(user_agent=\"foobar\")\n    page = await context.new_page()\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE),\n    )\n    assert request.getHeader(\"user-agent\") == \"foobar\"\n    await context.close()\n\n\nasync def test_user_agent_should_emulate_device_user_agent(\n    playwright: Playwright, browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(\n        user_agent=playwright.devices[\"iPhone 6\"][\"user_agent\"]\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/mobile.html\")\n    assert \"iPhone\" in await page.evaluate(\"navigator.userAgent\")\n    await context.close()\n\n\nasync def test_user_agent_should_make_a_copy_of_default_options(\n    browser: Browser, server: Server\n) -> None:\n    options: Any = {\"user_agent\": \"foobar\"}\n    context = await browser.new_context(**options)\n    options[\"user_agent\"] = \"wrong\"\n    page = await context.new_page()\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        page.goto(server.EMPTY_PAGE),\n    )\n    assert request.getHeader(\"user-agent\") == \"foobar\"\n    await context.close()\n\n\nasync def test_page_event_should_bypass_csp_meta_tag(\n    browser: Browser, server: Server\n) -> None:\n    async def baseline() -> None:\n        context = await browser.new_context()\n        page = await context.new_page()\n        await page.goto(server.PREFIX + \"/csp.html\")\n        try:\n            await page.add_script_tag(content=\"window.__injected = 42;\")\n        except Error:\n            pass\n        assert await page.evaluate(\"window.__injected\") is None\n        await context.close()\n\n    await baseline()\n\n    # By-pass CSP and try one more time.\n    async def override() -> None:\n        context = await browser.new_context(bypass_csp=True)\n        page = await context.new_page()\n        await page.goto(server.PREFIX + \"/csp.html\")\n        await page.add_script_tag(content=\"window.__injected = 42;\")\n        assert await page.evaluate(\"() => window.__injected\") == 42\n        await context.close()\n\n    await override()\n\n\nasync def test_page_event_should_bypass_csp_header(\n    browser: Browser, server: Server\n) -> None:\n    # Make sure CSP prohibits add_script_tag.\n    server.set_csp(\"/empty.html\", 'default-src \"self\"')\n\n    async def baseline() -> None:\n        context = await browser.new_context()\n        page = await context.new_page()\n        await page.goto(server.EMPTY_PAGE)\n        try:\n            await page.add_script_tag(content=\"window.__injected = 42;\")\n        except Error:\n            pass\n        assert await page.evaluate(\"() => window.__injected\") is None\n        await context.close()\n\n    await baseline()\n\n    # By-pass CSP and try one more time.\n    async def override() -> None:\n        context = await browser.new_context(bypass_csp=True)\n        page = await context.new_page()\n        await page.goto(server.EMPTY_PAGE)\n        await page.add_script_tag(content=\"window.__injected = 42;\")\n        assert await page.evaluate(\"window.__injected\") == 42\n        await context.close()\n\n    await override()\n\n\nasync def test_page_event_should_bypass_after_cross_process_navigation(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(bypass_csp=True)\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/csp.html\")\n    await page.add_script_tag(content=\"window.__injected = 42;\")\n    assert await page.evaluate(\"window.__injected\") == 42\n\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/csp.html\")\n    await page.add_script_tag(content=\"window.__injected = 42;\")\n    assert await page.evaluate(\"window.__injected\") == 42\n    await context.close()\n\n\nasync def test_page_event_should_bypass_csp_in_iframes_as_well(\n    browser: Browser, server: Server, utils: Utils\n) -> None:\n    async def baseline() -> None:\n        # Make sure CSP prohibits add_script_tag in an iframe.\n        context = await browser.new_context()\n        page = await context.new_page()\n        await page.goto(server.EMPTY_PAGE)\n        frame = await utils.attach_frame(page, \"frame1\", server.PREFIX + \"/csp.html\")\n        try:\n            await frame.add_script_tag(content=\"window.__injected = 42;\")\n        except Error:\n            pass\n        assert await frame.evaluate(\"window.__injected\") is None\n        await context.close()\n\n    await baseline()\n\n    # By-pass CSP and try one more time.\n    async def override() -> None:\n        context = await browser.new_context(bypass_csp=True)\n        page = await context.new_page()\n        await page.goto(server.EMPTY_PAGE)\n        frame = await utils.attach_frame(page, \"frame1\", server.PREFIX + \"/csp.html\")\n        try:\n            await frame.add_script_tag(content=\"window.__injected = 42;\")\n        except Error:\n            pass\n        assert await frame.evaluate(\"window.__injected\") == 42\n        await context.close()\n\n    await override()\n\n\nasync def test_csp_should_work(browser: Browser, is_webkit: bool) -> None:\n    async def baseline() -> None:\n        context = await browser.new_context(java_script_enabled=False)\n        page = await context.new_page()\n        await page.goto('data:text/html, <script>var something = \"forbidden\"</script>')\n        with pytest.raises(Error) as exc_info:\n            await page.evaluate(\"something\")\n            if is_webkit:\n                assert \"Can't find variable: something\" in exc_info.value.message\n            else:\n                assert \"something is not defined\" in exc_info.value.message\n        await context.close()\n\n    await baseline()\n\n    async def override() -> None:\n        context = await browser.new_context()\n        page = await context.new_page()\n        await page.goto('data:text/html, <script>var something = \"forbidden\"</script>')\n        assert await page.evaluate(\"something\") == \"forbidden\"\n        await context.close()\n\n    await override()\n\n\nasync def test_csp_should_be_able_to_navigate_after_disabling_javascript(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(java_script_enabled=False)\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await context.close()\n\n\nasync def test_pages_should_return_all_of_the_pages(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    second = await context.new_page()\n    all_pages = context.pages\n    assert len(all_pages) == 2\n    assert page in all_pages\n    assert second in all_pages\n\n\nasync def test_pages_should_close_all_belonging_pages_once_closing_context(\n    context: BrowserContext,\n) -> None:\n    await context.new_page()\n    assert len(context.pages) == 1\n    await context.close()\n    assert context.pages == []\n\n\nasync def test_expose_binding_should_work(context: BrowserContext) -> None:\n    binding_source = []\n\n    def binding(source: Any, a: int, b: int) -> int:\n        binding_source.append(source)\n        return a + b\n\n    await context.expose_binding(\"add\", lambda source, a, b: binding(source, a, b))\n\n    page = await context.new_page()\n    result = await page.evaluate(\"add(5, 6)\")\n    assert binding_source[0][\"context\"] == context\n    assert binding_source[0][\"page\"] == page\n    assert binding_source[0][\"frame\"] == page.main_frame\n    assert result == 11\n\n\nasync def test_expose_function_should_work(context: BrowserContext) -> None:\n    await context.expose_function(\"add\", lambda a, b: a + b)\n    page = await context.new_page()\n    await page.expose_function(\"mul\", lambda a, b: a * b)\n    await context.expose_function(\"sub\", lambda a, b: a - b)\n    result = await page.evaluate(\n        \"\"\"async function() {\n      return { mul: await mul(9, 4), add: await add(9, 4), sub: await sub(9, 4) }\n    }\"\"\"\n    )\n\n    assert result == {\"mul\": 36, \"add\": 13, \"sub\": 5}\n\n\nasync def test_expose_function_should_throw_for_duplicate_registrations(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.expose_function(\"foo\", lambda: None)\n    await context.expose_function(\"bar\", lambda: None)\n    with pytest.raises(Error) as exc_info:\n        await context.expose_function(\"foo\", lambda: None)\n    assert exc_info.value.message == 'Function \"foo\" has been already registered'\n    page = await context.new_page()\n    with pytest.raises(Error) as exc_info:\n        await page.expose_function(\"foo\", lambda: None)\n    assert (\n        exc_info.value.message\n        == 'Function \"foo\" has been already registered in the browser context'\n    )\n    await page.expose_function(\"baz\", lambda: None)\n    with pytest.raises(Error) as exc_info:\n        await context.expose_function(\"baz\", lambda: None)\n    assert (\n        exc_info.value.message\n        == 'Function \"baz\" has been already registered in one of the pages'\n    )\n\n\nasync def test_expose_function_should_be_callable_from_inside_add_init_script(\n    context: BrowserContext, server: Server\n) -> None:\n    args = []\n    await context.expose_function(\"woof\", lambda arg: args.append(arg))\n    await context.add_init_script(\"woof('context')\")\n    page = await context.new_page()\n    await page.evaluate(\"undefined\")\n    assert args == [\"context\"]\n    args = []\n    await page.add_init_script(\"woof('page')\")\n    await page.reload()\n    assert args == [\"context\", \"page\"]\n\n\nasync def test_expose_bindinghandle_should_work(context: BrowserContext) -> None:\n    targets: List[JSHandle] = []\n\n    def logme(t: JSHandle) -> int:\n        targets.append(t)\n        return 17\n\n    page = await context.new_page()\n    await page.expose_binding(\"logme\", lambda source, t: logme(t), handle=True)\n    result = await page.evaluate(\"logme({ foo: 42 })\")\n    assert (await targets[0].evaluate(\"x => x.foo\")) == 42\n    assert result == 17\n\n\nasync def test_auth_should_fail_without_credentials(\n    context: BrowserContext, server: Server, fails_on_401: bool\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    page = await context.new_page()\n    try:\n        response = await page.goto(server.EMPTY_PAGE)\n        assert response\n        assert response.status == 401\n    except Error as exc:\n        assert fails_on_401\n        assert \"net::ERR_INVALID_AUTH_CREDENTIALS\" in exc.message\n\n\nasync def test_auth_should_work_with_correct_credentials(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    page = await context.new_page()\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    await context.close()\n\n\nasync def test_auth_should_fail_with_wrong_credentials(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\"username\": \"foo\", \"password\": \"bar\"}\n    )\n    page = await context.new_page()\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 401\n    await context.close()\n\n\nasync def test_auth_should_return_resource_body(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/playground.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    page = await context.new_page()\n    response = await page.goto(server.PREFIX + \"/playground.html\")\n    assert response\n    assert response.status == 200\n    assert await page.title() == \"Playground\"\n    assert \"Playground\" in await response.text()\n    await context.close()\n\n\nasync def test_should_work_with_correct_credentials_and_matching_origin(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX,\n        }\n    )\n    page = await context.new_page()\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    await context.close()\n\n\nasync def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n        }\n    )\n    page = await context.new_page()\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    await context.close()\n\n\nasync def test_should_fail_with_correct_credentials_and_mismatching_scheme(\n    browser: Browser, server: Server, fails_on_401: bool\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.replace(\"http://\", \"https://\"),\n        }\n    )\n    page = await context.new_page()\n    try:\n        response = await page.goto(server.EMPTY_PAGE)\n        assert response\n        assert response.status == 401\n    except Error as exc:\n        assert fails_on_401\n        assert \"net::ERR_INVALID_AUTH_CREDENTIALS\" in exc.message\n    await context.close()\n\n\nasync def test_should_fail_with_correct_credentials_and_mismatching_hostname(\n    browser: Browser, server: Server, fails_on_401: bool\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    hostname = urlparse(server.PREFIX).hostname\n    assert hostname\n    origin = server.PREFIX.replace(hostname, \"mismatching-hostname\")\n    context = await browser.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    page = await context.new_page()\n    try:\n        response = await page.goto(server.EMPTY_PAGE)\n        assert response\n        assert response.status == 401\n    except Error as exc:\n        assert fails_on_401\n        assert \"net::ERR_INVALID_AUTH_CREDENTIALS\" in exc.message\n    await context.close()\n\n\nasync def test_should_fail_with_correct_credentials_and_mismatching_port(\n    browser: Browser, server: Server, fails_on_401: bool\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    origin = server.PREFIX.replace(str(server.PORT), str(server.PORT + 1))\n    context = await browser.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    page = await context.new_page()\n    try:\n        response = await page.goto(server.EMPTY_PAGE)\n        assert response\n        assert response.status == 401\n    except Error as exc:\n        assert fails_on_401\n        assert \"net::ERR_INVALID_AUTH_CREDENTIALS\" in exc.message\n    await context.close()\n\n\nasync def test_offline_should_work_with_initial_option(\n    browser: Browser,\n    server: Server,\n    browser_name: str,\n) -> None:\n    context = await browser.new_context(offline=True)\n    page = await context.new_page()\n    frame_navigated_task = asyncio.create_task(page.wait_for_event(\"framenavigated\"))\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.EMPTY_PAGE)\n    if browser_name == \"firefox\":\n        await frame_navigated_task\n    assert exc_info.value\n    await context.set_offline(False)\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    await context.close()\n\n\nasync def test_offline_should_emulate_navigator_online(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    assert await page.evaluate(\"window.navigator.onLine\")\n    await context.set_offline(True)\n    assert await page.evaluate(\"window.navigator.onLine\") is False\n    await context.set_offline(False)\n    assert await page.evaluate(\"window.navigator.onLine\")\n\n\nasync def test_page_event_should_have_url(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with context.expect_page() as other_page_info:\n        await page.evaluate(\"url => window.open(url)\", server.EMPTY_PAGE)\n    other_page = await other_page_info.value\n    assert other_page.url == server.EMPTY_PAGE\n\n\nasync def test_page_event_should_have_url_after_domcontentloaded(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with context.expect_page() as other_page_info:\n        await page.evaluate(\"url => window.open(url)\", server.EMPTY_PAGE)\n    other_page = await other_page_info.value\n    await other_page.wait_for_load_state(\"domcontentloaded\")\n    assert other_page.url == server.EMPTY_PAGE\n\n\nasync def test_page_event_should_have_about_blank_url_with_domcontentloaded(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with context.expect_page() as other_page_info:\n        await page.evaluate(\"url => window.open(url)\", \"about:blank\")\n    other_page = await other_page_info.value\n    await other_page.wait_for_load_state(\"domcontentloaded\")\n    assert other_page.url == \"about:blank\"\n\n\nasync def test_page_event_should_have_about_blank_for_empty_url_with_domcontentloaded(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with context.expect_page() as other_page_info:\n        await page.evaluate(\"window.open()\")\n    other_page = await other_page_info.value\n    await other_page.wait_for_load_state(\"domcontentloaded\")\n    assert other_page.url == \"about:blank\"\n\n\nasync def test_page_event_should_report_when_a_new_page_is_created_and_closed(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with context.expect_page() as page_info:\n        await page.evaluate(\n            \"url => window.open(url)\", server.CROSS_PROCESS_PREFIX + \"/empty.html\"\n        )\n    other_page = await page_info.value\n\n    # The url is about:blank in FF when 'page' event is fired.\n    assert server.CROSS_PROCESS_PREFIX in other_page.url\n    assert await other_page.evaluate(\"['Hello', 'world'].join(' ')\") == \"Hello world\"\n    assert await other_page.query_selector(\"body\")\n\n    all_pages = context.pages\n    assert page in all_pages\n    assert other_page in all_pages\n\n    close_event_received = []\n    other_page.once(\"close\", lambda _: close_event_received.append(True))\n    await other_page.close()\n    assert close_event_received == [True]\n\n    all_pages = context.pages\n    assert page in all_pages\n    assert other_page not in all_pages\n\n\nasync def test_page_event_should_report_initialized_pages(\n    context: BrowserContext, server: Server\n) -> None:\n    async with context.expect_page() as page_info:\n        await context.new_page()\n    new_page = await page_info.value\n    assert new_page.url == \"about:blank\"\n\n    async with context.expect_page() as popup_info:\n        await new_page.evaluate(\"window.open('about:blank')\")\n    popup = await popup_info.value\n    assert popup.url == \"about:blank\"\n\n\nasync def test_page_event_should_have_an_opener(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with context.expect_page() as page_info:\n        await page.goto(server.PREFIX + \"/popup/window-open.html\")\n    popup = await page_info.value\n    assert popup.url == server.PREFIX + \"/popup/popup.html\"\n    assert await popup.opener() == page\n    assert await page.opener() is None\n\n\nasync def test_page_event_should_fire_page_lifecycle_events(\n    context: BrowserContext, server: Server\n) -> None:\n    events: List[str] = []\n\n    def handle_page(page: Page) -> None:\n        events.append(\"CREATED: \" + page.url)\n        page.on(\"close\", lambda _: events.append(\"DESTROYED: \" + page.url))\n\n    context.on(\"page\", handle_page)\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.close()\n    assert events == [\"CREATED: about:blank\", f\"DESTROYED: {server.EMPTY_PAGE}\"]\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_page_event_should_work_with_shift_clicking(\n    context: BrowserContext, server: Server\n) -> None:\n    # WebKit: Shift+Click does not open a new window.\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a href=\"/one-style.html\">yo</a>')\n    async with context.expect_page() as page_info:\n        await page.click(\"a\", modifiers=[\"Shift\"])\n    popup = await page_info.value\n    assert await popup.opener() is None\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_page_event_should_work_with_ctrl_clicking(\n    context: BrowserContext, server: Server\n) -> None:\n    # Firefox: reports an opener in this case.\n    # WebKit: Ctrl+Click does not open a new tab.\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a href=\"/one-style.html\">yo</a>')\n    async with context.expect_page() as popup_info:\n        await page.click(\"a\", modifiers=[\"ControlOrMeta\"])\n    popup = await popup_info.value\n    assert await popup.opener() is None\n\n\nasync def test_strict_selectors_on_context(browser: Browser, server: Server) -> None:\n    context = await browser.new_context(strict_selectors=True)\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <button>Hello</button>\n        <button>Hello</button>\n    \"\"\"\n    )\n    with pytest.raises(Error):\n        await page.text_content(\"button\")\n    with pytest.raises(Error):\n        await page.query_selector(\"button\")\n    await context.close()\n\n\nasync def test_should_support_forced_colors(browser: Browser) -> None:\n    context = await browser.new_context(forced_colors=\"active\")\n    page = await context.new_page()\n    assert await page.evaluate(\"matchMedia('(forced-colors: active)').matches\")\n    assert not await page.evaluate(\"matchMedia('(forced-colors: none)').matches\")\n"
  },
  {
    "path": "tests/async/test_browsercontext_add_cookies.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport datetime\nfrom typing import Callable, List\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext, Error, Page\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import must\n\n\nasync def test_should_work(context: BrowserContext, page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"password\", \"value\": \"123456\"}]\n    )\n    assert await page.evaluate(\"() => document.cookie\") == \"password=123456\"\n\n\nasync def test_should_roundtrip_cookie(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    # @see https://en.wikipedia.org/wiki/Year_2038_problem\n    date = int(datetime.datetime(2038, 1, 1).timestamp() * 1000)\n    document_cookie = await page.evaluate(\n        \"\"\"timestamp => {\n    const date = new Date(timestamp);\n    document.cookie = `username=John Doe;expires=${date.toUTCString()}`;\n    return document.cookie;\n  }\"\"\",\n        date,\n    )\n    assert document_cookie == \"username=John Doe\"\n    cookies = await context.cookies()\n    await context.clear_cookies()\n    assert await context.cookies() == []\n    # TODO: We are waiting for PEP705 so SetCookieParam can be readonly and matches the Cookie type.\n    await context.add_cookies(cookies)  # type: ignore\n    assert await context.cookies() == cookies\n\n\nasync def test_should_send_cookie_header(\n    server: Server, context: BrowserContext\n) -> None:\n    cookie: List[str] = []\n\n    def handler(request: TestServerRequest) -> None:\n        cookie.extend(must(request.requestHeaders.getRawHeaders(\"cookie\")))\n        request.finish()\n\n    server.set_route(\"/empty.html\", handler)\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"cookie\", \"value\": \"value\"}]\n    )\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    assert cookie == [\"cookie=value\"]\n\n\nasync def test_should_isolate_cookies_in_browser_contexts(\n    context: BrowserContext, server: Server, browser: Browser\n) -> None:\n    another_context = await browser.new_context()\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"isolatecookie\", \"value\": \"page1value\"}]\n    )\n    await another_context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"isolatecookie\", \"value\": \"page2value\"}]\n    )\n\n    cookies_1 = await context.cookies()\n    cookies_2 = await another_context.cookies()\n    assert len(cookies_1) == 1\n    assert len(cookies_2) == 1\n    assert cookies_1[0][\"name\"] == \"isolatecookie\"\n    assert cookies_1[0][\"value\"] == \"page1value\"\n    assert cookies_2[0][\"name\"] == \"isolatecookie\"\n    assert cookies_2[0][\"value\"] == \"page2value\"\n    await another_context.close()\n\n\nasync def test_should_isolate_session_cookies(\n    context: BrowserContext, server: Server, browser: Browser\n) -> None:\n    server.set_route(\n        \"/setcookie.html\",\n        lambda r: (\n            r.setHeader(\"Set-Cookie\", \"session=value\"),\n            r.finish(),\n        ),\n    )\n\n    page_1 = await context.new_page()\n    await page_1.goto(server.PREFIX + \"/setcookie.html\")\n    ##\n    page_2 = await context.new_page()\n    await page_2.goto(server.EMPTY_PAGE)\n    cookies_2 = await context.cookies()\n    assert len(cookies_2) == 1\n    assert \",\".join(list(map(lambda c: c[\"value\"], cookies_2))) == \"value\"\n    ##\n    context_b = await browser.new_context()\n    page_3 = await context_b.new_page()\n    await page_3.goto(server.EMPTY_PAGE)\n    cookies_3 = await context_b.cookies()\n    assert cookies_3 == []\n    await context_b.close()\n\n\nasync def test_should_isolate_persistent_cookies(\n    context: BrowserContext, server: Server, browser: Browser\n) -> None:\n    server.set_route(\n        \"/setcookie.html\",\n        lambda r: (\n            r.setHeader(\"Set-Cookie\", \"persistent=persistent-value; max-age=3600\"),\n            r.finish(),\n        ),\n    )\n\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/setcookie.html\")\n\n    context_1 = context\n    context_2 = await browser.new_context()\n    [page_1, page_2] = await asyncio.gather(context_1.new_page(), context_2.new_page())\n    await asyncio.gather(page_1.goto(server.EMPTY_PAGE), page_2.goto(server.EMPTY_PAGE))\n    [cookies_1, cookies_2] = await asyncio.gather(\n        context_1.cookies(), context_2.cookies()\n    )\n    assert len(cookies_1) == 1\n    assert cookies_1[0][\"name\"] == \"persistent\"\n    assert cookies_1[0][\"value\"] == \"persistent-value\"\n    assert len(cookies_2) == 0\n    await context_2.close()\n\n\nasync def test_should_isolate_send_cookie_header(\n    server: Server, context: BrowserContext, browser: Browser\n) -> None:\n    cookie: List[str] = []\n\n    def handler(request: TestServerRequest) -> None:\n        cookie.extend(request.requestHeaders.getRawHeaders(\"cookie\") or [])\n        request.finish()\n\n    server.set_route(\"/empty.html\", handler)\n\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"sendcookie\", \"value\": \"value\"}]\n    )\n\n    page_1 = await context.new_page()\n    await page_1.goto(server.EMPTY_PAGE)\n    assert cookie == [\"sendcookie=value\"]\n    cookie.clear()\n    ##\n    context_2 = await browser.new_context()\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.EMPTY_PAGE)\n    assert cookie == []\n    await context_2.close()\n\n\nasync def test_should_isolate_cookies_between_launches(\n    browser_factory: Callable[..., \"asyncio.Future[Browser]\"], server: Server\n) -> None:\n    browser_1 = await browser_factory()\n    context_1 = await browser_1.new_context()\n    await context_1.add_cookies(\n        [\n            {\n                \"url\": server.EMPTY_PAGE,\n                \"name\": \"cookie-in-context-1\",\n                \"value\": \"value\",\n                \"expires\": int(datetime.datetime.now().timestamp() + 10000),\n            }\n        ]\n    )\n    await browser_1.close()\n\n    browser_2 = await browser_factory()\n    context_2 = await browser_2.new_context()\n    cookies = await context_2.cookies()\n    assert cookies == []\n    await browser_2.close()\n\n\nasync def test_should_set_multiple_cookies(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [\n            {\"url\": server.EMPTY_PAGE, \"name\": \"multiple-1\", \"value\": \"123456\"},\n            {\"url\": server.EMPTY_PAGE, \"name\": \"multiple-2\", \"value\": \"bar\"},\n        ]\n    )\n    assert (\n        await page.evaluate(\n            \"\"\"() => {\n    const cookies = document.cookie.split(';');\n    return cookies.map(cookie => cookie.trim()).sort();\n  }\"\"\"\n        )\n        == [\"multiple-1=123456\", \"multiple-2=bar\"]\n    )\n\n\nasync def test_should_have_expires_set_to_neg_1_for_session_cookies(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"expires\", \"value\": \"123456\"}]\n    )\n    cookies = await context.cookies()\n    assert cookies[0][\"expires\"] == -1\n\n\nasync def test_should_set_cookie_with_reasonable_defaults(\n    context: BrowserContext,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"defaults\", \"value\": \"123456\"}]\n    )\n    cookies = await context.cookies()\n    cookies.sort(key=lambda r: r[\"name\"])\n    assert cookies == [\n        {\n            \"name\": \"defaults\",\n            \"value\": \"123456\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\nasync def test_should_set_a_cookie_with_a_path(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await context.add_cookies(\n        [\n            {\n                \"domain\": \"localhost\",\n                \"path\": \"/grid.html\",\n                \"name\": \"gridcookie\",\n                \"value\": \"GRID\",\n            }\n        ]\n    )\n    assert await context.cookies() == [\n        {\n            \"name\": \"gridcookie\",\n            \"value\": \"GRID\",\n            \"domain\": \"localhost\",\n            \"path\": \"/grid.html\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n    assert await page.evaluate(\"document.cookie\") == \"gridcookie=GRID\"\n    await page.goto(server.EMPTY_PAGE)\n    assert await page.evaluate(\"document.cookie\") == \"\"\n    await page.goto(server.PREFIX + \"/grid.html\")\n    assert await page.evaluate(\"document.cookie\") == \"gridcookie=GRID\"\n\n\nasync def test_should_not_set_a_cookie_with_blank_page_url(\n    context: BrowserContext, server: Server\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await context.add_cookies(\n            [\n                {\"url\": server.EMPTY_PAGE, \"name\": \"example-cookie\", \"value\": \"best\"},\n                {\"url\": \"about:blank\", \"name\": \"example-cookie-blank\", \"value\": \"best\"},\n            ]\n        )\n    assert (\n        'Blank page can not have cookie \"example-cookie-blank\"'\n        in exc_info.value.message\n    )\n\n\nasync def test_should_not_set_a_cookie_on_a_data_url_page(\n    context: BrowserContext,\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await context.add_cookies(\n            [\n                {\n                    \"url\": \"data:,Hello%2C%20World!\",\n                    \"name\": \"example-cookie\",\n                    \"value\": \"best\",\n                }\n            ]\n        )\n    assert (\n        'Data URL page can not have cookie \"example-cookie\"' in exc_info.value.message\n    )\n\n\nasync def test_should_default_to_setting_secure_cookie_for_https_websites(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    SECURE_URL = \"https://example.com\"\n    await context.add_cookies([{\"url\": SECURE_URL, \"name\": \"foo\", \"value\": \"bar\"}])\n    [cookie] = await context.cookies(SECURE_URL)\n    assert cookie[\"secure\"]\n\n\nasync def test_should_be_able_to_set_unsecure_cookie_for_http_website(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    HTTP_URL = \"http://example.com\"\n    await context.add_cookies([{\"url\": HTTP_URL, \"name\": \"foo\", \"value\": \"bar\"}])\n    [cookie] = await context.cookies(HTTP_URL)\n    assert not cookie[\"secure\"]\n\n\nasync def test_should_set_a_cookie_on_a_different_domain(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [{\"url\": \"https://www.example.com\", \"name\": \"example-cookie\", \"value\": \"best\"}]\n    )\n    assert await page.evaluate(\"document.cookie\") == \"\"\n    assert await context.cookies(\"https://www.example.com\") == [\n        {\n            \"name\": \"example-cookie\",\n            \"value\": \"best\",\n            \"domain\": \"www.example.com\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": True,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\nasync def test_should_set_cookies_for_a_frame(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [{\"url\": server.PREFIX, \"name\": \"frame-cookie\", \"value\": \"value\"}]\n    )\n    await page.evaluate(\n        \"\"\"src => {\n    let fulfill;\n    const promise = new Promise(x => fulfill = x);\n    const iframe = document.createElement('iframe');\n    document.body.appendChild(iframe);\n    iframe.onload = fulfill;\n    iframe.src = src;\n    return promise;\n  }\"\"\",\n        server.PREFIX + \"/grid.html\",\n    )\n\n    assert await page.frames[1].evaluate(\"document.cookie\") == \"frame-cookie=value\"\n\n\nasync def test_should_not_block_third_party_cookies(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    is_chromium: bool,\n    is_firefox: bool,\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"src => {\n    let fulfill;\n    const promise = new Promise(x => fulfill = x);\n    const iframe = document.createElement('iframe');\n    document.body.appendChild(iframe);\n    iframe.onload = fulfill;\n    iframe.src = src;\n    return promise;\n  }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/grid.html\",\n    )\n    await page.frames[1].evaluate(\"document.cookie = 'username=John Doe'\")\n    await page.wait_for_timeout(2000)\n    allows_third_party = is_firefox\n    cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + \"/grid.html\")\n\n    if allows_third_party:\n        assert cookies == [\n            {\n                \"domain\": \"127.0.0.1\",\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"name\": \"username\",\n                \"path\": \"/\",\n                \"sameSite\": \"Lax\" if is_chromium else \"None\",\n                \"secure\": False,\n                \"value\": \"John Doe\",\n            }\n        ]\n    else:\n        assert cookies == []\n"
  },
  {
    "path": "tests/async/test_browsercontext_clearcookies.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom urllib.parse import urlparse\n\nfrom playwright.async_api import Browser, BrowserContext, Page\nfrom tests.server import Server\n\n\nasync def test_should_clear_cookies(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"cookie1\", \"value\": \"1\"}]\n    )\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1\"\n    await context.clear_cookies()\n    assert await context.cookies() == []\n    await page.reload()\n    assert await page.evaluate(\"document.cookie\") == \"\"\n\n\nasync def test_should_isolate_cookies_when_clearing(\n    context: BrowserContext, server: Server, browser: Browser\n) -> None:\n    another_context = await browser.new_context()\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"page1cookie\", \"value\": \"page1value\"}]\n    )\n    await another_context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"page2cookie\", \"value\": \"page2value\"}]\n    )\n\n    assert len(await context.cookies()) == 1\n    assert len(await another_context.cookies()) == 1\n\n    await context.clear_cookies()\n    assert len(await context.cookies()) == 0\n    assert len(await another_context.cookies()) == 1\n\n    await another_context.clear_cookies()\n    assert len(await context.cookies()) == 0\n    assert len(await another_context.cookies()) == 0\n    await another_context.close()\n\n\nasync def test_should_remove_cookies_by_name(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n            {\n                \"name\": \"cookie2\",\n                \"value\": \"2\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n        ]\n    )\n    await page.goto(server.PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1; cookie2=2\"\n    await context.clear_cookies(name=\"cookie1\")\n    assert await page.evaluate(\"document.cookie\") == \"cookie2=2\"\n\n\nasync def test_should_remove_cookies_by_name_regex(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n            {\n                \"name\": \"cookie2\",\n                \"value\": \"2\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n        ]\n    )\n    await page.goto(server.PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1; cookie2=2\"\n    await context.clear_cookies(name=re.compile(\"coo.*1\"))\n    assert await page.evaluate(\"document.cookie\") == \"cookie2=2\"\n\n\nasync def test_should_remove_cookies_by_domain(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n            {\n                \"name\": \"cookie2\",\n                \"value\": \"2\",\n                \"domain\": urlparse(server.CROSS_PROCESS_PREFIX).hostname,\n                \"path\": \"/\",\n            },\n        ]\n    )\n    await page.goto(server.PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1\"\n    await page.goto(server.CROSS_PROCESS_PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie2=2\"\n    await context.clear_cookies(domain=urlparse(server.CROSS_PROCESS_PREFIX).hostname)\n    assert await page.evaluate(\"document.cookie\") == \"\"\n    await page.goto(server.PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1\"\n\n\nasync def test_should_remove_cookies_by_path(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/api/v1\",\n            },\n            {\n                \"name\": \"cookie2\",\n                \"value\": \"2\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/api/v2\",\n            },\n            {\n                \"name\": \"cookie3\",\n                \"value\": \"3\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n        ]\n    )\n    await page.goto(server.PREFIX + \"/api/v1\")\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1; cookie3=3\"\n    await context.clear_cookies(path=\"/api/v1\")\n    assert await page.evaluate(\"document.cookie\") == \"cookie3=3\"\n    await page.goto(server.PREFIX + \"/api/v2\")\n    assert await page.evaluate(\"document.cookie\") == \"cookie2=2; cookie3=3\"\n    await page.goto(server.PREFIX + \"/\")\n    assert await page.evaluate(\"document.cookie\") == \"cookie3=3\"\n\n\nasync def test_should_remove_cookies_by_name_and_domain(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.PREFIX).hostname,\n                \"path\": \"/\",\n            },\n            {\n                \"name\": \"cookie1\",\n                \"value\": \"1\",\n                \"domain\": urlparse(server.CROSS_PROCESS_PREFIX).hostname,\n                \"path\": \"/\",\n            },\n        ]\n    )\n    await page.goto(server.PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1\"\n    await context.clear_cookies(name=\"cookie1\", domain=urlparse(server.PREFIX).hostname)\n    assert await page.evaluate(\"document.cookie\") == \"\"\n    await page.goto(server.CROSS_PROCESS_PREFIX)\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1\"\n"
  },
  {
    "path": "tests/async/test_browsercontext_client_certificates.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nimport threading\nfrom pathlib import Path\nfrom typing import Dict, Generator, Optional, cast\n\nimport OpenSSL.crypto\nimport OpenSSL.SSL\nimport pytest\nfrom twisted.internet import reactor as _twisted_reactor\nfrom twisted.internet import ssl\nfrom twisted.internet.selectreactor import SelectReactor\nfrom twisted.web import resource, server\nfrom twisted.web.http import Request\n\nfrom playwright.async_api import Browser, BrowserType, Playwright, expect\n\nssl.optionsForClientTLS\nreactor = cast(SelectReactor, _twisted_reactor)\n\n\n@pytest.fixture(scope=\"function\", autouse=True)\ndef _skip_webkit_darwin(browser_name: str) -> None:\n    if browser_name == \"webkit\" and sys.platform == \"darwin\":\n        pytest.skip(\"WebKit does not proxy localhost on macOS\")\n\n\nclass HttpsResource(resource.Resource):\n    serverCertificate: ssl.PrivateCertificate\n    isLeaf = True\n\n    def _verify_cert_chain(self, cert: Optional[OpenSSL.crypto.X509]) -> bool:\n        if not cert:\n            return False\n        store = OpenSSL.crypto.X509Store()\n        store.add_cert(self.serverCertificate.original)\n        store_ctx = OpenSSL.crypto.X509StoreContext(store, cert)\n        try:\n            store_ctx.verify_certificate()\n            return True\n        except OpenSSL.crypto.X509StoreContextError:\n            return False\n\n    def render_GET(self, request: Request) -> bytes:\n        tls_socket: OpenSSL.SSL.Connection = request.transport.getHandle()  # type: ignore\n        cert = tls_socket.get_peer_certificate()\n        parts = []\n\n        if self._verify_cert_chain(cert):\n            request.setResponseCode(200)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": f\"Hello {cert.get_subject().CN}, your certificate was issued by {cert.get_issuer().CN}!\",  # type: ignore\n                }\n            )\n        elif cert and cert.get_subject():\n            request.setResponseCode(403)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": f\"Sorry {cert.get_subject().CN}, certificates from {cert.get_issuer().CN} are not welcome here.\",\n                }\n            )\n        else:\n            request.setResponseCode(401)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": \"Sorry, but you need to provide a client certificate to continue.\",\n                }\n            )\n        return b\"\".join(\n            [\n                f'<div data-testid=\"{part[\"key\"]}\">{part[\"value\"]}</div>'.encode()\n                for part in parts\n            ]\n        )\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef _client_certificate_server(assetdir: Path) -> Generator[None, None, None]:\n    certAuthCert = ssl.Certificate.loadPEM(\n        (assetdir / \"client-certificates/server/server_cert.pem\").read_text()\n    )\n    serverCert = ssl.PrivateCertificate.loadPEM(\n        (assetdir / \"client-certificates/server/server_key.pem\").read_text()\n        + (assetdir / \"client-certificates/server/server_cert.pem\").read_text()\n    )\n\n    contextFactory = serverCert.options(certAuthCert)\n    contextFactory.requireCertificate = False\n    resource = HttpsResource()\n    resource.serverCertificate = serverCert\n    site = server.Site(resource)\n\n    def _run() -> None:\n        reactor.listenSSL(8000, site, contextFactory)\n\n    thread = threading.Thread(target=_run)\n    thread.start()\n    yield\n    thread.join()\n\n\nasync def test_should_throw_with_untrusted_client_certs(\n    playwright: Playwright, assetdir: Path\n) -> None:\n    serverURL = \"https://localhost:8000/\"\n    request = await playwright.request.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": serverURL,\n                \"certPath\": assetdir\n                / \"client-certificates/client/self-signed/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/self-signed/key.pem\",\n            }\n        ],\n    )\n    with pytest.raises(Exception, match=\"alert unknown ca\"):\n        await request.get(serverURL)\n    await request.dispose()\n\n\nasync def test_should_work_with_new_context(browser: Browser, assetdir: Path) -> None:\n    context = await browser.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    page = await context.new_page()\n    await page.goto(\"https://localhost:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    await page.goto(\"https://127.0.0.1:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n\n    response = await page.context.request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in await response.text()\n    )\n    response = await page.context.request.get(\"https://127.0.0.1:8000\")\n    assert (\n        \"Hello Alice, your certificate was issued by localhost!\"\n        in await response.text()\n    )\n    await context.close()\n\n\nasync def test_should_work_with_new_context_passing_as_content(\n    browser: Browser, assetdir: Path\n) -> None:\n    context = await browser.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"cert\": (\n                    assetdir / \"client-certificates/client/trusted/cert.pem\"\n                ).read_bytes(),\n                \"key\": (\n                    assetdir / \"client-certificates/client/trusted/key.pem\"\n                ).read_bytes(),\n            }\n        ],\n    )\n    page = await context.new_page()\n    await page.goto(\"https://localhost:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    await page.goto(\"https://127.0.0.1:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n\n    response = await page.context.request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in await response.text()\n    )\n    response = await page.context.request.get(\"https://127.0.0.1:8000\")\n    assert (\n        \"Hello Alice, your certificate was issued by localhost!\"\n        in await response.text()\n    )\n    await context.close()\n\n\nasync def test_should_work_with_new_persistent_context(\n    browser_type: BrowserType, assetdir: Path, launch_arguments: Dict\n) -> None:\n    context = await browser_type.launch_persistent_context(\n        \"\",\n        **launch_arguments,\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    page = await context.new_page()\n    await page.goto(\"https://localhost:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    await page.goto(\"https://127.0.0.1:8000\")\n    await expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n    await context.close()\n\n\nasync def test_should_work_with_global_api_request_context(\n    playwright: Playwright, assetdir: Path\n) -> None:\n    request = await playwright.request.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    response = await request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in await response.text()\n    )\n    response = await request.get(\"https://127.0.0.1:8000\")\n    assert (\n        \"Hello Alice, your certificate was issued by localhost!\"\n        in await response.text()\n    )\n    await request.dispose()\n"
  },
  {
    "path": "tests/async/test_browsercontext_cookies.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\n\nimport pytest\n\nfrom playwright.async_api import BrowserContext, Page\nfrom tests.server import Server\n\n\nasync def test_should_return_no_cookies_in_pristine_browser_context(\n    context: BrowserContext,\n) -> None:\n    assert await context.cookies() == []\n\n\nasync def test_should_get_a_cookie(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    document_cookie = await page.evaluate(\n        \"\"\"() => {\n    document.cookie = 'username=John Doe';\n    return document.cookie;\n  }\"\"\"\n    )\n    assert document_cookie == \"username=John Doe\"\n    assert await context.cookies() == [\n        {\n            \"name\": \"username\",\n            \"value\": \"John Doe\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\nasync def test_should_get_a_non_session_cookie(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    # @see https://en.wikipedia.org/wiki/Year_2038_problem\n    date = int(datetime.datetime(2038, 1, 1).timestamp() * 1000)\n    document_cookie = await page.evaluate(\n        \"\"\"timestamp => {\n    const date = new Date(timestamp);\n    document.cookie = `username=John Doe;expires=${date.toUTCString()}`;\n    return document.cookie;\n  }\"\"\",\n        date,\n    )\n    assert document_cookie == \"username=John Doe\"\n    cookies = await context.cookies()\n    expires = cookies[0][\"expires\"]\n    del cookies[0][\"expires\"]\n    # Browsers start to cap cookies with 400 days max expires value.\n    # See https://github.com/httpwg/http-extensions/pull/1732\n    # Chromium patch: https://chromium.googlesource.com/chromium/src/+/aaa5d2b55478eac2ee642653dcd77a50ac3faff6\n    # We want to make sure that expires date is at least 400 days in future.\n    # We use 355 to prevent flakes and not think about timezones!\n    assert datetime.datetime.fromtimestamp(\n        expires\n    ) - datetime.datetime.now() > datetime.timedelta(days=355)\n    assert cookies == [\n        {\n            \"name\": \"username\",\n            \"value\": \"John Doe\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\nasync def test_should_properly_report_httpOnly_cookie(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    server.set_route(\n        \"/empty.html\",\n        lambda r: (\n            r.setHeader(\"Set-Cookie\", \"name=value;httpOnly; Path=/\"),\n            r.finish(),\n        ),\n    )\n\n    await page.goto(server.EMPTY_PAGE)\n    cookies = await context.cookies()\n    assert len(cookies) == 1\n    assert cookies[0][\"httpOnly\"] is True\n\n\nasync def test_should_properly_report_strict_sameSite_cookie(\n    context: BrowserContext, page: Page, server: Server, is_webkit: bool, is_win: bool\n) -> None:\n    if is_webkit and is_win:\n        pytest.skip()\n\n    server.set_route(\n        \"/empty.html\",\n        lambda r: (\n            r.setHeader(\"Set-Cookie\", \"name=value;sameSite=Strict\"),\n            r.finish(),\n        ),\n    )\n    await page.goto(server.EMPTY_PAGE)\n    cookies = await context.cookies()\n    assert len(cookies) == 1\n    assert cookies[0][\"sameSite\"] == \"Strict\"\n\n\nasync def test_should_properly_report_lax_sameSite_cookie(\n    context: BrowserContext, page: Page, server: Server, is_webkit: bool, is_win: bool\n) -> None:\n    if is_webkit and is_win:\n        pytest.skip()\n\n    server.set_route(\n        \"/empty.html\",\n        lambda r: (\n            r.setHeader(\"Set-Cookie\", \"name=value;sameSite=Lax\"),\n            r.finish(),\n        ),\n    )\n    await page.goto(server.EMPTY_PAGE)\n    cookies = await context.cookies()\n    assert len(cookies) == 1\n    assert cookies[0][\"sameSite\"] == \"Lax\"\n\n\nasync def test_should_get_multiple_cookies(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    document_cookie = await page.evaluate(\n        \"\"\"() => {\n    document.cookie = 'username=John Doe';\n    document.cookie = 'password=1234';\n    return document.cookie.split('; ').sort().join('; ');\n  }\"\"\"\n    )\n    cookies = await context.cookies()\n    cookies.sort(key=lambda r: r[\"name\"])\n    assert document_cookie == \"password=1234; username=John Doe\"\n    assert cookies == [\n        {\n            \"name\": \"password\",\n            \"value\": \"1234\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        },\n        {\n            \"name\": \"username\",\n            \"value\": \"John Doe\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        },\n    ]\n\n\nasync def test_should_get_cookies_from_multiple_urls(\n    context: BrowserContext, default_same_site_cookie_value: str\n) -> None:\n    await context.add_cookies(\n        [\n            {\"url\": \"https://foo.com\", \"name\": \"doggo\", \"value\": \"woofs\"},\n            {\"url\": \"https://bar.com\", \"name\": \"catto\", \"value\": \"purrs\"},\n            {\"url\": \"https://baz.com\", \"name\": \"birdo\", \"value\": \"tweets\"},\n        ]\n    )\n    cookies = await context.cookies([\"https://foo.com\", \"https://baz.com\"])\n    cookies.sort(key=lambda r: r[\"name\"])\n\n    assert cookies == [\n        {\n            \"name\": \"birdo\",\n            \"value\": \"tweets\",\n            \"domain\": \"baz.com\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": True,\n            \"sameSite\": default_same_site_cookie_value,\n        },\n        {\n            \"name\": \"doggo\",\n            \"value\": \"woofs\",\n            \"domain\": \"foo.com\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": True,\n            \"sameSite\": default_same_site_cookie_value,\n        },\n    ]\n"
  },
  {
    "path": "tests/async/test_browsercontext_events.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Optional\n\nimport pytest\n\nfrom playwright.async_api import BrowserContext, Page\nfrom tests.utils import must\n\nfrom ..server import Server, TestServerRequest\n\n\nasync def test_console_event_should_work(page: Page) -> None:\n    [message, _] = await asyncio.gather(\n        page.context.wait_for_event(\"console\"),\n        page.evaluate(\"() => console.log('hello')\"),\n    )\n    assert message.text == \"hello\"\n    assert message.page == page\n\n\nasync def test_console_event_should_work_in_popup(page: Page) -> None:\n    [message, popup, _] = await asyncio.gather(\n        page.context.wait_for_event(\"console\"),\n        page.wait_for_event(\"popup\"),\n        page.evaluate(\n            \"\"\"() => {\n            const win = window.open('');\n            win.console.log('hello');\n        }\"\"\"\n        ),\n    )\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_console_event_should_work_in_popup_2(\n    page: Page, browser_name: str\n) -> None:\n    [message, popup, _] = await asyncio.gather(\n        page.context.wait_for_event(\"console\", lambda msg: msg.type == \"log\"),\n        page.context.wait_for_event(\"page\"),\n        page.evaluate(\n            \"\"\"async () => {\n            const win = window.open('javascript:console.log(\"hello\")');\n            await new Promise(f => setTimeout(f, 0));\n            win.close();\n        }\"\"\"\n        ),\n    )\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_console_event_should_work_in_immediately_closed_popup(\n    page: Page, browser_name: str\n) -> None:\n    [message, popup, _] = await asyncio.gather(\n        page.context.wait_for_event(\"console\"),\n        page.wait_for_event(\"popup\"),\n        page.evaluate(\n            \"\"\"async () => {\n            const win = window.open();\n            win.console.log('hello');\n            win.close();\n        }\"\"\"\n        ),\n    )\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\nasync def test_dialog_event_should_work1(page: Page) -> None:\n    prompt_task: Optional[asyncio.Future[str]] = None\n\n    async def open_dialog() -> None:\n        nonlocal prompt_task\n        prompt_task = asyncio.create_task(page.evaluate(\"() => prompt('hey?')\"))\n\n    [dialog1, dialog2, _] = await asyncio.gather(\n        page.context.wait_for_event(\"dialog\"),\n        page.wait_for_event(\"dialog\"),\n        open_dialog(),\n    )\n    assert dialog1 == dialog2\n    assert dialog1.message == \"hey?\"\n    assert dialog1.page == page\n    await dialog1.accept(\"hello\")\n    assert await must(prompt_task) == \"hello\"\n\n\nasync def test_dialog_event_should_work_in_popup(page: Page) -> None:\n    prompt_task: Optional[asyncio.Future[str]] = None\n\n    async def open_dialog() -> None:\n        nonlocal prompt_task\n        prompt_task = asyncio.create_task(\n            page.evaluate(\"() => window.open('').prompt('hey?')\")\n        )\n\n    [dialog, popup, _] = await asyncio.gather(\n        page.context.wait_for_event(\"dialog\"),\n        page.wait_for_event(\"popup\"),\n        open_dialog(),\n    )\n    assert dialog.message == \"hey?\"\n    assert dialog.page == popup\n    await dialog.accept(\"hello\")\n    assert await must(prompt_task) == \"hello\"\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_dialog_event_should_work_in_popup_2(\n    page: Page, browser_name: str\n) -> None:\n    promise = asyncio.create_task(\n        page.evaluate(\"() => window.open('javascript:prompt(\\\"hey?\\\")')\")\n    )\n    dialog = await page.context.wait_for_event(\"dialog\")\n    assert dialog.message == \"hey?\"\n    assert dialog.page is None\n    await dialog.accept(\"hello\")\n    await promise\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_dialog_event_should_work_in_immdiately_closed_popup(page: Page) -> None:\n    [message, popup, _] = await asyncio.gather(\n        page.context.wait_for_event(\"console\"),\n        page.wait_for_event(\"popup\"),\n        page.evaluate(\n            \"\"\"() => {\n            const win = window.open();\n            win.console.log('hello');\n            win.close();\n        }\"\"\"\n        ),\n    )\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\nasync def test_dialog_event_should_work_with_inline_script_tag(\n    page: Page, server: Server\n) -> None:\n    def handle_route(request: TestServerRequest) -> None:\n        request.setHeader(\"content-type\", \"text/html\")\n        request.write(b\"\"\"<script>window.result = prompt('hey?')</script>\"\"\")\n        request.finish()\n\n    server.set_route(\"/popup.html\", handle_route)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<a href='popup.html' target=_blank>Click me</a>\")\n\n    promise = asyncio.create_task(page.click(\"a\"))\n    [dialog, popup] = await asyncio.gather(\n        page.context.wait_for_event(\"dialog\"),\n        page.wait_for_event(\"popup\"),\n    )\n\n    assert dialog.message == \"hey?\"\n    assert dialog.page == popup\n    await dialog.accept(\"hello\")\n    await promise\n    assert await popup.evaluate(\"window.result\") == \"hello\"\n\n\nasync def test_console_event_should_work_with_context_manager(page: Page) -> None:\n    async with page.context.expect_console_message() as cm_info:\n        await page.evaluate(\"() => console.log('hello')\")\n    message = await cm_info.value\n    assert message.text == \"hello\"\n    assert message.page == page\n\n\nasync def test_page_error_event_should_work(page: Page) -> None:\n    async with page.context.expect_event(\"weberror\") as page_error_info:\n        await page.set_content('<script>throw new Error(\"boom\")</script>')\n    page_error = await page_error_info.value\n    assert page_error.page == page\n    assert \"boom\" in page_error.error.stack\n\n\nasync def test_weberror_event_should_work(context: BrowserContext, page: Page) -> None:\n    async with context.expect_event(\"weberror\") as error_info:\n        await page.goto('data:text/html,<script>throw new Error(\"Test\")</script>')\n    error = await error_info.value\n    assert error.page == page\n    assert error.error.message == \"Test\"\n"
  },
  {
    "path": "tests/async/test_browsercontext_proxy.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nfrom typing import AsyncGenerator, Awaitable, Callable\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext\nfrom tests.server import Server, TestServerRequest\n\n\n@pytest.fixture(scope=\"session\")\nasync def browser(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\",\n) -> AsyncGenerator[Browser, None]:\n    browser = await browser_factory(proxy={\"server\": \"dummy\"})\n    yield browser\n    await browser.close()\n\n\nasync def test_should_use_proxy(\n    context_factory: \"Callable[..., asyncio.Future[BrowserContext]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    context = await context_factory(proxy={\"server\": f\"localhost:{server.PORT}\"})\n    page = await context.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_proxy_should_allow_none_for_optional_settings(\n    context_factory: \"Callable[..., asyncio.Future[BrowserContext]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    context = await context_factory(\n        proxy={\n            \"server\": f\"localhost:{server.PORT}\",\n            \"username\": None,\n            \"password\": None,\n            \"bypass\": None,\n        }\n    )\n    page = await context.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_should_use_proxy_for_second_page(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    context = await context_factory(proxy={\"server\": f\"localhost:{server.PORT}\"})\n\n    page1 = await context.new_page()\n    await page1.goto(\"http://non-existent.com/target.html\")\n    assert await page1.title() == \"Served by the proxy\"\n\n    page2 = await context.new_page()\n    await page2.goto(\"http://non-existent.com/target.html\")\n    assert await page2.title() == \"Served by the proxy\"\n\n\nasync def test_should_work_with_ip_port_notion(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    context = await context_factory(proxy={\"server\": f\"127.0.0.1:{server.PORT}\"})\n    page = await context.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_should_authenticate(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\", server: Server\n) -> None:\n    def handler(req: TestServerRequest) -> None:\n        auth = req.getHeader(\"proxy-authorization\")\n        if not auth:\n            req.setHeader(\n                b\"Proxy-Authenticate\", b'Basic realm=\"Access to internal site\"'\n            )\n            req.setResponseCode(407)\n        else:\n            req.write(f\"<html><title>{auth}</title></html>\".encode(\"utf-8\"))\n        req.finish()\n\n    server.set_route(\"/target.html\", handler)\n\n    context = await context_factory(\n        proxy={\n            \"server\": f\"localhost:{server.PORT}\",\n            \"username\": \"user\",\n            \"password\": \"secret\",\n        }\n    )\n    page = await context.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Basic \" + base64.b64encode(b\"user:secret\").decode(\n        \"utf-8\"\n    )\n\n\nasync def test_should_authenticate_with_empty_password(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\", server: Server\n) -> None:\n    def handler(req: TestServerRequest) -> None:\n        auth = req.getHeader(\"proxy-authorization\")\n        if not auth:\n            req.setHeader(\n                b\"Proxy-Authenticate\", b'Basic realm=\"Access to internal site\"'\n            )\n            req.setResponseCode(407)\n        else:\n            req.write(f\"<html><title>{auth}</title></html>\".encode(\"utf-8\"))\n        req.finish()\n\n    server.set_route(\"/target.html\", handler)\n\n    context = await context_factory(\n        proxy={\"server\": f\"localhost:{server.PORT}\", \"username\": \"user\", \"password\": \"\"}\n    )\n    page = await context.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Basic \" + base64.b64encode(b\"user:\").decode(\"utf-8\")\n"
  },
  {
    "path": "tests/async/test_browsercontext_request_fallback.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Any, Callable, Coroutine, cast\n\nfrom playwright.async_api import BrowserContext, Page, Request, Route\nfrom tests.server import Server\n\n\nasync def test_should_work(page: Page, context: BrowserContext, server: Server) -> None:\n    await context.route(\"**/*\", lambda route: asyncio.create_task(route.fallback()))\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_should_fall_back(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    intercepted = []\n\n    def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\"**/empty.html\", _handler1)\n\n    def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\n        \"**/empty.html\",\n        _handler2,\n    )\n\n    def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\"**/empty.html\", _handler3)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_fall_back_async_delayed(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    intercepted = []\n\n    def create_handler(i: int) -> Callable[[Route], Coroutine]:\n        async def handler(route: Route) -> None:\n            intercepted.append(i)\n            await asyncio.sleep(0.1)\n            await route.fallback()\n\n        return handler\n\n    await context.route(\"**/empty.html\", create_handler(1))\n    await context.route(\"**/empty.html\", create_handler(2))\n    await context.route(\"**/empty.html\", create_handler(3))\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_chain_once(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await context.route(\n        \"**/madeup.txt\",\n        lambda route: asyncio.create_task(\n            route.fulfill(status=200, body=\"fulfilled one\")\n        ),\n        times=1,\n    )\n    await context.route(\n        \"**/madeup.txt\", lambda route: asyncio.create_task(route.fallback()), times=1\n    )\n\n    resp = await page.goto(server.PREFIX + \"/madeup.txt\")\n    assert resp\n    body = await resp.body()\n    assert body == b\"fulfilled one\"\n\n\nasync def test_should_fall_back_after_exception(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await context.route(\"**/empty.html\", lambda route: route.continue_())\n\n    async def handler(route: Route) -> None:\n        try:\n            await route.fulfill(response=cast(Any, {}))\n        except Exception:\n            await route.fallback()\n\n    await context.route(\"**/empty.html\", handler)\n\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_should_amend_http_headers(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    values = []\n\n    async def handler(route: Route) -> None:\n        values.append(route.request.headers.get(\"foo\"))\n        values.append(await route.request.header_value(\"FOO\"))\n        await route.continue_()\n\n    await context.route(\"**/sleep.zzz\", handler)\n\n    async def handler_with_header_mods(route: Route) -> None:\n        await route.fallback(headers={**route.request.headers, \"FOO\": \"bar\"})\n\n    await context.route(\"**/*\", handler_with_header_mods)\n\n    await page.goto(server.EMPTY_PAGE)\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        await page.evaluate(\"() => fetch('/sleep.zzz')\")\n    values.append(server_request_info.value.getHeader(\"foo\"))\n    assert values == [\"bar\", \"bar\", \"bar\"]\n\n\nasync def test_should_delete_header_with_undefined_value(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\n        \"/something\",\n        lambda r: (\n            r.setHeader(\"Acces-Control-Allow-Origin\", \"*\"),\n            r.write(b\"done\"),\n            r.finish(),\n        ),\n    )\n\n    intercepted_request = []\n\n    async def capture_and_continue(route: Route, request: Request) -> None:\n        intercepted_request.append(request)\n        await route.continue_()\n\n    await context.route(\"**/*\", capture_and_continue)\n\n    async def delete_foo_header(route: Route, request: Request) -> None:\n        headers = await request.all_headers()\n        del headers[\"foo\"]\n        await route.fallback(headers=headers)\n\n    await context.route(server.PREFIX + \"/something\", delete_foo_header)\n\n    [server_req, text] = await asyncio.gather(\n        server.wait_for_request(\"/something\"),\n        page.evaluate(\n            \"\"\"\n            async url => {\n                const data = await fetch(url, {\n                    headers: {\n                    foo: 'a',\n                    bar: 'b',\n                    }\n                });\n                return data.text();\n                }\n            \"\"\",\n            server.PREFIX + \"/something\",\n        ),\n    )\n\n    assert text == \"done\"\n    assert not intercepted_request[0].headers.get(\"foo\")\n    assert intercepted_request[0].headers.get(\"bar\") == \"b\"\n    assert not server_req.getHeader(\"foo\")\n    assert server_req.getHeader(\"bar\") == \"b\"\n\n\nasync def test_should_amend_method(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    method = []\n\n    def _handler1(route: Route) -> None:\n        method.append(route.request.method)\n        asyncio.create_task(route.continue_())\n\n    await context.route(\"**/*\", _handler1)\n    await context.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fallback(method=\"POST\"))\n    )\n\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"() => fetch('/sleep.zzz')\"),\n    )\n\n    assert method == [\"POST\"]\n    assert request.method == b\"POST\"\n\n\nasync def test_should_override_request_url(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    url = []\n\n    def _handler1(route: Route) -> None:\n        url.append(route.request.url)\n        asyncio.create_task(route.continue_())\n\n    await context.route(\n        \"**/global-var.html\",\n        _handler1,\n    )\n\n    def _handler2(route: Route) -> None:\n        asyncio.create_task(route.fallback(url=server.PREFIX + \"/global-var.html\"))\n\n    await context.route(\n        \"**/foo\",\n        _handler2,\n    )\n\n    [server_request, response, _] = await asyncio.gather(\n        server.wait_for_request(\"/global-var.html\"),\n        page.wait_for_event(\"response\"),\n        page.goto(server.PREFIX + \"/foo\"),\n    )\n\n    assert url == [server.PREFIX + \"/global-var.html\"]\n    assert response.url == server.PREFIX + \"/global-var.html\"\n    assert response.request.url == server.PREFIX + \"/global-var.html\"\n    assert await page.evaluate(\"() => window['globalVar']\") == 123\n    assert server_request.uri == b\"/global-var.html\"\n    assert server_request.method == b\"GET\"\n\n\nasync def test_should_amend_post_data(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    post_data = []\n\n    def _handler1(route: Route) -> None:\n        post_data.append(route.request.post_data)\n        asyncio.create_task(route.continue_())\n\n    await context.route(\"**/*\", _handler1)\n    await context.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fallback(post_data=\"doggo\"))\n    )\n    [server_request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    assert post_data == [\"doggo\"]\n    assert server_request.post_body == b\"doggo\"\n\n\nasync def test_should_amend_binary_post_data(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    post_data_buffer = []\n\n    def _handler1(route: Route) -> None:\n        post_data_buffer.append(route.request.post_data)\n        asyncio.create_task(route.continue_())\n\n    await context.route(\"**/*\", _handler1)\n\n    def _handler2(route: Route) -> None:\n        asyncio.create_task(route.fallback(post_data=b\"\\x00\\x01\\x02\\x03\\x04\"))\n\n    await context.route(\"**/*\", _handler2)\n\n    [server_request, result] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    # FIXME: should this be bytes?\n    assert post_data_buffer == [\"\\x00\\x01\\x02\\x03\\x04\"]\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body == b\"\\x00\\x01\\x02\\x03\\x04\"\n"
  },
  {
    "path": "tests/async/test_browsercontext_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom pathlib import Path\n\nimport pytest\nfrom twisted.web import http\n\nfrom playwright.async_api import BrowserContext, Page, Route\nfrom tests.server import Server\n\n\nasync def test_should_fulfill_intercepted_response(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    async def handle(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(\n            response=response,\n            status=201,\n            headers={\"foo\": \"bar\"},\n            content_type=\"text/plain\",\n            body=\"Yo, page!\",\n        )\n\n    await context.route(\"**/*\", handle)\n    response = await page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 201\n    assert response.headers[\"foo\"] == \"bar\"\n    assert response.headers[\"content-type\"] == \"text/plain\"\n    assert await page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\nasync def test_should_fulfill_response_with_empty_body(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    async def handle(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(\n            response=response, status=201, body=\"\", headers={\"content-length\": \"0\"}\n        )\n\n    await context.route(\"**/*\", handle)\n    response = await page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"\"\n\n\nasync def test_should_override_with_defaults_when_intercepted_response_not_provided(\n    page: Page, context: BrowserContext, server: Server, browser_name: str\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"my content\".encode())\n        request.finish()\n\n    server.set_route(\"/empty.html\", server_handler)\n\n    async def handle(route: Route) -> None:\n        await page.request.fetch(route.request)\n        await route.fulfill(status=201)\n\n    await context.route(\"**/*\", handle)\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"\"\n    if browser_name == \"webkit\":\n        assert response.headers == {\"content-type\": \"text/plain\"}\n    else:\n        assert response.headers == {}\n\n\nasync def test_should_fulfill_with_any_response(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"Woo-hoo\".encode())\n        request.finish()\n\n    server.set_route(\"/sample\", server_handler)\n    sample_response = await page.request.get(server.PREFIX + \"/sample\")\n    await context.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            response=sample_response, status=201, content_type=\"text/plain\"\n        ),\n    )\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"Woo-hoo\"\n    assert response.headers[\"foo\"] == \"bar\"\n\n\nasync def test_should_support_fulfill_after_intercept(\n    page: Page, context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    request_future = asyncio.create_task(server.wait_for_request(\"/title.html\"))\n\n    async def handle_route(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(response=response)\n\n    await context.route(\"**\", handle_route)\n    response = await page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    request = await request_future\n    assert request.uri.decode() == \"/title.html\"\n    original = (assetdir / \"title.html\").read_text()\n    assert await response.text() == original\n\n\nasync def test_should_give_access_to_the_intercepted_response(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_task: \"asyncio.Future[Route]\" = asyncio.Future()\n    await context.route(\"**/title.html\", lambda route: route_task.set_result(route))\n\n    eval_task = asyncio.create_task(\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + \"/title.html\")\n    )\n\n    route = await route_task\n    response = await page.request.fetch(route.request)\n\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.url.endswith(\"/title.html\") is True\n    assert response.headers[\"content-type\"] == \"text/html; charset=utf-8\"\n    assert list(\n        filter(\n            lambda header: header[\"name\"].lower() == \"content-type\",\n            response.headers_array,\n        )\n    ) == [{\"name\": \"Content-Type\", \"value\": \"text/html; charset=utf-8\"}]\n\n    await asyncio.gather(\n        route.fulfill(response=response),\n        eval_task,\n    )\n\n\nasync def test_should_give_access_to_the_intercepted_response_body(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_task: \"asyncio.Future[Route]\" = asyncio.Future()\n    await context.route(\"**/simple.json\", lambda route: route_task.set_result(route))\n\n    eval_task = asyncio.create_task(\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + \"/simple.json\")\n    )\n\n    route = await route_task\n    response = await page.request.fetch(route.request)\n\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n    await asyncio.gather(\n        route.fulfill(response=response),\n        eval_task,\n    )\n\n\nasync def test_should_show_exception_after_fulfill(page: Page, server: Server) -> None:\n    async def _handle(route: Route) -> None:\n        await route.continue_()\n        raise Exception(\"Exception text!?\")\n\n    await page.route(\"*/**\", _handle)\n    await page.goto(server.EMPTY_PAGE)\n    # Any next API call should throw because handler did throw during previous goto()\n    with pytest.raises(Exception, match=\"Exception text!?\"):\n        await page.goto(server.EMPTY_PAGE)\n"
  },
  {
    "path": "tests/async/test_browsercontext_route.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport re\nfrom typing import Awaitable, Callable, List\n\nimport pytest\n\nfrom playwright.async_api import (\n    Browser,\n    BrowserContext,\n    Error,\n    Page,\n    Request,\n    Route,\n    expect,\n)\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import must\n\n\nasync def test_route_should_intercept(context: BrowserContext, server: Server) -> None:\n    intercepted = []\n\n    def handle(route: Route, request: Request) -> None:\n        intercepted.append(True)\n        assert \"empty.html\" in request.url\n        assert request.headers[\"user-agent\"]\n        assert request.method == \"GET\"\n        assert request.post_data is None\n        assert request.is_navigation_request()\n        assert request.resource_type == \"document\"\n        assert request.frame == page.main_frame\n        assert request.frame.url == \"about:blank\"\n        asyncio.create_task(route.continue_())\n\n    await context.route(\"**/empty.html\", lambda route, request: handle(route, request))\n    page = await context.new_page()\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert intercepted == [True]\n    await context.close()\n\n\nasync def test_route_should_unroute(context: BrowserContext, server: Server) -> None:\n    page = await context.new_page()\n\n    intercepted: List[int] = []\n\n    def handler(route: Route, request: Request, ordinal: int) -> None:\n        intercepted.append(ordinal)\n        asyncio.create_task(route.continue_())\n\n    await context.route(\"**/*\", lambda route, request: handler(route, request, 1))\n    await context.route(\n        \"**/empty.html\", lambda route, request: handler(route, request, 2)\n    )\n    await context.route(\n        \"**/empty.html\", lambda route, request: handler(route, request, 3)\n    )\n\n    def handler4(route: Route, request: Request) -> None:\n        handler(route, request, 4)\n\n    await context.route(re.compile(\"empty.html\"), handler4)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [4]\n\n    intercepted = []\n    await context.unroute(re.compile(\"empty.html\"), handler4)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3]\n\n    intercepted = []\n    await context.unroute(\"**/empty.html\")\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [1]\n\n\nasync def test_route_should_yield_to_page_route(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.route(\n        \"**/empty.html\",\n        lambda route, request: asyncio.create_task(\n            route.fulfill(status=200, body=\"context\")\n        ),\n    )\n\n    page = await context.new_page()\n    await page.route(\n        \"**/empty.html\",\n        lambda route, request: asyncio.create_task(\n            route.fulfill(status=200, body=\"page\")\n        ),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert await response.text() == \"page\"\n\n\nasync def test_route_should_fall_back_to_context_route(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.route(\n        \"**/empty.html\",\n        lambda route, request: asyncio.create_task(\n            route.fulfill(status=200, body=\"context\")\n        ),\n    )\n\n    page = await context.new_page()\n    await page.route(\n        \"**/non-empty.html\",\n        lambda route, request: asyncio.create_task(\n            route.fulfill(status=200, body=\"page\")\n        ),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert await response.text() == \"context\"\n\n\nasync def test_should_support_set_cookie_header(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\",\n    default_same_site_cookie_value: str,\n) -> None:\n    context = await context_factory()\n    page = await context.new_page()\n    await page.route(\n        \"https://example.com/\",\n        lambda route: route.fulfill(\n            headers={\n                \"Set-Cookie\": \"name=value; domain=.example.com; Path=/\",\n            },\n            content_type=\"text/html\",\n            body=\"done\",\n        ),\n    )\n    await page.goto(\"https://example.com\")\n    cookies = await context.cookies()\n    assert len(cookies) == 1\n    assert cookies[0] == {\n        \"sameSite\": default_same_site_cookie_value,\n        \"name\": \"name\",\n        \"value\": \"value\",\n        \"domain\": \".example.com\",\n        \"path\": \"/\",\n        \"expires\": -1,\n        \"httpOnly\": False,\n        \"secure\": False,\n    }\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_should_ignore_secure_set_cookie_header_for_insecure_request(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\",\n) -> None:\n    context = await context_factory()\n    page = await context.new_page()\n    await page.route(\n        \"http://example.com/\",\n        lambda route: route.fulfill(\n            headers={\n                \"Set-Cookie\": \"name=value; domain=.example.com; Path=/; Secure\",\n            },\n            content_type=\"text/html\",\n            body=\"done\",\n        ),\n    )\n    await page.goto(\"http://example.com\")\n    cookies = await context.cookies()\n    assert len(cookies) == 0\n\n\nasync def test_should_use_set_cookie_header_in_future_requests(\n    context_factory: \"Callable[..., Awaitable[BrowserContext]]\",\n    server: Server,\n    default_same_site_cookie_value: str,\n) -> None:\n    context = await context_factory()\n    page = await context.new_page()\n\n    await page.route(\n        server.EMPTY_PAGE,\n        lambda route: route.fulfill(\n            headers={\n                \"Set-Cookie\": \"name=value\",\n            },\n            content_type=\"text/html\",\n            body=\"done\",\n        ),\n    )\n    await page.goto(server.EMPTY_PAGE)\n    assert await context.cookies() == [\n        {\n            \"sameSite\": default_same_site_cookie_value,\n            \"name\": \"name\",\n            \"value\": \"value\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n        }\n    ]\n\n    cookie = \"\"\n\n    def _handle_request(request: TestServerRequest) -> None:\n        nonlocal cookie\n        cookie = must(request.getHeader(\"cookie\"))\n        request.finish()\n\n    server.set_route(\"/foo.html\", _handle_request)\n    await page.goto(server.PREFIX + \"/foo.html\")\n    assert cookie == \"name=value\"\n\n\nasync def test_should_work_with_ignore_https_errors(\n    browser: Browser, https_server: Server\n) -> None:\n    context = await browser.new_context(ignore_https_errors=True)\n    page = await context.new_page()\n\n    await page.route(\"**/*\", lambda route: route.continue_())\n    response = await page.goto(https_server.EMPTY_PAGE)\n    assert must(response).status == 200\n    await context.close()\n\n\nasync def test_should_support_the_times_parameter_with_route_matching(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted: List[int] = []\n\n    async def _handle_request(route: Route) -> None:\n        intercepted.append(1)\n        await route.continue_()\n\n    await context.route(\"**/empty.html\", _handle_request, times=1)\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.EMPTY_PAGE)\n    assert len(intercepted) == 1\n\n\nasync def test_should_work_if_handler_with_times_parameter_was_removed_from_another_handler(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted = []\n\n    async def _handler(route: Route) -> None:\n        intercepted.append(\"first\")\n        await route.continue_()\n\n    await context.route(\"**/*\", _handler, times=1)\n\n    async def _handler2(route: Route) -> None:\n        intercepted.append(\"second\")\n        await context.unroute(\"**/*\", _handler)\n        await route.fallback()\n\n    await context.route(\"**/*\", _handler2)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [\"second\"]\n    intercepted.clear()\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [\"second\"]\n\n\nasync def test_should_support_async_handler_with_times(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    async def _handler(route: Route) -> None:\n        await asyncio.sleep(0.1)\n        await route.fulfill(\n            body=\"<html>intercepted</html>\",\n            content_type=\"text/html\",\n        )\n\n    await context.route(\"**/empty.html\", _handler, times=1)\n    await page.goto(server.EMPTY_PAGE)\n    await expect(page.locator(\"body\")).to_have_text(\"intercepted\")\n    await page.goto(server.EMPTY_PAGE)\n    await expect(page.locator(\"body\")).not_to_have_text(\"intercepted\")\n\n\nasync def test_should_override_post_body_with_empty_string(\n    context: BrowserContext, server: Server, page: Page\n) -> None:\n    await context.route(\n        \"**/empty.html\",\n        lambda route: route.continue_(\n            post_data=\"\",\n        ),\n    )\n\n    req = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        page.set_content(\n            \"\"\"\n            <script>\n            (async () => {\n                await fetch('%s', {\n                    method: 'POST',\n                    body: 'original',\n                });\n            })()\n            </script>\n            \"\"\"\n            % server.EMPTY_PAGE\n        ),\n    )\n\n    assert req[0].post_body == b\"\"\n\n\nasync def test_should_chain_fallback(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted: List[int] = []\n\n    async def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler1)\n\n    async def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler2)\n\n    async def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler3)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_chain_fallback_with_dynamic_url(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted: List[int] = []\n\n    async def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        await route.fallback(url=server.EMPTY_PAGE)\n\n    await context.route(\"**/bar\", _handler1)\n\n    async def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        await route.fallback(url=\"http://localhost/bar\")\n\n    await context.route(\"**/foo\", _handler2)\n\n    async def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        await route.fallback(url=\"http://localhost/foo\")\n\n    await context.route(\"**/empty.html\", _handler3)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_not_chain_fulfill(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    await context.route(\"**/empty.html\", handler)\n    await context.route(\n        \"**/empty.html\",\n        lambda route: asyncio.create_task(route.fulfill(status=200, body=\"fulfilled\")),\n    )\n    await context.route(\n        \"**/empty.html\", lambda route: asyncio.create_task(route.fallback())\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    body = await response.body()\n    assert body == b\"fulfilled\"\n    assert not failed[0]\n\n\nasync def test_should_not_chain_abort(\n    page: Page,\n    context: BrowserContext,\n    server: Server,\n    is_webkit: bool,\n    is_firefox: bool,\n) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    await context.route(\"**/empty.html\", handler)\n    await context.route(\n        \"**/empty.html\", lambda route: asyncio.create_task(route.abort())\n    )\n    await context.route(\n        \"**/empty.html\", lambda route: asyncio.create_task(route.fallback())\n    )\n\n    with pytest.raises(Error) as excinfo:\n        await page.goto(server.EMPTY_PAGE)\n    if is_webkit:\n        assert \"Blocked by Web Inspector\" in excinfo.value.message\n    elif is_firefox:\n        assert \"NS_ERROR_FAILURE\" in excinfo.value.message\n    else:\n        assert \"net::ERR_FAILED\" in excinfo.value.message\n    assert not failed[0]\n\n\nasync def test_should_chain_fallback_into_page(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted = []\n\n    def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\"**/empty.html\", _handler1)\n\n    def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\"**/empty.html\", _handler2)\n\n    def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        asyncio.create_task(route.fallback())\n\n    await context.route(\"**/empty.html\", _handler3)\n\n    def _handler4(route: Route) -> None:\n        intercepted.append(4)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler4)\n\n    def _handler5(route: Route) -> None:\n        intercepted.append(5)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler5)\n\n    def _handler6(route: Route) -> None:\n        intercepted.append(6)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler6)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [6, 5, 4, 3, 2, 1]\n\n\nasync def test_should_fall_back_async(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    intercepted = []\n\n    async def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        await asyncio.sleep(0.1)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler1)\n\n    async def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        await asyncio.sleep(0.1)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler2)\n\n    async def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        await asyncio.sleep(0.1)\n        await route.fallback()\n\n    await context.route(\"**/empty.html\", _handler3)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n"
  },
  {
    "path": "tests/async/test_browsercontext_service_worker_policy.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom playwright.async_api import Browser\nfrom tests.server import Server\n\n\nasync def test_should_allow_service_workers_by_default(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    await page.evaluate(\"() => window.activationPromise\")\n    await context.close()\n\n\nasync def test_block_blocks_service_worker_registration(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(service_workers=\"block\")\n    page = await context.new_page()\n    async with page.expect_console_message(\n        lambda m: \"Service Worker registration blocked by Playwright\" == m.text\n    ):\n        await page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    await context.close()\n"
  },
  {
    "path": "tests/async/test_browsercontext_storage_state.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nfrom pathlib import Path\n\nfrom playwright.async_api import Browser, BrowserContext, Page, StorageState\nfrom tests.server import Server\n\n\nasync def test_should_capture_local_storage(context: BrowserContext) -> None:\n    page1 = await context.new_page()\n    await page1.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fulfill(body=\"<html></html>\"))\n    )\n    await page1.goto(\"https://www.example.com\")\n    await page1.evaluate(\"localStorage['name1'] = 'value1'\")\n    await page1.goto(\"https://www.domain.com\")\n    await page1.evaluate(\"localStorage['name2'] = 'value2'\")\n\n    state = await context.storage_state()\n    origins = state[\"origins\"]\n    assert len(origins) == 2\n    assert origins[0] == {\n        \"origin\": \"https://www.domain.com\",\n        \"localStorage\": [{\"name\": \"name2\", \"value\": \"value2\"}],\n    }\n    assert origins[1] == {\n        \"origin\": \"https://www.example.com\",\n        \"localStorage\": [{\"name\": \"name1\", \"value\": \"value1\"}],\n    }\n\n\nasync def test_should_set_local_storage(browser: Browser) -> None:\n    storage_state: StorageState = {\n        \"origins\": [\n            {\n                \"origin\": \"https://www.example.com\",\n                \"localStorage\": [{\"name\": \"name1\", \"value\": \"value1\"}],\n            }\n        ]\n    }\n    # We intentionally hide the indexed_db part in our API for now\n    storage_state[\"origins\"][0][\"indexedDB\"] = [  # type: ignore\n        {\n            \"name\": \"db\",\n            \"version\": 42,\n            \"stores\": [\n                {\n                    \"name\": \"store\",\n                    \"autoIncrement\": False,\n                    \"records\": [{\"key\": \"bar\", \"value\": \"foo\"}],\n                    \"indexes\": [],\n                }\n            ],\n        }\n    ]\n    context = await browser.new_context(storage_state=storage_state)\n\n    page = await context.new_page()\n    await page.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fulfill(body=\"<html></html>\"))\n    )\n    await page.goto(\"https://www.example.com\")\n    local_storage = await page.evaluate(\"window.localStorage\")\n    assert local_storage == {\"name1\": \"value1\"}\n\n    indexed_db = await page.evaluate(\n        \"\"\"async () => {\n        return new Promise((resolve, reject) => {\n            const openRequest = indexedDB.open('db', 42);\n            openRequest.addEventListener('success', () => {\n                const db = openRequest.result;\n                const transaction = db.transaction('store', 'readonly');\n                const getRequest = transaction.objectStore('store').get('bar');\n                getRequest.addEventListener('success', () => resolve(getRequest.result));\n                getRequest.addEventListener('error', () => reject(getRequest.error));\n            });\n            openRequest.addEventListener('error', () => reject(openRequest.error));\n        });\n    }\"\"\"\n    )\n    assert indexed_db == \"foo\"\n    await context.close()\n\n\nasync def test_should_round_trip_through_the_file(\n    browser: Browser, context: BrowserContext, tmp_path: Path\n) -> None:\n    page1 = await context.new_page()\n    await page1.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(route.fulfill(body=\"<html></html>\")),\n    )\n    await page1.goto(\"https://www.example.com\")\n    await page1.evaluate(\n        \"\"\"() => {\n            localStorage[\"name1\"] = \"value1\"\n            document.cookie = \"username=John Doe\"\n            return document.cookie\n        }\"\"\"\n    )\n\n    path = tmp_path / \"storage-state.json\"\n    state = await context.storage_state(path=path)\n    with open(path, \"r\") as f:\n        written = json.load(f)\n    assert state == written\n\n    context2 = await browser.new_context(storage_state=path)\n    page2 = await context2.new_page()\n    await page2.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(route.fulfill(body=\"<html></html>\")),\n    )\n    await page2.goto(\"https://www.example.com\")\n    local_storage = await page2.evaluate(\"window.localStorage\")\n    assert local_storage == {\"name1\": \"value1\"}\n    cookie = await page2.evaluate(\"document.cookie\")\n    assert cookie == \"username=John Doe\"\n    await context2.close()\n\n\nasync def test_should_serialiser_storage_state_with_lone_surrogates(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"chars => window.localStorage.setItem('foo', String.fromCharCode(55934))\"\"\"\n    )\n    storage_state = await context.storage_state()\n    # 65533 is the Unicode replacement character\n    assert storage_state[\"origins\"][0][\"localStorage\"][0][\"value\"] == chr(65533)\n\n\nasync def test_should_serialise_indexed_db(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"async () => {\n            await new Promise((resolve, reject) => {\n                const openRequest = indexedDB.open('db', 42);\n                openRequest.onupgradeneeded = () => {\n                openRequest.result.createObjectStore('store');\n                };\n                openRequest.onsuccess = () => {\n                const request = openRequest.result.transaction('store', 'readwrite')\n                    .objectStore('store')\n                    .put('foo', 'bar');\n                request.addEventListener('success', resolve);\n                request.addEventListener('error', reject);\n                };\n            });\n        }\"\"\"\n    )\n    assert await page.context.storage_state() == {\"cookies\": [], \"origins\": []}\n    assert await page.context.storage_state(indexed_db=True) == {\n        \"cookies\": [],\n        \"origins\": [\n            {\n                \"origin\": f\"http://localhost:{server.PORT}\",\n                \"localStorage\": [],\n                \"indexedDB\": [\n                    {\n                        \"name\": \"db\",\n                        \"version\": 42,\n                        \"stores\": [\n                            {\n                                \"name\": \"store\",\n                                \"autoIncrement\": False,\n                                \"records\": [{\"key\": \"bar\", \"value\": \"foo\"}],\n                                \"indexes\": [],\n                            }\n                        ],\n                    }\n                ],\n            }\n        ],\n    }\n"
  },
  {
    "path": "tests/async/test_browsertype_connect.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport os\nimport re\nfrom pathlib import Path\nfrom typing import AsyncContextManager, Callable\n\nimport pytest\n\nfrom playwright.async_api import BrowserType, Error, Playwright, Route, expect\nfrom tests.conftest import RemoteServer\nfrom tests.server import Server, TestServerRequest, WebSocketProtocol\nfrom tests.utils import chromium_version_less_than\n\nfrom .conftest import TraceViewerPage\n\n\nasync def test_should_print_custom_ws_close_error(\n    server: Server, browser_type: BrowserType\n) -> None:\n    def _handle_ws(ws: WebSocketProtocol) -> None:\n        def _onMessage(payload: bytes, isBinary: bool) -> None:\n            ws.sendClose(code=4123, reason=\"Oh my!\")\n\n        setattr(ws, \"onMessage\", _onMessage)\n\n    server.once_web_socket_connection(_handle_ws)\n    with pytest.raises(Error, match=\"Oh my!\"):\n        await browser_type.connect(f\"ws://localhost:{server.PORT}/ws\")\n\n\nasync def test_browser_type_connect_should_be_able_to_reconnect_to_a_browser(\n    server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    browser_context = await browser.new_context()\n    assert len(browser_context.pages) == 0\n    page = await browser_context.new_page()\n    assert len(browser_context.pages) == 1\n    assert await page.evaluate(\"11 * 11\") == 121\n    await page.goto(server.EMPTY_PAGE)\n    await browser.close()\n\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    browser_context = await browser.new_context()\n    page = await browser_context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await browser.close()\n\n\nasync def test_browser_type_connect_should_be_able_to_connect_two_browsers_at_the_same_time(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser1 = await browser_type.connect(remote_server.ws_endpoint)\n    assert len(browser1.contexts) == 0\n    await browser1.new_context()\n    assert len(browser1.contexts) == 1\n\n    browser2 = await browser_type.connect(remote_server.ws_endpoint)\n    assert len(browser2.contexts) == 0\n    await browser2.new_context()\n    assert len(browser2.contexts) == 1\n    assert len(browser1.contexts) == 1\n\n    await browser1.close()\n    page2 = await browser2.new_page()\n    # original browser should still work\n    assert await page2.evaluate(\"7 * 6\") == 42\n\n    await browser2.close()\n\n\nasync def test_browser_type_connect_disconnected_event_should_be_emitted_when_browser_is_closed_or_server_is_closed(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser1 = await browser_type.connect(remote.ws_endpoint)\n    browser2 = await browser_type.connect(remote.ws_endpoint)\n\n    disconnected1 = []\n    disconnected2 = []\n    browser1.on(\"disconnected\", lambda _: disconnected1.append(True))\n    browser2.on(\"disconnected\", lambda _: disconnected2.append(True))\n\n    page2 = await browser2.new_page()\n\n    await browser1.close()\n    assert len(disconnected1) == 1\n    assert len(disconnected2) == 0\n\n    remote.kill()\n    assert len(disconnected1) == 1\n\n    with pytest.raises(Error):\n        # Tickle connection so that it gets a chance to dispatch disconnect event.\n        await page2.title()\n    assert len(disconnected2) == 1\n\n\nasync def test_browser_type_connect_disconnected_event_should_be_emitted_when_remote_killed_connection(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = await browser_type.connect(remote.ws_endpoint)\n\n    disconnected = []\n    browser.on(\"disconnected\", lambda _: disconnected.append(True))\n    page = await browser.new_page()\n    remote.kill()\n    with pytest.raises(Error):\n        await page.title()\n    assert len(disconnected) == 1\n\n\nasync def test_browser_type_disconnected_event_should_have_browser_as_argument(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    event_payloads = []\n    browser.on(\"disconnected\", lambda b: event_payloads.append(b))\n    await browser.close()\n    assert event_payloads[0] == browser\n\n\nasync def test_browser_type_connect_set_browser_connected_state(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    assert browser.is_connected()\n    await browser.close()\n    assert browser.is_connected() is False\n\n\nasync def test_browser_type_connect_should_throw_when_used_after_is_connected_returns_false(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    page = await browser.new_page()\n\n    remote_server.kill()\n\n    with pytest.raises(Error) as exc_info:\n        await page.evaluate(\"1 + 1\")\n    assert \"has been closed\" in exc_info.value.message\n    assert browser.is_connected() is False\n\n\nasync def test_browser_type_connect_should_reject_navigation_when_browser_closes(\n    server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    page = await browser.new_page()\n    await browser.close()\n\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/one-style.html\")\n    assert \"has been closed\" in exc_info.value.message\n\n\nasync def test_should_not_allow_getting_the_path(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer], server: Server\n) -> None:\n    def handle_download(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.setHeader(\"Content-Disposition\", \"attachment\")\n        request.write(b\"Hello world\")\n        request.finish()\n\n    server.set_route(\"/download\", handle_download)\n\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    with pytest.raises(Error) as exc:\n        await download.path()\n    assert (\n        exc.value.message\n        == \"Path is not available when using browser_type.connect(). Use save_as() to save a local copy.\"\n    )\n    remote_server.kill()\n\n\nasync def test_prevent_getting_video_path(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    tmp_path: Path,\n    server: Server,\n) -> None:\n    remote_server = launch_server()\n    browser = await browser_type.connect(remote_server.ws_endpoint)\n    page = await browser.new_page(record_video_dir=tmp_path)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await browser.close()\n    assert page.video\n    with pytest.raises(Error) as exc:\n        await page.video.path()\n    assert (\n        exc.value.message\n        == \"Path is not available when using browserType.connect(). Use save_as() to save a local copy.\"\n    )\n    remote_server.kill()\n\n\nasync def test_connect_to_closed_server_without_hangs(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    remote_server.kill()\n    with pytest.raises(Error) as exc:\n        await browser_type.connect(remote_server.ws_endpoint)\n    assert \"WebSocket error: \" in exc.value.message\n\n\nasync def test_should_fulfill_with_global_fetch_result(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    playwright: Playwright,\n    server: Server,\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    async def handle_request(route: Route) -> None:\n        request = await playwright.request.new_context()\n        response = await request.get(server.PREFIX + \"/simple.json\")\n        await route.fulfill(response=response)\n        await request.dispose()\n\n    await page.route(\"**/*\", handle_request)\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    assert await response.json() == {\"foo\": \"bar\"}\n\n    remote.kill()\n\n\nasync def test_should_upload_large_file(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    server: Server,\n    tmp_path: Path,\n) -> None:\n    remote = launch_server()\n\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    await page.goto(server.PREFIX + \"/input/fileupload.html\")\n    large_file_path = tmp_path / \"200MB.zip\"\n    data = b\"A\" * 1024\n    with large_file_path.open(\"wb\") as f:\n        for i in range(0, 200 * 1024 * 1024, len(data)):\n            f.write(data)\n    input = page.locator('input[type=\"file\"]')\n    events = await input.evaluate_handle(\n        \"\"\"\n        e => {\n            const events = [];\n            e.addEventListener('input', () => events.push('input'));\n            e.addEventListener('change', () => events.push('change'));\n            return events;\n        }\n    \"\"\"\n    )\n\n    await input.set_input_files(large_file_path)\n    assert await input.evaluate(\"e => e.files[0].name\") == \"200MB.zip\"\n    assert await events.evaluate(\"e => e\") == [\"input\", \"change\"]\n\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/upload\"),\n        page.click(\"input[type=submit]\", timeout=60_000),\n    )\n\n    contents = request.args[b\"file1\"][0]\n    assert len(contents) == 200 * 1024 * 1024\n    assert contents[:1024] == data\n    # flake8: noqa: E203\n    assert contents[len(contents) - 1024 :] == data\n    assert request.post_body\n    match = re.search(\n        rb'^.*Content-Disposition: form-data; name=\"(?P<name>.*)\"; filename=\"(?P<filename>.*)\".*$',\n        request.post_body,\n        re.MULTILINE,\n    )\n    assert match\n    assert match.group(\"name\") == b\"file1\"\n    assert match.group(\"filename\") == b\"200MB.zip\"\n\n\nasync def test_should_record_trace_with_source(\n    launch_server: Callable[[], RemoteServer],\n    server: Server,\n    tmp_path: Path,\n    browser_type: BrowserType,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    remote = launch_server()\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    await context.tracing.start(sources=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button>Click</button>\")\n    await page.click(\"'Click'\")\n    path = tmp_path / \"trace1.zip\"\n    await context.tracing.stop(path=path)\n\n    await context.close()\n    await browser.close()\n\n    async with show_trace_viewer(path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n            ]\n        )\n        await trace_viewer.show_source_tab()\n        await expect(trace_viewer.stack_frames).to_contain_text(\n            [\n                re.compile(r\"test_should_record_trace_with_source\"),\n            ]\n        )\n        await trace_viewer.select_action(\"Set content\")\n        # Check that the source file is shown\n        await expect(\n            trace_viewer.page.locator(\".source-tab-file-name\")\n        ).to_have_attribute(\"title\", re.compile(r\".*test_browsertype_connect\\.py\"))\n        await expect(trace_viewer.page.locator(\".source-line-running\")).to_contain_text(\n            'page.set_content(\"<button>Click</button>\")'\n        )\n\n\nasync def test_should_record_trace_with_relative_trace_path(\n    launch_server: Callable[[], RemoteServer],\n    server: Server,\n    browser_type: BrowserType,\n) -> None:\n    remote = launch_server()\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    await context.tracing.start(sources=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button>Click</button>\")\n    await page.click(\"'Click'\")\n    try:\n        await context.tracing.stop(path=\"trace1.zip\")\n\n        await context.close()\n        await browser.close()\n\n        # make sure trace1.zip exists\n        assert Path(\"trace1.zip\").exists()\n    finally:\n        Path(\"trace1.zip\").unlink()\n\n\nasync def test_set_input_files_should_preserve_last_modified_timestamp(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    assetdir: Path,\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    await page.set_content(\"<input type=file multiple=true/>\")\n    input = page.locator(\"input\")\n    files = [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]\n    await input.set_input_files([assetdir / file for file in files])\n    assert await input.evaluate(\"input => [...input.files].map(f => f.name)\") == files\n    timestamps = await input.evaluate(\n        \"input => [...input.files].map(f => f.lastModified)\"\n    )\n    expected_timestamps = [os.path.getmtime(assetdir / file) * 1000 for file in files]\n\n    # On Linux browser sometimes reduces the timestamp by 1ms: 1696272058110.0715  -> 1696272058109 or even\n    # rounds it to seconds in WebKit: 1696272058110 -> 1696272058000.\n    for i in range(len(timestamps)):\n        assert abs(timestamps[i] - expected_timestamps[i]) < 1000\n\n\nasync def test_should_upload_a_folder(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    server: Server,\n    tmp_path: Path,\n    browser_name: str,\n    browser_version: str,\n    headless: bool,\n) -> None:\n    remote = launch_server()\n\n    browser = await browser_type.connect(remote.ws_endpoint)\n    context = await browser.new_context()\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/input/folderupload.html\")\n    input = await page.query_selector(\"input\")\n    assert input\n    dir = tmp_path / \"file-upload-test\"\n    dir.mkdir()\n    (dir / \"file1.txt\").write_text(\"file1 content\")\n    (dir / \"file2\").write_text(\"file2 content\")\n    (dir / \"sub-dir\").mkdir()\n    (dir / \"sub-dir\" / \"really.txt\").write_text(\"sub-dir file content\")\n    await input.set_input_files(dir)\n    assert set(\n        await input.evaluate(\"e => [...e.files].map(f => f.webkitRelativePath)\")\n    ) == set(\n        [\n            \"file-upload-test/file1.txt\",\n            \"file-upload-test/file2\",\n            # https://issues.chromium.org/issues/345393164\n            *(\n                []\n                if browser_name == \"chromium\"\n                and headless\n                and chromium_version_less_than(browser_version, \"127.0.6533.0\")\n                else [\"file-upload-test/sub-dir/really.txt\"]\n            ),\n        ]\n    )\n    webkit_relative_paths = await input.evaluate(\n        \"e => [...e.files].map(f => f.webkitRelativePath)\"\n    )\n    for i, webkit_relative_path in enumerate(webkit_relative_paths):\n        content = await input.evaluate(\n            \"\"\"(e, i) => {\n            const reader = new FileReader();\n            const promise = new Promise(fulfill => reader.onload = fulfill);\n            reader.readAsText(e.files[i]);\n            return promise.then(() => reader.result);\n        }\"\"\",\n            i,\n        )\n        assert content == (dir / \"..\" / webkit_relative_path).read_text()\n"
  },
  {
    "path": "tests/async/test_browsertype_connect_cdp.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Dict\n\nimport pytest\nimport requests\n\nfrom playwright.async_api import BrowserType, Error\nfrom tests.server import Server, WebSocketProtocol, find_free_port\n\npytestmark = pytest.mark.only_browser(\"chromium\")\n\n\nasync def test_connect_to_an_existing_cdp_session(\n    launch_arguments: Dict, browser_type: BrowserType\n) -> None:\n    port = find_free_port()\n    browser_server = await browser_type.launch(\n        **launch_arguments, args=[f\"--remote-debugging-port={port}\"]\n    )\n    cdp_browser = await browser_type.connect_over_cdp(f\"http://127.0.0.1:{port}\")\n    assert len(cdp_browser.contexts) == 1\n    await cdp_browser.close()\n    await browser_server.close()\n\n\nasync def test_connect_to_an_existing_cdp_session_twice(\n    launch_arguments: Dict, browser_type: BrowserType, server: Server\n) -> None:\n    port = find_free_port()\n    browser_server = await browser_type.launch(\n        **launch_arguments, args=[f\"--remote-debugging-port={port}\"]\n    )\n    endpoint_url = f\"http://127.0.0.1:{port}\"\n    cdp_browser1 = await browser_type.connect_over_cdp(endpoint_url)\n    cdp_browser2 = await browser_type.connect_over_cdp(endpoint_url)\n    assert len(cdp_browser1.contexts) == 1\n    page1 = await cdp_browser1.contexts[0].new_page()\n    await page1.goto(server.EMPTY_PAGE)\n\n    assert len(cdp_browser2.contexts) == 1\n    page2 = await cdp_browser2.contexts[0].new_page()\n    await page2.goto(server.EMPTY_PAGE)\n\n    assert len(cdp_browser1.contexts[0].pages) == 2\n    assert len(cdp_browser2.contexts[0].pages) == 2\n\n    await cdp_browser1.close()\n    await cdp_browser2.close()\n    await browser_server.close()\n\n\ndef _ws_endpoint_from_url(url: str) -> str:\n    response = requests.get(url)\n    assert response.ok\n    response_body = response.json()\n    return response_body[\"webSocketDebuggerUrl\"]\n\n\nasync def test_conect_over_a_ws_endpoint(\n    launch_arguments: Dict, browser_type: BrowserType, server: Server\n) -> None:\n    port = find_free_port()\n    browser_server = await browser_type.launch(\n        **launch_arguments, args=[f\"--remote-debugging-port={port}\"]\n    )\n    ws_endpoint = _ws_endpoint_from_url(f\"http://127.0.0.1:{port}/json/version/\")\n\n    cdp_browser1 = await browser_type.connect_over_cdp(ws_endpoint)\n    assert len(cdp_browser1.contexts) == 1\n    await cdp_browser1.close()\n\n    cdp_browser2 = await browser_type.connect_over_cdp(ws_endpoint)\n    assert len(cdp_browser2.contexts) == 1\n    await cdp_browser2.close()\n    await browser_server.close()\n\n\nasync def test_connect_over_cdp_passing_header_works(\n    browser_type: BrowserType, server: Server\n) -> None:\n    server.send_on_web_socket_connection(b\"incoming\")\n    request = asyncio.create_task(server.wait_for_request(\"/ws\"))\n    with pytest.raises(Error):\n        await browser_type.connect_over_cdp(\n            f\"ws://127.0.0.1:{server.PORT}/ws\", headers={\"foo\": \"bar\"}\n        )\n    assert (await request).getHeader(\"foo\") == \"bar\"\n\n\nasync def test_should_print_custom_ws_close_error(\n    browser_type: BrowserType, server: Server\n) -> None:\n    def _handle_ws(ws: WebSocketProtocol) -> None:\n        def _onMessage(payload: bytes, isBinary: bool) -> None:\n            ws.sendClose(code=4123, reason=\"Oh my!\")\n\n        setattr(ws, \"onMessage\", _onMessage)\n\n    server.once_web_socket_connection(_handle_ws)\n    with pytest.raises(Error, match=\"Browser logs:\\n\\nOh my!\\n\"):\n        await browser_type.connect_over_cdp(\n            f\"ws://127.0.0.1:{server.PORT}/ws\", headers={\"foo\": \"bar\"}\n        )\n"
  },
  {
    "path": "tests/async/test_cdp_session.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error, Page\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_work(page: Page) -> None:\n    client = await page.context.new_cdp_session(page)\n    events = []\n    client.on(\"Runtime.consoleAPICalled\", lambda params: events.append(params))\n    await client.send(\"Runtime.enable\")\n    result = await client.send(\n        \"Runtime.evaluate\",\n        {\"expression\": \"window.foo = 'bar'; console.log('log'); 'result'\"},\n    )\n    assert result == {\"result\": {\"type\": \"string\", \"value\": \"result\"}}\n    foo = await page.evaluate(\"() => window.foo\")\n    assert foo == \"bar\"\n    assert events[0][\"args\"][0][\"value\"] == \"log\"\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_receive_events(page: Page, server: Server) -> None:\n    client = await page.context.new_cdp_session(page)\n    await client.send(\"Network.enable\")\n    events = []\n    client.on(\"Network.requestWillBeSent\", lambda event: events.append(event))\n    await page.goto(server.EMPTY_PAGE)\n    assert len(events) == 1\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_be_able_to_detach_session(page: Page) -> None:\n    client = await page.context.new_cdp_session(page)\n    await client.send(\"Runtime.enable\")\n    eval_response = await client.send(\n        \"Runtime.evaluate\", {\"expression\": \"1 + 2\", \"returnByValue\": True}\n    )\n    assert eval_response[\"result\"][\"value\"] == 3\n    await client.detach()\n    with pytest.raises(Error) as exc_info:\n        await client.send(\n            \"Runtime.evaluate\", {\"expression\": \"3 + 1\", \"returnByValue\": True}\n        )\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_not_break_page_close(browser: Browser) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    session = await page.context.new_cdp_session(page)\n    await session.detach()\n    await page.close()\n    await context.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_detach_when_page_closes(browser: Browser) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    session = await context.new_cdp_session(page)\n    await page.close()\n    with pytest.raises(Error):\n        await session.detach()\n    await context.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_work_with_main_frame(browser: Browser) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    client = await context.new_cdp_session(page.main_frame)\n    await client.send(\"Runtime.enable\")\n    await client.send(\n        \"Runtime.evaluate\",\n        {\"expression\": \"window.foo = 'bar'\"},\n    )\n    assert await page.evaluate(\"() => window.foo\") == \"bar\"\n"
  },
  {
    "path": "tests/async/test_check.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page\n\n\nasync def test_check_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    await page.check(\"input\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_not_check_the_checked_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    await page.check(\"input\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_uncheck_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    await page.uncheck(\"input\")\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_not_uncheck_the_unchecked_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    await page.uncheck(\"input\")\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_check_the_box_by_label(page: Page) -> None:\n    await page.set_content(\n        '<label for=\"checkbox\"><input id=\"checkbox\" type=\"checkbox\"></input></label>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_the_box_outside_label(page: Page) -> None:\n    await page.set_content(\n        '<label for=\"checkbox\">Text</label><div><input id=\"checkbox\" type=\"checkbox\"></input></div>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_the_box_inside_label_without_id(page: Page) -> None:\n    await page.set_content(\n        '<label>Text<span><input id=\"checkbox\" type=\"checkbox\"></input></span></label>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_radio(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <input type='radio'>one</input>\n      <input id='two' type='radio'>two</input>\n      <input type='radio'>three</input>\"\"\"\n    )\n    await page.check(\"#two\")\n    assert await page.evaluate(\"two.checked\")\n\n\nasync def test_check_the_box_by_aria_role(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<div role='checkbox' id='checkbox'>CHECKBOX</div>\n      <script>\n        checkbox.addEventListener('click', () => checkbox.setAttribute('aria-checked', 'true'))\n      </script>\"\"\"\n    )\n    await page.check(\"div\")\n    assert await page.evaluate(\"checkbox.getAttribute ('aria-checked')\")\n"
  },
  {
    "path": "tests/async/test_chromium_tracing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.async_api import Browser, Page\nfrom tests.server import Server\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_output_a_trace(\n    browser: Browser, page: Page, server: Server, tmp_path: Path\n) -> None:\n    output_file = tmp_path / \"trace.json\"\n    await browser.start_tracing(page=page, screenshots=True, path=output_file)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await browser.stop_tracing()\n    assert os.path.getsize(output_file) > 0\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_create_directories_as_needed(\n    browser: Browser, page: Page, server: Server, tmp_path: Path\n) -> None:\n    output_file = tmp_path / \"these\" / \"are\" / \"directories\" / \"trace.json\"\n    await browser.start_tracing(page=page, screenshots=True, path=output_file)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await browser.stop_tracing()\n    assert os.path.getsize(output_file) > 0\n\n\nasync def rafraf(target: Page, count: int = 1) -> None:\n    for _ in range(count):\n        await target.evaluate(\n            \"async () => await new Promise(f => window.requestAnimationFrame(() => window.requestAnimationFrame(f)));\"\n        )\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_run_with_custom_categories_if_provided(\n    browser: Browser, page: Page, tmp_path: Path\n) -> None:\n    output_file = tmp_path / \"trace.json\"\n    await browser.start_tracing(\n        page=page,\n        path=output_file,\n        categories=[\"disabled-by-default-cc.debug\"],\n    )\n    await rafraf(page)\n    await browser.stop_tracing()\n    with open(output_file, mode=\"r\") as of:\n        trace_json = json.load(of)\n        trace_config = trace_json[\"metadata\"].get(\"trace-config\")\n        trace_events = trace_json[\"traceEvents\"]\n        assert (\n            trace_config is not None and \"disabled-by-default-cc.debug\" in trace_config\n        ) or any(\n            event.get(\"cat\") == \"disabled-by-default-cc.debug\" for event in trace_events\n        )\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_throw_if_tracing_on_two_pages(\n    browser: Browser, page: Page, tmp_path: Path\n) -> None:\n    output_file_1 = tmp_path / \"trace1.json\"\n    await browser.start_tracing(page=page, screenshots=True, path=output_file_1)\n    output_file_2 = tmp_path / \"trace2.json\"\n    with pytest.raises(Exception):\n        await browser.start_tracing(page=page, screenshots=True, path=output_file_2)\n    await browser.stop_tracing()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_return_a_buffer(\n    browser: Browser, page: Page, server: Server, tmp_path: Path\n) -> None:\n    output_file = tmp_path / \"trace.json\"\n    await browser.start_tracing(page=page, path=output_file, screenshots=True)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    value = await browser.stop_tracing()\n    with open(output_file, mode=\"r\") as trace_file:\n        assert trace_file.read() == value.decode()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_work_without_options(\n    browser: Browser, page: Page, server: Server\n) -> None:\n    await browser.start_tracing()\n    await page.goto(server.PREFIX + \"/grid.html\")\n    trace = await browser.stop_tracing()\n    assert trace\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_support_a_buffer_without_a_path(\n    browser: Browser, page: Page, server: Server\n) -> None:\n    await browser.start_tracing(page=page, screenshots=True)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await rafraf(page)\n    trace = await browser.stop_tracing()\n    assert \"screenshot\" in trace.decode()\n"
  },
  {
    "path": "tests/async/test_click.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Optional\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error, Page, Playwright, TimeoutError\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def give_it_a_chance_to_click(page: Page) -> None:\n    for _ in range(5):\n        await page.evaluate(\n            \"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))\"\n        )\n\n\nasync def test_click_the_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_svg(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <svg height=\"100\" width=\"100\">\n            <circle onclick=\"javascript:window.__CLICKED=42\" cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n        </svg>\n        \"\"\"\n    )\n    await page.click(\"circle\")\n    assert await page.evaluate(\"window.__CLICKED\") == 42\n\n\nasync def test_click_the_button_if_window_node_is_removed(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\"() => delete window.Node\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_click_on_a_span_with_an_inline_element_inside(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <style>\n        span::before {\n            content: 'q'\n        }\n        </style>\n        <span onclick='javascript:window.CLICKED=42'></span>\n        \"\"\"\n    )\n    await page.click(\"span\")\n    assert await page.evaluate(\"window.CLICKED\") == 42\n\n\nasync def test_click_not_throw_when_page_closes(browser: Browser) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    try:\n        await asyncio.gather(\n            page.close(),\n            page.mouse.click(1, 2),\n        )\n    except Error:\n        pass\n    await context.close()\n\n\nasync def test_click_the_button_after_navigation(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_click_the_button_after_a_cross_origin_navigation_(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_click_with_disabled_javascript(browser: Browser, server: Server) -> None:\n    context = await browser.new_context(java_script_enabled=False)\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/wrappedlink.html\")\n    async with page.expect_navigation():\n        await page.click(\"a\")\n    assert page.url == server.PREFIX + \"/wrappedlink.html#clicked\"\n    await context.close()\n\n\nasync def test_click_when_one_of_inline_box_children_is_outside_of_viewport(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <style>\n        i {\n            position: absolute\n            top: -1000px\n        }\n        </style>\n        <span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>\n        \"\"\"\n    )\n    await page.click(\"span\")\n    assert await page.evaluate(\"() => window.CLICKED\") == 42\n\n\nasync def test_select_the_text_by_triple_clicking(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    text = \"This is the text that we are going to try to select. Let's see how it goes.\"\n    await page.fill(\"textarea\", text)\n    await page.click(\"textarea\", click_count=3)\n    assert (\n        await page.evaluate(\n            \"\"\"() => {\n                textarea = document.querySelector('textarea')\n                return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd)\n            }\"\"\"\n        )\n        == text\n    )\n\n\nasync def test_click_offscreen_buttons(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/offscreenbuttons.html\")\n    messages = []\n    page.on(\"console\", lambda msg: messages.append(msg.text))\n    for i in range(11):\n        # We might've scrolled to click a button - reset to (0, 0).\n        await page.evaluate(\"window.scrollTo(0, 0)\")\n        await page.click(f\"#btn{i}\")\n    assert messages == [\n        \"button #0 clicked\",\n        \"button #1 clicked\",\n        \"button #2 clicked\",\n        \"button #3 clicked\",\n        \"button #4 clicked\",\n        \"button #5 clicked\",\n        \"button #6 clicked\",\n        \"button #7 clicked\",\n        \"button #8 clicked\",\n        \"button #9 clicked\",\n        \"button #10 clicked\",\n    ]\n\n\nasync def test_waitFor_visible_when_already_visible(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_wait_with_force(page: Page, server: Server) -> None:\n    error: Optional[Error] = None\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.style.display = 'none'\")\n    try:\n        await page.click(\"button\", force=True)\n    except Error as e:\n        error = e\n    assert error\n    assert \"Element is not visible\" in error.message\n    assert await page.evaluate(\"result\") == \"Was not clicked\"\n\n\nasync def test_wait_for_display_none_to_be_gone(page: Page, server: Server) -> None:\n    done = []\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.style.display = 'none'\")\n\n    async def click() -> None:\n        await page.click(\"button\", timeout=0)\n        done.append(True)\n\n    clicked = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"result\") == \"Was not clicked\"\n    assert done == []\n    await page.eval_on_selector(\"button\", \"b => b.style.display = 'block'\")\n    await clicked\n    assert done == [True]\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_wait_for_visibility_hidden_to_be_gone(\n    page: Page, server: Server\n) -> None:\n    done = []\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.style.visibility = 'hidden'\")\n\n    async def click() -> None:\n        await page.click(\"button\", timeout=0)\n        done.append(True)\n\n    clicked = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"result\") == \"Was not clicked\"\n    assert done == []\n    await page.eval_on_selector(\"button\", \"b => b.style.visibility = 'visible'\")\n    await clicked\n    assert done == [True]\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_timeout_waiting_for_display_none_to_be_gone(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.style.display = 'none'\")\n    try:\n        await page.click(\"button\", timeout=5000)\n    except Error as e:\n        error = e\n    assert \"Timeout 5000ms exceeded\" in error.message\n    assert \"waiting for element to be visible, enabled and stable\" in error.message\n    assert \"element is not visible\" in error.message\n    assert \"retrying click action\" in error.message\n\n\nasync def test_timeout_waiting_for_visbility_hidden_to_be_gone(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.style.visibility = 'hidden'\")\n    try:\n        await page.click(\"button\", timeout=5000)\n    except Error as e:\n        error = e\n    assert \"Timeout 5000ms exceeded\" in error.message\n    assert \"waiting for element to be visible, enabled and stable\" in error.message\n    assert \"element is not visible\" in error.message\n    assert \"retrying click action\" in error.message\n\n\nasync def test_waitFor_visible_when_parent_is_hidden(\n    page: Page, server: Server\n) -> None:\n    done = []\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"b => b.parentElement.style.display = 'none'\")\n\n    async def click() -> None:\n        await page.click(\"button\", timeout=0)\n        done.append(True)\n\n    clicked = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert done == []\n    await page.eval_on_selector(\n        \"button\", \"b => b.parentElement.style.display = 'block'\"\n    )\n    await clicked\n    assert done == [True]\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_wrapped_links(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/wrappedlink.html\")\n    await page.click(\"a\")\n    assert await page.evaluate(\"window.__clicked\")\n\n\nasync def test_click_on_checkbox_input_and_toggle(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/checkbox.html\")\n    assert await page.evaluate(\"() => result.check\") is None\n    await page.click(\"input#agree\")\n    assert await page.evaluate(\"result.check\")\n    assert await page.evaluate(\"result.events\") == [\n        \"mouseover\",\n        \"mouseenter\",\n        \"mousemove\",\n        \"mousedown\",\n        \"mouseup\",\n        \"click\",\n        \"input\",\n        \"change\",\n    ]\n    await page.click(\"input#agree\")\n    assert await page.evaluate(\"result.check\") is False\n\n\nasync def test_click_on_checkbox_label_and_toggle(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/checkbox.html\")\n    assert await page.evaluate(\"result.check\") is None\n    await page.click('label[for=\"agree\"]')\n    assert await page.evaluate(\"result.check\")\n    assert await page.evaluate(\"result.events\") == [\n        \"click\",\n        \"input\",\n        \"change\",\n    ]\n    await page.click('label[for=\"agree\"]')\n    assert await page.evaluate(\"result.check\") is False\n\n\nasync def test_not_hang_with_touch_enabled_viewports(\n    playwright: Playwright, browser: Browser\n) -> None:\n    iphone_6 = playwright.devices[\"iPhone 6\"]\n    context = await browser.new_context(\n        viewport=iphone_6[\"viewport\"], has_touch=iphone_6[\"has_touch\"]\n    )\n    page = await context.new_page()\n    await page.mouse.down()\n    await page.mouse.move(100, 10)\n    await page.mouse.up()\n    await context.close()\n\n\nasync def test_scroll_and_click_the_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    await page.click(\"#button-5\")\n    assert (\n        await page.evaluate(\"document.querySelector('#button-5').textContent\")\n        == \"clicked\"\n    )\n    await page.click(\"#button-80\")\n    assert (\n        await page.evaluate(\"document.querySelector('#button-80').textContent\")\n        == \"clicked\"\n    )\n\n\nasync def test_double_click_the_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\n        \"\"\"() => {\n            window.double = false;\n            button = document.querySelector('button');\n            button.addEventListener('dblclick', event => window.double = true);\n        }\"\"\"\n    )\n\n    await page.dblclick(\"button\")\n    assert await page.evaluate(\"double\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_a_partially_obscured_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\n        \"\"\"() => {\n            button = document.querySelector('button');\n            button.textContent = 'Some really long text that will go offscreen';\n            button.style.position = 'absolute';\n            button.style.left = '368px';\n        }\"\"\"\n    )\n\n    await page.click(\"button\")\n    assert await page.evaluate(\"() => window.result\") == \"Clicked\"\n\n\nasync def test_click_a_rotated_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/rotatedButton.html\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_fire_contextmenu_event_on_right_click(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    await page.click(\"#button-8\", button=\"right\")\n    assert (\n        await page.evaluate(\"document.querySelector('#button-8').textContent\")\n        == \"context menu\"\n    )\n\n\nasync def test_click_links_which_cause_navigation(page: Page, server: Server) -> None:\n    await page.set_content(f'<a href=\"{server.EMPTY_PAGE}\">empty.html</a>')\n    # This await should not hang.\n    await page.click(\"a\")\n\n\nasync def test_click_the_button_inside_an_iframe(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<div style=\"width:100px;height:100px\">spacer</div>')\n    await utils.attach_frame(page, \"button-test\", server.PREFIX + \"/input/button.html\")\n    frame = page.frames[1]\n    button = await frame.query_selector(\"button\")\n    assert button\n    await button.click()\n    assert await frame.evaluate(\"window.result\") == \"Clicked\"\n\n\nasync def test_click_the_button_with_device_scale_factor_set(\n    browser: Browser, server: Server, utils: Utils\n) -> None:\n    context = await browser.new_context(\n        viewport={\"width\": 400, \"height\": 400}, device_scale_factor=5\n    )\n    page = await context.new_page()\n    assert await page.evaluate(\"window.devicePixelRatio\") == 5\n    await page.set_content('<div style=\"width:100px;height:100px\">spacer</div>')\n    await utils.attach_frame(page, \"button-test\", server.PREFIX + \"/input/button.html\")\n    frame = page.frames[1]\n    button = await frame.query_selector(\"button\")\n    assert button\n    await button.click()\n    assert await frame.evaluate(\"window.result\") == \"Clicked\"\n    await context.close()\n\n\nasync def test_click_the_button_with_px_border_with_offset(\n    page: Page, server: Server, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"button => button.style.borderWidth = '8px'\")\n    await page.click(\"button\", position={\"x\": 20, \"y\": 10})\n    assert await page.evaluate(\"result\") == \"Clicked\"\n    # Safari reports border-relative offsetX/offsetY.\n    assert await page.evaluate(\"offsetX\") == 20 + 8 if is_webkit else 20\n    assert await page.evaluate(\"offsetY\") == 10 + 8 if is_webkit else 10\n\n\nasync def test_click_the_button_with_em_border_with_offset(\n    page: Page, server: Server, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"button => button.style.borderWidth = '2em'\")\n    await page.eval_on_selector(\"button\", \"button => button.style.fontSize = '12px'\")\n    await page.click(\"button\", position={\"x\": 20, \"y\": 10})\n    assert await page.evaluate(\"result\") == \"Clicked\"\n    # Safari reports border-relative offsetX/offsetY.\n    assert await page.evaluate(\"offsetX\") == 12 * 2 + 20 if is_webkit else 20\n    assert await page.evaluate(\"offsetY\") == 12 * 2 + 10 if is_webkit else 10\n\n\nasync def test_click_a_very_large_button_with_offset(\n    page: Page, server: Server, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\"button\", \"button => button.style.borderWidth = '8px'\")\n    await page.eval_on_selector(\n        \"button\", \"button => button.style.height = button.style.width = '2000px'\"\n    )\n    await page.click(\"button\", position={\"x\": 1900, \"y\": 1910})\n    assert await page.evaluate(\"() => window.result\") == \"Clicked\"\n    # Safari reports border-relative offsetX/offsetY.\n    assert await page.evaluate(\"() => offsetX\") == 1900 + 8 if is_webkit else 1900\n    assert await page.evaluate(\"() => offsetY\") == 1910 + 8 if is_webkit else 1910\n\n\nasync def test_click_a_button_in_scrolling_container_with_offset(\n    page: Page, server: Server, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"button => {\n            container = document.createElement('div');\n            container.style.overflow = 'auto';\n            container.style.width = '200px';\n            container.style.height = '200px';\n            button.parentElement.insertBefore(container, button);\n            container.appendChild(button);\n            button.style.height = '2000px';\n            button.style.width = '2000px';\n            button.style.borderWidth = '8px';\n        }\"\"\",\n    )\n\n    await page.click(\"button\", position={\"x\": 1900, \"y\": 1910})\n    assert await page.evaluate(\"window.result\") == \"Clicked\"\n    # Safari reports border-relative offsetX/offsetY.\n    assert await page.evaluate(\"offsetX\") == 1900 + 8 if is_webkit else 1900\n    assert await page.evaluate(\"offsetY\") == 1910 + 8 if is_webkit else 1910\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_click_the_button_with_offset_with_page_scale(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(\n        viewport={\"width\": 400, \"height\": 400}, is_mobile=True\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"button => {\n      button.style.borderWidth = '8px'\n      document.body.style.margin = '0'\n    }\"\"\",\n    )\n\n    await page.click(\"button\", position={\"x\": 20, \"y\": 10})\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n    def _assert_close_to(expected: int, actual: int) -> None:\n        if abs(expected - actual) > 2:\n            raise AssertionError(f\"Expected: {expected}, received: {actual}\")\n\n    # Expect 20;10 + 8px of border in each direction. Allow some delta as different\n    # browsers round up or down differently during css -> dip -> css conversion.\n    _assert_close_to(28, await page.evaluate(\"pageX\"))\n    _assert_close_to(18, await page.evaluate(\"pageY\"))\n    await context.close()\n\n\nasync def test_wait_for_stable_position(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"button => {\n            button.style.transition = 'margin 500ms linear 0s';\n            button.style.marginLeft = '200px';\n            button.style.borderWidth = '0';\n            button.style.width = '200px';\n            button.style.height = '20px';\n            // Set display to \"block\" - otherwise Firefox layouts with non-even\n            // values on Linux.\n            button.style.display = 'block';\n            document.body.style.margin = '0';\n        }\"\"\",\n    )\n    # rafraf for Firefox to kick in the animation.\n    await page.evaluate(\n        \"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))\"\n    )\n\n    await page.click(\"button\")\n    assert await page.evaluate(\"window.result\") == \"Clicked\"\n    assert await page.evaluate(\"pageX\") == 300\n    assert await page.evaluate(\"pageY\") == 10\n\n\nasync def test_timeout_waiting_for_stable_position(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.evaluate(\n        \"\"\"button => {\n            button.style.transition = 'margin 5s linear 0s'\n            button.style.marginLeft = '200px'\n        }\"\"\"\n    )\n    # rafraf for Firefox to kick in the animation.\n    await page.evaluate(\n        \"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))\"\n    )\n\n    with pytest.raises(Error) as exc_info:\n        await button.click(timeout=3000)\n    error = exc_info.value\n    assert \"Timeout 3000ms exceeded.\" in error.message\n    assert \"waiting for element to be visible, enabled and stable\" in error.message\n    assert \"element is not stable\" in error.message\n    assert \"retrying click action\" in error.message\n\n\nasync def test_wait_for_becoming_hit_target(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.eval_on_selector(\n        \"button\",\n        \"\"\"button => {\n            button.style.borderWidth = '0';\n            button.style.width = '200px';\n            button.style.height = '20px';\n            document.body.style.margin = '0';\n            document.body.style.position = 'relative';\n            flyOver = document.createElement('div');\n            flyOver.className = 'flyover';\n            flyOver.style.position = 'absolute';\n            flyOver.style.width = '400px';\n            flyOver.style.height = '20px';\n            flyOver.style.left = '-200px';\n            flyOver.style.top = '0';\n            flyOver.style.background = 'red';\n            document.body.appendChild(flyOver);\n        }\"\"\",\n    )\n\n    clicked = [False]\n\n    async def click() -> None:\n        await page.click(\"button\")\n        clicked.append(True)\n\n    click_promise = asyncio.create_task(click())\n    assert clicked == [False]\n\n    await page.eval_on_selector(\".flyover\", \"flyOver => flyOver.style.left = '0'\")\n    await give_it_a_chance_to_click(page)\n    assert clicked == [False]\n\n    await page.eval_on_selector(\".flyover\", \"flyOver => flyOver.style.left = '200px'\")\n    await click_promise\n    assert clicked == [False, True]\n    assert await page.evaluate(\"() => window.result\") == \"Clicked\"\n\n\nasync def test_timeout_waiting_for_hit_target(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await page.evaluate(\n        \"\"\"() => {\n      document.body.style.position = 'relative'\n      blocker = document.createElement('div')\n      blocker.id = 'blocker';\n      blocker.style.position = 'absolute'\n      blocker.style.width = '400px'\n      blocker.style.height = '20px'\n      blocker.style.left = '0'\n      blocker.style.top = '0'\n      document.body.appendChild(blocker)\n      }\"\"\"\n    )\n    error = None\n    try:\n        await button.click(timeout=5000)\n    except TimeoutError as e:\n        error = e\n    assert error\n    assert \"Timeout 5000ms exceeded.\" in error.message\n    assert '<div id=\"blocker\"></div> intercepts pointer events' in error.message\n    assert \"retrying click action\" in error.message\n\n\nasync def test_fail_when_obscured_and_not_waiting_for_hit_target(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await page.evaluate(\n        \"\"\"() => {\n            document.body.style.position = 'relative'\n            blocker = document.createElement('div')\n            blocker.style.position = 'absolute'\n            blocker.style.width = '400px'\n            blocker.style.height = '20px'\n            blocker.style.left = '0'\n            blocker.style.top = '0'\n            document.body.appendChild(blocker)\n        }\"\"\"\n    )\n\n    await button.click(force=True)\n    assert await page.evaluate(\"window.result\") == \"Was not clicked\"\n\n\nasync def test_wait_for_button_to_be_enabled(page: Page, server: Server) -> None:\n    await page.set_content(\n        '<button onclick=\"javascript:window.__CLICKED=true;\" disabled><span>Click target</span></button>'\n    )\n    done = []\n\n    async def click() -> None:\n        await page.click(\"text=Click target\")\n        done.append(True)\n\n    click_promise = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"() => window.__CLICKED\") is None\n    assert done == []\n    await page.evaluate(\"document.querySelector('button').removeAttribute('disabled')\")\n    await click_promise\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_timeout_waiting_for_button_to_be_enabled(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        '<button onclick=\"javascript:window.__CLICKED=true;\" disabled><span>Click target</span></button>'\n    )\n    error: Optional[Error] = None\n    try:\n        await page.click(\"text=Click target\", timeout=3000)\n    except TimeoutError as e:\n        error = e\n    assert await page.evaluate(\"window.__CLICKED\") is None\n    assert error\n    assert \"Timeout 3000ms exceeded\" in error.message\n    assert \"element is not enabled\" in error.message\n    assert \"retrying click action\" in error.message\n\n\nasync def test_wait_for_input_to_be_enabled(page: Page, server: Server) -> None:\n    await page.set_content(\n        '<input onclick=\"javascript:window.__CLICKED=true;\" disabled>'\n    )\n    done = []\n\n    async def click() -> None:\n        await page.click(\"input\")\n        done.append(True)\n\n    click_promise = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"window.__CLICKED\") is None\n    assert done == []\n    await page.evaluate(\"document.querySelector('input').removeAttribute('disabled')\")\n    await click_promise\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_wait_for_select_to_be_enabled(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <select disabled><option selected>Hello</option></select>\n        <script>\n        document.querySelector('select').addEventListener('mousedown', event => {\n            window.__CLICKED=true;\n            event.preventDefault();\n        });\n        </script>\n    \"\"\"\n    )\n    done = []\n\n    async def click() -> None:\n        await page.click(\"select\")\n        done.append(True)\n\n    click_promise = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"window.__CLICKED\") is None\n    assert done == []\n    await page.evaluate(\"document.querySelector('select').removeAttribute('disabled')\")\n    await click_promise\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_click_disabled_div(page: Page, server: Server) -> None:\n    await page.set_content(\n        '<div onclick=\"javascript:window.__CLICKED=true;\" disabled>Click target</div>'\n    )\n    await page.click(\"text=Click target\")\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_climb_dom_for_inner_label_with_pointer_events_none(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        '<button onclick=\"javascript:window.__CLICKED=true;\"><label style=\"pointer-events:none\">Click target</label></button>'\n    )\n    await page.click(\"text=Click target\")\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_climb_up_to_role_button(page: Page, server: Server) -> None:\n    await page.set_content(\n        '<div role=button onclick=\"javascript:window.__CLICKED=true;\"><div style=\"pointer-events:none\"><span><div>Click target</div></span></div>'\n    )\n    await page.click(\"text=Click target\")\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_wait_for_BUTTON_to_be_clickable_when_it_has_pointer_events_none(\n    page: Page,\n) -> None:\n    await page.set_content(\n        '<button onclick=\"javascript:window.__CLICKED=true;\" style=\"pointer-events:none\"><span>Click target</span></button>'\n    )\n    done = []\n\n    async def click() -> None:\n        await page.click(\"text=Click target\")\n        done.append(True)\n\n    click_promise = asyncio.create_task(click())\n    await give_it_a_chance_to_click(page)\n    assert await page.evaluate(\"window.__CLICKED\") is None\n    assert done == []\n    await page.evaluate(\n        \"document.querySelector('button').style.removeProperty('pointer-events')\"\n    )\n    await click_promise\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_wait_for_LABEL_to_be_clickable_when_it_has_pointer_events_none(\n    page: Page,\n) -> None:\n    await page.set_content(\n        '<label onclick=\"javascript:window.__CLICKED=true;\" style=\"pointer-events:none\"><span>Click target</span></label>'\n    )\n    click_promise = asyncio.create_task(page.click(\"text=Click target\"))\n    #  Do a few roundtrips to the page.\n    for _ in range(5):\n        assert await page.evaluate(\"window.__CLICKED\") is None\n    #  remove 'pointer-events: none' css from button.\n    await page.evaluate(\n        \"document.querySelector('label').style.removeProperty('pointer-events')\"\n    )\n    await click_promise\n    assert await page.evaluate(\"window.__CLICKED\")\n\n\nasync def test_update_modifiers_correctly(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.click(\"button\", modifiers=[\"Shift\"])\n    assert await page.evaluate(\"shiftKey\")\n    await page.click(\"button\", modifiers=[])\n    assert await page.evaluate(\"shiftKey\") is False\n\n    await page.keyboard.down(\"Shift\")\n    await page.click(\"button\", modifiers=[])\n    assert await page.evaluate(\"shiftKey\") is False\n    await page.click(\"button\")\n    assert await page.evaluate(\"shiftKey\")\n    await page.keyboard.up(\"Shift\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"shiftKey\") is False\n\n\nasync def test_click_an_offscreen_element_when_scroll_behavior_is_smooth(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <div style=\"border: 1px solid black; height: 500px; overflow: auto; width: 500px; scroll-behavior: smooth\">\n            <button style=\"margin-top: 2000px\" onClick=\"window.clicked = true\">hi</button>\n        </div>\n        \"\"\"\n    )\n    await page.click(\"button\")\n    assert await page.evaluate(\"window.clicked\")\n\n\nasync def test_report_nice_error_when_element_is_detached_and_force_clicked(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/animating-button.html\")\n    await page.evaluate(\"addButton()\")\n    handle = await page.query_selector(\"button\")\n    assert handle\n    await page.evaluate(\"stopButton(true)\")\n    error: Optional[Error] = None\n    try:\n        await handle.click(force=True)\n    except Error as e:\n        error = e\n    assert await page.evaluate(\"window.clicked\") is None\n    assert error\n    assert \"Element is not attached to the DOM\" in error.message\n\n\nasync def test_fail_when_element_detaches_after_animation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/animating-button.html\")\n    await page.evaluate(\"addButton()\")\n    handle = await page.query_selector(\"button\")\n    assert handle\n    promise = asyncio.create_task(handle.click())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await page.evaluate(\"stopButton(true)\")\n    with pytest.raises(Error) as exc_info:\n        await promise\n    assert await page.evaluate(\"window.clicked\") is None\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\nasync def test_retry_when_element_detaches_after_animation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/animating-button.html\")\n    await page.evaluate(\"addButton()\")\n    clicked = []\n\n    async def click() -> None:\n        await page.click(\"button\")\n        clicked.append(True)\n\n    promise = asyncio.create_task(click())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    assert clicked == []\n    assert await page.evaluate(\"window.clicked\") is None\n    await page.evaluate(\"stopButton(true)\")\n    await page.evaluate(\"addButton()\")\n    assert clicked == []\n    assert await page.evaluate(\"window.clicked\") is None\n    await page.evaluate(\"stopButton(true)\")\n    await page.evaluate(\"addButton()\")\n    assert clicked == []\n    assert await page.evaluate(\"window.clicked\") is None\n    await page.evaluate(\"stopButton(false)\")\n    await promise\n    assert clicked == [True]\n    assert await page.evaluate(\"window.clicked\")\n\n\nasync def test_retry_when_element_is_animating_from_outside_the_viewport(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"<style>\n        @keyframes move {\n            from { left: -300px; }\n            to { left: 0; }\n        }\n        button {\n            position: absolute\n            left: -300px\n            top: 0\n            bottom: 0\n            width: 200px\n        }\n        button.animated {\n            animation: 1s linear 1s move forwards\n        }\n        </style>\n        <div style=\"position: relative; width: 300px; height: 300px;\">\n            <button onclick=\"window.clicked=true\"></button>\n        </div>\n        \"\"\"\n    )\n    handle = await page.query_selector(\"button\")\n    assert handle\n    promise = asyncio.create_task(handle.click())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await handle.evaluate(\"button => button.className = 'animated'\")\n    await promise\n    assert await page.evaluate(\"window.clicked\")\n\n\nasync def test_fail_when_element_is_animating_from_outside_the_viewport_with_force(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"<style>\n        @keyframes move {\n            from { left: -300px; }\n            to { left: 0; }\n        }\n        button {\n            position: absolute;\n            left: -300px;\n            top: 0;\n            bottom: 0\n            width: 200px;\n        }\n        button.animated {\n            animation: 1s linear 1s move forwards;\n        }\n        </style>\n        <div style=\"position: relative; width: 300px; height: 300px;\">\n            <button onclick=\"window.clicked=true\"></button>\n        </div>\n        \"\"\"\n    )\n    handle = await page.query_selector(\"button\")\n    assert handle\n    promise = asyncio.create_task(handle.click(force=True))\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await handle.evaluate(\"button => button.className = 'animated'\")\n    error: Optional[Error] = None\n    try:\n        await promise\n    except Error as e:\n        error = e\n    assert await page.evaluate(\"window.clicked\") is None\n    assert error\n    assert \"Element is outside of the viewport\" in error.message\n\n\nasync def test_not_retarget_when_element_changes_on_hover(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/react.html\")\n    await page.evaluate(\n        \"\"\"() => {\n          renderComponent(e('div', {}, [e(MyButton, { name: 'button1', renameOnHover: true }), e(MyButton, { name: 'button2' })] ));\n        }\"\"\"\n    )\n    await page.click(\"text=button1\")\n    assert await page.evaluate(\"window.button1\")\n    assert await page.evaluate(\"window.button2\") is None\n\n\nasync def test_not_retarget_when_element_is_recycled_on_hover(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/react.html\")\n    await page.evaluate(\n        \"\"\"() => {\n            function shuffle() {\n                renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));\n            }\n            renderComponent(e('div', {}, [e(MyButton, { name: 'button1', onHover: shuffle }), e(MyButton, { name: 'button2' })] ));\n        }\"\"\"\n    )\n\n    await page.click(\"text=button1\")\n    assert await page.evaluate(\"window.button1\") is None\n    assert await page.evaluate(\"window.button2\")\n\n\nasync def test_click_the_button_when_window_inner_width_is_corrupted(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\"window.innerWidth = 0\")\n    await page.click(\"button\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_timeout_when_click_opens_alert(page: Page, server: Server) -> None:\n    await page.set_content('<div onclick=\"window.alert(123)\">Click me</div>')\n    async with page.expect_event(\"dialog\") as dialog_info:\n        with pytest.raises(Error) as exc_info:\n            await page.click(\"div\", timeout=3000)\n        assert \"Timeout 3000ms exceeded\" in exc_info.value.message\n    dialog = await dialog_info.value\n    await dialog.dismiss()\n\n\nasync def test_check_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    await page.check(\"input\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_not_check_the_checked_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    await page.check(\"input\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_uncheck_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    await page.uncheck(\"input\")\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_not_uncheck_the_unchecked_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    await page.uncheck(\"input\")\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_check_the_box_by_label(page: Page) -> None:\n    await page.set_content(\n        '<label for=\"checkbox\"><input id=\"checkbox\" type=\"checkbox\"></input></label>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_the_box_outside_label(page: Page) -> None:\n    await page.set_content(\n        '<label for=\"checkbox\">Text</label><div><input id=\"checkbox\" type=\"checkbox\"></input></div>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_the_box_inside_label_without_id(page: Page) -> None:\n    await page.set_content(\n        '<label>Text<span><input id=\"checkbox\" type=\"checkbox\"></input></span></label>'\n    )\n    await page.check(\"label\")\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_check_radio(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <input type='radio'>one</input>\n        <input id='two' type='radio'>two</input>\n        <input type='radio'>three</input>\"\"\"\n    )\n    await page.check(\"#two\")\n    assert await page.evaluate(\"two.checked\")\n\n\nasync def test_check_the_box_by_aria_role(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<div role='checkbox' id='checkbox'>CHECKBOX</div>\n        <script>\n            checkbox.addEventListener('click', () => checkbox.setAttribute('aria-checked', 'true'))\n        </script>\"\"\"\n    )\n    await page.check(\"div\")\n    assert await page.evaluate(\"checkbox.getAttribute ('aria-checked')\")\n\n\nasync def test_click_with_tweened_mouse_movement(page: Page, browser_name: str) -> None:\n    await page.set_content(\n        \"\"\"\n        <body style=\"margin: 0; padding: 0; height: 500px; width: 500px;\">\n          <div style=\"position: relative; top: 280px; left: 150px; width: 100px; height: 40px\">Click me</div>\n        </body>\n        \"\"\"\n    )\n\n    # The test becomes flaky on WebKit without next line.\n    if browser_name == \"webkit\":\n        await page.evaluate(\"() => new Promise(requestAnimationFrame)\")\n    await page.mouse.move(100, 100)\n    await page.evaluate(\n        \"\"\"() => {\n        window.result = [];\n        document.addEventListener('mousemove', event => {\n          window.result.push([event.clientX, event.clientY]);\n        });\n      }\"\"\"\n    )\n    # Centerpoint at 150 + 100/2, 280 + 40/2 = 200, 300\n    await page.locator(\"div\").click(steps=5)\n    assert await page.evaluate(\"result\") == [\n        [120, 140],\n        [140, 180],\n        [160, 220],\n        [180, 260],\n        [200, 300],\n    ]\n"
  },
  {
    "path": "tests/async/test_console.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import List\n\nimport pytest\n\nfrom playwright.async_api import ConsoleMessage, Page\nfrom tests.server import Server\n\n\nasync def test_console_should_work(page: Page, browser_name: str) -> None:\n    messages: List[ConsoleMessage] = []\n    page.once(\"console\", lambda m: messages.append(m))\n    async with page.expect_console_message() as message_info:\n        await page.evaluate('() => console.log(\"hello\", 5, {foo: \"bar\"})')\n    message = await message_info.value\n    if browser_name != \"firefox\":\n        assert message.text == \"hello 5 {foo: bar}\"\n        assert str(message) == \"hello 5 {foo: bar}\"\n    else:\n        assert message.text == \"hello 5 JSHandle@object\"\n        assert str(message) == \"hello 5 JSHandle@object\"\n    assert message.type == \"log\"\n    assert await message.args[0].json_value() == \"hello\"\n    assert await message.args[1].json_value() == 5\n    assert await message.args[2].json_value() == {\"foo\": \"bar\"}\n\n\nasync def test_console_should_emit_same_log_twice(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m.text))\n    await page.evaluate('() => { for (let i = 0; i < 2; ++i ) console.log(\"hello\"); } ')\n    assert messages == [\"hello\", \"hello\"]\n\n\nasync def test_console_should_use_text_for__str__(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m))\n    await page.evaluate('() => console.log(\"Hello world\")')\n    assert len(messages) == 1\n    assert str(messages[0]) == \"Hello world\"\n\n\nasync def test_console_should_work_for_different_console_api_calls(page: Page) -> None:\n    messages: List[ConsoleMessage] = []\n    page.on(\"console\", lambda m: messages.append(m))\n    # All console events will be reported before 'page.evaluate' is finished.\n    await page.evaluate(\n        \"\"\"() => {\n      // A pair of time/timeEnd generates only one Console API call.\n      console.time('calling console.time');\n      console.timeEnd('calling console.time');\n      console.trace('calling console.trace');\n      console.dir('calling console.dir');\n      console.warn('calling console.warn');\n      console.error('calling console.error');\n      console.log(Promise.resolve('should not wait until resolved!'));\n    }\"\"\"\n    )\n    assert list(map(lambda msg: msg.type, messages)) == [\n        \"timeEnd\",\n        \"trace\",\n        \"dir\",\n        \"warning\",\n        \"error\",\n        \"log\",\n    ]\n\n    assert \"calling console.time\" in messages[0].text\n    assert list(map(lambda msg: msg.text, messages[1:])) == [\n        \"calling console.trace\",\n        \"calling console.dir\",\n        \"calling console.warn\",\n        \"calling console.error\",\n        \"Promise\",\n    ]\n\n\nasync def test_console_should_not_fail_for_window_object(\n    page: Page, browser_name: str\n) -> None:\n    async with page.expect_console_message() as message_info:\n        await page.evaluate(\"console.error(window)\")\n    message = await message_info.value\n    if browser_name != \"firefox\":\n        assert message.text == \"Window\"\n    else:\n        assert message.text == \"JSHandle@object\"\n\n\n# Upstream issue https://bugs.webkit.org/show_bug.cgi?id=229515\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_console_should_trigger_correct_log(page: Page, server: Server) -> None:\n    await page.goto(\"about:blank\")\n    async with page.expect_console_message() as message_info:\n        await page.evaluate(\"async url => fetch(url).catch(e => {})\", server.EMPTY_PAGE)\n    message = await message_info.value\n    assert \"Access-Control-Allow-Origin\" in message.text or \"CORS\" in message.text\n    assert message.type == \"error\"\n\n\nasync def test_console_should_have_location_for_console_api_calls(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_console_message() as message_info:\n        await page.goto(server.PREFIX + \"/consolelog.html\")\n    message = await message_info.value\n    assert message.text == \"yellow\"\n    assert message.type == \"log\"\n    location = message.location\n    # Engines have different column notion.\n    assert location[\"url\"] == server.PREFIX + \"/consolelog.html\"\n    assert location[\"lineNumber\"] == 7\n\n\nasync def test_console_should_not_throw_when_there_are_console_messages_in_detached_iframes(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as page_info:\n        await page.evaluate(\n            \"\"\"async() => {\n                // 1. Create a popup that Playwright is not connected to.\n                const win = window.open('');\n                window._popup = win;\n                if (window.document.readyState !== 'complete')\n                await new Promise(f => window.addEventListener('load', f));\n                // 2. In this popup, create an iframe that console.logs a message.\n                win.document.body.innerHTML = `<iframe src='/consolelog.html'></iframe>`;\n                const frame = win.document.querySelector('iframe');\n                if (!frame.contentDocument || frame.contentDocument.readyState !== 'complete')\n                await new Promise(f => frame.addEventListener('load', f));\n                // 3. After that, remove the iframe.\n                frame.remove();\n            }\"\"\"\n        )\n    popup = await page_info.value\n    # 4. Connect to the popup and make sure it doesn't throw.\n    assert await popup.evaluate(\"1 + 1\") == 2\n"
  },
  {
    "path": "tests/async/test_context_manager.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import BrowserContext, BrowserType\n\n\nasync def test_context_managers(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    async with await browser_type.launch(**launch_arguments) as browser:\n        async with await browser.new_context() as context:\n            async with await context.new_page():\n                assert len(context.pages) == 1\n            assert len(context.pages) == 0\n            assert len(browser.contexts) == 1\n        assert len(browser.contexts) == 0\n    assert not browser.is_connected()\n\n\nasync def test_context_managers_not_hang(context: BrowserContext) -> None:\n    with pytest.raises(Exception, match=\"Oops!\"):\n        async with await context.new_page():\n            raise Exception(\"Oops!\")\n"
  },
  {
    "path": "tests/async/test_defaultbrowsercontext.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport os\nfrom pathlib import Path\nfrom typing import (\n    Any,\n    AsyncGenerator,\n    Awaitable,\n    Callable,\n    Dict,\n    List,\n    Literal,\n    Optional,\n    Sequence,\n    Tuple,\n)\n\nimport pytest\n\nfrom playwright.async_api import (\n    BrowserContext,\n    BrowserType,\n    Cookie,\n    Error,\n    Page,\n    expect,\n)\nfrom tests.server import Server\nfrom tests.utils import must\n\nfrom .utils import Utils\n\n\n@pytest.fixture()\nasync def launch_persistent(\n    tmp_path: Path, launch_arguments: Dict, browser_type: BrowserType\n) -> AsyncGenerator[Callable[..., Awaitable[Tuple[Page, BrowserContext]]], None]:\n    context: Optional[BrowserContext] = None\n\n    async def _launch(**options: Any) -> Tuple[Page, BrowserContext]:\n        nonlocal context\n        if context:\n            raise ValueError(\"can only launch one persistent context\")\n        context = await browser_type.launch_persistent_context(\n            str(tmp_path), **{**launch_arguments, **options}\n        )\n        assert context\n        return (context.pages[0], context)\n\n    yield _launch\n    await must(context).close()\n\n\nasync def test_context_cookies_should_work(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    default_same_site_cookie_value: str,\n) -> None:\n    (page, context) = await launch_persistent()\n    await page.goto(server.EMPTY_PAGE)\n    document_cookie = await page.evaluate(\n        \"\"\"() => {\n    document.cookie = 'username=John Doe';\n    return document.cookie;\n  }\"\"\"\n    )\n\n    assert document_cookie == \"username=John Doe\"\n    assert _filter_cookies(await page.context.cookies()) == [\n        {\n            \"name\": \"username\",\n            \"value\": \"John Doe\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\nasync def test_context_add_cookies_should_work(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    default_same_site_cookie_value: Literal[\"Lax\", \"None\", \"Strict\"],\n) -> None:\n    (page, context) = await launch_persistent()\n    await page.goto(server.EMPTY_PAGE)\n    await page.context.add_cookies(\n        [\n            {\n                \"url\": server.EMPTY_PAGE,\n                \"name\": \"username\",\n                \"value\": \"John Doe\",\n                \"sameSite\": default_same_site_cookie_value,\n            }\n        ]\n    )\n    assert await page.evaluate(\"() => document.cookie\") == \"username=John Doe\"\n    assert _filter_cookies(await page.context.cookies()) == [\n        {\n            \"name\": \"username\",\n            \"value\": \"John Doe\",\n            \"domain\": \"localhost\",\n            \"path\": \"/\",\n            \"expires\": -1,\n            \"httpOnly\": False,\n            \"secure\": False,\n            \"sameSite\": default_same_site_cookie_value,\n        }\n    ]\n\n\ndef _filter_cookies(cookies: Sequence[Cookie]) -> List[Cookie]:\n    return list(\n        filter(lambda cookie: not cookie[\"domain\"].endswith(\"microsoft.com\"), cookies)\n    )\n\n\nasync def test_context_clear_cookies_should_work(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent()\n    await page.goto(server.EMPTY_PAGE)\n    await page.context.add_cookies(\n        [\n            {\"url\": server.EMPTY_PAGE, \"name\": \"cookie1\", \"value\": \"1\"},\n            {\"url\": server.EMPTY_PAGE, \"name\": \"cookie2\", \"value\": \"2\"},\n        ]\n    )\n    assert await page.evaluate(\"document.cookie\") == \"cookie1=1; cookie2=2\"\n    await page.context.clear_cookies()\n    await page.reload()\n    assert _filter_cookies(await page.context.cookies([])) == []\n    assert await page.evaluate(\"document.cookie\") == \"\"\n\n\nasync def test_should_not_block_third_party_cookies(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    is_firefox: bool,\n) -> None:\n    (page, context) = await launch_persistent()\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"src => {\n    let fulfill;\n    const promise = new Promise(x => fulfill = x);\n    const iframe = document.createElement('iframe');\n    document.body.appendChild(iframe);\n    iframe.onload = fulfill;\n    iframe.src = src;\n    return promise;\n  }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/grid.html\",\n    )\n    document_cookie = await page.frames[1].evaluate(\n        \"\"\"() => {\n    document.cookie = 'username=John Doe';\n    return document.cookie;\n  }\"\"\"\n    )\n\n    await page.wait_for_timeout(2000)\n    allows_third_party = is_firefox\n    assert document_cookie == (\"username=John Doe\" if allows_third_party else \"\")\n    cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + \"/grid.html\")\n    if allows_third_party:\n        assert cookies == [\n            {\n                \"domain\": \"127.0.0.1\",\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"name\": \"username\",\n                \"path\": \"/\",\n                \"sameSite\": \"None\",\n                \"secure\": False,\n                \"value\": \"John Doe\",\n            }\n        ]\n    else:\n        assert cookies == []\n\n\nasync def test_should_support_viewport_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    utils: Utils,\n) -> None:\n    (page, context) = await launch_persistent(viewport={\"width\": 456, \"height\": 789})\n    await utils.verify_viewport(page, 456, 789)\n    page2 = await context.new_page()\n    await utils.verify_viewport(page2, 456, 789)\n\n\nasync def test_should_support_device_scale_factor_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(device_scale_factor=3)\n    assert await page.evaluate(\"window.devicePixelRatio\") == 3\n\n\nasync def test_should_support_user_agent_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    server: Server,\n) -> None:\n    (page, context) = await launch_persistent(user_agent=\"foobar\")\n    assert await page.evaluate(\"() => navigator.userAgent\") == \"foobar\"\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        page.goto(server.EMPTY_PAGE),\n    )\n    assert request.getHeader(\"user-agent\") == \"foobar\"\n\n\nasync def test_should_support_bypass_csp_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    server: Server,\n) -> None:\n    (page, context) = await launch_persistent(bypass_csp=True)\n    await page.goto(server.PREFIX + \"/csp.html\")\n    await page.add_script_tag(content=\"window.__injected = 42;\")\n    assert await page.evaluate(\"() => window.__injected\") == 42\n\n\nasync def test_should_support_javascript_enabled_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n    is_webkit: bool,\n) -> None:\n    (page, context) = await launch_persistent(java_script_enabled=False)\n    await page.goto('data:text/html, <script>var something = \"forbidden\"</script>')\n    with pytest.raises(Error) as exc:\n        await page.evaluate(\"something\")\n    if is_webkit:\n        assert \"Can't find variable: something\" in exc.value.message\n    else:\n        assert \"something is not defined\" in exc.value.message\n\n\nasync def test_should_support_http_credentials_option(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    server.set_auth(\"/playground.html\", \"user\", \"pass\")\n    response = await page.goto(server.PREFIX + \"/playground.html\")\n    assert response\n    assert response.status == 200\n\n\nasync def test_should_support_offline_option(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(offline=True)\n    with pytest.raises(Error):\n        await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_should_support_has_touch_option(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(has_touch=True)\n    await page.goto(server.PREFIX + \"/mobile.html\")\n    assert await page.evaluate('() => \"ontouchstart\" in window')\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_should_work_in_persistent_context(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    # Firefox does not support mobile.\n    (page, context) = await launch_persistent(\n        viewport={\"width\": 320, \"height\": 480}, is_mobile=True\n    )\n    await page.goto(server.PREFIX + \"/empty.html\")\n    assert await page.evaluate(\"() => window.innerWidth\") == 980\n\n\nasync def test_should_support_color_scheme_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(color_scheme=\"dark\")\n    assert (\n        await page.evaluate('() => matchMedia(\"(prefers-color-scheme: light)\").matches')\n        is False\n    )\n    assert await page.evaluate(\n        '() => matchMedia(\"(prefers-color-scheme: dark)\").matches'\n    )\n\n\nasync def test_should_support_timezone_id_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(timezone_id=\"America/Jamaica\")\n    assert (\n        await page.evaluate(\"() => new Date(1479579154987).toString()\")\n        == \"Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)\"\n    )\n\n\nasync def test_should_support_contrast_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, _) = await launch_persistent(contrast=\"more\")\n    assert await page.evaluate('() => matchMedia(\"(prefers-contrast: more)\").matches')\n    assert not await page.evaluate(\n        '() => matchMedia(\"(prefers-contrast: no-preference)\").matches'\n    )\n\n\nasync def test_should_support_locale_option(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(locale=\"fr-FR\")\n    assert await page.evaluate(\"() => navigator.language\") == \"fr-FR\"\n\n\nasync def test_should_support_geolocation_and_permission_option(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(\n        geolocation={\"longitude\": 10, \"latitude\": 10}, permissions=[\"geolocation\"]\n    )\n    await page.goto(server.EMPTY_PAGE)\n    geolocation = await page.evaluate(\n        \"\"\"() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n    resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n  }))\"\"\"\n    )\n    assert geolocation == {\"latitude\": 10, \"longitude\": 10}\n\n\nasync def test_should_support_ignore_https_errors_option(\n    https_server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(ignore_https_errors=True)\n    response = await page.goto(https_server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n\n\nasync def test_should_support_extra_http_headers_option(\n    server: Server,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(extra_http_headers={\"foo\": \"bar\"})\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        page.goto(server.EMPTY_PAGE),\n    )\n    assert request.getHeader(\"foo\") == \"bar\"\n\n\nasync def test_should_accept_user_data_dir(\n    tmp_path: Path,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent()\n    # Note: we need an open page to make sure its functional.\n    assert len(os.listdir(tmp_path)) > 0\n    await context.close()\n    assert len(os.listdir(tmp_path)) > 0\n\n\nasync def test_should_restore_state_from_userDataDir(\n    browser_type: BrowserType,\n    launch_arguments: Dict,\n    server: Server,\n    tmp_path_factory: pytest.TempPathFactory,\n) -> None:\n    user_data_dir1 = tmp_path_factory.mktemp(\"test\")\n    browser_context = await browser_type.launch_persistent_context(\n        user_data_dir1, **launch_arguments\n    )\n    page = await browser_context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate('() => localStorage.hey = \"hello\"')\n    await browser_context.close()\n\n    browser_context2 = await browser_type.launch_persistent_context(\n        user_data_dir1, **launch_arguments\n    )\n    page2 = await browser_context2.new_page()\n    await page2.goto(server.EMPTY_PAGE)\n    assert await page2.evaluate(\"() => localStorage.hey\") == \"hello\"\n    await browser_context2.close()\n\n    user_data_dir2 = tmp_path_factory.mktemp(\"test\")\n    browser_context3 = await browser_type.launch_persistent_context(\n        user_data_dir2, **launch_arguments\n    )\n    page3 = await browser_context3.new_page()\n    await page3.goto(server.EMPTY_PAGE)\n    assert await page3.evaluate(\"() => localStorage.hey\") != \"hello\"\n    await browser_context3.close()\n\n\nasync def test_should_have_default_url_when_launching_browser(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent()\n    urls = list(map(lambda p: p.url, context.pages))\n    assert urls == [\"about:blank\"]\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_should_throw_if_page_argument_is_passed(\n    browser_type: BrowserType, server: Server, tmp_path: Path, launch_arguments: Dict\n) -> None:\n    options = {**launch_arguments, \"args\": [server.EMPTY_PAGE]}\n    with pytest.raises(Error) as exc:\n        await browser_type.launch_persistent_context(tmp_path, **options)\n    assert \"can not specify page\" in exc.value.message\n\n\nasync def test_should_fire_close_event_for_a_persistent_context(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent()\n    fired_event: \"asyncio.Future[bool]\" = asyncio.Future()\n    context.on(\"close\", lambda _: fired_event.set_result(True))\n    await context.close()\n    await fired_event\n\n\nasync def test_should_support_reduced_motion(\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent(reduced_motion=\"reduce\")\n    assert await page.evaluate(\"matchMedia('(prefers-reduced-motion: reduce)').matches\")\n\n\nasync def test_should_support_har_option(\n    assetdir: Path,\n    launch_persistent: \"Callable[..., asyncio.Future[Tuple[Page, BrowserContext]]]\",\n) -> None:\n    (page, context) = await launch_persistent()\n    await page.route_from_har(har=assetdir / \"har-fulfill.har\")\n    await page.goto(\"http://no.playwright/\")\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    await expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n"
  },
  {
    "path": "tests/async/test_device_descriptors.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import Playwright\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_work(playwright: Playwright, launch_arguments: Dict) -> None:\n    device_descriptor = playwright.devices[\"Pixel 2\"]\n    device_type = device_descriptor[\"default_browser_type\"]\n    browser = await playwright[device_type].launch(**launch_arguments)\n    context = await browser.new_context(\n        **device_descriptor,\n    )\n    page = await context.new_page()\n    assert device_descriptor[\"default_browser_type\"] == \"chromium\"\n    assert browser.browser_type.name == \"chromium\"\n\n    assert \"Pixel 2\" in device_descriptor[\"user_agent\"]\n    assert \"Pixel 2\" in await page.evaluate(\"navigator.userAgent\")\n\n    assert device_descriptor[\"device_scale_factor\"] > 2\n    assert await page.evaluate(\"window.devicePixelRatio\") > 2\n\n    assert device_descriptor[\"viewport\"][\"height\"] > 700\n    assert device_descriptor[\"viewport\"][\"height\"] < 800\n    inner_height = await page.evaluate(\"window.screen.availHeight\")\n    assert inner_height > 700\n    assert inner_height < 800\n\n    assert device_descriptor[\"viewport\"][\"width\"] > 400\n    assert device_descriptor[\"viewport\"][\"width\"] < 500\n    inner_width = await page.evaluate(\"window.screen.availWidth\")\n    assert inner_width > 400\n    assert inner_width < 500\n\n    assert device_descriptor[\"has_touch\"]\n    assert device_descriptor[\"is_mobile\"]\n\n    await browser.close()\n"
  },
  {
    "path": "tests/async/test_dialog.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Browser, Dialog, Page\n\n\nasync def test_should_fire(page: Page) -> None:\n    result = []\n\n    async def on_dialog(dialog: Dialog) -> None:\n        result.append(True)\n        assert dialog.type == \"alert\"\n        assert dialog.default_value == \"\"\n        assert dialog.message == \"yo\"\n        await dialog.accept()\n\n    page.on(\"dialog\", on_dialog)\n    await page.evaluate(\"alert('yo')\")\n    assert result\n\n\nasync def test_should_allow_accepting_prompts(page: Page) -> None:\n    result = []\n\n    async def on_dialog(dialog: Dialog) -> None:\n        result.append(True)\n        assert dialog.type == \"prompt\"\n        assert dialog.default_value == \"yes.\"\n        assert dialog.message == \"question?\"\n        await dialog.accept(\"answer!\")\n\n    page.on(\"dialog\", on_dialog)\n    assert await page.evaluate(\"prompt('question?', 'yes.')\") == \"answer!\"\n    assert result\n\n\nasync def test_should_dismiss_the_prompt(page: Page) -> None:\n    result = []\n\n    async def on_dialog(dialog: Dialog) -> None:\n        result.append(True)\n        await dialog.dismiss()\n\n    page.on(\"dialog\", on_dialog)\n    assert await page.evaluate(\"prompt('question?')\") is None\n    assert result\n\n\nasync def test_should_accept_the_confirm_prompt(page: Page) -> None:\n    result = []\n\n    async def on_dialog(dialog: Dialog) -> None:\n        result.append(True)\n        await dialog.accept()\n\n    page.on(\"dialog\", on_dialog)\n    assert await page.evaluate(\"confirm('boolean?')\") is True\n    assert result\n\n\nasync def test_should_dismiss_the_confirm_prompt(page: Page) -> None:\n    result = []\n\n    async def on_dialog(dialog: Dialog) -> None:\n        result.append(True)\n        await dialog.dismiss()\n\n    page.on(\"dialog\", on_dialog)\n    assert await page.evaluate(\"confirm('boolean?')\") is False\n    assert result\n\n\nasync def test_should_be_able_to_close_context_with_open_alert(\n    browser: Browser,\n) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    async with page.expect_event(\"dialog\"):\n        await page.evaluate(\"() => setTimeout(() => alert('hello'), 0)\", None)\n    await context.close()\n\n\nasync def test_should_auto_dismiss_the_prompt_without_listeners(page: Page) -> None:\n    result = await page.evaluate('() => prompt(\"question?\")')\n    assert not result\n\n\nasync def test_should_auto_dismiss_the_alert_without_listeners(page: Page) -> None:\n    await page.set_content(\n        '<div onclick=\"window.alert(123); window._clicked=true\">Click me</div>'\n    )\n    await page.click(\"div\")\n    assert await page.evaluate('\"window._clicked\"')\n"
  },
  {
    "path": "tests/async/test_dispatch_event.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom playwright.async_api import Page, Selectors\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def test_should_dispatch_click_event(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_should_dispatch_click_event_properties(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    assert await page.evaluate(\"() => bubbles\")\n    assert await page.evaluate(\"() => cancelable\")\n    assert await page.evaluate(\"() => composed\")\n\n\nasync def test_should_dispatch_click_svg(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <svg height=\"100\" width=\"100\">\n        <circle onclick=\"javascript:window.__CLICKED=42\" cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n      </svg>\n    \"\"\"\n    )\n    await page.dispatch_event(\"circle\", \"click\")\n    assert await page.evaluate(\"() => window.__CLICKED\") == 42\n\n\nasync def test_should_dispatch_click_on_a_span_with_an_inline_element_inside(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <style>\n      span::before {\n        content: 'q';\n      }\n      </style>\n      <span onclick='javascript:window.CLICKED=42'></span>\n    \"\"\"\n    )\n    await page.dispatch_event(\"span\", \"click\")\n    assert await page.evaluate(\"() => window.CLICKED\") == 42\n\n\nasync def test_should_dispatch_click_after_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_should_dispatch_click_after_a_cross_origin_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/input/button.html\")\n    await page.dispatch_event(\"button\", \"click\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n\n\nasync def test_should_not_fail_when_element_is_blocked_on_hover(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<style>\n      container { display: block; position: relative; width: 200px; height: 50px; }\n      div, button { position: absolute; left: 0; top: 0; bottom: 0; right: 0; }\n      div { pointer-events: none; }\n      container:hover div { pointer-events: auto; background: red; }\n    </style>\n    <container>\n      <button onclick=\"window.clicked=true\">Click me</button>\n      <div></div>\n    </container>\"\"\"\n    )\n    await page.dispatch_event(\"button\", \"click\")\n    assert await page.evaluate(\"() => window.clicked\")\n\n\nasync def test_should_dispatch_click_when_node_is_added_in_shadow_dom(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    watchdog = page.dispatch_event(\"span\", \"click\")\n    await page.evaluate(\n        \"\"\"() => {\n      const div = document.createElement('div');\n      div.attachShadow({mode: 'open'});\n      document.body.appendChild(div);\n    }\"\"\"\n    )\n    await page.evaluate(\"() => new Promise(f => setTimeout(f, 100))\")\n    await page.evaluate(\n        \"\"\"() => {\n      const span = document.createElement('span');\n      span.textContent = 'Hello from shadow';\n      span.addEventListener('click', () => window.clicked = true);\n      document.querySelector('div').shadowRoot.appendChild(span);\n    }\"\"\"\n    )\n    await watchdog\n    assert await page.evaluate(\"() => window.clicked\")\n\n\nasync def test_should_be_atomic(selectors: Selectors, page: Page, utils: Utils) -> None:\n    await utils.register_selector_engine(\n        selectors,\n        \"dispatch_event\",\n        \"\"\"{\n            create(root, target) { },\n            query(root, selector) {\n                const result = root.querySelector(selector);\n                if (result)\n                Promise.resolve().then(() => result.onclick = \"\");\n                return result;\n            },\n            queryAll(root, selector) {\n                const result = Array.from(root.querySelectorAll(selector));\n                for (const e of result)\n                Promise.resolve().then(() => result.onclick = \"\");\n                return result;\n            }\n        }\"\"\",\n    )\n    await page.set_content('<div onclick=\"window._clicked=true\">Hello</div>')\n    await page.dispatch_event(\"dispatch_event=div\", \"click\")\n    assert await page.evaluate(\"() => window._clicked\")\n\n\nasync def test_should_dispatch_drag_drop_events(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    dataTransfer = await page.evaluate_handle(\"() => new DataTransfer()\")\n    await page.dispatch_event(\"#source\", \"dragstart\", {\"dataTransfer\": dataTransfer})\n    await page.dispatch_event(\"#target\", \"drop\", {\"dataTransfer\": dataTransfer})\n    assert await page.evaluate(\n        \"\"\"() => {\n      return source.parentElement === target;\n    }\"\"\"\n    )\n\n\nasync def test_should_dispatch_drag_and_drop_events_element_handle(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    dataTransfer = await page.evaluate_handle(\"() => new DataTransfer()\")\n    source = await page.query_selector(\"#source\")\n    assert source\n    await source.dispatch_event(\"dragstart\", {\"dataTransfer\": dataTransfer})\n    target = await page.query_selector(\"#target\")\n    assert target\n    await target.dispatch_event(\"drop\", {\"dataTransfer\": dataTransfer})\n    assert await page.evaluate(\n        \"\"\"() => {\n      return source.parentElement === target;\n    }\"\"\"\n    )\n\n\nasync def test_should_dispatch_click_event_element_handle(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.dispatch_event(\"click\")\n    assert await page.evaluate(\"() => result\") == \"Clicked\"\n"
  },
  {
    "path": "tests/async/test_download.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\nimport os\nfrom asyncio.futures import Future\nfrom pathlib import Path\nfrom typing import Callable, Generator\n\nimport pytest\n\nfrom playwright.async_api import Browser, Download, Error, Page\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\ndef assert_file_content(path: Path, content: str) -> None:\n    with open(path, \"r\") as fd:\n        assert fd.read() == content\n\n\n@pytest.fixture(autouse=True)\ndef after_each_hook(server: Server) -> Generator[None, None, None]:\n    def handle_download(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.setHeader(\"Content-Disposition\", \"attachment\")\n        request.write(b\"Hello world\")\n        request.finish()\n\n    def handle_download_with_file_name(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.setHeader(\"Content-Disposition\", \"attachment; filename=file.txt\")\n        request.write(b\"Hello world\")\n        request.finish()\n\n    server.set_route(\"/download\", handle_download)\n    server.set_route(\"/downloadWithFilename\", handle_download_with_file_name)\n    yield\n\n\nasync def test_should_report_downloads_with_accept_downloads_false(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        f'<a href=\"{server.PREFIX}/downloadWithFilename\">download</a>'\n    )\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    assert download.page is page\n    assert download.url == f\"{server.PREFIX}/downloadWithFilename\"\n    assert download.suggested_filename == \"file.txt\"\n    assert (\n        repr(download)\n        == f\"<Download url={download.url!r} suggested_filename={download.suggested_filename!r}>\"\n    )\n    assert await download.path()\n    assert await download.failure() is None\n\n\nasync def test_should_report_downloads_with_accept_downloads_true(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    path = await download.path()\n    assert os.path.isfile(path)\n    assert_file_content(path, \"Hello world\")\n    await page.close()\n\n\nasync def test_should_save_to_user_specified_path(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    await download.save_as(user_path)\n    assert user_path.exists()\n    assert user_path.read_text(\"utf-8\") == \"Hello world\"\n    await page.close()\n\n\nasync def test_should_save_to_user_specified_path_without_updating_original_path(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    await download.save_as(user_path)\n    assert user_path.exists()\n    assert user_path.read_text(\"utf-8\") == \"Hello world\"\n\n    originalPath = Path(await download.path())\n    assert originalPath.exists()\n    assert originalPath.read_text(\"utf-8\") == \"Hello world\"\n    await page.close()\n\n\nasync def test_should_save_to_two_different_paths_with_multiple_save_as_calls(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    await download.save_as(user_path)\n    assert user_path.exists()\n    assert user_path.read_text(\"utf-8\") == \"Hello world\"\n\n    anotheruser_path = tmp_path / \"download (2).txt\"\n    await download.save_as(anotheruser_path)\n    assert anotheruser_path.exists()\n    assert anotheruser_path.read_text(\"utf-8\") == \"Hello world\"\n    await page.close()\n\n\nasync def test_should_save_to_overwritten_filepath(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    await download.save_as(user_path)\n    assert len(list(tmp_path.glob(\"*.*\"))) == 1\n    await download.save_as(user_path)\n    assert len(list(tmp_path.glob(\"*.*\"))) == 1\n    assert user_path.exists()\n    assert user_path.read_text(\"utf-8\") == \"Hello world\"\n    await page.close()\n\n\nasync def test_should_create_subdirectories_when_saving_to_non_existent_user_specified_path(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    nested_path = tmp_path / \"these\" / \"are\" / \"directories\" / \"download.txt\"\n    await download.save_as(nested_path)\n    assert nested_path.exists()\n    assert nested_path.read_text(\"utf-8\") == \"Hello world\"\n    await page.close()\n\n\nasync def test_should_error_when_saving_with_downloads_disabled(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=False)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    with pytest.raises(Error) as exc:\n        await download.save_as(user_path)\n    assert (\n        \"Pass 'accept_downloads=True' when you are creating your browser context\"\n        in exc.value.message\n    )\n    assert (\n        \"Pass 'accept_downloads=True' when you are creating your browser context.\"\n        == await download.failure()\n    )\n    await page.close()\n\n\nasync def test_should_error_when_saving_after_deletion(\n    tmp_path: Path, browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    user_path = tmp_path / \"download.txt\"\n    await download.delete()\n    with pytest.raises(Error) as exc:\n        await download.save_as(user_path)\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc.value.message\n    await page.close()\n\n\nasync def test_should_report_non_navigation_downloads(\n    browser: Browser, server: Server\n) -> None:\n    # Mac WebKit embedder does not download in this case, although Safari does.\n    def handle_download(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.write(b\"Hello world\")\n        request.finish()\n\n    server.set_route(\"/download\", handle_download)\n\n    page = await browser.new_page(accept_downloads=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        f'<a download=\"file.txt\" href=\"{server.PREFIX}/download\">download</a>'\n    )\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    assert download.suggested_filename == \"file.txt\"\n    path = await download.path()\n    assert os.path.exists(path)\n    assert_file_content(path, \"Hello world\")\n    await page.close()\n\n\nasync def test_report_download_path_within_page_on_download_handler_for_files(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    on_download_path: Future[Path] = asyncio.Future()\n\n    async def on_download(download: Download) -> None:\n        on_download_path.set_result(await download.path())\n\n    page.once(\n        \"download\",\n        lambda res: asyncio.create_task(on_download(res)),\n    )\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    await page.click(\"a\")\n    path = await on_download_path\n    assert_file_content(path, \"Hello world\")\n    await page.close()\n\n\nasync def test_download_report_download_path_within_page_on_handle_for_blobs(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    on_download_path: \"asyncio.Future[Path]\" = asyncio.Future()\n\n    async def on_download(download: Download) -> None:\n        on_download_path.set_result(await download.path())\n\n    page.once(\n        \"download\",\n        lambda res: asyncio.create_task(on_download(res)),\n    )\n\n    await page.goto(server.PREFIX + \"/download-blob.html\")\n    await page.click(\"a\")\n    path = await on_download_path\n    assert_file_content(path, \"Hello world\")\n    await page.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_report_alt_click_downloads(\n    browser: Browser, server: Server\n) -> None:\n    # Firefox does not download on alt-click by default.\n    # Our WebKit embedder does not download on alt-click, although Safari does.\n    def handle_download(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.write(b\"Hello world\")\n        request.finish()\n\n    server.set_route(\"/download\", handle_download)\n\n    page = await browser.new_page(accept_downloads=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\", modifiers=[\"Alt\"])\n    download = await download_info.value\n    path = await download.path()\n    assert os.path.exists(path)\n    assert_file_content(path, \"Hello world\")\n    await page.close()\n\n\nasync def test_should_report_new_window_downloads(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(\n        f'<a target=_blank href=\"{server.PREFIX}/download\">download</a>'\n    )\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    path = await download.path()\n    assert os.path.exists(path)\n    await page.close()\n\n\nasync def test_should_delete_file(browser: Browser, server: Server) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    path = await download.path()\n    assert os.path.exists(path)\n    await download.delete()\n    assert os.path.exists(path) is False\n    await page.close()\n\n\nasync def test_should_delete_downloads_on_context_destruction(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download1 = await download_info.value\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download2 = await download_info.value\n    path1 = await download1.path()\n    path2 = await download2.path()\n    assert os.path.exists(path1)\n    assert os.path.exists(path2)\n    await page.context.close()\n    assert os.path.exists(path1) is False\n    assert os.path.exists(path2) is False\n\n\nasync def test_should_delete_downloads_on_browser_gone(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    browser = await browser_factory()\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/download\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download1 = await download_info.value\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download2 = await download_info.value\n    path1 = await download1.path()\n    path2 = await download2.path()\n    assert os.path.exists(path1)\n    assert os.path.exists(path2)\n    await browser.close()\n    assert os.path.exists(path1) is False\n    assert os.path.exists(path2) is False\n    assert os.path.exists(os.path.join(path1, \"..\")) is False\n\n\nasync def test_download_cancel_should_work(browser: Browser, server: Server) -> None:\n    def handle_download(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"application/octet-stream\")\n        request.setHeader(\"Content-Disposition\", \"attachment\")\n        # Chromium requires a large enough payload to trigger the download event soon enough\n        request.write(b\"a\" * 4096)\n        request.write(b\"foo\")\n\n    server.set_route(\"/downloadWithDelay\", handle_download)\n    page = await browser.new_page(accept_downloads=True)\n    await page.set_content(f'<a href=\"{server.PREFIX}/downloadWithDelay\">download</a>')\n    async with page.expect_download() as download_info:\n        await page.click(\"a\")\n    download = await download_info.value\n    await download.cancel()\n    assert await download.failure() == \"canceled\"\n    await page.close()\n"
  },
  {
    "path": "tests/async/test_element_handle.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Optional, cast\n\nimport pytest\n\nfrom playwright.async_api import Browser, ElementHandle, Error, FloatRect, Page\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def test_bounding_box(page: Page, server: Server) -> None:\n    await page.set_viewport_size({\"width\": 500, \"height\": 500})\n    await page.goto(server.PREFIX + \"/grid.html\")\n    element_handle = await page.query_selector(\".box:nth-of-type(13)\")\n    assert element_handle\n    box = await element_handle.bounding_box()\n    assert box == {\"x\": 100, \"y\": 50, \"width\": 50, \"height\": 50}\n\n\nasync def test_bounding_box_handle_nested_frames(page: Page, server: Server) -> None:\n    await page.set_viewport_size({\"width\": 500, \"height\": 500})\n    await page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    nested_frame = page.frame(name=\"dos\")\n    assert nested_frame\n    element_handle = await nested_frame.query_selector(\"div\")\n    assert element_handle\n    box = await element_handle.bounding_box()\n    assert box == {\"x\": 24, \"y\": 224, \"width\": 268, \"height\": 18}\n\n\nasync def test_bounding_box_return_null_for_invisible_elements(\n    page: Page, server: Server\n) -> None:\n    await page.set_content('<div style=\"display:none\">hi</div>')\n    element = await page.query_selector(\"div\")\n    assert element\n    assert await element.bounding_box() is None\n\n\nasync def test_bounding_box_force_a_layout(page: Page, server: Server) -> None:\n    await page.set_viewport_size({\"width\": 500, \"height\": 500})\n    await page.set_content('<div style=\"width: 100px; height: 100px\">hello</div>')\n    element_handle = await page.query_selector(\"div\")\n    assert element_handle\n    await page.evaluate('element => element.style.height = \"200px\"', element_handle)\n    box = await element_handle.bounding_box()\n    assert box == {\"x\": 8, \"y\": 8, \"width\": 100, \"height\": 200}\n\n\nasync def test_bounding_box_with_SVG_nodes(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" height=\"500\">\n             <rect id=\"theRect\" x=\"30\" y=\"50\" width=\"200\" height=\"300\"></rect>\n           </svg>\"\"\"\n    )\n    element = await page.query_selector(\"#therect\")\n    assert element\n    pw_bounding_box = await element.bounding_box()\n    web_bounding_box = await page.evaluate(\n        \"\"\"e => {\n            rect = e.getBoundingClientRect()\n            return {x: rect.x, y: rect.y, width: rect.width, height: rect.height}\n        }\"\"\",\n        element,\n    )\n    assert pw_bounding_box == web_bounding_box\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_bounding_box_with_page_scale(browser: Browser, server: Server) -> None:\n    context = await browser.new_context(\n        viewport={\"width\": 400, \"height\": 400}, is_mobile=True\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.evaluate(\n        \"\"\"button => {\n            document.body.style.margin = '0'\n            button.style.borderWidth = '0'\n            button.style.width = '200px'\n            button.style.height = '20px'\n            button.style.marginLeft = '17px'\n            button.style.marginTop = '23px'\n        }\"\"\"\n    )\n\n    box = await button.bounding_box()\n    assert box\n    assert round(box[\"x\"] * 100) == 17 * 100\n    assert round(box[\"y\"] * 100) == 23 * 100\n    assert round(box[\"width\"] * 100) == 200 * 100\n    assert round(box[\"height\"] * 100) == 20 * 100\n    await context.close()\n\n\nasync def test_bounding_box_when_inline_box_child_is_outside_of_viewport(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n            <style>\n            i {\n            position: absolute\n            top: -1000px\n            }\n            body {\n            margin: 0\n            font-size: 12px\n            }\n            </style>\n            <span><i>woof</i><b>doggo</b></span>\n        \"\"\"\n    )\n    handle = await page.query_selector(\"span\")\n    assert handle\n    box = await handle.bounding_box()\n    web_bounding_box = await handle.evaluate(\n        \"\"\"e => {\n        rect = e.getBoundingClientRect();\n        return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};\n    }\"\"\"\n    )\n\n    def roundbox(b: Optional[FloatRect]) -> FloatRect:\n        assert b\n        return {\n            \"x\": round(b[\"x\"] * 100),\n            \"y\": round(b[\"y\"] * 100),\n            \"width\": round(b[\"width\"] * 100),\n            \"height\": round(b[\"height\"] * 100),\n        }\n\n    assert roundbox(box) == roundbox(web_bounding_box)\n\n\nasync def test_content_frame(page: Page, server: Server, utils: Utils) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    element_handle = await page.query_selector(\"#frame1\")\n    assert element_handle\n    frame = await element_handle.content_frame()\n    assert frame == page.frames[1]\n\n\nasync def test_content_frame_for_non_iframes(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = cast(ElementHandle, await frame.evaluate_handle(\"document.body\"))\n    assert await element_handle.content_frame() is None\n\n\nasync def test_content_frame_for_document_element(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = cast(\n        ElementHandle, await frame.evaluate_handle(\"document.documentElement\")\n    )\n    assert await element_handle.content_frame() is None\n\n\nasync def test_owner_frame(page: Page, server: Server, utils: Utils) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = cast(ElementHandle, await frame.evaluate_handle(\"document.body\"))\n    assert await element_handle.owner_frame() == frame\n\n\nasync def test_owner_frame_for_cross_process_iframes(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(\n        page, \"frame1\", server.CROSS_PROCESS_PREFIX + \"/empty.html\"\n    )\n    frame = page.frames[1]\n    element_handle = cast(ElementHandle, await frame.evaluate_handle(\"document.body\"))\n    assert await element_handle.owner_frame() == frame\n\n\nasync def test_owner_frame_for_document(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = cast(ElementHandle, await frame.evaluate_handle(\"document\"))\n    assert await element_handle.owner_frame() == frame\n\n\nasync def test_owner_frame_for_iframe_elements(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.main_frame\n    element_handle = cast(\n        ElementHandle, await frame.evaluate_handle('document.querySelector(\"#frame1\")')\n    )\n    assert await element_handle.owner_frame() == frame\n\n\nasync def test_owner_frame_for_cross_frame_evaluations(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.main_frame\n    element_handle = cast(\n        ElementHandle,\n        await frame.evaluate_handle(\n            'document.querySelector(\"#frame1\").contentWindow.document.body'\n        ),\n    )\n    assert await element_handle.owner_frame() == frame.child_frames[0]\n\n\nasync def test_owner_frame_for_detached_elements(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    div_handle = cast(\n        ElementHandle,\n        await page.evaluate_handle(\n            \"\"\"() => {\n            div = document.createElement('div');\n            document.body.appendChild(div);\n            return div;\n        }\"\"\"\n        ),\n    )\n    assert div_handle\n\n    assert await div_handle.owner_frame() == page.main_frame\n    await page.evaluate(\n        \"\"\"() => {\n            div = document.querySelector('div')\n            document.body.removeChild(div)\n        }\"\"\"\n    )\n    assert await div_handle.owner_frame() == page.main_frame\n\n\nasync def test_owner_frame_for_adopted_elements(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window.__popup = window.open(url)\", server.EMPTY_PAGE\n        )\n    popup = await popup_info.value\n    div_handle = cast(\n        ElementHandle,\n        await page.evaluate_handle(\n            \"\"\"() => {\n            div = document.createElement('div');\n            document.body.appendChild(div);\n            return div;\n        }\"\"\"\n        ),\n    )\n    assert div_handle\n    assert await div_handle.owner_frame() == page.main_frame\n    await popup.wait_for_load_state(\"domcontentloaded\")\n    await page.evaluate(\n        \"\"\"() => {\n            div = document.querySelector('div');\n            window.__popup.document.body.appendChild(div);\n        }\"\"\"\n    )\n    assert await div_handle.owner_frame() == popup.main_frame\n\n\nasync def test_click(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.click()\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_with_node_removed(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate('delete window[\"Node\"]')\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.click()\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_for_shadow_dom_v1(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/shadow.html\")\n    button_handle = cast(ElementHandle, await page.evaluate_handle(\"button\"))\n    await button_handle.click()\n    assert await page.evaluate(\"clicked\")\n\n\nasync def test_click_for_TextNodes(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    buttonTextNode = cast(\n        ElementHandle,\n        await page.evaluate_handle('document.querySelector(\"button\").firstChild'),\n    )\n    await buttonTextNode.click()\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_click_throw_for_detached_nodes(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await page.evaluate(\"button => button.remove()\", button)\n    with pytest.raises(Error) as exc_info:\n        await button.click()\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\nasync def test_click_throw_for_hidden_nodes_with_force(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await page.evaluate('button => button.style.display = \"none\"', button)\n    with pytest.raises(Error) as exc_info:\n        await button.click(force=True)\n    assert \"Element is not visible\" in exc_info.value.message\n\n\nasync def test_click_throw_for_recursively_hidden_nodes_with_force(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    await page.evaluate('button => button.parentElement.style.display = \"none\"', button)\n    with pytest.raises(Error) as exc_info:\n        await button.click(force=True)\n    assert \"Element is not visible\" in exc_info.value.message\n\n\nasync def test_click_throw_for__br__elements_with_force(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"hello<br>goodbye\")\n    br = await page.query_selector(\"br\")\n    assert br\n    with pytest.raises(Error) as exc_info:\n        await br.click(force=True)\n    assert \"Element is outside of the viewport\" in exc_info.value.message\n\n\nasync def test_double_click_the_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\n        \"\"\"() => {\n            window.double = false;\n            button = document.querySelector('button');\n            button.addEventListener('dblclick', event => {\n            window.double = true;\n            });\n        }\"\"\"\n    )\n    button = await page.query_selector(\"button\")\n    assert button\n    await button.dblclick()\n    assert await page.evaluate(\"double\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_hover(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    button = await page.query_selector(\"#button-6\")\n    assert button\n    await button.hover()\n    assert (\n        await page.evaluate('document.querySelector(\"button:hover\").id') == \"button-6\"\n    )\n\n\nasync def test_hover_when_node_is_removed(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    await page.evaluate('delete window[\"Node\"]')\n    button = await page.query_selector(\"#button-6\")\n    assert button\n    await button.hover()\n    assert (\n        await page.evaluate('document.querySelector(\"button:hover\").id') == \"button-6\"\n    )\n\n\nasync def test_scroll(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/offscreenbuttons.html\")\n    for i in range(11):\n        button = await page.query_selector(f\"#btn{i}\")\n        assert button\n        before = await button.evaluate(\n            \"\"\"button => {\n                return button.getBoundingClientRect().right - window.innerWidth\n            }\"\"\"\n        )\n\n        assert before == 10 * i\n        await button.scroll_into_view_if_needed()\n        after = await button.evaluate(\n            \"\"\"button => {\n                return button.getBoundingClientRect().right - window.innerWidth\n            }\"\"\"\n        )\n\n        assert after <= 0\n        await page.evaluate(\"() => window.scrollTo(0, 0)\")\n\n\nasync def test_scroll_should_throw_for_detached_element(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<div>Hello</div>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    await div.evaluate(\"div => div.remove()\")\n    with pytest.raises(Error) as exc_info:\n        await div.scroll_into_view_if_needed()\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\nasync def waiting_helper(page: Page, after: str) -> None:\n    div = await page.query_selector(\"div\")\n    assert div\n    done = []\n\n    async def scroll() -> None:\n        done.append(False)\n        await div.scroll_into_view_if_needed()\n        done.append(True)\n\n    promise = asyncio.create_task(scroll())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await page.evaluate(\"() => new Promise(f => setTimeout(f, 1000))\")\n    assert done == [False]\n    await div.evaluate(after)\n    await promise\n    assert done == [False, True]\n\n\nasync def test_should_wait_for_display_none_to_become_visible(page: Page) -> None:\n    await page.set_content('<div style=\"display:none\">Hello</div>')\n    await waiting_helper(page, 'div => div.style.display = \"block\"')\n\n\nasync def test_should_work_for_visibility_hidden_element(page: Page) -> None:\n    await page.set_content('<div style=\"visibility:hidden\">Hello</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    await div.scroll_into_view_if_needed()\n\n\nasync def test_should_work_for_zero_sized_element(page: Page) -> None:\n    await page.set_content('<div style=\"height:0\">Hello</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    await div.scroll_into_view_if_needed()\n\n\nasync def test_should_wait_for_nested_display_none_to_become_visible(\n    page: Page,\n) -> None:\n    await page.set_content('<span style=\"display:none\"><div>Hello</div></span>')\n    await waiting_helper(page, 'div => div.parentElement.style.display = \"block\"')\n\n\nasync def test_should_timeout_waiting_for_visible(page: Page) -> None:\n    await page.set_content('<div style=\"display:none\">Hello</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    with pytest.raises(Error) as exc_info:\n        await div.scroll_into_view_if_needed(timeout=3000)\n    assert \"element is not visible\" in exc_info.value.message\n    assert \"retrying scroll into view action\" in exc_info.value.message\n\n\nasync def test_fill_input(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    handle = await page.query_selector(\"input\")\n    assert handle\n    await handle.fill(\"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_input_when_Node_is_removed(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.evaluate('delete window[\"Node\"]')\n    handle = await page.query_selector(\"input\")\n    assert handle\n    await handle.fill(\"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_select_textarea(\n    page: Page, server: Server, is_firefox: bool, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.evaluate('textarea => textarea.value = \"some value\"')\n    await textarea.select_text()\n    if is_firefox or is_webkit:\n        assert await textarea.evaluate(\"el => el.selectionStart\") == 0\n        assert await textarea.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert (\n            await page.evaluate(\"() => window.getSelection().toString()\")\n            == \"some value\"\n        )\n\n\nasync def test_select_input(\n    page: Page, server: Server, is_firefox: bool, is_webkit: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    input = await page.query_selector(\"input\")\n    assert input\n    await input.evaluate('input => input.value = \"some value\"')\n    await input.select_text()\n    if is_firefox or is_webkit:\n        assert await input.evaluate(\"el => el.selectionStart\") == 0\n        assert await input.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert (\n            await page.evaluate(\"() => window.getSelection().toString()\")\n            == \"some value\"\n        )\n\n\nasync def test_select_text_select_plain_div(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    div = await page.query_selector(\"div.plain\")\n    assert div\n    await div.select_text()\n    assert await page.evaluate(\"() => window.getSelection().toString()\") == \"Plain div\"\n\n\nasync def test_select_text_timeout_waiting_for_invisible_element(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.evaluate('e => e.style.display = \"none\"')\n    with pytest.raises(Error) as exc_info:\n        await textarea.select_text(timeout=3000)\n    assert \"element is not visible\" in exc_info.value.message\n\n\nasync def test_select_text_wait_for_visible(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.evaluate('textarea => textarea.value = \"some value\"')\n    await textarea.evaluate('e => e.style.display = \"none\"')\n    done = []\n\n    async def select_text() -> None:\n        done.append(False)\n        await textarea.select_text(timeout=3000)\n        done.append(True)\n\n    promise = asyncio.create_task(select_text())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await page.evaluate(\"() => new Promise(f => setTimeout(f, 1000))\")\n    await textarea.evaluate('e => e.style.display = \"block\"')\n    await promise\n    assert done == [False, True]\n\n\nasync def test_a_nice_preview(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/dom.html\")\n    outer = await page.query_selector(\"#outer\")\n    inner = await page.query_selector(\"#inner\")\n    assert inner\n    check = await page.query_selector(\"#check\")\n    text = await inner.evaluate_handle(\"e => e.firstChild\")\n    await page.evaluate(\"1\")  # Give them a chance to calculate the preview.\n    assert str(outer) == 'JSHandle@<div id=\"outer\" name=\"value\">…</div>'\n    assert str(inner) == 'JSHandle@<div id=\"inner\">Text,↵more text</div>'\n    assert str(text) == \"JSHandle@#text=Text,↵more text\"\n    assert (\n        str(check) == 'JSHandle@<input checked id=\"check\" foo=\"bar\"\" type=\"checkbox\"/>'\n    )\n\n\nasync def test_get_attribute(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = await page.query_selector(\"#outer\")\n    assert handle\n    assert await handle.get_attribute(\"name\") == \"value\"\n    assert await page.get_attribute(\"#outer\", \"name\") == \"value\"\n\n\nasync def test_inner_html(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = await page.query_selector(\"#outer\")\n    assert handle\n    assert await handle.inner_html() == '<div id=\"inner\">Text,\\nmore text</div>'\n    assert await page.inner_html(\"#outer\") == '<div id=\"inner\">Text,\\nmore text</div>'\n\n\nasync def test_inner_text(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = await page.query_selector(\"#inner\")\n    assert handle\n    assert await handle.inner_text() == \"Text, more text\"\n    assert await page.inner_text(\"#inner\") == \"Text, more text\"\n\n\nasync def test_inner_text_should_throw(page: Page, server: Server) -> None:\n    await page.set_content(\"<svg>text</svg>\")\n    with pytest.raises(Error) as exc_info1:\n        await page.inner_text(\"svg\")\n    assert \" Node is not an HTMLElement\" in exc_info1.value.message\n\n    handle = await page.query_selector(\"svg\")\n    assert handle\n    with pytest.raises(Error) as exc_info2:\n        await handle.inner_text()\n    assert \" Node is not an HTMLElement\" in exc_info2.value.message\n\n\nasync def test_text_content(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = await page.query_selector(\"#inner\")\n    assert handle\n    assert await handle.text_content() == \"Text,\\nmore text\"\n    assert await page.text_content(\"#inner\") == \"Text,\\nmore text\"\n\n\nasync def test_check_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    input = await page.query_selector(\"input\")\n    assert input\n    await input.check()\n    assert await page.evaluate(\"checkbox.checked\")\n\n\nasync def test_uncheck_the_box(page: Page) -> None:\n    await page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    input = await page.query_selector(\"input\")\n    assert input\n    await input.uncheck()\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_select_single_option(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    select = await page.query_selector(\"select\")\n    assert select\n    await select.select_option(value=\"blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_focus_a_button(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = await page.query_selector(\"button\")\n    assert button\n    assert await button.evaluate(\"button => document.activeElement === button\") is False\n    await button.focus()\n    assert await button.evaluate(\"button => document.activeElement === button\")\n\n\nasync def test_is_visible_and_is_hidden_should_work(page: Page) -> None:\n    await page.set_content(\"<div>Hi</div><span></span>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    assert await div.is_visible()\n    assert await div.is_hidden() is False\n    assert await page.is_visible(\"div\")\n    assert await page.is_hidden(\"div\") is False\n    span = await page.query_selector(\"span\")\n    assert span\n    assert await span.is_visible() is False\n    assert await span.is_hidden()\n    assert await page.is_visible(\"span\") is False\n    assert await page.is_hidden(\"span\")\n\n\nasync def test_is_enabled_and_is_disabled_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <button disabled>button1</button>\n        <button>button2</button>\n        <div>div</div>\n    \"\"\"\n    )\n    div = await page.query_selector(\"div\")\n    assert div\n    assert await div.is_enabled()\n    assert await div.is_disabled() is False\n    assert await page.is_enabled(\"div\")\n    assert await page.is_disabled(\"div\") is False\n    button1 = await page.query_selector(\":text('button1')\")\n    assert button1\n    assert await button1.is_enabled() is False\n    assert await button1.is_disabled()\n    assert await page.is_enabled(\":text('button1')\") is False\n    assert await page.is_disabled(\":text('button1')\")\n    button2 = await page.query_selector(\":text('button2')\")\n    assert button2\n    assert await button2.is_enabled()\n    assert await button2.is_disabled() is False\n    assert await page.is_enabled(\":text('button2')\")\n    assert await page.is_disabled(\":text('button2')\") is False\n\n\nasync def test_is_editable_should_work(page: Page) -> None:\n    await page.set_content(\n        \"<input id=input1 disabled><textarea></textarea><input id=input2>\"\n    )\n    await page.eval_on_selector(\"textarea\", \"t => t.readOnly = true\")\n    input1 = await page.query_selector(\"#input1\")\n    assert input1\n    assert await input1.is_editable() is False\n    assert await page.is_editable(\"#input1\") is False\n    input2 = await page.query_selector(\"#input2\")\n    assert input2\n    assert await input2.is_editable()\n    assert await page.is_editable(\"#input2\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    assert await textarea.is_editable() is False\n    assert await page.is_editable(\"textarea\") is False\n\n\nasync def test_is_checked_should_work(page: Page) -> None:\n    await page.set_content('<input type=\"checkbox\" checked><div>Not a checkbox</div>')\n    handle = await page.query_selector(\"input\")\n    assert handle\n    assert await handle.is_checked()\n    assert await page.is_checked(\"input\")\n    await handle.evaluate(\"input => input.checked = false\")\n    assert await handle.is_checked() is False\n    assert await page.is_checked(\"input\") is False\n    with pytest.raises(Error) as exc_info:\n        await page.is_checked(\"div\")\n    assert \"Not a checkbox or radio button\" in exc_info.value.message\n\n\nasync def test_input_value(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    element = await page.query_selector(\"input\")\n    assert element\n    await element.fill(\"my-text-content\")\n    assert await element.input_value() == \"my-text-content\"\n\n    await element.fill(\"\")\n    assert await element.input_value() == \"\"\n\n\nasync def test_set_checked(page: Page) -> None:\n    await page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    input = await page.query_selector(\"input\")\n    assert input\n    await input.set_checked(True)\n    assert await page.evaluate(\"checkbox.checked\")\n    await input.set_checked(False)\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_should_allow_disposing_twice(page: Page) -> None:\n    await page.set_content(\"<section>39</section>\")\n    element = await page.query_selector(\"section\")\n    assert element\n    await element.dispose()\n    await element.dispose()\n"
  },
  {
    "path": "tests/async/test_element_handle_wait_for_element_state.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import List\n\nimport pytest\n\nfrom playwright.async_api import ElementHandle, Error, Page\nfrom tests.server import Server\n\n\nasync def give_it_a_chance_to_resolve(page: Page) -> None:\n    for i in range(5):\n        await page.evaluate(\n            \"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))\"\n        )\n\n\nasync def wait_for_state(div: ElementHandle, state: str, done: List[bool]) -> None:\n    await div.wait_for_element_state(state)  # type: ignore\n    done[0] = True\n\n\nasync def wait_for_state_to_throw(\n    div: ElementHandle, state: str\n) -> pytest.ExceptionInfo[Error]:\n    with pytest.raises(Error) as exc_info:\n        await div.wait_for_element_state(state)  # type: ignore\n    return exc_info\n\n\nasync def test_should_wait_for_visible(page: Page) -> None:\n    await page.set_content('<div style=\"display:none\">content</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(div, \"visible\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    assert div\n    await div.evaluate('div => div.style.display = \"block\"')\n    await promise\n\n\nasync def test_should_wait_for_already_visible(page: Page) -> None:\n    await page.set_content(\"<div>content</div>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    await div.wait_for_element_state(\"visible\")\n\n\nasync def test_should_timeout_waiting_for_visible(page: Page) -> None:\n    await page.set_content('<div style=\"display:none\">content</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    with pytest.raises(Error) as exc_info:\n        await div.wait_for_element_state(\"visible\", timeout=1000)\n    assert \"Timeout 1000ms exceeded\" in exc_info.value.message\n\n\nasync def test_should_throw_waiting_for_visible_when_detached(page: Page) -> None:\n    await page.set_content('<div style=\"display:none\">content</div>')\n    div = await page.query_selector(\"div\")\n    assert div\n    promise = asyncio.create_task(wait_for_state_to_throw(div, \"visible\"))\n    await div.evaluate(\"div => div.remove()\")\n    exc_info = await promise\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\nasync def test_should_wait_for_hidden(page: Page) -> None:\n    await page.set_content(\"<div>content</div>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(div, \"hidden\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    await div.evaluate('div => div.style.display = \"none\"')\n    await promise\n\n\nasync def test_should_wait_for_already_hidden(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    await div.wait_for_element_state(\"hidden\")\n\n\nasync def test_should_wait_for_hidden_when_detached(page: Page) -> None:\n    await page.set_content(\"<div>content</div>\")\n    div = await page.query_selector(\"div\")\n    assert div\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(div, \"hidden\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    assert div\n    await div.evaluate(\"div => div.remove()\")\n    await promise\n\n\nasync def test_should_wait_for_enabled_button(page: Page, server: Server) -> None:\n    await page.set_content(\"<button disabled><span>Target</span></button>\")\n    span = await page.query_selector(\"text=Target\")\n    assert span\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(span, \"enabled\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    await span.evaluate(\"span => span.parentElement.disabled = false\")\n    await promise\n\n\nasync def test_should_throw_waiting_for_enabled_when_detached(page: Page) -> None:\n    await page.set_content(\"<button disabled>Target</button>\")\n    button = await page.query_selector(\"button\")\n    assert button\n    promise = asyncio.create_task(wait_for_state_to_throw(button, \"enabled\"))\n    await button.evaluate(\"button => button.remove()\")\n    exc_info = await promise\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\nasync def test_should_wait_for_disabled_button(page: Page) -> None:\n    await page.set_content(\"<button><span>Target</span></button>\")\n    span = await page.query_selector(\"text=Target\")\n    assert span\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(span, \"disabled\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    await span.evaluate(\"span => span.parentElement.disabled = true\")\n    await promise\n\n\nasync def test_should_wait_for_editable_input(page: Page, server: Server) -> None:\n    await page.set_content(\"<input readonly>\")\n    input = await page.query_selector(\"input\")\n    assert input\n    done = [False]\n    promise = asyncio.create_task(wait_for_state(input, \"editable\", done))\n    await give_it_a_chance_to_resolve(page)\n    assert done[0] is False\n    await input.evaluate(\"input => input.readOnly = false\")\n    await promise\n"
  },
  {
    "path": "tests/async/test_emulation_focus.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\n\nfrom playwright.async_api import Page\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def test_should_think_that_it_is_focused_by_default(page: Page) -> None:\n    assert await page.evaluate(\"document.hasFocus()\")\n\n\nasync def test_should_think_that_all_pages_are_focused(page: Page) -> None:\n    page2 = await page.context.new_page()\n    assert await page.evaluate(\"document.hasFocus()\")\n    assert await page2.evaluate(\"document.hasFocus()\")\n    await page2.close()\n\n\nasync def test_should_focus_popups_by_default(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"url => { window.open(url); }\", server.EMPTY_PAGE)\n    popup = await popup_info.value\n    assert await popup.evaluate(\"document.hasFocus()\")\n    assert await page.evaluate(\"document.hasFocus()\")\n\n\nasync def test_should_provide_target_for_keyboard_events(\n    page: Page, server: Server\n) -> None:\n    page2 = await page.context.new_page()\n    await asyncio.gather(\n        page.goto(server.PREFIX + \"/input/textarea.html\"),\n        page2.goto(server.PREFIX + \"/input/textarea.html\"),\n    )\n    await asyncio.gather(\n        page.focus(\"input\"),\n        page2.focus(\"input\"),\n    )\n    text = \"first\"\n    text2 = \"second\"\n    await asyncio.gather(\n        page.keyboard.type(text),\n        page2.keyboard.type(text2),\n    )\n    results = await asyncio.gather(\n        page.evaluate(\"result\"),\n        page2.evaluate(\"result\"),\n    )\n    assert results == [text, text2]\n\n\nasync def test_should_not_affect_mouse_event_target_page(\n    page: Page, server: Server\n) -> None:\n    page2 = await page.context.new_page()\n    click_counter = \"\"\"() => {\n      document.onclick = () => window.click_count = (window.click_count || 0) + 1;\n    }\"\"\"\n    await asyncio.gather(\n        page.evaluate(click_counter),\n        page2.evaluate(click_counter),\n        page.focus(\"body\"),\n        page2.focus(\"body\"),\n    )\n    await asyncio.gather(\n        page.mouse.click(1, 1),\n        page2.mouse.click(1, 1),\n    )\n    counters = await asyncio.gather(\n        page.evaluate(\"window.click_count\"),\n        page2.evaluate(\"window.click_count\"),\n    )\n    assert counters == [1, 1]\n\n\nasync def test_should_change_document_activeElement(page: Page, server: Server) -> None:\n    page2 = await page.context.new_page()\n    await asyncio.gather(\n        page.goto(server.PREFIX + \"/input/textarea.html\"),\n        page2.goto(server.PREFIX + \"/input/textarea.html\"),\n    )\n    await asyncio.gather(\n        page.focus(\"input\"),\n        page2.focus(\"textarea\"),\n    )\n    active = await asyncio.gather(\n        page.evaluate(\"document.activeElement.tagName\"),\n        page2.evaluate(\"document.activeElement.tagName\"),\n    )\n    assert active == [\"INPUT\", \"TEXTAREA\"]\n\n\nasync def test_should_change_focused_iframe(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    [frame1, frame2] = await asyncio.gather(\n        utils.attach_frame(page, \"frame1\", server.PREFIX + \"/input/textarea.html\"),\n        utils.attach_frame(page, \"frame2\", server.PREFIX + \"/input/textarea.html\"),\n    )\n    logger = \"\"\"() => {\n        self._events = [];\n        const element = document.querySelector('input');\n        element.onfocus = element.onblur = (e) => self._events.push(e.type);\n    }\"\"\"\n    await asyncio.gather(\n        frame1.evaluate(logger),\n        frame2.evaluate(logger),\n    )\n    focused = await asyncio.gather(\n        frame1.evaluate(\"document.hasFocus()\"),\n        frame2.evaluate(\"document.hasFocus()\"),\n    )\n    assert focused == [False, False]\n    await frame1.focus(\"input\")\n    events = await asyncio.gather(\n        frame1.evaluate(\"self._events\"),\n        frame2.evaluate(\"self._events\"),\n    )\n    assert events == [[\"focus\"], []]\n    focused = await asyncio.gather(\n        frame1.evaluate(\"document.hasFocus()\"),\n        frame2.evaluate(\"document.hasFocus()\"),\n    )\n    assert focused == [True, False]\n    await frame2.focus(\"input\")\n    events = await asyncio.gather(\n        frame1.evaluate(\"self._events\"),\n        frame2.evaluate(\"self._events\"),\n    )\n    assert events == [[\"focus\", \"blur\"], [\"focus\"]]\n    focused = await asyncio.gather(\n        frame1.evaluate(\"document.hasFocus()\"),\n        frame2.evaluate(\"document.hasFocus()\"),\n    )\n    assert focused == [False, True]\n"
  },
  {
    "path": "tests/async/test_expect_misc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Page, TimeoutError, expect\nfrom tests.server import Server\n\n\nasync def test_to_be_in_viewport_should_work(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n      <div id=big style=\"height: 10000px;\"></div>\n      <div id=small>foo</div>\n    \"\"\"\n    )\n    await expect(page.locator(\"#big\")).to_be_in_viewport()\n    await expect(page.locator(\"#small\")).not_to_be_in_viewport()\n    await page.locator(\"#small\").scroll_into_view_if_needed()\n    await expect(page.locator(\"#small\")).to_be_in_viewport()\n    await expect(page.locator(\"#small\")).to_be_in_viewport(ratio=1)\n\n\nasync def test_to_be_in_viewport_should_respect_ratio_option(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <style>body, div, html { padding: 0; margin: 0; }</style>\n      <div id=big style=\"height: 400vh;\"></div>\n    \"\"\"\n    )\n    await expect(page.locator(\"div\")).to_be_in_viewport()\n    await expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.1)\n    await expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.2)\n\n    await expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.25)\n    # In this test, element's ratio is 0.25.\n    await expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.26)\n\n    await expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.3)\n    await expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.7)\n    await expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.8)\n\n\nasync def test_to_be_in_viewport_should_have_good_stack(\n    page: Page, server: Server\n) -> None:\n    with pytest.raises(AssertionError) as exc_info:\n        await expect(page.locator(\"body\")).not_to_be_in_viewport(timeout=1000)\n    assert 'unexpected value \"viewport ratio' in str(exc_info.value)\n\n\nasync def test_to_be_in_viewport_should_report_intersection_even_if_fully_covered_by_other_element(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <h1>hello</h1>\n      <div style=\"position: relative; height: 10000px; top: -5000px;></div>\n    \"\"\"\n    )\n    await expect(page.locator(\"h1\")).to_be_in_viewport()\n\n\nasync def test_should_have_timeout_error_name(page: Page) -> None:\n    with pytest.raises(TimeoutError) as exc_info:\n        await page.wait_for_selector(\"#not-found\", timeout=1)\n    assert exc_info.value.name == \"TimeoutError\"\n"
  },
  {
    "path": "tests/async/test_extension.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom pathlib import Path\nfrom typing import Any, AsyncGenerator, Awaitable, Callable, Dict, List, Optional\n\nimport pytest\n\nfrom playwright.async_api import BrowserContext, BrowserType\n\nfrom ..server import Server\n\n\n@pytest.fixture()\nasync def launch_persistent_context(\n    browser_type: BrowserType,\n    browser_channel: Optional[str],\n    tmp_path: Path,\n    launch_arguments: Dict[str, Any],\n    is_headless_shell: bool,\n) -> AsyncGenerator[Callable[..., Awaitable[BrowserContext]], None]:\n    if browser_channel and browser_channel.startswith(\"chrome\"):\n        pytest.skip(\n            \"--load-extension is not supported in Chrome anymore. https://groups.google.com/a/chromium.org/g/chromium-extensions/c/1-g8EFx2BBY/m/S0ET5wPjCAAJ\"\n        )\n    if is_headless_shell:\n        pytest.skip(\"Headless Shell has no support for extensions\")\n\n    contexts: List[BrowserContext] = []\n\n    async def launch(extension_path: str, **kwargs: Any) -> BrowserContext:\n        context = await browser_type.launch_persistent_context(\n            str(tmp_path),\n            **launch_arguments,\n            **kwargs,\n            args=[\n                f\"--disable-extensions-except={extension_path}\",\n                f\"--load-extension={extension_path}\",\n            ],\n        )\n        contexts.append(context)\n        return context\n\n    yield launch\n\n    for context in contexts:\n        await context.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_give_access_to_the_service_worker(\n    launch_persistent_context: Callable[..., Awaitable[BrowserContext]],\n    assetdir: Path,\n) -> None:\n    extension_path = str(assetdir / \"extension-mv3-simple\")\n    context = await launch_persistent_context(extension_path)\n    service_workers = context.service_workers\n    service_worker = (\n        service_workers[0]\n        if len(service_workers)\n        else await context.wait_for_event(\"serviceworker\")\n    )\n    assert service_worker\n    assert service_worker in context.service_workers\n    while not await service_worker.evaluate(\"globalThis.MAGIC\") == 42:\n        await context.pages[0].wait_for_timeout(100)\n    await context.close()\n    assert len(context.background_pages) == 0\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_give_access_to_the_service_worker_when_recording_video(\n    launch_persistent_context: Callable[..., Awaitable[BrowserContext]],\n    tmp_path: Path,\n    assetdir: Path,\n) -> None:\n    extension_path = str(assetdir / \"extension-mv3-simple\")\n    context = await launch_persistent_context(\n        extension_path, record_video_dir=(tmp_path / \"videos\")\n    )\n    service_workers = context.service_workers\n    service_worker = (\n        service_workers[0]\n        if len(service_workers)\n        else await context.wait_for_event(\"serviceworker\")\n    )\n    assert service_worker\n    assert service_worker in context.service_workers\n    while not await service_worker.evaluate(\"globalThis.MAGIC\") == 42:\n        await context.pages[0].wait_for_timeout(100)\n    await context.close()\n    assert len(context.background_pages) == 0\n\n\n# https://github.com/microsoft/playwright/issues/32762\n@pytest.mark.only_browser(\"chromium\")\nasync def test_should_report_console_messages_from_content_script(\n    launch_persistent_context: Callable[..., Awaitable[BrowserContext]],\n    assetdir: Path,\n    server: Server,\n) -> None:\n    extension_path = str(assetdir / \"extension-mv3-with-logging\")\n    context = await launch_persistent_context(extension_path)\n    page = await context.new_page()\n    [message, _] = await asyncio.gather(\n        page.context.wait_for_event(\n            \"console\",\n            lambda e: \"Test console log from a third-party execution context\" in e.text,\n        ),\n        page.goto(server.EMPTY_PAGE),\n    )\n    assert \"Test console log from a third-party execution context\" in message.text\n    await context.close()\n"
  },
  {
    "path": "tests/async/test_fetch_browser_context.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nimport json\nfrom typing import Any, Callable, cast\nfrom urllib.parse import parse_qs\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext, Error, FilePayload, Page\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import must\n\n\nasync def test_get_should_work(context: BrowserContext, server: Server) -> None:\n    response = await context.request.get(server.PREFIX + \"/simple.json\")\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n\nasync def test_fetch_should_work(context: BrowserContext, server: Server) -> None:\n    response = await context.request.fetch(server.PREFIX + \"/simple.json\")\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n\nasync def test_should_throw_on_network_error(\n    context: BrowserContext, server: Server\n) -> None:\n    server.set_route(\"/test\", lambda request: request.loseConnection())\n    with pytest.raises(Error, match=\"socket hang up\"):\n        await context.request.fetch(server.PREFIX + \"/test\")\n\n\nasync def test_should_add_session_cookies_to_request(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"username\",\n                \"value\": \"John Doe\",\n                \"url\": server.EMPTY_PAGE,\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ]\n    )\n    [server_req, response] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.get(server.EMPTY_PAGE),\n    )\n    assert server_req.getHeader(\"Cookie\") == \"username=John Doe\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_support_query_params(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    expected_params = {\"p1\": \"v1\", \"парам2\": \"знач2\"}\n    [server_req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        getattr(context.request, method)(\n            server.EMPTY_PAGE + \"?p1=foo\", params=expected_params\n        ),\n    )\n    assert list(map(lambda x: x.decode(), server_req.args[\"p1\".encode()])) == [\n        \"foo\",\n        \"v1\",\n    ]\n    assert server_req.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_support_params_passed_as_object(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    params = {\n        \"param1\": \"value1\",\n        \"парам2\": \"знач2\",\n    }\n    [server_req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        getattr(context.request, method)(server.EMPTY_PAGE, params=params),\n    )\n    assert server_req.args[\"param1\".encode()][0].decode() == \"value1\"\n    assert len(server_req.args[\"param1\".encode()]) == 1\n    assert server_req.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_support_params_passed_as_strings(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    params = \"?param1=value1&param1=value2&парам2=знач2\"\n    [server_req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        getattr(context.request, method)(server.EMPTY_PAGE, params=params),\n    )\n    assert list(map(lambda x: x.decode(), server_req.args[\"param1\".encode()])) == [\n        \"value1\",\n        \"value2\",\n    ]\n    assert len(server_req.args[\"param1\".encode()]) == 2\n    assert server_req.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_support_fail_on_status_code(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    with pytest.raises(Error, match=\"404 Not Found\"):\n        await getattr(context.request, method)(\n            server.PREFIX + \"/this-does-clearly-not-exist.html\",\n            fail_on_status_code=True,\n        )\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_support_ignore_https_errors_option(\n    context: BrowserContext, https_server: Server, method: str\n) -> None:\n    response = await getattr(context.request, method)(\n        https_server.EMPTY_PAGE, ignore_https_errors=True\n    )\n    assert response.ok\n    assert response.status == 200\n\n\nasync def test_should_not_add_context_cookie_if_cookie_header_passed_as_parameter(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.add_cookies(\n        [\n            {\n                \"name\": \"username\",\n                \"value\": \"John Doe\",\n                \"url\": server.EMPTY_PAGE,\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ]\n    )\n    [server_req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.get(server.EMPTY_PAGE, headers={\"Cookie\": \"foo=bar\"}),\n    )\n    assert server_req.getHeader(\"Cookie\") == \"foo=bar\"\n\n\nasync def test_should_support_http_credentials_send_immediately_for_browser_context(\n    context_factory: \"Callable[..., asyncio.Future[BrowserContext]]\", server: Server\n) -> None:\n    context = await context_factory(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n            \"send\": \"always\",\n        }\n    )\n    # First request\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"), context.request.get(server.EMPTY_PAGE)\n    )\n    expected_auth = \"Basic \" + base64.b64encode(b\"user:pass\").decode()\n    assert server_request.getHeader(\"authorization\") == expected_auth\n    assert response.status == 200\n\n    # Second request\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.get(server.CROSS_PROCESS_PREFIX + \"/empty.html\"),\n    )\n    # Not sent to another origin.\n    assert server_request.getHeader(\"authorization\") is None\n    assert response.status == 200\n\n\nasync def test_support_http_credentials_send_immediately_for_browser_new_page(\n    server: Server, browser: Browser\n) -> None:\n    page = await browser.new_page(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n            \"send\": \"always\",\n        }\n    )\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"), page.request.get(server.EMPTY_PAGE)\n    )\n    assert (\n        server_request.getHeader(\"authorization\")\n        == \"Basic \" + base64.b64encode(b\"user:pass\").decode()\n    )\n    assert response.status == 200\n\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        page.request.get(server.CROSS_PROCESS_PREFIX + \"/empty.html\"),\n    )\n    # Not sent to another origin.\n    assert server_request.getHeader(\"authorization\") is None\n    assert response.status == 200\n\n    await page.close()\n\n\n@pytest.mark.parametrize(\"method\", [\"delete\", \"patch\", \"post\", \"put\"])\nasync def test_should_support_post_data(\n    context: BrowserContext, method: str, server: Server\n) -> None:\n    async def support_post_data(fetch_data: Any, request_post_data: Any) -> None:\n        [request, response] = await asyncio.gather(\n            server.wait_for_request(\"/simple.json\"),\n            getattr(context.request, method)(\n                server.PREFIX + \"/simple.json\", data=fetch_data\n            ),\n        )\n        assert request.method.decode() == method.upper()\n        assert request.post_body == request_post_data\n        assert response.status == 200\n        assert response.url == server.PREFIX + \"/simple.json\"\n        assert request.getHeader(\"Content-Length\") == str(len(must(request.post_body)))\n\n    await support_post_data(\"My request\", \"My request\".encode())\n    await support_post_data(b\"My request\", \"My request\".encode())\n    await support_post_data([\"my\", \"request\"], json.dumps([\"my\", \"request\"]).encode())\n    await support_post_data({\"my\": \"request\"}, json.dumps({\"my\": \"request\"}).encode())\n    with pytest.raises(Error, match=\"Unsupported 'data' type: <class 'function'>\"):\n        await support_post_data(lambda: None, None)\n\n\nasync def test_should_support_application_x_www_form_urlencoded(\n    context: BrowserContext, server: Server\n) -> None:\n    [request, response] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.post(\n            server.PREFIX + \"/empty.html\",\n            form={\n                \"firstName\": \"John\",\n                \"lastName\": \"Doe\",\n                \"file\": \"f.js\",\n            },\n        ),\n    )\n    assert request.method == b\"POST\"\n    assert request.getHeader(\"Content-Type\") == \"application/x-www-form-urlencoded\"\n    assert request.post_body\n    body = request.post_body.decode()\n    assert request.getHeader(\"Content-Length\") == str(len(body))\n    params = parse_qs(request.post_body)\n    assert params[b\"firstName\"] == [b\"John\"]\n    assert params[b\"lastName\"] == [b\"Doe\"]\n    assert params[b\"file\"] == [b\"f.js\"]\n\n\nasync def test_should_support_multipart_form_data(\n    context: BrowserContext, server: Server\n) -> None:\n    file: FilePayload = {\n        \"name\": \"f.js\",\n        \"mimeType\": \"text/javascript\",\n        \"buffer\": b\"var x = 10;\\r\\n;console.log(x);\",\n    }\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.post(\n            server.PREFIX + \"/empty.html\",\n            multipart={\n                \"firstName\": \"John\",\n                \"lastName\": \"Doe\",\n                \"file\": file,\n            },\n        ),\n    )\n    assert request.method == b\"POST\"\n    assert cast(str, request.getHeader(\"Content-Type\")).startswith(\n        \"multipart/form-data; \"\n    )\n    assert must(request.getHeader(\"Content-Length\")) == str(\n        len(must(request.post_body))\n    )\n    assert request.args[b\"firstName\"] == [b\"John\"]\n    assert request.args[b\"lastName\"] == [b\"Doe\"]\n    assert request.args[b\"file\"][0] == file[\"buffer\"]\n\n\nasync def test_should_add_default_headers(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    [request, response] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        context.request.get(server.EMPTY_PAGE),\n    )\n    assert request.getHeader(\"Accept\") == \"*/*\"\n    assert request.getHeader(\"Accept-Encoding\") == \"gzip,deflate,br\"\n    assert request.getHeader(\"User-Agent\") == await page.evaluate(\n        \"() => navigator.userAgent\"\n    )\n\n\nasync def test_should_work_after_context_dispose(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.close(reason=\"Test ended.\")\n    with pytest.raises(Error, match=\"Test ended.\"):\n        await context.request.get(server.EMPTY_PAGE)\n\n\nasync def test_should_retry_ECONNRESET(context: BrowserContext, server: Server) -> None:\n    request_count = 0\n\n    def _handle_request(req: TestServerRequest) -> None:\n        nonlocal request_count\n        request_count += 1\n        if request_count <= 3:\n            assert req.transport\n            req.transport.abortConnection()\n            return\n        req.setHeader(\"content-type\", \"text/plain\")\n        req.write(b\"Hello!\")\n        req.finish()\n\n    server.set_route(\"/test\", _handle_request)\n    response = await context.request.fetch(server.PREFIX + \"/test\", max_retries=3)\n    assert response.status == 200\n    assert await response.text() == \"Hello!\"\n    assert request_count == 4\n"
  },
  {
    "path": "tests/async/test_fetch_global.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nimport json\nimport sys\nfrom pathlib import Path\nfrom typing import Any\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom playwright.async_api import APIResponse, Error, Playwright, StorageState\nfrom tests.server import Server, TestServerRequest\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\nasync def test_should_work(playwright: Playwright, method: str, server: Server) -> None:\n    request = await playwright.request.new_context()\n    response: APIResponse = await getattr(request, method)(\n        server.PREFIX + \"/simple.json\"\n    )\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert await response.text() == (\"\" if method == \"head\" else '{\"foo\": \"bar\"}\\n')\n\n\nasync def test_should_dispose_global_request(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    response = await request.get(server.PREFIX + \"/simple.json\")\n    assert await response.json() == {\"foo\": \"bar\"}\n    await response.dispose()\n    with pytest.raises(Error, match=\"Response has been disposed\"):\n        await response.body()\n\n\nasync def test_should_dispose_with_custom_error_message(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    await request.dispose(reason=\"My reason\")\n    with pytest.raises(Error, match=\"My reason\"):\n        await request.get(server.EMPTY_PAGE)\n\n\nasync def test_should_support_global_user_agent_option(\n    playwright: Playwright, server: Server\n) -> None:\n    api_request_context = await playwright.request.new_context(user_agent=\"My Agent\")\n    response = await api_request_context.get(server.PREFIX + \"/empty.html\")\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        api_request_context.get(server.EMPTY_PAGE),\n    )\n    assert response.ok is True\n    assert response.url == server.EMPTY_PAGE\n    assert request.getHeader(\"user-agent\") == \"My Agent\"\n\n\nasync def test_should_support_global_timeout_option(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context(timeout=100)\n    server.set_route(\"/empty.html\", lambda req: None)\n    with pytest.raises(Error, match=\"Timeout 100ms exceeded\"):\n        await request.get(server.EMPTY_PAGE)\n\n\nasync def test_should_support_timeout_option_in_get_method(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    server.set_route(\"/empty.html\", lambda req: None)\n    with pytest.raises(Error, match=\"APIRequestContext.get: Timeout 123ms exceeded.\"):\n        await request.get(server.EMPTY_PAGE, timeout=123)\n\n\nasync def test_should_propagate_extra_http_headers_with_redirects(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/simple.json\")\n    request = await playwright.request.new_context(\n        extra_http_headers={\"My-Secret\": \"Value\"}\n    )\n    [req1, req2, req3, _] = await asyncio.gather(\n        server.wait_for_request(\"/a/redirect1\"),\n        server.wait_for_request(\"/b/c/redirect2\"),\n        server.wait_for_request(\"/simple.json\"),\n        request.get(f\"{server.PREFIX}/a/redirect1\"),\n    )\n    assert req1.getHeader(\"my-secret\") == \"Value\"\n    assert req2.getHeader(\"my-secret\") == \"Value\"\n    assert req3.getHeader(\"my-secret\") == \"Value\"\n\n\nasync def test_should_support_global_http_credentials_option(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request1 = await playwright.request.new_context()\n    response1 = await request1.get(server.EMPTY_PAGE)\n    assert response1.status == 401\n    await response1.dispose()\n\n    request2 = await playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    response2 = await request2.get(server.EMPTY_PAGE)\n    assert response2.status == 200\n    assert response2.ok is True\n    await response2.dispose()\n\n\nasync def test_should_return_error_with_wrong_credentials(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = await playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"wrong\"}\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    assert response.ok is False\n\n\nasync def test_should_work_with_correct_credentials_and_matching_origin(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = await playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX,\n        }\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 200\n    await response.dispose()\n\n\nasync def test_should_work_with_correct_credentials_and_matching_origin_case_insensitive(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = await playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n        }\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 200\n    await response.dispose()\n\n\nasync def test_should_return_error_with_correct_credentials_and_mismatching_scheme(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = await playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.replace(\"http://\", \"https://\"),\n        }\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    await response.dispose()\n\n\nasync def test_should_return_error_with_correct_credentials_and_mismatching_hostname(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    hostname = urlparse(server.PREFIX).hostname\n    assert hostname\n    origin = server.PREFIX.replace(hostname, \"mismatching-hostname\")\n    request = await playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    await response.dispose()\n\n\nasync def test_should_return_error_with_correct_credentials_and_mismatching_port(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    origin = server.PREFIX.replace(str(server.PORT), str(server.PORT + 1))\n    request = await playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    response = await request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    await response.dispose()\n\n\nasync def test_support_http_credentials_send_immediately(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n            \"send\": \"always\",\n        }\n    )\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"), request.get(server.EMPTY_PAGE)\n    )\n    assert (\n        server_request.getHeader(\"authorization\")\n        == \"Basic \" + base64.b64encode(b\"user:pass\").decode()\n    )\n    assert response.status == 200\n\n    server_request, response = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.get(server.CROSS_PROCESS_PREFIX + \"/empty.html\"),\n    )\n    # Not sent to another origin.\n    assert server_request.getHeader(\"authorization\") is None\n    assert response.status == 200\n\n\nasync def test_should_support_global_ignore_https_errors_option(\n    playwright: Playwright, https_server: Server\n) -> None:\n    request = await playwright.request.new_context(ignore_https_errors=True)\n    response = await request.get(https_server.EMPTY_PAGE)\n    assert response.status == 200\n    assert response.ok is True\n    assert response.url == https_server.EMPTY_PAGE\n    await response.dispose()\n\n\nasync def test_should_resolve_url_relative_to_global_base_url_option(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context(base_url=server.PREFIX)\n    response = await request.get(\"/empty.html\")\n    assert response.status == 200\n    assert response.ok is True\n    assert response.url == server.EMPTY_PAGE\n    await response.dispose()\n\n\nasync def test_should_use_playwright_as_a_user_agent(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    [server_req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.get(server.EMPTY_PAGE),\n    )\n    assert str(server_req.getHeader(\"User-Agent\")).startswith(\"Playwright/\")\n    await request.dispose()\n\n\nasync def test_should_return_empty_body(playwright: Playwright, server: Server) -> None:\n    request = await playwright.request.new_context()\n    response = await request.get(server.EMPTY_PAGE)\n    body = await response.body()\n    assert len(body) == 0\n    assert await response.text() == \"\"\n    await request.dispose()\n    with pytest.raises(Error, match=\"Response has been disposed\"):\n        await response.body()\n\n\nasync def test_storage_state_should_round_trip_through_file(\n    playwright: Playwright, tmp_path: Path\n) -> None:\n    expected: StorageState = {\n        \"cookies\": [\n            {\n                \"name\": \"a\",\n                \"value\": \"b\",\n                \"domain\": \"a.b.one.com\",\n                \"path\": \"/\",\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ],\n        \"origins\": [],\n    }\n    request = await playwright.request.new_context(storage_state=expected)\n    path = tmp_path / \"storage-state.json\"\n    actual = await request.storage_state(path=path)\n    assert actual == expected\n\n    written = path.read_text(\"utf8\")\n    assert json.loads(written) == expected\n\n    request2 = await playwright.request.new_context(storage_state=path)\n    state2 = await request2.storage_state()\n    assert state2 == expected\n\n\nserialization_data = [\n    [{\"foo\": \"bar\"}],\n    [[\"foo\", \"bar\", 2021]],\n    [\"foo\"],\n    [True],\n    [2021],\n]\n\n\n@pytest.mark.parametrize(\"serialization\", serialization_data)\nasync def test_should_json_stringify_body_when_content_type_is_application_json(\n    playwright: Playwright, server: Server, serialization: Any\n) -> None:\n    request = await playwright.request.new_context()\n    [req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.post(\n            server.EMPTY_PAGE,\n            headers={\"content-type\": \"application/json\"},\n            data=serialization,\n        ),\n    )\n    body = req.post_body\n    assert body\n    assert body.decode() == json.dumps(serialization)\n    await request.dispose()\n\n\n@pytest.mark.parametrize(\"serialization\", serialization_data)\nasync def test_should_not_double_stringify_body_when_content_type_is_application_json(\n    playwright: Playwright, server: Server, serialization: Any\n) -> None:\n    request = await playwright.request.new_context()\n    stringified_value = json.dumps(serialization)\n    [req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.post(\n            server.EMPTY_PAGE,\n            headers={\"content-type\": \"application/json\"},\n            data=stringified_value,\n        ),\n    )\n\n    body = req.post_body\n    assert body\n    assert body.decode() == stringified_value\n    await request.dispose()\n\n\nasync def test_should_accept_already_serialized_data_as_bytes_when_content_type_is_application_json(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    stringified_value = json.dumps({\"foo\": \"bar\"}).encode()\n    [req, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.post(\n            server.EMPTY_PAGE,\n            headers={\"content-type\": \"application/json\"},\n            data=stringified_value,\n        ),\n    )\n    body = req.post_body\n    assert body == stringified_value\n    await request.dispose()\n\n\nasync def test_should_contain_default_user_agent(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    [server_request, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"),\n        request.get(server.EMPTY_PAGE),\n    )\n    user_agent = server_request.getHeader(\"user-agent\")\n    assert user_agent\n    assert \"python\" in user_agent\n    assert f\"{sys.version_info.major}.{sys.version_info.minor}\" in user_agent\n\n\nasync def test_should_throw_an_error_when_max_redirects_is_exceeded(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/b/c/redirect3\")\n    server.set_redirect(\"/b/c/redirect3\", \"/b/c/redirect4\")\n    server.set_redirect(\"/b/c/redirect4\", \"/simple.json\")\n\n    request = await playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        for max_redirects in [1, 2, 3]:\n            with pytest.raises(Error) as exc_info:\n                await request.fetch(\n                    server.PREFIX + \"/a/redirect1\",\n                    method=method,\n                    max_redirects=max_redirects,\n                )\n            assert \"Max redirect count exceeded\" in str(exc_info)\n\n\nasync def test_should_not_follow_redirects_when_max_redirects_is_set_to_0(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/simple.json\")\n\n    request = await playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        response = await request.fetch(\n            server.PREFIX + \"/a/redirect1\", method=method, max_redirects=0\n        )\n        assert response.headers[\"location\"] == \"/b/c/redirect2\"\n        assert response.status == 302\n\n\nasync def test_should_throw_an_error_when_max_redirects_is_less_than_0(\n    playwright: Playwright,\n    server: Server,\n) -> None:\n    request = await playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        with pytest.raises(AssertionError) as exc_info:\n            await request.fetch(\n                server.PREFIX + \"/a/redirect1\", method=method, max_redirects=-1\n            )\n        assert \"'max_redirects' must be greater than or equal to '0'\" in str(exc_info)\n\n\nasync def test_should_serialize_request_data(\n    playwright: Playwright, server: Server\n) -> None:\n    request = await playwright.request.new_context()\n    server.set_route(\"/echo\", lambda req: (req.write(req.post_body), req.finish()))\n    for data, expected in [\n        ({\"foo\": None}, '{\"foo\": null}'),\n        ([], \"[]\"),\n        ({}, \"{}\"),\n        (\"\", \"\"),\n    ]:\n        response = await request.post(server.PREFIX + \"/echo\", data=data)\n        assert response.status == 200\n        assert await response.text() == expected\n    await request.dispose()\n\n\nasync def test_should_retry_ECONNRESET(playwright: Playwright, server: Server) -> None:\n    request_count = 0\n\n    def _handle_request(req: TestServerRequest) -> None:\n        nonlocal request_count\n        request_count += 1\n        if request_count <= 3:\n            assert req.transport\n            req.transport.abortConnection()\n            return\n        req.setHeader(\"content-type\", \"text/plain\")\n        req.write(b\"Hello!\")\n        req.finish()\n\n    server.set_route(\"/test\", _handle_request)\n    request = await playwright.request.new_context()\n    response = await request.fetch(server.PREFIX + \"/test\", max_retries=3)\n    assert response.status == 200\n    assert await response.text() == \"Hello!\"\n    assert request_count == 4\n    await request.dispose()\n\n\nasync def test_should_throw_when_fail_on_status_code_is_true(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_route(\n        \"/empty.html\",\n        lambda req: (\n            req.setResponseCode(404),\n            req.setHeader(\"Content-Length\", \"10\"),\n            req.setHeader(\"Content-Type\", \"text/plain\"),\n            req.write(b\"Not found.\"),\n            req.finish(),\n        ),\n    )\n    request = await playwright.request.new_context(fail_on_status_code=True)\n    with pytest.raises(Error, match=\"404 Not Found\"):\n        await request.fetch(server.EMPTY_PAGE)\n    await request.dispose()\n\n\nasync def test_should_not_throw_when_fail_on_status_code_is_false(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_route(\n        \"/empty.html\",\n        lambda req: (\n            req.setResponseCode(404),\n            req.setHeader(\"Content-Length\", \"10\"),\n            req.setHeader(\"Content-Type\", \"text/plain\"),\n            req.write(b\"Not found.\"),\n            req.finish(),\n        ),\n    )\n    request = await playwright.request.new_context(fail_on_status_code=False)\n    response = await request.fetch(server.EMPTY_PAGE)\n    assert response.status == 404\n    await request.dispose()\n\n\nasync def test_should_follow_max_redirects(\n    playwright: Playwright, server: Server\n) -> None:\n    redirect_count = 0\n\n    def _handle_request(req: TestServerRequest) -> None:\n        nonlocal redirect_count\n        redirect_count += 1\n        req.setResponseCode(301)\n        req.setHeader(\"Location\", server.EMPTY_PAGE)\n        req.finish()\n\n    server.set_route(\"/empty.html\", _handle_request)\n    request = await playwright.request.new_context(max_redirects=1)\n    with pytest.raises(Error, match=\"Max redirect count exceeded\"):\n        await request.fetch(server.EMPTY_PAGE)\n    assert redirect_count == 2\n    await request.dispose()\n"
  },
  {
    "path": "tests/async/test_fill.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page\nfrom tests.server import Server\n\n\nasync def test_fill_textarea(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    await page.fill(\"textarea\", \"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_is_enabled_for_non_editable_button(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <button>button</button>\n    \"\"\"\n    )\n    button = page.locator(\"button\")\n    assert await button.is_enabled() is True\n\n\nasync def test_fill_input(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    await page.fill(\"input\", \"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n"
  },
  {
    "path": "tests/async/test_focus.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Page\n\n\nasync def test_should_work(page: Page) -> None:\n    await page.set_content(\"<div id=d1 tabIndex=0></div>\")\n    assert await page.evaluate(\"() => document.activeElement.nodeName\") == \"BODY\"\n    await page.focus(\"#d1\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"d1\"\n\n\nasync def test_should_emit_focus_event(page: Page) -> None:\n    await page.set_content(\"<div id=d1 tabIndex=0></div>\")\n    focused = []\n    await page.expose_function(\"focusEvent\", lambda: focused.append(True))\n    await page.evaluate(\"() => d1.addEventListener('focus', focusEvent)\")\n    await page.focus(\"#d1\")\n    assert focused == [True]\n\n\nasync def test_should_emit_blur_event(page: Page) -> None:\n    await page.set_content(\n        \"<div id=d1 tabIndex=0>DIV1</div><div id=d2 tabIndex=0>DIV2</div>\"\n    )\n    await page.focus(\"#d1\")\n    focused = []\n    blurred = []\n    await page.expose_function(\"focusEvent\", lambda: focused.append(True))\n    await page.expose_function(\"blurEvent\", lambda: blurred.append(True))\n    await page.evaluate(\"() => d1.addEventListener('blur', blurEvent)\")\n    await page.evaluate(\"() => d2.addEventListener('focus', focusEvent)\")\n    await page.focus(\"#d2\")\n    assert focused == [True]\n    assert blurred == [True]\n\n\nasync def test_should_traverse_focus(page: Page) -> None:\n    await page.set_content('<input id=\"i1\"><input id=\"i2\">')\n    focused = []\n    await page.expose_function(\"focusEvent\", lambda: focused.append(True))\n    await page.evaluate(\"() => i2.addEventListener('focus', focusEvent)\")\n\n    await page.focus(\"#i1\")\n    await page.keyboard.type(\"First\")\n    await page.keyboard.press(\"Tab\")\n    await page.keyboard.type(\"Last\")\n\n    assert focused == [True]\n    assert await page.eval_on_selector(\"#i1\", \"e => e.value\") == \"First\"\n    assert await page.eval_on_selector(\"#i2\", \"e => e.value\") == \"Last\"\n\n\nasync def test_should_traverse_focus_in_all_directions(page: Page) -> None:\n    await page.set_content('<input value=\"1\"><input value=\"2\"><input value=\"3\">')\n    await page.keyboard.press(\"Tab\")\n    assert await page.evaluate(\"() => document.activeElement.value\") == \"1\"\n    await page.keyboard.press(\"Tab\")\n    assert await page.evaluate(\"() => document.activeElement.value\") == \"2\"\n    await page.keyboard.press(\"Tab\")\n    assert await page.evaluate(\"() => document.activeElement.value\") == \"3\"\n    await page.keyboard.press(\"Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.value\") == \"2\"\n    await page.keyboard.press(\"Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.value\") == \"1\"\n\n\n@pytest.mark.only_platform(\"darwin\")\n@pytest.mark.only_browser(\"webkit\")\nasync def test_should_traverse_only_form_elements(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <input id=\"input-1\">\n      <button id=\"button\">button</button>\n      <a href id=\"link\">link</a>\n      <input id=\"input-2\">\n    \"\"\"\n    )\n    await page.keyboard.press(\"Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"input-1\"\n    await page.keyboard.press(\"Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"input-2\"\n    await page.keyboard.press(\"Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"input-1\"\n    await page.keyboard.press(\"Alt+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"button\"\n    await page.keyboard.press(\"Alt+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"link\"\n    await page.keyboard.press(\"Alt+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"input-2\"\n    await page.keyboard.press(\"Alt+Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"link\"\n    await page.keyboard.press(\"Alt+Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"button\"\n    await page.keyboard.press(\"Alt+Shift+Tab\")\n    assert await page.evaluate(\"() => document.activeElement.id\") == \"input-1\"\n"
  },
  {
    "path": "tests/async/test_frames.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Optional\n\nimport pytest\n\nfrom playwright.async_api import Error, Page\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def test_evaluate_handle(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    main_frame = page.main_frame\n    assert main_frame.page == page\n    window_handle = await main_frame.evaluate_handle(\"window\")\n    assert window_handle\n\n\nasync def test_frame_element(page: Page, server: Server, utils: Utils) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    frame1 = await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    assert frame1\n    await utils.attach_frame(page, \"frame2\", server.EMPTY_PAGE)\n    frame3 = await utils.attach_frame(page, \"frame3\", server.EMPTY_PAGE)\n    assert frame3\n    frame1handle1 = await page.query_selector(\"#frame1\")\n    assert frame1handle1\n    frame1handle2 = await frame1.frame_element()\n    frame3handle1 = await page.query_selector(\"#frame3\")\n    assert frame3handle1\n    frame3handle2 = await frame3.frame_element()\n    assert await frame1handle1.evaluate(\"(a, b) => a === b\", frame1handle2)\n    assert await frame3handle1.evaluate(\"(a, b) => a === b\", frame3handle2)\n    assert await frame1handle1.evaluate(\"(a, b) => a === b\", frame3handle1) is False\n\n\nasync def test_frame_element_with_content_frame(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    frame = await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    handle = await frame.frame_element()\n    content_frame = await handle.content_frame()\n    assert content_frame == frame\n\n\nasync def test_frame_element_throw_when_detached(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    frame1 = await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    await page.eval_on_selector(\"#frame1\", \"e => e.remove()\")\n    error: Optional[Error] = None\n    try:\n        await frame1.frame_element()\n    except Error as e:\n        error = e\n    assert error\n    assert error.message == \"Frame.frame_element: Frame has been detached.\"\n\n\nasync def test_evaluate_throw_for_detached_frames(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    frame1 = await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    assert frame1\n    await utils.detach_frame(page, \"frame1\")\n    error: Optional[Error] = None\n    try:\n        await frame1.evaluate(\"7 * 8\")\n    except Error as e:\n        error = e\n    assert error\n    assert \"Frame was detached\" in error.message\n\n\nasync def test_evaluate_isolated_between_frames(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    assert len(page.frames) == 2\n    [frame1, frame2] = page.frames\n    assert frame1 != frame2\n\n    await asyncio.gather(\n        frame1.evaluate(\"window.a = 1\"), frame2.evaluate(\"window.a = 2\")\n    )\n    [a1, a2] = await asyncio.gather(\n        frame1.evaluate(\"window.a\"), frame2.evaluate(\"window.a\")\n    )\n    assert a1 == 1\n    assert a2 == 2\n\n\nasync def test_should_handle_nested_frames(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    assert utils.dump_frames(page.main_frame) == [\n        \"http://localhost:<PORT>/frames/nested-frames.html\",\n        \"    http://localhost:<PORT>/frames/frame.html (aframe)\",\n        \"    http://localhost:<PORT>/frames/two-frames.html (2frames)\",\n        \"        http://localhost:<PORT>/frames/frame.html (dos)\",\n        \"        http://localhost:<PORT>/frames/frame.html (uno)\",\n    ]\n\n\nasync def test_should_send_events_when_frames_are_manipulated_dynamically(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    # validate frameattached events\n    attached_frames = []\n    page.on(\"frameattached\", lambda frame: attached_frames.append(frame))\n    await utils.attach_frame(page, \"frame1\", \"./assets/frame.html\")\n    assert len(attached_frames) == 1\n    assert \"/assets/frame.html\" in attached_frames[0].url\n\n    # validate framenavigated events\n    navigated_frames = []\n    page.on(\"framenavigated\", lambda frame: navigated_frames.append(frame))\n    await page.evaluate(\n        \"\"\"() => {\n            frame = document.getElementById('frame1')\n            frame.src = './empty.html'\n            return new Promise(x => frame.onload = x)\n        }\"\"\"\n    )\n\n    assert len(navigated_frames) == 1\n    assert navigated_frames[0].url == server.EMPTY_PAGE\n\n    # validate framedetached events\n    detached_frames = []\n    page.on(\"framedetached\", lambda frame: detached_frames.append(frame))\n    await utils.detach_frame(page, \"frame1\")\n    assert len(detached_frames) == 1\n    assert detached_frames[0].is_detached()\n\n\nasync def test_framenavigated_when_navigating_on_anchor_urls(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_event(\"framenavigated\"):\n        await page.goto(server.EMPTY_PAGE + \"#foo\")\n    assert page.url == server.EMPTY_PAGE + \"#foo\"\n\n\nasync def test_persist_main_frame_on_cross_process_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    main_frame = page.main_frame\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    assert page.main_frame == main_frame\n\n\nasync def test_should_not_send_attach_detach_events_for_main_frame(\n    page: Page, server: Server\n) -> None:\n    has_events = []\n    page.on(\"frameattached\", lambda frame: has_events.append(True))\n    page.on(\"framedetached\", lambda frame: has_events.append(True))\n    await page.goto(server.EMPTY_PAGE)\n    assert has_events == []\n\n\nasync def test_detach_child_frames_on_navigation(page: Page, server: Server) -> None:\n    attached_frames = []\n    detached_frames = []\n    navigated_frames = []\n    page.on(\"frameattached\", lambda frame: attached_frames.append(frame))\n    page.on(\"framedetached\", lambda frame: detached_frames.append(frame))\n    page.on(\"framenavigated\", lambda frame: navigated_frames.append(frame))\n    await page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    assert len(attached_frames) == 4\n    assert len(detached_frames) == 0\n    assert len(navigated_frames) == 5\n\n    attached_frames = []\n    detached_frames = []\n    navigated_frames = []\n    await page.goto(server.EMPTY_PAGE)\n    assert len(attached_frames) == 0\n    assert len(detached_frames) == 4\n    assert len(navigated_frames) == 1\n\n\nasync def test_framesets(page: Page, server: Server) -> None:\n    attached_frames = []\n    detached_frames = []\n    navigated_frames = []\n    page.on(\"frameattached\", lambda frame: attached_frames.append(frame))\n    page.on(\"framedetached\", lambda frame: detached_frames.append(frame))\n    page.on(\"framenavigated\", lambda frame: navigated_frames.append(frame))\n    await page.goto(server.PREFIX + \"/frames/frameset.html\")\n    assert len(attached_frames) == 4\n    assert len(detached_frames) == 0\n    assert len(navigated_frames) == 5\n\n    attached_frames = []\n    detached_frames = []\n    navigated_frames = []\n    await page.goto(server.EMPTY_PAGE)\n    assert len(attached_frames) == 0\n    assert len(detached_frames) == 4\n    assert len(navigated_frames) == 1\n\n\nasync def test_frame_from_inside_shadow_dom(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/shadow.html\")\n    await page.evaluate(\n        \"\"\"async url => {\n            frame = document.createElement('iframe');\n            frame.src = url;\n            document.body.shadowRoot.appendChild(frame);\n            await new Promise(x => frame.onload = x);\n        }\"\"\",\n        server.EMPTY_PAGE,\n    )\n    assert len(page.frames) == 2\n    assert page.frames[1].url == server.EMPTY_PAGE\n\n\nasync def test_frame_name(page: Page, server: Server, utils: Utils) -> None:\n    await utils.attach_frame(page, \"theFrameId\", server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"url => {\n            frame = document.createElement('iframe');\n            frame.name = 'theFrameName';\n            frame.src = url;\n            document.body.appendChild(frame);\n            return new Promise(x => frame.onload = x);\n        }\"\"\",\n        server.EMPTY_PAGE,\n    )\n    assert page.frames[0].name == \"\"\n    assert page.frames[1].name == \"theFrameId\"\n    assert page.frames[2].name == \"theFrameName\"\n\n\nasync def test_frame_parent(page: Page, server: Server, utils: Utils) -> None:\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame2\", server.EMPTY_PAGE)\n    assert page.frames[0].parent_frame is None\n    assert page.frames[1].parent_frame == page.main_frame\n    assert page.frames[2].parent_frame == page.main_frame\n\n\nasync def test_should_report_different_frame_instance_when_frame_re_attaches(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    frame1 = await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"() => {\n            window.frame = document.querySelector('#frame1')\n            window.frame.remove()\n        }\"\"\"\n    )\n\n    assert frame1.is_detached()\n    async with page.expect_event(\"frameattached\") as frame2_info:\n        await page.evaluate(\"() => document.body.appendChild(window.frame)\")\n\n    frame2 = await frame2_info.value\n    assert frame2.is_detached() is False\n    assert frame1 != frame2\n\n\nasync def test_strict_mode(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <button>Hello</button>\n        <button>Hello</button>\n    \"\"\"\n    )\n    with pytest.raises(Error):\n        await page.text_content(\"button\", strict=True)\n    with pytest.raises(Error):\n        await page.query_selector(\"button\", strict=True)\n"
  },
  {
    "path": "tests/async/test_geolocation.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext, Error, Page\nfrom tests.server import Server\n\n\nasync def test_should_work(page: Page, server: Server, context: BrowserContext) -> None:\n    await context.grant_permissions([\"geolocation\"])\n    await page.goto(server.EMPTY_PAGE)\n    await context.set_geolocation({\"latitude\": 10, \"longitude\": 10})\n    geolocation = await page.evaluate(\n        \"\"\"() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n      resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n    }))\"\"\"\n    )\n    assert geolocation == {\"latitude\": 10, \"longitude\": 10}\n\n\nasync def test_should_throw_when_invalid_longitude(context: BrowserContext) -> None:\n    with pytest.raises(Error) as exc:\n        await context.set_geolocation({\"latitude\": 10, \"longitude\": 200})\n    assert (\n        \"geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.\"\n        in exc.value.message\n    )\n\n\nasync def test_should_isolate_contexts(\n    page: Page, server: Server, context: BrowserContext, browser: Browser\n) -> None:\n    await context.grant_permissions([\"geolocation\"])\n    await context.set_geolocation({\"latitude\": 10, \"longitude\": 10})\n    await page.goto(server.EMPTY_PAGE)\n\n    context2 = await browser.new_context(\n        permissions=[\"geolocation\"], geolocation={\"latitude\": 10.5, \"longitude\": 10.5}\n    )\n\n    page2 = await context2.new_page()\n    await page2.goto(server.EMPTY_PAGE)\n\n    geolocation = await page.evaluate(\n        \"\"\"() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n      resolve({latitude: position.coords.latitude, longitude: position.coords.longitude})\n    }))\"\"\"\n    )\n    assert geolocation == {\"latitude\": 10, \"longitude\": 10}\n\n    geolocation2 = await page2.evaluate(\n        \"\"\"() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n      resolve({latitude: position.coords.latitude, longitude: position.coords.longitude})\n    }))\"\"\"\n    )\n    assert geolocation2 == {\"latitude\": 10.5, \"longitude\": 10.5}\n\n    await context2.close()\n\n\nasync def test_should_use_context_options(browser: Browser, server: Server) -> None:\n    context = await browser.new_context(\n        geolocation={\"latitude\": 10, \"longitude\": 10}, permissions=[\"geolocation\"]\n    )\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n\n    geolocation = await page.evaluate(\n        \"\"\"() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {\n      resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});\n    }))\"\"\"\n    )\n    assert geolocation == {\"latitude\": 10, \"longitude\": 10}\n    await context.close()\n\n\nasync def test_watch_position_should_be_notified(\n    page: Page, server: Server, context: BrowserContext\n) -> None:\n    await context.grant_permissions([\"geolocation\"])\n    await page.goto(server.EMPTY_PAGE)\n    messages = []\n    page.on(\"console\", lambda message: messages.append(message.text))\n\n    await context.set_geolocation({\"latitude\": 0, \"longitude\": 0})\n    await page.evaluate(\n        \"\"\"() => {\n      navigator.geolocation.watchPosition(pos => {\n        const coords = pos.coords;\n        console.log(`lat=${coords.latitude} lng=${coords.longitude}`);\n      }, err => {});\n    }\"\"\"\n    )\n\n    async with page.expect_console_message(lambda m: \"lat=0 lng=10\" in m.text):\n        await context.set_geolocation({\"latitude\": 0, \"longitude\": 10})\n\n    async with page.expect_console_message(lambda m: \"lat=20 lng=30\" in m.text):\n        await context.set_geolocation({\"latitude\": 20, \"longitude\": 30})\n\n    async with page.expect_console_message(lambda m: \"lat=40 lng=50\" in m.text):\n        await context.set_geolocation({\"latitude\": 40, \"longitude\": 50})\n\n    all_messages = \"|\".join(messages)\n    assert \"lat=0 lng=10\" in all_messages\n    assert \"lat=20 lng=30\" in all_messages\n    assert \"lat=40 lng=50\" in all_messages\n\n\nasync def test_should_use_context_options_for_popup(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await context.grant_permissions([\"geolocation\"])\n    await context.set_geolocation({\"latitude\": 10, \"longitude\": 10})\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window._popup = window.open(url)\",\n            server.PREFIX + \"/geolocation.html\",\n        )\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    geolocation = await popup.evaluate(\"() => window.geolocationPromise\")\n    assert geolocation == {\"latitude\": 10, \"longitude\": 10}\n"
  },
  {
    "path": "tests/async/test_har.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nimport os\nimport re\nimport zipfile\nfrom pathlib import Path\nfrom typing import Awaitable, Callable, cast\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext, Error, Page, Route, expect\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import must\n\n\nasync def test_should_work(browser: Browser, server: Server, tmp_path: Path) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(record_har_path=path)\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n\n\nasync def test_should_omit_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        record_har_path=path,\n        record_har_content=\"omit\",\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert \"text\" not in content1\n        assert \"encoding\" not in content1\n\n\nasync def test_should_omit_content_legacy(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        record_har_path=path, record_har_omit_content=True\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert \"text\" not in content1\n        assert \"encoding\" not in content1\n\n\nasync def test_should_attach_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har.zip\")\n    context = await browser.new_context(\n        record_har_path=path,\n        record_har_content=\"attach\",\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await page.evaluate(\"() => fetch('/pptr.png').then(r => r.arrayBuffer())\")\n    await context.close()\n    with zipfile.ZipFile(path) as z:\n        with z.open(\"har.har\") as har:\n            entries = json.load(har)[\"log\"][\"entries\"]\n\n            assert \"encoding\" not in entries[0][\"response\"][\"content\"]\n            assert (\n                entries[0][\"response\"][\"content\"][\"mimeType\"]\n                == \"text/html; charset=utf-8\"\n            )\n            assert (\n                \"75841480e2606c03389077304342fac2c58ccb1b\"\n                in entries[0][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[0][\"response\"][\"content\"][\"size\"] >= 96\n            assert entries[0][\"response\"][\"content\"][\"compression\"] == 0\n\n            assert \"encoding\" not in entries[1][\"response\"][\"content\"]\n            assert (\n                entries[1][\"response\"][\"content\"][\"mimeType\"]\n                == \"text/css; charset=utf-8\"\n            )\n            assert (\n                \"79f739d7bc88e80f55b9891a22bf13a2b4e18adb\"\n                in entries[1][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[1][\"response\"][\"content\"][\"size\"] >= 37\n            assert entries[1][\"response\"][\"content\"][\"compression\"] == 0\n\n            assert \"encoding\" not in entries[2][\"response\"][\"content\"]\n            assert entries[2][\"response\"][\"content\"][\"mimeType\"] == \"image/png\"\n            assert (\n                \"a4c3a18f0bb83f5d9fe7ce561e065c36205762fa\"\n                in entries[2][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[2][\"response\"][\"content\"][\"size\"] >= 6000\n            assert entries[2][\"response\"][\"content\"][\"compression\"] == 0\n\n            with z.open(\"75841480e2606c03389077304342fac2c58ccb1b.html\") as f:\n                assert b\"HAR Page\" in f.read()\n\n            with z.open(\"79f739d7bc88e80f55b9891a22bf13a2b4e18adb.css\") as f:\n                assert b\"pink\" in f.read()\n\n            with z.open(\"a4c3a18f0bb83f5d9fe7ce561e065c36205762fa.png\") as f:\n                assert len(f.read()) == entries[2][\"response\"][\"content\"][\"size\"]\n\n\nasync def test_should_not_omit_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        record_har_path=path, record_har_omit_content=False\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        content1 = data[\"log\"][\"entries\"][0][\"response\"][\"content\"]\n        assert \"text\" in content1\n\n\nasync def test_should_include_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(record_har_path=path)\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert content1[\"mimeType\"] == \"text/html; charset=utf-8\"\n        assert \"HAR Page\" in content1[\"text\"]\n\n\nasync def test_should_default_to_full_mode(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        record_har_path=path,\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert log[\"entries\"][0][\"request\"][\"bodySize\"] >= 0\n\n\nasync def test_should_support_minimal_mode(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        record_har_path=path,\n        record_har_mode=\"minimal\",\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert log[\"entries\"][0][\"request\"][\"bodySize\"] == -1\n\n\nasync def test_should_filter_by_glob(\n    browser: Browser, server: Server, tmp_path: str\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        base_url=server.PREFIX,\n        record_har_path=path,\n        record_har_url_filter=\"/*.css\",\n        ignore_https_errors=True,\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert len(log[\"entries\"]) == 1\n        assert log[\"entries\"][0][\"request\"][\"url\"].endswith(\"one-style.css\")\n\n\nasync def test_should_filter_by_regexp(\n    browser: Browser, server: Server, tmp_path: str\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = await browser.new_context(\n        base_url=server.PREFIX,\n        record_har_path=path,\n        record_har_url_filter=re.compile(\"HAR.X?HTML\", re.I),\n        ignore_https_errors=True,\n    )\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert len(log[\"entries\"]) == 1\n        assert log[\"entries\"][0][\"request\"][\"url\"].endswith(\"har.html\")\n\n\nasync def test_should_context_route_from_har_matching_the_method_and_following_redirects(\n    context: BrowserContext, assetdir: Path\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-fulfill.har\")\n    page = await context.new_page()\n    await page.goto(\"http://no.playwright/\")\n    # HAR contains a redirect for the script that should be followed automatically.\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    # HAR contains a POST for the css file that should not be used.\n    await expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\nasync def test_should_page_route_from_har_matching_the_method_and_following_redirects(\n    page: Page, assetdir: Path\n) -> None:\n    await page.route_from_har(har=assetdir / \"har-fulfill.har\")\n    await page.goto(\"http://no.playwright/\")\n    # HAR contains a redirect for the script that should be followed automatically.\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    # HAR contains a POST for the css file that should not be used.\n    await expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\nasync def test_fallback_continue_should_continue_when_not_found_in_har(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-fulfill.har\", not_found=\"fallback\")\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await expect(page.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_by_default_should_abort_requests_not_found_in_har(\n    context: BrowserContext,\n    server: Server,\n    assetdir: Path,\n    is_chromium: bool,\n    is_webkit: bool,\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-fulfill.har\")\n    page = await context.new_page()\n\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.EMPTY_PAGE)\n    assert exc_info.value\n    if is_chromium:\n        assert \"net::ERR_FAILED\" in exc_info.value.message\n    elif is_webkit:\n        assert \"Blocked by Web Inspector\" in exc_info.value.message\n    else:\n        assert \"NS_ERROR_FAILURE\" in exc_info.value.message\n\n\nasync def test_fallback_continue_should_continue_requests_on_bad_har(\n    context: BrowserContext, server: Server, tmp_path: Path\n) -> None:\n    path_to_invalid_har = tmp_path / \"invalid.har\"\n    with path_to_invalid_har.open(\"w\") as f:\n        json.dump({\"log\": {}}, f)\n    await context.route_from_har(har=path_to_invalid_har, not_found=\"fallback\")\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await expect(page.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_should_only_handle_requests_matching_url_filter(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-fulfill.har\", not_found=\"fallback\", url=\"**/*.js\"\n    )\n    page = await context.new_page()\n\n    async def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        await route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    await context.route(\"http://no.playwright/\", handler)\n    await page.goto(\"http://no.playwright/\")\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    await expect(page.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgba(0, 0, 0, 0)\"\n    )\n\n\nasync def test_should_only_handle_requests_matching_url_filter_no_fallback(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-fulfill.har\", url=\"**/*.js\")\n    page = await context.new_page()\n\n    async def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        await route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    await context.route(\"http://no.playwright/\", handler)\n    await page.goto(\"http://no.playwright/\")\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    await expect(page.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgba(0, 0, 0, 0)\"\n    )\n\n\nasync def test_should_only_handle_requests_matching_url_filter_no_fallback_page(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    await page.route_from_har(har=assetdir / \"har-fulfill.har\", url=\"**/*.js\")\n\n    async def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        await route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    await page.route(\"http://no.playwright/\", handler)\n    await page.goto(\"http://no.playwright/\")\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    await expect(page.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgba(0, 0, 0, 0)\"\n    )\n\n\nasync def test_should_support_regex_filter(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-fulfill.har\",\n        url=re.compile(r\".*(\\.js|.*\\.css|no.playwright\\/)\"),\n    )\n    page = await context.new_page()\n    await page.goto(\"http://no.playwright/\")\n    assert await page.evaluate(\"window.value\") == \"foo\"\n    await expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\nasync def test_should_change_document_url_after_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-redirect.har\")\n    page = await context.new_page()\n\n    async with page.expect_navigation() as navigation_info:\n        await asyncio.gather(\n            page.wait_for_url(\"https://www.theverge.com/\"),\n            page.goto(\"https://theverge.com/\"),\n        )\n\n    response = await navigation_info.value\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert await page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\nasync def test_should_change_document_url_after_redirected_navigation_on_click(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a href=\"https://theverge.com/\">click me</a>')\n    async with page.expect_navigation() as navigation_info:\n        await asyncio.gather(\n            page.wait_for_url(\"https://www.theverge.com/\"),\n            page.click(\"text=click me\"),\n        )\n\n    response = await navigation_info.value\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert await page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\nasync def test_should_go_back_to_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = await context.new_page()\n    await page.goto(\"https://theverge.com/\")\n    await page.goto(server.EMPTY_PAGE)\n    await expect(page).to_have_url(server.EMPTY_PAGE)\n\n    response = await page.go_back()\n    assert response\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert await page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\nasync def test_should_go_forward_to_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = await context.new_page()\n    await page.goto(\"https://theverge.com/\")\n    await page.goto(server.EMPTY_PAGE)\n    await expect(page).to_have_url(server.EMPTY_PAGE)\n    await page.goto(\"https://theverge.com/\")\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    await page.go_back()\n    await expect(page).to_have_url(server.EMPTY_PAGE)\n    response = await page.go_forward()\n    assert response\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert await page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\nasync def test_should_reload_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = await context.new_page()\n    await page.goto(\"https://theverge.com/\")\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    response = await page.reload()\n    assert response\n    await expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert await page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\nasync def test_should_fulfill_from_har_with_content_in_a_file(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    await context.route_from_har(har=assetdir / \"har-sha1.har\")\n    page = await context.new_page()\n    await page.goto(\"http://no.playwright/\")\n    assert await page.content() == \"<html><head></head><body>Hello, world</body></html>\"\n\n\nasync def test_should_round_trip_har_zip(\n    browser: Browser, server: Server, assetdir: Path, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context_1 = await browser.new_context(\n        record_har_mode=\"minimal\", record_har_path=har_path\n    )\n    page_1 = await context_1.new_page()\n    await page_1.goto(server.PREFIX + \"/one-style.html\")\n    await context_1.close()\n\n    context_2 = await browser.new_context()\n    await context_2.route_from_har(har=har_path, not_found=\"abort\")\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page_2.content()\n    await expect(page_2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_should_round_trip_har_with_post_data(\n    browser: Browser, server: Server, assetdir: Path, tmp_path: Path\n) -> None:\n    server.set_route(\"/echo\", lambda req: (req.write(req.post_body), req.finish()))\n    fetch_function = \"\"\"\n        async (body) => {\n            const response = await fetch('/echo', { method: 'POST', body });\n            return await response.text();\n        };\n    \"\"\"\n    har_path = tmp_path / \"har.zip\"\n    context_1 = await browser.new_context(\n        record_har_mode=\"minimal\", record_har_path=har_path\n    )\n    page_1 = await context_1.new_page()\n    await page_1.goto(server.EMPTY_PAGE)\n\n    assert await page_1.evaluate(fetch_function, \"1\") == \"1\"\n    assert await page_1.evaluate(fetch_function, \"2\") == \"2\"\n    assert await page_1.evaluate(fetch_function, \"3\") == \"3\"\n    await context_1.close()\n\n    context_2 = await browser.new_context()\n    await context_2.route_from_har(har=har_path, not_found=\"abort\")\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.EMPTY_PAGE)\n    assert await page_2.evaluate(fetch_function, \"1\") == \"1\"\n    assert await page_2.evaluate(fetch_function, \"2\") == \"2\"\n    assert await page_2.evaluate(fetch_function, \"3\") == \"3\"\n    with pytest.raises(Exception):\n        await page_2.evaluate(fetch_function, \"4\")\n\n\nasync def test_should_disambiguate_by_header(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    server.set_route(\n        \"/echo\",\n        lambda req: (req.write(cast(str, req.getHeader(\"baz\")).encode()), req.finish()),\n    )\n    fetch_function = \"\"\"\n        async (bazValue) => {\n            const response = await fetch('/echo', {\n            method: 'POST',\n            body: '',\n            headers: {\n                foo: 'foo-value',\n                bar: 'bar-value',\n                baz: bazValue,\n            }\n            });\n            return await response.text();\n        };\n    \"\"\"\n    har_path = tmp_path / \"har.zip\"\n    context_1 = await browser.new_context(\n        record_har_mode=\"minimal\", record_har_path=har_path\n    )\n    page_1 = await context_1.new_page()\n    await page_1.goto(server.EMPTY_PAGE)\n\n    assert await page_1.evaluate(fetch_function, \"baz1\") == \"baz1\"\n    assert await page_1.evaluate(fetch_function, \"baz2\") == \"baz2\"\n    assert await page_1.evaluate(fetch_function, \"baz3\") == \"baz3\"\n    await context_1.close()\n\n    context_2 = await browser.new_context()\n    await context_2.route_from_har(har=har_path)\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.EMPTY_PAGE)\n    assert await page_2.evaluate(fetch_function, \"baz1\") == \"baz1\"\n    assert await page_2.evaluate(fetch_function, \"baz2\") == \"baz2\"\n    assert await page_2.evaluate(fetch_function, \"baz3\") == \"baz3\"\n    assert await page_2.evaluate(fetch_function, \"baz4\") == \"baz1\"\n\n\nasync def test_should_produce_extracted_zip(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.har\"\n    context = await browser.new_context(\n        record_har_mode=\"minimal\", record_har_path=har_path, record_har_content=\"attach\"\n    )\n    page_1 = await context.new_page()\n    await page_1.goto(server.PREFIX + \"/one-style.html\")\n    await context.close()\n\n    assert har_path.exists()\n    with har_path.open() as r:\n        content = r.read()\n        assert \"log\" in content\n        assert \"background-color\" not in r.read()\n\n    context_2 = await browser.new_context()\n    await context_2.route_from_har(har_path, not_found=\"abort\")\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page_2.content()\n    await expect(page_2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_should_update_har_zip_for_context(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context = await browser.new_context()\n    await context.route_from_har(har_path, update=True)\n    page_1 = await context.new_page()\n    await page_1.goto(server.PREFIX + \"/one-style.html\")\n    await context.close()\n\n    assert har_path.exists()\n\n    context_2 = await browser.new_context()\n    await context_2.route_from_har(har_path, not_found=\"abort\")\n    page_2 = await context_2.new_page()\n    await page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page_2.content()\n    await expect(page_2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_page_unroute_all_should_stop_page_route_from_har(\n    context_factory: Callable[[], Awaitable[BrowserContext]],\n    server: Server,\n    assetdir: Path,\n) -> None:\n    har_path = assetdir / \"har-fulfill.har\"\n    context1 = await context_factory()\n    page1 = await context1.new_page()\n    # The har file contains requests for another domain, so the router\n    # is expected to abort all requests.\n    await page1.route_from_har(har_path, not_found=\"abort\")\n    with pytest.raises(Error) as exc_info:\n        await page1.goto(server.EMPTY_PAGE)\n    assert exc_info.value\n    await page1.unroute_all(behavior=\"wait\")\n    response = must(await page1.goto(server.EMPTY_PAGE))\n    assert response.ok\n\n\nasync def test_context_unroute_call_should_stop_context_route_from_har(\n    context_factory: Callable[[], Awaitable[BrowserContext]],\n    server: Server,\n    assetdir: Path,\n) -> None:\n    har_path = assetdir / \"har-fulfill.har\"\n    context1 = await context_factory()\n    page1 = await context1.new_page()\n    # The har file contains requests for another domain, so the router\n    # is expected to abort all requests.\n    await context1.route_from_har(har_path, not_found=\"abort\")\n    with pytest.raises(Error) as exc_info:\n        await page1.goto(server.EMPTY_PAGE)\n    assert exc_info.value\n    await context1.unroute_all(behavior=\"wait\")\n    response = must(await page1.goto(server.EMPTY_PAGE))\n    assert must(response).ok\n\n\nasync def test_should_update_har_zip_for_page(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context = await browser.new_context()\n    page_1 = await context.new_page()\n    await page_1.route_from_har(har_path, update=True)\n    await page_1.goto(server.PREFIX + \"/one-style.html\")\n    await context.close()\n\n    assert har_path.exists()\n\n    context_2 = await browser.new_context()\n    page_2 = await context_2.new_page()\n    await page_2.route_from_har(har_path, not_found=\"abort\")\n    await page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page_2.content()\n    await expect(page_2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_should_update_har_zip_for_page_with_different_options(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context1 = await browser.new_context()\n    page1 = await context1.new_page()\n    await page1.route_from_har(\n        har_path, update=True, update_content=\"embed\", update_mode=\"full\"\n    )\n    await page1.goto(server.PREFIX + \"/one-style.html\")\n    await context1.close()\n\n    context2 = await browser.new_context()\n    page2 = await context2.new_page()\n    await page2.route_from_har(har_path, not_found=\"abort\")\n    await page2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page2.content()\n    await expect(page2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n    await context2.close()\n\n\nasync def test_should_update_extracted_har_zip_for_page(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.har\"\n    context = await browser.new_context()\n    page_1 = await context.new_page()\n    await page_1.route_from_har(har_path, update=True)\n    await page_1.goto(server.PREFIX + \"/one-style.html\")\n    await context.close()\n\n    assert har_path.exists()\n    with har_path.open() as r:\n        content = r.read()\n        assert \"log\" in content\n        assert \"background-color\" not in r.read()\n\n    context_2 = await browser.new_context()\n    page_2 = await context_2.new_page()\n    await page_2.route_from_har(har_path, not_found=\"abort\")\n    await page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in await page_2.content()\n    await expect(page_2.locator(\"body\")).to_have_css(\n        \"background-color\", \"rgb(255, 192, 203)\"\n    )\n\n\nasync def test_should_ignore_aborted_requests(\n    context_factory: Callable[[], Awaitable[BrowserContext]],\n    server: Server,\n    tmp_path: Path,\n) -> None:\n    path = tmp_path / \"test.har\"\n    server.set_route(\"/x\", lambda request: request.loseConnection())\n    context1 = await context_factory()\n    await context1.route_from_har(har=path, update=True)\n    page1 = await context1.new_page()\n    await page1.goto(server.EMPTY_PAGE)\n    req_promise = asyncio.create_task(server.wait_for_request(\"/x\"))\n    eval_task = asyncio.create_task(\n        page1.evaluate(\n            \"url => fetch(url).catch(e => 'cancelled')\", server.PREFIX + \"/x\"\n        )\n    )\n    await req_promise\n    req = await eval_task\n    assert req == \"cancelled\"\n    await context1.close()\n\n    server.reset()\n\n    def _handle_route(req: TestServerRequest) -> None:\n        req.setHeader(\"Content-Type\", \"text/plain\")\n        req.write(b\"test\")\n        req.finish()\n\n    server.set_route(\"/x\", _handle_route)\n    context2 = await context_factory()\n    await context2.route_from_har(path)\n    page2 = await context2.new_page()\n    await page2.goto(server.EMPTY_PAGE)\n    eval_task = asyncio.create_task(\n        page2.evaluate(\n            \"url => fetch(url).catch(e => 'cancelled')\", server.PREFIX + \"/x\"\n        )\n    )\n\n    async def _timeout() -> str:\n        await asyncio.sleep(1)\n        return \"timeout\"\n\n    done, _ = await asyncio.wait(\n        [eval_task, asyncio.create_task(_timeout())],\n        return_when=asyncio.FIRST_COMPLETED,\n    )\n    assert next(iter(done)).result() == \"timeout\"\n    eval_task.cancel()\n"
  },
  {
    "path": "tests/async/test_headful.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom pathlib import Path\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import BrowserType\nfrom tests.server import Server\n\n\nasync def test_should_have_default_url_when_launching_browser(\n    browser_type: BrowserType, launch_arguments: Dict, tmp_path: Path\n) -> None:\n    browser_context = await browser_type.launch_persistent_context(\n        tmp_path, **{**launch_arguments, \"headless\": False}\n    )\n    urls = [page.url for page in browser_context.pages]\n    assert urls == [\"about:blank\"]\n    await browser_context.close()\n\n\nasync def test_should_close_browser_with_beforeunload_page(\n    browser_type: BrowserType, launch_arguments: Dict, server: Server, tmp_path: Path\n) -> None:\n    browser_context = await browser_type.launch_persistent_context(\n        tmp_path, **{**launch_arguments, \"headless\": False}\n    )\n    page = await browser_context.new_page()\n    await page.goto(server.PREFIX + \"/beforeunload.html\")\n    # We have to interact with a page so that 'beforeunload' handlers\n    # fire.\n    await page.click(\"body\")\n    await browser_context.close()\n\n\nasync def test_should_not_crash_when_creating_second_context(\n    browser_type: BrowserType, launch_arguments: Dict, server: Server\n) -> None:\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    browser_context = await browser.new_context()\n    await browser_context.new_page()\n    await browser_context.close()\n    browser_context = await browser.new_context()\n    await browser_context.new_page()\n    await browser_context.close()\n    await browser.close()\n\n\nasync def test_should_click_background_tab(\n    browser_type: BrowserType, launch_arguments: Dict, server: Server\n) -> None:\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    page = await browser.new_page()\n    await page.set_content(\n        f'<button>Hello</button><a target=_blank href=\"{server.EMPTY_PAGE}\">empty.html</a>'\n    )\n    await page.click(\"a\")\n    await page.click(\"button\")\n    await browser.close()\n\n\nasync def test_should_close_browser_after_context_menu_was_triggered(\n    browser_type: BrowserType, launch_arguments: Dict, server: Server\n) -> None:\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    page = await browser.new_page()\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await page.click(\"body\", button=\"right\")\n    await browser.close()\n\n\nasync def test_should_not_block_third_party_cookies(\n    browser_type: BrowserType,\n    launch_arguments: Dict,\n    server: Server,\n    is_chromium: bool,\n    is_firefox: bool,\n) -> None:\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    page = await browser.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"src => {\n    let fulfill;\n    const promise = new Promise(x => fulfill = x);\n    const iframe = document.createElement('iframe');\n    document.body.appendChild(iframe);\n    iframe.onload = fulfill;\n    iframe.src = src;\n    return promise;\n  }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/grid.html\",\n    )\n    document_cookie = await page.frames[1].evaluate(\n        \"\"\"() => {\n    document.cookie = 'username=John Doe';\n    return document.cookie;\n  }\"\"\"\n    )\n\n    await page.wait_for_timeout(2000)\n    allows_third_party = is_firefox\n    assert document_cookie == (\"username=John Doe\" if allows_third_party else \"\")\n    cookies = await page.context.cookies(server.CROSS_PROCESS_PREFIX + \"/grid.html\")\n    if allows_third_party:\n        assert cookies == [\n            {\n                \"domain\": \"127.0.0.1\",\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"name\": \"username\",\n                \"path\": \"/\",\n                \"sameSite\": \"Lax\" if is_chromium else \"None\",\n                \"secure\": False,\n                \"value\": \"John Doe\",\n            }\n        ]\n    else:\n        assert cookies == []\n\n    await browser.close()\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_should_not_override_viewport_size_when_passed_null(\n    browser_type: BrowserType, launch_arguments: Dict, server: Server\n) -> None:\n    # Our WebKit embedder does not respect window features.\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    context = await browser.new_context(no_viewport=True)\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"\"\"() => {\n                const win = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=600,height=300,top=0,left=0');\n                win.resizeTo(500, 450);\n            }\"\"\"\n        )\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    await popup.wait_for_function(\n        \"\"\"() => window.outerWidth === 500 && window.outerHeight === 450\"\"\"\n    )\n    await context.close()\n    await browser.close()\n\n\nasync def test_page_bring_to_front_should_work(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = await browser_type.launch(**{**launch_arguments, \"headless\": False})\n    page1 = await browser.new_page()\n    await page1.set_content(\"Page1\")\n    page2 = await browser.new_page()\n    await page2.set_content(\"Page2\")\n\n    await page1.bring_to_front()\n    assert await page1.evaluate(\"document.visibilityState\") == \"visible\"\n    assert await page2.evaluate(\"document.visibilityState\") == \"visible\"\n\n    await page2.bring_to_front()\n    assert await page1.evaluate(\"document.visibilityState\") == \"visible\"\n    assert await page2.evaluate(\"document.visibilityState\") == \"visible\"\n    await browser.close()\n"
  },
  {
    "path": "tests/async/test_ignore_https_errors.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error\nfrom tests.server import Server\n\n\nasync def test_ignore_https_error_should_work(\n    browser: Browser, https_server: Server\n) -> None:\n    context = await browser.new_context(ignore_https_errors=True)\n    page = await context.new_page()\n    response = await page.goto(https_server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    await context.close()\n\n\nasync def test_ignore_https_error_should_work_negative_case(\n    browser: Browser, https_server: Server\n) -> None:\n    context = await browser.new_context()\n    page = await context.new_page()\n    with pytest.raises(Error):\n        await page.goto(https_server.EMPTY_PAGE)\n    await context.close()\n"
  },
  {
    "path": "tests/async/test_input.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport os\nimport re\nimport shutil\nimport sys\nfrom pathlib import Path\nfrom typing import Any\n\nimport pytest\n\nfrom playwright._impl._path_utils import get_file_dirname\nfrom playwright.async_api import Error, FilePayload, Page\nfrom tests.server import Server\nfrom tests.utils import chromium_version_less_than, must\n\n_dirname = get_file_dirname()\nFILE_TO_UPLOAD = _dirname / \"..\" / \"assets/file-to-upload.txt\"\n\n\nasync def test_should_upload_the_file(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/fileupload.html\")\n    file_path = os.path.relpath(FILE_TO_UPLOAD, os.getcwd())\n    input = await page.query_selector(\"input\")\n    assert input\n    await input.set_input_files(file_path)\n    assert await page.evaluate(\"e => e.files[0].name\", input) == \"file-to-upload.txt\"\n    assert (\n        await page.evaluate(\n            \"\"\"e => {\n        reader = new FileReader()\n        promise = new Promise(fulfill => reader.onload = fulfill)\n        reader.readAsText(e.files[0])\n        return promise.then(() => reader.result)\n    }\"\"\",\n            input,\n        )\n        == \"contents of the file\\n\"\n    )\n\n\nasync def test_should_work(page: Page, assetdir: Path) -> None:\n    await page.set_content(\"<input type=file>\")\n    await page.set_input_files(\"input\", assetdir / \"file-to-upload.txt\")\n    assert await page.eval_on_selector(\"input\", \"input => input.files.length\") == 1\n    assert (\n        await page.eval_on_selector(\"input\", \"input => input.files[0].name\")\n        == \"file-to-upload.txt\"\n    )\n\n\nasync def test_should_set_from_memory(page: Page) -> None:\n    await page.set_content(\"<input type=file>\")\n    file: FilePayload = {\n        \"name\": \"test.txt\",\n        \"mimeType\": \"text/plain\",\n        \"buffer\": b\"this is a test\",\n    }\n    await page.set_input_files(\n        \"input\",\n        files=[file],\n    )\n    assert await page.eval_on_selector(\"input\", \"input => input.files.length\") == 1\n    assert (\n        await page.eval_on_selector(\"input\", \"input => input.files[0].name\")\n        == \"test.txt\"\n    )\n\n\nasync def test_should_emit_event(page: Page) -> None:\n    await page.set_content(\"<input type=file>\")\n    fc_done: asyncio.Future = asyncio.Future()\n    page.once(\"filechooser\", lambda file_chooser: fc_done.set_result(file_chooser))\n    await page.click(\"input\")\n    file_chooser = await fc_done\n    assert file_chooser\n    assert (\n        repr(file_chooser)\n        == f\"<FileChooser page={file_chooser.page} element={file_chooser.element}>\"\n    )\n\n\nasync def test_should_work_when_file_input_is_attached_to_dom(page: Page) -> None:\n    await page.set_content(\"<input type=file>\")\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    assert file_chooser\n\n\nasync def test_should_work_when_file_input_is_not_attached_to_DOM(page: Page) -> None:\n    async with page.expect_file_chooser() as fc_info:\n        await page.evaluate(\n            \"\"\"() => {\n                el = document.createElement('input')\n                el.type = 'file'\n                el.click()\n            }\"\"\"\n        )\n    file_chooser = await fc_info.value\n    assert file_chooser\n\n\nasync def test_should_return_the_same_file_chooser_when_there_are_many_watchdogs_simultaneously(\n    page: Page,\n) -> None:\n    await page.set_content(\"<input type=file>\")\n    results = await asyncio.gather(\n        page.wait_for_event(\"filechooser\"),\n        page.wait_for_event(\"filechooser\"),\n        page.eval_on_selector(\"input\", \"input => input.click()\"),\n    )\n    assert results[0] == results[1]\n\n\nasync def test_should_accept_single_file(page: Page) -> None:\n    await page.set_content('<input type=file oninput=\"javascript:console.timeStamp()\">')\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    assert file_chooser.page == page\n    assert file_chooser.element\n    await file_chooser.set_files(FILE_TO_UPLOAD)\n    assert await page.eval_on_selector(\"input\", \"input => input.files.length\") == 1\n    assert (\n        await page.eval_on_selector(\"input\", \"input => input.files[0].name\")\n        == \"file-to-upload.txt\"\n    )\n\n\nasync def test_should_be_able_to_read_selected_file(page: Page) -> None:\n    page.once(\n        \"filechooser\", lambda file_chooser: file_chooser.set_files(FILE_TO_UPLOAD)\n    )\n    await page.set_content(\"<input type=file>\")\n    content = await page.eval_on_selector(\n        \"input\",\n        \"\"\"async picker => {\n            picker.click();\n            await new Promise(x => picker.oninput = x);\n            const reader = new FileReader();\n            const promise = new Promise(fulfill => reader.onload = fulfill);\n            reader.readAsText(picker.files[0]);\n            return promise.then(() => reader.result);\n        }\"\"\",\n    )\n    assert content == \"contents of the file\\n\"\n\n\nasync def test_should_be_able_to_reset_selected_files_with_empty_file_list(\n    page: Page,\n) -> None:\n    await page.set_content(\"<input type=file>\")\n    page.once(\n        \"filechooser\", lambda file_chooser: file_chooser.set_files(FILE_TO_UPLOAD)\n    )\n    file_length = 0\n    async with page.expect_file_chooser():\n        file_length = await page.eval_on_selector(\n            \"input\",\n            \"\"\"async picker => {\n                picker.click();\n                await new Promise(x => picker.oninput = x);\n                return picker.files.length;\n            }\"\"\",\n        )\n    assert file_length == 1\n\n    page.once(\"filechooser\", lambda file_chooser: file_chooser.set_files([]))\n    async with page.expect_file_chooser():\n        file_length = await page.eval_on_selector(\n            \"input\",\n            \"\"\"async picker => {\n                picker.click();\n                await new Promise(x => picker.oninput = x);\n                return picker.files.length;\n            }\"\"\",\n        )\n    assert file_length == 0\n\n\nasync def test_should_not_accept_multiple_files_for_single_file_input(\n    page: Page, assetdir: Path\n) -> None:\n    await page.set_content(\"<input type=file>\")\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    with pytest.raises(Exception) as exc_info:\n        await file_chooser.set_files(\n            [\n                os.path.realpath(assetdir / \"file-to-upload.txt\"),\n                os.path.realpath(assetdir / \"pptr.png\"),\n            ]\n        )\n    assert exc_info.value\n\n\nasync def test_should_emit_input_and_change_events(page: Page) -> None:\n    events = []\n    await page.expose_function(\"eventHandled\", lambda e: events.append(e))\n    await page.set_content(\n        \"\"\"\n            <input id=input type=file></input>\n            <script>\n            input.addEventListener('input', e => eventHandled({ type: e.type }))\n            input.addEventListener('change', e => eventHandled({ type: e.type }))\n            </script>\n        \"\"\"\n    )\n    await must(await page.query_selector(\"input\")).set_input_files(FILE_TO_UPLOAD)\n    assert len(events) == 2\n    assert events[0][\"type\"] == \"input\"\n    assert events[1][\"type\"] == \"change\"\n\n\nasync def test_should_work_for_single_file_pick(page: Page) -> None:\n    await page.set_content(\"<input type=file>\")\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    assert file_chooser.is_multiple() is False\n\n\nasync def test_should_work_for_multiple(page: Page) -> None:\n    await page.set_content(\"<input multiple type=file>\")\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    assert file_chooser.is_multiple()\n\n\nasync def test_should_work_for_webkitdirectory(page: Page) -> None:\n    await page.set_content(\"<input multiple webkitdirectory type=file>\")\n    async with page.expect_file_chooser() as fc_info:\n        await page.click(\"input\")\n    file_chooser = await fc_info.value\n    assert file_chooser.is_multiple()\n\n\ndef _assert_wheel_event(expected: Any, received: Any, browser_name: str) -> None:\n    # Chromium reports deltaX/deltaY scaled by host device scale factor.\n    # https://bugs.chromium.org/p/chromium/issues/detail?id=1324819\n    # https://github.com/microsoft/playwright/issues/7362\n    # Different bots have different scale factors (usually 1 or 2), so we just ignore the values\n    # instead of guessing the host scale factor.\n    if sys.platform == \"darwin\" and browser_name == \"chromium\":\n        del expected[\"deltaX\"]\n        del expected[\"deltaY\"]\n        del received[\"deltaX\"]\n        del received[\"deltaY\"]\n    assert received == expected\n\n\nasync def test_wheel_should_work(page: Page, browser_name: str) -> None:\n    await page.set_content(\n        \"\"\"\n        <div style=\"width: 5000px; height: 5000px;\"></div>\n    \"\"\"\n    )\n    await page.mouse.move(50, 60)\n    await _listen_for_wheel_events(page, \"div\")\n    await page.mouse.wheel(0, 100)\n    _assert_wheel_event(\n        await page.evaluate(\"window.lastEvent\"),\n        {\n            \"deltaX\": 0,\n            \"deltaY\": 100,\n            \"clientX\": 50,\n            \"clientY\": 60,\n            \"deltaMode\": 0,\n            \"ctrlKey\": False,\n            \"shiftKey\": False,\n            \"altKey\": False,\n            \"metaKey\": False,\n        },\n        browser_name,\n    )\n    await page.wait_for_function(\"window.scrollY === 100\")\n\n\nasync def _listen_for_wheel_events(page: Page, selector: str) -> None:\n    await page.evaluate(\n        \"\"\"\n        selector => {\n            document.querySelector(selector).addEventListener('wheel', (e) => {\n            window['lastEvent'] = {\n                deltaX: e.deltaX,\n                deltaY: e.deltaY,\n                clientX: e.clientX,\n                clientY: e.clientY,\n                deltaMode: e.deltaMode,\n                ctrlKey: e.ctrlKey,\n                shiftKey: e.shiftKey,\n                altKey: e.altKey,\n                metaKey: e.metaKey,\n            };\n            }, { passive: false });\n        }\n    \"\"\",\n        selector,\n    )\n\n\nasync def test_should_upload_large_file(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    await page.goto(server.PREFIX + \"/input/fileupload.html\")\n    large_file_path = tmp_path / \"200MB.zip\"\n    data = b\"A\" * 1024\n    with large_file_path.open(\"wb\") as f:\n        for i in range(0, 200 * 1024 * 1024, len(data)):\n            f.write(data)\n    input = page.locator('input[type=\"file\"]')\n    events = await input.evaluate_handle(\n        \"\"\"\n        e => {\n            const events = [];\n            e.addEventListener('input', () => events.push('input'));\n            e.addEventListener('change', () => events.push('change'));\n            return events;\n        }\n    \"\"\"\n    )\n\n    await input.set_input_files(large_file_path)\n    assert await input.evaluate(\"e => e.files[0].name\") == \"200MB.zip\"\n    assert await events.evaluate(\"e => e\") == [\"input\", \"change\"]\n\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/upload\"),\n        page.click(\"input[type=submit]\"),\n    )\n\n    contents = request.args[b\"file1\"][0]\n    assert len(contents) == 200 * 1024 * 1024\n    assert contents[:1024] == data\n    # flake8: noqa: E203\n    assert contents[len(contents) - 1024 :] == data\n    assert request.post_body\n    match = re.search(\n        rb'^.*Content-Disposition: form-data; name=\"(?P<name>.*)\"; filename=\"(?P<filename>.*)\".*$',\n        request.post_body,\n        re.MULTILINE,\n    )\n    assert match\n    assert match.group(\"name\") == b\"file1\"\n    assert match.group(\"filename\") == b\"200MB.zip\"\n\n\nasync def test_set_input_files_should_preserve_last_modified_timestamp(\n    page: Page,\n    assetdir: Path,\n) -> None:\n    await page.set_content(\"<input type=file multiple=true/>\")\n    input = page.locator(\"input\")\n    files = [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]\n    await input.set_input_files([assetdir / file for file in files])\n    assert await input.evaluate(\"input => [...input.files].map(f => f.name)\") == files\n    timestamps = await input.evaluate(\n        \"input => [...input.files].map(f => f.lastModified)\"\n    )\n    expected_timestamps = [os.path.getmtime(assetdir / file) * 1000 for file in files]\n\n    # On Linux browser sometimes reduces the timestamp by 1ms: 1696272058110.0715  -> 1696272058109 or even\n    # rounds it to seconds in WebKit: 1696272058110 -> 1696272058000.\n    for i in range(len(timestamps)):\n        assert abs(timestamps[i] - expected_timestamps[i]) < 1000\n\n\nasync def test_should_upload_multiple_large_file(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    files_count = 10\n    await page.goto(server.PREFIX + \"/input/fileupload-multi.html\")\n    upload_file = tmp_path / \"50MB_1.zip\"\n    data = b\"A\" * 1024\n    with upload_file.open(\"wb\") as f:\n        # 49 is close to the actual limit\n        for i in range(0, 49 * 1024):\n            f.write(data)\n    input = page.locator('input[type=\"file\"]')\n    upload_files = [upload_file]\n    for i in range(2, files_count + 1):\n        dst_file = tmp_path / f\"50MB_{i}.zip\"\n        shutil.copy(upload_file, dst_file)\n        upload_files.append(dst_file)\n    async with page.expect_file_chooser() as fc_info:\n        await input.click()\n    file_chooser = await fc_info.value\n    await file_chooser.set_files(upload_files)\n    files_len = await page.evaluate(\n        'document.getElementsByTagName(\"input\")[0].files.length'\n    )\n    assert file_chooser.is_multiple()\n    assert files_len == files_count\n    for path in upload_files:\n        path.unlink()\n\n\nasync def test_should_upload_a_folder(\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    browser_name: str,\n    browser_version: str,\n    headless: bool,\n) -> None:\n    await page.goto(server.PREFIX + \"/input/folderupload.html\")\n    input = await page.query_selector(\"input\")\n    assert input\n    dir = tmp_path / \"file-upload-test\"\n    dir.mkdir()\n    (dir / \"file1.txt\").write_text(\"file1 content\")\n    (dir / \"file2\").write_text(\"file2 content\")\n    (dir / \"sub-dir\").mkdir()\n    (dir / \"sub-dir\" / \"really.txt\").write_text(\"sub-dir file content\")\n    await input.set_input_files(dir)\n    assert set(\n        await input.evaluate(\"e => [...e.files].map(f => f.webkitRelativePath)\")\n    ) == set(\n        [\n            \"file-upload-test/file1.txt\",\n            \"file-upload-test/file2\",\n            # https://issues.chromium.org/issues/345393164\n            *(\n                []\n                if browser_name == \"chromium\"\n                and headless\n                and chromium_version_less_than(browser_version, \"127.0.6533.0\")\n                else [\"file-upload-test/sub-dir/really.txt\"]\n            ),\n        ]\n    )\n    webkit_relative_paths = await input.evaluate(\n        \"e => [...e.files].map(f => f.webkitRelativePath)\"\n    )\n    for i, webkit_relative_path in enumerate(webkit_relative_paths):\n        content = await input.evaluate(\n            \"\"\"(e, i) => {\n            const reader = new FileReader();\n            const promise = new Promise(fulfill => reader.onload = fulfill);\n            reader.readAsText(e.files[i]);\n            return promise.then(() => reader.result);\n        }\"\"\",\n            i,\n        )\n        assert content == (dir / \"..\" / webkit_relative_path).read_text()\n\n\nasync def test_should_upload_a_folder_and_throw_for_multiple_directories(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    await page.goto(server.PREFIX + \"/input/folderupload.html\")\n    input = page.locator(\"input\")\n    dir = tmp_path / \"file-upload-test\"\n    dir.mkdir()\n    (dir / \"folder1\").mkdir()\n    (dir / \"folder1\" / \"file1.txt\").write_text(\"file1 content\")\n    (dir / \"folder2\").mkdir()\n    (dir / \"folder2\" / \"file2.txt\").write_text(\"file2 content\")\n    with pytest.raises(Error) as exc_info:\n        await input.set_input_files([dir / \"folder1\", dir / \"folder2\"])\n    assert \"Multiple directories are not supported\" in exc_info.value.message\n\n\nasync def test_should_throw_if_a_directory_and_files_are_passed(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    await page.goto(server.PREFIX + \"/input/folderupload.html\")\n    input = page.locator(\"input\")\n    dir = tmp_path / \"file-upload-test\"\n    dir.mkdir()\n    (dir / \"file1.txt\").write_text(\"file1 content\")\n    with pytest.raises(Error) as exc_info:\n        await input.set_input_files([dir, dir / \"file1.txt\"])\n    assert (\n        \"File paths must be all files or a single directory\" in exc_info.value.message\n    )\n\n\nasync def test_should_throw_when_upload_a_folder_in_a_normal_file_upload_input(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    await page.goto(server.PREFIX + \"/input/fileupload.html\")\n    input = await page.query_selector(\"input\")\n    assert input\n    dir = tmp_path / \"file-upload-test\"\n    dir.mkdir()\n    (dir / \"file1.txt\").write_text(\"file1 content\")\n    with pytest.raises(Error) as exc_info:\n        await input.set_input_files(dir)\n    assert (\n        \"File input does not support directories, pass individual files instead\"\n        in exc_info.value.message\n    )\n"
  },
  {
    "path": "tests/async/test_issues.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom asyncio import FIRST_COMPLETED, CancelledError, create_task, wait\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserType, Page, Playwright\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_issue_189(browser_type: BrowserType, launch_arguments: Dict) -> None:\n    browser = await browser_type.launch(\n        **launch_arguments, ignore_default_args=[\"--mute-audio\"]\n    )\n    page = await browser.new_page()\n    assert await page.evaluate(\"1 + 1\") == 2\n    await browser.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_issue_195(playwright: Playwright, browser: Browser) -> None:\n    iphone_11 = playwright.devices[\"iPhone 11\"]\n    context = await browser.new_context(**iphone_11)\n    await context.close()\n\n\nasync def test_connection_task_cancel(page: Page) -> None:\n    await page.set_content(\"<input />\")\n    done, pending = await wait(\n        {\n            create_task(page.wait_for_selector(\"input\")),\n            create_task(page.wait_for_selector(\"#will-never-resolve\")),\n        },\n        return_when=FIRST_COMPLETED,\n    )\n    assert len(done) == 1\n    assert len(pending) == 1\n    for task in pending:\n        task.cancel()\n        with pytest.raises(CancelledError):\n            await task\n    assert list(pending)[0].cancelled()\n"
  },
  {
    "path": "tests/async/test_jshandle.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport math\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict\n\nfrom playwright.async_api import Page\n\n\nasync def test_jshandle_evaluate_work(page: Page) -> None:\n    window_handle = await page.evaluate_handle(\"window\")\n    assert window_handle\n    assert (\n        repr(window_handle) == f\"<JSHandle preview={window_handle._impl_obj._preview}>\"\n    )\n\n\nasync def test_jshandle_evaluate_accept_object_handle_as_argument(page: Page) -> None:\n    navigator_handle = await page.evaluate_handle(\"navigator\")\n    text = await page.evaluate(\"e => e.userAgent\", navigator_handle)\n    assert \"Mozilla\" in text\n\n\nasync def test_jshandle_evaluate_accept_handle_to_primitive_types(page: Page) -> None:\n    handle = await page.evaluate_handle(\"5\")\n    is_five = await page.evaluate(\"e => Object.is(e, 5)\", handle)\n    assert is_five\n\n\nasync def test_jshandle_evaluate_accept_nested_handle(page: Page) -> None:\n    foo = await page.evaluate_handle('({ x: 1, y: \"foo\" })')\n    result = await page.evaluate(\"({ foo }) => foo\", {\"foo\": foo})\n    assert result == {\"x\": 1, \"y\": \"foo\"}\n\n\nasync def test_jshandle_evaluate_accept_nested_window_handle(page: Page) -> None:\n    foo = await page.evaluate_handle(\"window\")\n    result = await page.evaluate(\"({ foo }) => foo === window\", {\"foo\": foo})\n    assert result\n\n\nasync def test_jshandle_evaluate_accept_multiple_nested_handles(page: Page) -> None:\n    foo = await page.evaluate_handle('({ x: 1, y: \"foo\" })')\n    bar = await page.evaluate_handle(\"5\")\n    baz = await page.evaluate_handle('[\"baz\"]')\n    result = await page.evaluate(\n        \"x => JSON.stringify(x)\",\n        {\"a1\": {\"foo\": foo}, \"a2\": {\"bar\": bar, \"arr\": [{\"baz\": baz}]}},\n    )\n    assert json.loads(result) == {\n        \"a1\": {\"foo\": {\"x\": 1, \"y\": \"foo\"}},\n        \"a2\": {\"bar\": 5, \"arr\": [{\"baz\": [\"baz\"]}]},\n    }\n\n\nasync def test_jshandle_evaluate_should_work_for_circular_objects(page: Page) -> None:\n    a: Dict[str, Any] = {\"x\": 1}\n    a[\"y\"] = a\n    result = await page.evaluate(\"a => { a.y.x += 1; return a; }\", a)\n    assert result[\"x\"] == 2\n    assert result[\"y\"][\"x\"] == 2\n    assert result == result[\"y\"]\n\n\nasync def test_jshandle_evaluate_accept_same_nested_object_multiple_times(\n    page: Page,\n) -> None:\n    foo = {\"x\": 1}\n    assert await page.evaluate(\n        \"x => x\", {\"foo\": foo, \"bar\": [foo], \"baz\": {\"foo\": foo}}\n    ) == {\"foo\": {\"x\": 1}, \"bar\": [{\"x\": 1}], \"baz\": {\"foo\": {\"x\": 1}}}\n\n\nasync def test_jshandle_evaluate_accept_object_handle_to_unserializable_value(\n    page: Page,\n) -> None:\n    handle = await page.evaluate_handle(\"() => Infinity\")\n    assert await page.evaluate(\"e => Object.is(e, Infinity)\", handle)\n\n\nasync def test_jshandle_evaluate_pass_configurable_args(page: Page) -> None:\n    result = await page.evaluate(\n        \"\"\"arg => {\n            if (arg.foo !== 42)\n            throw new Error('Not a 42');\n            arg.foo = 17;\n            if (arg.foo !== 17)\n            throw new Error('Not 17');\n            delete arg.foo;\n            if (arg.foo === 17)\n            throw new Error('Still 17');\n            return arg;\n        }\"\"\",\n        {\"foo\": 42},\n    )\n    assert result == {}\n\n\nasync def test_jshandle_properties_get_property(page: Page) -> None:\n    handle1 = await page.evaluate_handle(\n        \"\"\"() => ({\n            one: 1,\n            two: 2,\n            three: 3\n        })\"\"\"\n    )\n    handle2 = await handle1.get_property(\"two\")\n    assert await handle2.json_value() == 2\n\n\nasync def test_jshandle_properties_work_with_undefined_null_and_empty(\n    page: Page,\n) -> None:\n    handle = await page.evaluate_handle(\n        \"\"\"() => ({\n            undefined: undefined,\n            null: null,\n        })\"\"\"\n    )\n    undefined_handle = await handle.get_property(\"undefined\")\n    assert await undefined_handle.json_value() is None\n    null_handle = await handle.get_property(\"null\")\n    assert await null_handle.json_value() is None\n    empty_handle = await handle.get_property(\"empty\")\n    assert await empty_handle.json_value() is None\n\n\nasync def test_jshandle_properties_work_with_unserializable_values(page: Page) -> None:\n    handle = await page.evaluate_handle(\n        \"\"\"() => ({\n            infinity: Infinity,\n            negInfinity: -Infinity,\n            nan: NaN,\n            negZero: -0\n        })\"\"\"\n    )\n    infinity_handle = await handle.get_property(\"infinity\")\n    assert await infinity_handle.json_value() == float(\"inf\")\n    neg_infinity_handle = await handle.get_property(\"negInfinity\")\n    assert await neg_infinity_handle.json_value() == float(\"-inf\")\n    nan_handle = await handle.get_property(\"nan\")\n    assert math.isnan(await nan_handle.json_value()) is True\n    neg_zero_handle = await handle.get_property(\"negZero\")\n    assert await neg_zero_handle.json_value() == float(\"-0\")\n\n\nasync def test_jshandle_properties_get_properties(page: Page) -> None:\n    handle = await page.evaluate_handle('() => ({ foo: \"bar\" })')\n    properties = await handle.get_properties()\n    assert \"foo\" in properties\n    foo = properties[\"foo\"]\n    assert await foo.json_value() == \"bar\"\n\n\nasync def test_jshandle_properties_return_empty_map_for_non_objects(page: Page) -> None:\n    handle = await page.evaluate_handle(\"123\")\n    properties = await handle.get_properties()\n    assert properties == {}\n\n\nasync def test_jshandle_json_value_work(page: Page) -> None:\n    handle = await page.evaluate_handle('() => ({foo: \"bar\"})')\n    json = await handle.json_value()\n    assert json == {\"foo\": \"bar\"}\n\n\nasync def test_jshandle_json_value_work_with_dates(page: Page) -> None:\n    handle = await page.evaluate_handle('() => new Date(\"2020-05-27T01:31:38.506Z\")')\n    json = await handle.json_value()\n    assert json == datetime.fromisoformat(\"2020-05-27T01:31:38.506\").replace(\n        tzinfo=timezone.utc\n    )\n\n\nasync def test_jshandle_json_value_should_work_for_circular_object(page: Page) -> None:\n    handle = await page.evaluate_handle(\"const a = {}; a.b = a; a\")\n    a: Dict[str, Any] = {}\n    a[\"b\"] = a\n    result = await handle.json_value()\n    # Node test looks like the below, but assert isn't smart enough to handle this:\n    # assert await handle.json_value() == a\n    assert result[\"b\"] == result\n\n\nasync def test_jshandle_as_element_work(page: Page) -> None:\n    handle = await page.evaluate_handle(\"document.body\")\n    element = handle.as_element()\n    assert element is not None\n\n\nasync def test_jshandle_as_element_return_none_for_non_elements(page: Page) -> None:\n    handle = await page.evaluate_handle(\"2\")\n    element = handle.as_element()\n    assert element is None\n\n\nasync def test_jshandle_to_string_work_for_primitives(page: Page) -> None:\n    number_handle = await page.evaluate_handle(\"2\")\n    assert str(number_handle) == \"2\"\n    string_handle = await page.evaluate_handle('\"a\"')\n    assert str(string_handle) == \"a\"\n\n\nasync def test_jshandle_to_string_work_for_complicated_objects(\n    page: Page, browser_name: str\n) -> None:\n    handle = await page.evaluate_handle(\"window\")\n    if browser_name != \"firefox\":\n        assert str(handle) == \"Window\"\n    else:\n        assert str(handle) == \"JSHandle@object\"\n\n\nasync def test_jshandle_to_string_work_for_promises(page: Page) -> None:\n    handle = await page.evaluate_handle(\"({b: Promise.resolve(123)})\")\n    b_handle = await handle.get_property(\"b\")\n    assert str(b_handle) == \"Promise\"\n"
  },
  {
    "path": "tests/async/test_keyboard.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport pytest\n\nfrom playwright.async_api import Error, JSHandle, Page\nfrom tests.server import Server\n\nfrom .utils import Utils\n\n\nasync def captureLastKeydown(page: Page) -> JSHandle:\n    lastEvent = await page.evaluate_handle(\n        \"\"\"() => {\n        const lastEvent = {\n        repeat: false,\n        location: -1,\n        code: '',\n        key: '',\n        metaKey: false,\n        keyIdentifier: 'unsupported'\n        };\n        document.addEventListener('keydown', e => {\n        lastEvent.repeat = e.repeat;\n        lastEvent.location = e.location;\n        lastEvent.key = e.key;\n        lastEvent.code = e.code;\n        lastEvent.metaKey = e.metaKey;\n        // keyIdentifier only exists in WebKit, and isn't in TypeScript's lib.\n        lastEvent.keyIdentifier = 'keyIdentifier' in e && e.keyIdentifier;\n        }, true);\n        return lastEvent;\n    }\"\"\"\n    )\n    return lastEvent\n\n\nasync def test_keyboard_type_into_a_textarea(page: Page) -> None:\n    await page.evaluate(\n        \"\"\"\n            const textarea = document.createElement('textarea');\n            document.body.appendChild(textarea);\n            textarea.focus();\n        \"\"\"\n    )\n    text = \"Hello world. I am the text that was typed!\"\n    await page.keyboard.type(text)\n    assert await page.evaluate('document.querySelector(\"textarea\").value') == text\n\n\nasync def test_keyboard_move_with_the_arrow_keys(page: Page, server: Server) -> None:\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    await page.type(\"textarea\", \"Hello World!\")\n    assert (\n        await page.evaluate(\"document.querySelector('textarea').value\")\n        == \"Hello World!\"\n    )\n    for _ in \"World!\":\n        await page.keyboard.press(\"ArrowLeft\")\n    await page.keyboard.type(\"inserted \")\n    assert (\n        await page.evaluate(\"document.querySelector('textarea').value\")\n        == \"Hello inserted World!\"\n    )\n    await page.keyboard.down(\"Shift\")\n    for _ in \"inserted \":\n        await page.keyboard.press(\"ArrowLeft\")\n    await page.keyboard.up(\"Shift\")\n    await page.keyboard.press(\"Backspace\")\n    assert (\n        await page.evaluate(\"document.querySelector('textarea').value\")\n        == \"Hello World!\"\n    )\n\n\nasync def test_keyboard_send_a_character_with_elementhandle_press(\n    page: Page, server: Server\n) -> None:\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.press(\"a\")\n    assert await page.evaluate(\"document.querySelector('textarea').value\") == \"a\"\n    await page.evaluate(\n        \"() => window.addEventListener('keydown', e => e.preventDefault(), true)\"\n    )\n    await textarea.press(\"b\")\n    assert await page.evaluate(\"document.querySelector('textarea').value\") == \"a\"\n\n\nasync def test_should_send_a_character_with_send_character(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.focus(\"textarea\")\n    await page.keyboard.insert_text(\"嗨\")\n    assert await page.evaluate('() => document.querySelector(\"textarea\").value') == \"嗨\"\n    await page.evaluate(\n        '() => window.addEventListener(\"keydown\", e => e.preventDefault(), true)'\n    )\n    await page.keyboard.insert_text(\"a\")\n    assert (\n        await page.evaluate('() => document.querySelector(\"textarea\").value') == \"嗨a\"\n    )\n\n\nasync def test_should_only_emit_input_event(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.focus(\"textarea\")\n    events = await page.evaluate_handle(\n        \"\"\"() => {\n    const events = [];\n    document.addEventListener('keydown', e => events.push(e.type));\n    document.addEventListener('keyup', e => events.push(e.type));\n    document.addEventListener('keypress', e => events.push(e.type));\n    document.addEventListener('input', e => events.push(e.type));\n    return events;\n  }\"\"\"\n    )\n\n    await page.keyboard.insert_text(\"hello world\")\n    assert await events.json_value() == [\"input\"]\n\n\nasync def test_should_report_shiftkey(\n    page: Page, server: Server, is_mac: bool, is_firefox: bool\n) -> None:\n    if is_firefox and is_mac:\n        pytest.skip()\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    keyboard = page.keyboard\n    codeForKey = {\"Shift\": 16, \"Alt\": 18, \"Control\": 17}\n    for modifierKey in codeForKey.keys():\n        await keyboard.down(modifierKey)\n        assert (\n            await page.evaluate(\"() => getResult()\")\n            == \"Keydown: \"\n            + modifierKey\n            + \" \"\n            + modifierKey\n            + \"Left \"\n            + str(codeForKey[modifierKey])\n            + \" [\"\n            + modifierKey\n            + \"]\"\n        )\n        await keyboard.down(\"!\")\n        # Shift+! will generate a keypress\n        if modifierKey == \"Shift\":\n            assert (\n                await page.evaluate(\"() => getResult()\")\n                == \"Keydown: ! Digit1 49 [\"\n                + modifierKey\n                + \"]\\nKeypress: ! Digit1 33 33 [\"\n                + modifierKey\n                + \"]\"\n            )\n        else:\n            assert (\n                await page.evaluate(\"() => getResult()\")\n                == \"Keydown: ! Digit1 49 [\" + modifierKey + \"]\"\n            )\n\n        await keyboard.up(\"!\")\n        assert (\n            await page.evaluate(\"() => getResult()\")\n            == \"Keyup: ! Digit1 49 [\" + modifierKey + \"]\"\n        )\n        await keyboard.up(modifierKey)\n        assert (\n            await page.evaluate(\"() => getResult()\")\n            == \"Keyup: \"\n            + modifierKey\n            + \" \"\n            + modifierKey\n            + \"Left \"\n            + str(codeForKey[modifierKey])\n            + \" []\"\n        )\n\n\nasync def test_should_report_multiple_modifiers(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    keyboard = page.keyboard\n    await keyboard.down(\"Control\")\n    assert (\n        await page.evaluate(\"() => getResult()\")\n        == \"Keydown: Control ControlLeft 17 [Control]\"\n    )\n    await keyboard.down(\"Alt\")\n    assert (\n        await page.evaluate(\"() => getResult()\")\n        == \"Keydown: Alt AltLeft 18 [Alt Control]\"\n    )\n    await keyboard.down(\";\")\n    assert (\n        await page.evaluate(\"() => getResult()\")\n        == \"Keydown: ; Semicolon 186 [Alt Control]\"\n    )\n    await keyboard.up(\";\")\n    assert (\n        await page.evaluate(\"() => getResult()\")\n        == \"Keyup: ; Semicolon 186 [Alt Control]\"\n    )\n    await keyboard.up(\"Control\")\n    assert (\n        await page.evaluate(\"() => getResult()\")\n        == \"Keyup: Control ControlLeft 17 [Alt]\"\n    )\n    await keyboard.up(\"Alt\")\n    assert await page.evaluate(\"() => getResult()\") == \"Keyup: Alt AltLeft 18 []\"\n\n\nasync def test_should_send_proper_codes_while_typing(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.type(\"!\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: ! Digit1 49 []\",\n            \"Keypress: ! Digit1 33 33 []\",\n            \"Keyup: ! Digit1 49 []\",\n        ]\n    )\n    await page.keyboard.type(\"^\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: ^ Digit6 54 []\",\n            \"Keypress: ^ Digit6 94 94 []\",\n            \"Keyup: ^ Digit6 54 []\",\n        ]\n    )\n\n\nasync def test_should_send_proper_codes_while_typing_with_shift(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    keyboard = page.keyboard\n    await keyboard.down(\"Shift\")\n    await page.keyboard.type(\"~\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: Shift ShiftLeft 16 [Shift]\",\n            \"Keydown: ~ Backquote 192 [Shift]\",  # 192 is ` keyCode\n            \"Keypress: ~ Backquote 126 126 [Shift]\",  # 126 is ~ charCode\n            \"Keyup: ~ Backquote 192 [Shift]\",\n        ]\n    )\n    await keyboard.up(\"Shift\")\n\n\nasync def test_should_not_type_canceled_events(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.focus(\"textarea\")\n    await page.evaluate(\n        \"\"\"() => {\n    window.addEventListener('keydown', event => {\n      event.stopPropagation();\n      event.stopImmediatePropagation();\n      if (event.key === 'l')\n        event.preventDefault();\n      if (event.key === 'o')\n        event.preventDefault();\n    }, false);\n  }\"\"\"\n    )\n\n    await page.keyboard.type(\"Hello World!\")\n    assert (\n        await page.eval_on_selector(\"textarea\", \"textarea => textarea.value\")\n        == \"He Wrd!\"\n    )\n\n\nasync def test_should_press_plus(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.press(\"+\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: + Equal 187 []\",  # 192 is ` keyCode\n            \"Keypress: + Equal 43 43 []\",  # 126 is ~ charCode\n            \"Keyup: + Equal 187 []\",\n        ]\n    )\n\n\nasync def test_should_press_shift_plus(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.press(\"Shift++\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: Shift ShiftLeft 16 [Shift]\",\n            \"Keydown: + Equal 187 [Shift]\",  # 192 is ` keyCode\n            \"Keypress: + Equal 43 43 [Shift]\",  # 126 is ~ charCode\n            \"Keyup: + Equal 187 [Shift]\",\n            \"Keyup: Shift ShiftLeft 16 []\",\n        ]\n    )\n\n\nasync def test_should_support_plus_separated_modifiers(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.press(\"Shift+~\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: Shift ShiftLeft 16 [Shift]\",\n            \"Keydown: ~ Backquote 192 [Shift]\",  # 192 is ` keyCode\n            \"Keypress: ~ Backquote 126 126 [Shift]\",  # 126 is ~ charCode\n            \"Keyup: ~ Backquote 192 [Shift]\",\n            \"Keyup: Shift ShiftLeft 16 []\",\n        ]\n    )\n\n\nasync def test_should_suport_multiple_plus_separated_modifiers(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.press(\"Control+Shift+~\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: Control ControlLeft 17 [Control]\",\n            \"Keydown: Shift ShiftLeft 16 [Control Shift]\",\n            \"Keydown: ~ Backquote 192 [Control Shift]\",  # 192 is ` keyCode\n            \"Keyup: ~ Backquote 192 [Control Shift]\",\n            \"Keyup: Shift ShiftLeft 16 [Control]\",\n            \"Keyup: Control ControlLeft 17 []\",\n        ]\n    )\n\n\nasync def test_should_shift_raw_codes(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/keyboard.html\")\n    await page.keyboard.press(\"Shift+Digit3\")\n    assert await page.evaluate(\"() => getResult()\") == \"\\n\".join(\n        [\n            \"Keydown: Shift ShiftLeft 16 [Shift]\",\n            \"Keydown: # Digit3 51 [Shift]\",  # 51 is # keyCode\n            \"Keypress: # Digit3 35 35 [Shift]\",  # 35 is # charCode\n            \"Keyup: # Digit3 51 [Shift]\",\n            \"Keyup: Shift ShiftLeft 16 []\",\n        ]\n    )\n\n\nasync def test_should_specify_repeat_property(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.focus(\"textarea\")\n    lastEvent = await captureLastKeydown(page)\n    await page.keyboard.down(\"a\")\n    assert await lastEvent.evaluate(\"e => e.repeat\") is False\n    await page.keyboard.press(\"a\")\n    assert await lastEvent.evaluate(\"e => e.repeat\")\n\n    await page.keyboard.down(\"b\")\n    assert await lastEvent.evaluate(\"e => e.repeat\") is False\n    await page.keyboard.down(\"b\")\n    assert await lastEvent.evaluate(\"e => e.repeat\")\n\n    await page.keyboard.up(\"a\")\n    await page.keyboard.down(\"a\")\n    assert await lastEvent.evaluate(\"e => e.repeat\") is False\n\n\nasync def test_should_type_all_kinds_of_characters(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.focus(\"textarea\")\n    text = \"This text goes onto two lines.\\nThis character is 嗨.\"\n    await page.keyboard.type(text)\n    assert await page.eval_on_selector(\"textarea\", \"t => t.value\") == text\n\n\nasync def test_should_specify_location(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    lastEvent = await captureLastKeydown(page)\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n\n    await textarea.press(\"Digit5\")\n    assert await lastEvent.evaluate(\"e => e.location\") == 0\n\n    await textarea.press(\"ControlLeft\")\n    assert await lastEvent.evaluate(\"e => e.location\") == 1\n\n    await textarea.press(\"ControlRight\")\n    assert await lastEvent.evaluate(\"e => e.location\") == 2\n\n    await textarea.press(\"NumpadSubtract\")\n    assert await lastEvent.evaluate(\"e => e.location\") == 3\n\n\nasync def test_should_press_enter(page: Page) -> None:\n    await page.set_content(\"<textarea></textarea>\")\n    await page.focus(\"textarea\")\n    lastEventHandle = await captureLastKeydown(page)\n\n    async def testEnterKey(key: str, expectedKey: str, expectedCode: str) -> None:\n        await page.keyboard.press(key)\n        lastEvent = await lastEventHandle.json_value()\n        assert lastEvent[\"key\"] == expectedKey\n        assert lastEvent[\"code\"] == expectedCode\n        value = await page.eval_on_selector(\"textarea\", \"t => t.value\")\n        assert value == \"\\n\"\n        await page.eval_on_selector(\"textarea\", \"t => t.value = ''\")\n\n    await testEnterKey(\"Enter\", \"Enter\", \"Enter\")\n    await testEnterKey(\"NumpadEnter\", \"Enter\", \"NumpadEnter\")\n    await testEnterKey(\"\\n\", \"Enter\", \"Enter\")\n    await testEnterKey(\"\\r\", \"Enter\", \"Enter\")\n\n\nasync def test_should_throw_unknown_keys(page: Page, server: Server) -> None:\n    with pytest.raises(Error) as exc:\n        await page.keyboard.press(\"NotARealKey\")\n    assert exc.value.message == 'Keyboard.press: Unknown key: \"NotARealKey\"'\n\n    with pytest.raises(Error) as exc:\n        await page.keyboard.press(\"ё\")\n    assert exc.value.message == 'Keyboard.press: Unknown key: \"ё\"'\n\n    with pytest.raises(Error) as exc:\n        await page.keyboard.press(\"😊\")\n    assert exc.value.message == 'Keyboard.press: Unknown key: \"😊\"'\n\n\nasync def test_should_type_emoji(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.type(\"textarea\", \"👹 Tokyo street Japan 🇯🇵\")\n    assert (\n        await page.eval_on_selector(\"textarea\", \"textarea => textarea.value\")\n        == \"👹 Tokyo street Japan 🇯🇵\"\n    )\n\n\nasync def test_should_type_emoji_into_an_iframe(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"emoji-test\", server.PREFIX + \"/input/textarea.html\")\n    frame = page.frames[1]\n    textarea = await frame.query_selector(\"textarea\")\n    assert textarea\n    await textarea.type(\"👹 Tokyo street Japan 🇯🇵\")\n    assert (\n        await frame.eval_on_selector(\"textarea\", \"textarea => textarea.value\")\n        == \"👹 Tokyo street Japan 🇯🇵\"\n    )\n\n\nasync def test_should_handle_select_all(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.type(\"some text\")\n    await page.keyboard.down(\"ControlOrMeta\")\n    await page.keyboard.press(\"a\")\n    await page.keyboard.up(\"ControlOrMeta\")\n    await page.keyboard.press(\"Backspace\")\n    assert await page.eval_on_selector(\"textarea\", \"textarea => textarea.value\") == \"\"\n\n\nasync def test_should_be_able_to_prevent_select_all(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.type(\"some text\")\n    await page.eval_on_selector(\n        \"textarea\",\n        \"\"\"textarea => {\n    textarea.addEventListener('keydown', event => {\n      if (event.key === 'a' && (event.metaKey || event.ctrlKey))\n        event.preventDefault();\n    }, false);\n  }\"\"\",\n    )\n\n    await page.keyboard.down(\"ControlOrMeta\")\n    await page.keyboard.press(\"a\")\n    await page.keyboard.up(\"ControlOrMeta\")\n    await page.keyboard.press(\"Backspace\")\n    assert (\n        await page.eval_on_selector(\"textarea\", \"textarea => textarea.value\")\n        == \"some tex\"\n    )\n\n\n@pytest.mark.only_platform(\"darwin\")\n@pytest.mark.skip_browser(\"firefox\")  # Upstream issue\nasync def test_should_support_macos_shortcuts(\n    page: Page, server: Server, is_firefox: bool, is_mac: bool\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = await page.query_selector(\"textarea\")\n    assert textarea\n    await textarea.type(\"some text\")\n    # select one word backwards\n    await page.keyboard.press(\"Shift+Control+Alt+KeyB\")\n    await page.keyboard.press(\"Backspace\")\n    assert (\n        await page.eval_on_selector(\"textarea\", \"textarea => textarea.value\") == \"some \"\n    )\n\n\nasync def test_should_press_the_meta_key(page: Page) -> None:\n    lastEvent = await captureLastKeydown(page)\n    await page.keyboard.press(\"Meta\")\n    v = await lastEvent.json_value()\n    metaKey = v[\"metaKey\"]\n    key = v[\"key\"]\n    code = v[\"code\"]\n    assert key == \"Meta\"\n    assert code == \"MetaLeft\"\n    assert metaKey\n\n\nasync def test_should_work_after_a_cross_origin_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/empty.html\")\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    lastEvent = await captureLastKeydown(page)\n    await page.keyboard.press(\"a\")\n    assert await lastEvent.evaluate(\"l => l.key\") == \"a\"\n\n\n# event.keyIdentifier has been removed from all browsers except WebKit\n@pytest.mark.only_browser(\"webkit\")\nasync def test_should_expose_keyIdentifier_in_webkit(\n    page: Page, server: Server\n) -> None:\n    lastEvent = await captureLastKeydown(page)\n    keyMap = {\n        \"ArrowUp\": \"Up\",\n        \"ArrowDown\": \"Down\",\n        \"ArrowLeft\": \"Left\",\n        \"ArrowRight\": \"Right\",\n        \"Backspace\": \"U+0008\",\n        \"Tab\": \"U+0009\",\n        \"Delete\": \"U+007F\",\n        \"a\": \"U+0041\",\n        \"b\": \"U+0042\",\n        \"F12\": \"F12\",\n    }\n    for key, keyIdentifier in keyMap.items():\n        await page.keyboard.press(key)\n        assert await lastEvent.evaluate(\"e => e.keyIdentifier\") == keyIdentifier\n\n\nasync def test_should_scroll_with_pagedown(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    # A click is required for WebKit to send the event into the body.\n    await page.click(\"body\")\n    await page.keyboard.press(\"PageDown\")\n    # We can't wait for the scroll to finish, so just wait for it to start.\n    await page.wait_for_function(\"() => scrollY > 0\")\n"
  },
  {
    "path": "tests/async/test_launcher.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport os\nfrom pathlib import Path\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import BrowserType, Error\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\nasync def test_browser_type_launch_should_reject_all_promises_when_browser_is_closed(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = await browser_type.launch(**launch_arguments)\n    page = await (await browser.new_context()).new_page()\n    never_resolves = asyncio.create_task(page.evaluate(\"() => new Promise(r => {})\"))\n    await page.close()\n    with pytest.raises(Error) as exc:\n        await never_resolves\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc.value.message\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_browser_type_launch_should_throw_if_page_argument_is_passed(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    with pytest.raises(Error) as exc:\n        await browser_type.launch(**launch_arguments, args=[\"http://example.com\"])\n    assert \"can not specify page\" in exc.value.message\n\n\nasync def test_browser_type_launch_should_reject_if_launched_browser_fails_immediately(\n    browser_type: BrowserType, launch_arguments: Dict, assetdir: Path\n) -> None:\n    with pytest.raises(Error):\n        await browser_type.launch(\n            **launch_arguments,\n            executable_path=assetdir / \"dummy_bad_browser_executable.js\",\n        )\n\n\nasync def test_browser_type_launch_should_reject_if_executable_path_is_invalid(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    with pytest.raises(Error) as exc:\n        await browser_type.launch(\n            **launch_arguments, executable_path=\"random-invalid-path\"\n        )\n    assert \"executable doesn't exist\" in exc.value.message\n\n\nasync def test_browser_type_executable_path_should_work(\n    browser_type: BrowserType, browser_channel: str\n) -> None:\n    if browser_channel:\n        return\n    executable_path = browser_type.executable_path\n    assert os.path.exists(executable_path)\n    assert os.path.realpath(executable_path) == os.path.realpath(executable_path)\n\n\nasync def test_browser_type_name_should_work(\n    browser_type: BrowserType, is_webkit: bool, is_firefox: bool, is_chromium: bool\n) -> None:\n    if is_webkit:\n        assert browser_type.name == \"webkit\"\n    elif is_firefox:\n        assert browser_type.name == \"firefox\"\n    elif is_chromium:\n        assert browser_type.name == \"chromium\"\n    else:\n        raise ValueError(\"Unknown browser\")\n\n\nasync def test_browser_close_should_fire_close_event_for_all_contexts(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = await browser_type.launch(**launch_arguments)\n    context = await browser.new_context()\n    closed = []\n    context.on(\"close\", lambda _: closed.append(True))\n    await browser.close()\n    assert closed == [True]\n\n\nasync def test_browser_close_should_be_callable_twice(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = await browser_type.launch(**launch_arguments)\n    await asyncio.gather(\n        browser.close(),\n        browser.close(),\n    )\n    await browser.close()\n"
  },
  {
    "path": "tests/async/test_listeners.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page, Response\nfrom tests.server import Server\n\n\nasync def test_listeners(page: Page, server: Server) -> None:\n    log = []\n\n    def print_response(response: Response) -> None:\n        log.append(response)\n\n    page.on(\"response\", print_response)\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    assert len(log) > 0\n    page.remove_listener(\"response\", print_response)\n\n    log = []\n    await page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    assert len(log) == 0\n"
  },
  {
    "path": "tests/async/test_locators.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport re\nimport traceback\nfrom typing import Callable\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom playwright._impl._path_utils import get_file_dirname\nfrom playwright.async_api import Error, Page, expect\nfrom tests.server import Server\n\n_dirname = get_file_dirname()\nFILE_TO_UPLOAD = _dirname / \"..\" / \"assets/file-to-upload.txt\"\n\n\nasync def test_locators_click_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    await button.click()\n    assert await page.evaluate(\"window['result']\") == \"Clicked\"\n\n\nasync def test_locators_click_should_work_with_node_removed(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\"delete window['Node']\")\n    button = page.locator(\"button\")\n    await button.click()\n    assert await page.evaluate(\"window['result']\") == \"Clicked\"\n\n\nasync def test_locators_click_should_work_for_text_nodes(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    await page.evaluate(\n        \"\"\"() => {\n        window['double'] = false;\n        const button = document.querySelector('button');\n        button.addEventListener('dblclick', event => {\n        window['double'] = true;\n        });\n    }\"\"\"\n    )\n    button = page.locator(\"button\")\n    await button.dblclick()\n    assert await page.evaluate(\"double\") is True\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_locators_should_have_repr(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    await button.click()\n    assert (\n        str(button)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/input/button.html'> selector='button'>\"\n    )\n\n\nasync def test_locators_get_attribute_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    button = page.locator(\"#outer\")\n    assert await button.get_attribute(\"name\") == \"value\"\n    assert await button.get_attribute(\"foo\") is None\n\n\nasync def test_locators_input_value_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    await page.fill(\"#textarea\", \"input value\")\n    text_area = page.locator(\"#textarea\")\n    assert await text_area.input_value() == \"input value\"\n\n\nasync def test_locators_inner_html_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#outer\")\n    assert await locator.inner_html() == '<div id=\"inner\">Text,\\nmore text</div>'\n\n\nasync def test_locators_inner_text_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#inner\")\n    assert await locator.inner_text() == \"Text, more text\"\n\n\nasync def test_locators_text_content_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#inner\")\n    assert await locator.text_content() == \"Text,\\nmore text\"\n\n\nasync def test_locators_is_hidden_and_is_visible_should_work(page: Page) -> None:\n    await page.set_content(\"<div>Hi</div><span></span>\")\n\n    div = page.locator(\"div\")\n    assert await div.is_visible() is True\n    assert await div.is_hidden() is False\n\n    span = page.locator(\"span\")\n    assert await span.is_visible() is False\n    assert await span.is_hidden() is True\n\n\nasync def test_locators_is_enabled_and_is_disabled_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <button disabled>button1</button>\n        <button>button2</button>\n        <div>div</div>\n    \"\"\"\n    )\n\n    div = page.locator(\"div\")\n    assert await div.is_enabled()\n    assert await div.is_disabled() is False\n\n    button1 = page.locator(':text(\"button1\")')\n    assert await button1.is_enabled() is False\n    assert await button1.is_disabled() is True\n\n    button1 = page.locator(':text(\"button2\")')\n    assert await button1.is_enabled()\n    assert await button1.is_disabled() is False\n\n\nasync def test_locators_is_editable_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <input id=input1 disabled><textarea></textarea><input id=input2>\n    \"\"\"\n    )\n\n    input1 = page.locator(\"#input1\")\n    assert await input1.is_editable() is False\n\n    input2 = page.locator(\"#input2\")\n    assert await input2.is_editable() is True\n\n\nasync def test_locators_is_checked_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <input type='checkbox' checked><div>Not a checkbox</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"input\")\n    assert await element.is_checked() is True\n    await element.evaluate(\"e => e.checked = false\")\n    assert await element.is_checked() is False\n\n\nasync def test_locators_all_text_contents_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div><div>C</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"div\")\n    assert await element.all_text_contents() == [\"A\", \"B\", \"C\"]\n\n\nasync def test_locators_all_inner_texts(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div><div>C</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"div\")\n    assert await element.all_inner_texts() == [\"A\", \"B\", \"C\"]\n\n\nasync def test_locators_should_query_existing_element(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/playground.html\")\n    await page.set_content(\n        \"\"\"<html><body><div class=\"second\"><div class=\"inner\">A</div></div></body></html>\"\"\"\n    )\n    html = page.locator(\"html\")\n    second = html.locator(\".second\")\n    inner = second.locator(\".inner\")\n    assert (\n        await page.evaluate(\"e => e.textContent\", await inner.element_handle()) == \"A\"\n    )\n\n\nasync def test_locators_evaluate_handle_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/dom.html\")\n    outer = page.locator(\"#outer\")\n    inner = outer.locator(\"#inner\")\n    check = inner.locator(\"#check\")\n    text = await inner.evaluate_handle(\"e => e.firstChild\")\n    await page.evaluate(\"1 + 1\")\n    assert (\n        str(outer)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer'>\"\n    )\n    assert (\n        str(inner)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer >> #inner'>\"\n    )\n    assert str(text) == \"JSHandle@#text=Text,↵more text\"\n    assert (\n        str(check)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer >> #inner >> #check'>\"\n    )\n\n\nasync def test_locators_should_query_existing_elements(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<html><body><div>A</div><br/><div>B</div></body></html>\"\"\"\n    )\n    html = page.locator(\"html\")\n    elements = await html.locator(\"div\").element_handles()\n    assert len(elements) == 2\n    result = []\n    for element in elements:\n        result.append(await page.evaluate(\"e => e.textContent\", element))\n    assert result == [\"A\", \"B\"]\n\n\nasync def test_locators_return_empty_array_for_non_existing_elements(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"<html><body><div>A</div><br/><div>B</div></body></html>\"\"\"\n    )\n    html = page.locator(\"html\")\n    elements = await html.locator(\"abc\").element_handles()\n    assert len(elements) == 0\n    assert elements == []\n\n\nasync def test_locators_evaluate_all_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<html><body><div class=\"tweet\"><div class=\"like\">100</div><div class=\"like\">10</div></div></body></html>\"\"\"\n    )\n    tweet = page.locator(\".tweet .like\")\n    content = await tweet.evaluate_all(\"nodes => nodes.map(n => n.innerText)\")\n    assert content == [\"100\", \"10\"]\n\n\nasync def test_locators_evaluate_all_should_work_with_missing_selector(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"<div class=\"a\">not-a-child-div</div><div id=\"myId\"></div\"\"\"\n    )\n    tweet = page.locator(\"#myId .a\")\n    nodes_length = await tweet.evaluate_all(\"nodes => nodes.length\")\n    assert nodes_length == 0\n\n\nasync def test_locators_hover_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/scrollable.html\")\n    button = page.locator(\"#button-6\")\n    await button.hover()\n    assert (\n        await page.evaluate(\"document.querySelector('button:hover').id\") == \"button-6\"\n    )\n\n\nasync def test_locators_fill_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    button = page.locator(\"input\")\n    await button.fill(\"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_locators_clear_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    button = page.locator(\"input\")\n    await button.fill(\"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n    await button.clear()\n    assert await page.evaluate(\"result\") == \"\"\n\n\nasync def test_locators_check_should_work(page: Page) -> None:\n    await page.set_content(\"<input id='checkbox' type='checkbox'></input>\")\n    button = page.locator(\"input\")\n    await button.check()\n    assert await page.evaluate(\"checkbox.checked\") is True\n\n\nasync def test_locators_uncheck_should_work(page: Page) -> None:\n    await page.set_content(\"<input id='checkbox' type='checkbox' checked></input>\")\n    button = page.locator(\"input\")\n    await button.uncheck()\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_locators_select_option_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    select = page.locator(\"select\")\n    await select.select_option(\"blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_locators_focus_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    assert await button.evaluate(\"button => document.activeElement === button\") is False\n    await button.focus()\n    assert await button.evaluate(\"button => document.activeElement === button\") is True\n\n\nasync def test_locators_dispatch_event_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    await button.dispatch_event(\"click\")\n    assert await page.evaluate(\"result\") == \"Clicked\"\n\n\nasync def test_locators_should_upload_a_file(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/fileupload.html\")\n    input = page.locator(\"input[type=file]\")\n\n    file_path = os.path.relpath(FILE_TO_UPLOAD, os.getcwd())\n    await input.set_input_files(file_path)\n    assert (\n        await page.evaluate(\"e => e.files[0].name\", await input.element_handle())\n        == \"file-to-upload.txt\"\n    )\n\n\nasync def test_locators_should_press(page: Page) -> None:\n    await page.set_content(\"<input type='text' />\")\n    await page.locator(\"input\").press(\"h\")\n    assert await page.eval_on_selector(\"input\", \"input => input.value\") == \"h\"\n\n\nasync def test_locators_should_scroll_into_view(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/offscreenbuttons.html\")\n    for i in range(11):\n        button = page.locator(f\"#btn{i}\")\n        before = await button.evaluate(\n            \"button => button.getBoundingClientRect().right - window.innerWidth\"\n        )\n        assert before == 10 * i\n        await button.scroll_into_view_if_needed()\n        after = await button.evaluate(\n            \"button => button.getBoundingClientRect().right - window.innerWidth\"\n        )\n        assert after <= 0\n        await page.evaluate(\"window.scrollTo(0, 0)\")\n\n\nasync def test_locators_should_select_textarea(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = page.locator(\"textarea\")\n    await textarea.evaluate(\"textarea => textarea.value = 'some value'\")\n    await textarea.select_text()\n    if browser_name == \"firefox\" or browser_name == \"webkit\":\n        assert await textarea.evaluate(\"el => el.selectionStart\") == 0\n        assert await textarea.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert await page.evaluate(\"window.getSelection().toString()\") == \"some value\"\n\n\nasync def test_locators_should_type(page: Page) -> None:\n    await page.set_content(\"<input type='text' />\")\n    await page.locator(\"input\").type(\"hello\")\n    assert await page.eval_on_selector(\"input\", \"input => input.value\") == \"hello\"\n\n\nasync def test_locators_should_press_sequentially(page: Page) -> None:\n    await page.set_content(\"<input type='text' />\")\n    await page.locator(\"input\").press_sequentially(\"hello\")\n    assert await page.eval_on_selector(\"input\", \"input => input.value\") == \"hello\"\n\n\nasync def test_locators_should_screenshot(\n    page: Page, server: Server, assert_to_be_golden: Callable[[bytes, str], None]\n) -> None:\n    await page.set_viewport_size(\n        {\n            \"width\": 500,\n            \"height\": 500,\n        }\n    )\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await page.evaluate(\"window.scrollBy(50, 100)\")\n    element = page.locator(\".box:nth-of-type(3)\")\n    assert_to_be_golden(\n        await element.screenshot(), \"screenshot-element-bounding-box.png\"\n    )\n\n\nasync def test_locators_should_return_bounding_box(page: Page, server: Server) -> None:\n    await page.set_viewport_size(\n        {\n            \"width\": 500,\n            \"height\": 500,\n        }\n    )\n    await page.goto(server.PREFIX + \"/grid.html\")\n    element = page.locator(\".box:nth-of-type(13)\")\n    box = await element.bounding_box()\n    assert box == {\n        \"x\": 100,\n        \"y\": 50,\n        \"width\": 50,\n        \"height\": 50,\n    }\n\n\nasync def test_locators_should_respect_first_and_last(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <section>\n            <div><p>A</p></div>\n            <div><p>A</p><p>A</p></div>\n            <div><p>A</p><p>A</p><p>A</p></div>\n        </section>\"\"\"\n    )\n    assert await page.locator(\"div >> p\").count() == 6\n    assert await page.locator(\"div\").locator(\"p\").count() == 6\n    assert await page.locator(\"div\").first.locator(\"p\").count() == 1\n    assert await page.locator(\"div\").last.locator(\"p\").count() == 3\n\n\nasync def test_locators_should_respect_nth(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n    <section>\n        <div><p>A</p></div>\n        <div><p>A</p><p>A</p></div>\n        <div><p>A</p><p>A</p><p>A</p></div>\n    </section>\"\"\"\n    )\n    assert await page.locator(\"div >> p\").nth(0).count() == 1\n    assert await page.locator(\"div\").nth(1).locator(\"p\").count() == 2\n    assert await page.locator(\"div\").nth(2).locator(\"p\").count() == 3\n\n\nasync def test_locators_should_throw_on_capture_without_nth(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <section><div><p>A</p></div></section>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"Can't query n-th element\"):\n        await page.locator(\"*css=div >> p\").nth(1).click()\n\n\nasync def test_locators_should_throw_due_to_strictness(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"strict mode violation\"):\n        await page.locator(\"div\").is_visible()\n\n\nasync def test_locators_should_throw_due_to_strictness_2(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <select><option>One</option><option>Two</option></select>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"strict mode violation\"):\n        await page.locator(\"option\").evaluate(\"e => {}\")\n\n\nasync def test_locators_set_checked(page: Page) -> None:\n    await page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    locator = page.locator(\"input\")\n    await locator.set_checked(True)\n    assert await page.evaluate(\"checkbox.checked\")\n    await locator.set_checked(False)\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_locators_wait_for(page: Page) -> None:\n    await page.set_content(\"<div></div>\")\n    locator = page.locator(\"div\")\n    task = locator.wait_for()\n    await page.eval_on_selector(\"div\", \"div => div.innerHTML = '<span>target</span>'\")\n    await task\n    assert await locator.text_content() == \"target\"\n\n\nasync def test_should_wait_for_hidden(page: Page) -> None:\n    await page.set_content(\"<div><span>target</span></div>\")\n    locator = page.locator(\"span\")\n    task = locator.wait_for(state=\"hidden\")\n    await page.eval_on_selector(\"div\", \"div => div.innerHTML = ''\")\n    await task\n\n\nasync def test_should_combine_visible_with_other_selectors(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<div>\n        <div class=\"item\" style=\"display: none\">Hidden data0</div>\n        <div class=\"item\">visible data1</div>\n        <div class=\"item\" style=\"display: none\">Hidden data1</div>\n        <div class=\"item\">visible data2</div>\n        <div class=\"item\" style=\"display: none\">Hidden data1</div>\n        <div class=\"item\">visible data3</div>\n        </div>\n    \"\"\"\n    )\n    locator = page.locator(\".item >> visible=true\").nth(1)\n    await expect(locator).to_have_text(\"visible data2\")\n    await expect(page.locator(\".item >> visible=true >> text=data3\")).to_have_text(\n        \"visible data3\"\n    )\n\n\nasync def test_should_support_filter_visible(page: Page) -> None:\n    await page.set_content(\n        \"\"\"<div>\n    <div class=\"item\" style=\"display: none\">Hidden data0</div>\n    <div class=\"item\">visible data1</div>\n    <div class=\"item\" style=\"display: none\">Hidden data1</div>\n    <div class=\"item\">visible data2</div>\n    <div class=\"item\" style=\"display: none\">Hidden data2</div>\n    <div class=\"item\">visible data3</div>\n    </div>\n    \"\"\"\n    )\n    locator = page.locator(\".item\").filter(visible=True).nth(1)\n    await expect(locator).to_have_text(\"visible data2\")\n    await expect(\n        page.locator(\".item\").filter(visible=True).get_by_text(\"data3\")\n    ).to_have_text(\"visible data3\")\n    await expect(\n        page.locator(\".item\").filter(visible=False).get_by_text(\"data1\")\n    ).to_have_text(\"Hidden data1\")\n\n\nasync def test_locator_count_should_work_with_deleted_map_in_main_world(\n    page: Page,\n) -> None:\n    await page.evaluate(\"Map = 1\")\n    await page.locator(\"#searchResultTableDiv .x-grid3-row\").count()\n    await expect(page.locator(\"#searchResultTableDiv .x-grid3-row\")).to_have_count(0)\n\n\nasync def test_locator_locator_and_framelocator_locator_should_accept_locator(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <div><input value=outer></div>\n        <iframe srcdoc=\"<div><input value=inner></div>\"></iframe>\n    \"\"\"\n    )\n\n    input_locator = page.locator(\"input\")\n    assert await input_locator.input_value() == \"outer\"\n    assert await page.locator(\"div\").locator(input_locator).input_value() == \"outer\"\n    assert (\n        await page.frame_locator(\"iframe\").locator(input_locator).input_value()\n        == \"inner\"\n    )\n    assert (\n        await page.frame_locator(\"iframe\")\n        .locator(\"div\")\n        .locator(input_locator)\n        .input_value()\n        == \"inner\"\n    )\n\n    div_locator = page.locator(\"div\")\n    assert await div_locator.locator(\"input\").input_value() == \"outer\"\n    assert (\n        await page.frame_locator(\"iframe\")\n        .locator(div_locator)\n        .locator(\"input\")\n        .input_value()\n        == \"inner\"\n    )\n\n\nasync def route_iframe(page: Page) -> None:\n    await page.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(\n            body='<iframe src=\"iframe.html\" name=\"frame1\">></iframe>',\n            content_type=\"text/html\",\n        ),\n    )\n    await page.route(\n        \"**/iframe.html\",\n        lambda route: route.fulfill(\n            body=\"\"\"<html>\n          <div>\n            <button>Hello iframe</button>\n            <iframe src=\"iframe-2.html\"></iframe>\n          </div>\n          <span>1</span>\n          <span>2</span>\n        </html>\"\"\",\n            content_type=\"text/html\",\n        ),\n    )\n    await page.route(\n        \"**/iframe-2.html\",\n        lambda route: route.fulfill(\n            body=\"<html><button>Hello nested iframe</button></html>\",\n            content_type=\"text/html\",\n        ),\n    )\n\n\nasync def test_locators_frame_should_work_with_iframe(\n    page: Page, server: Server\n) -> None:\n    await route_iframe(page)\n    await page.goto(server.EMPTY_PAGE)\n    button = page.frame_locator(\"iframe\").locator(\"button\")\n    await button.wait_for()\n    assert await button.inner_text() == \"Hello iframe\"\n    await button.click()\n\n\nasync def test_locators_frame_should_work_for_nested_iframe(\n    page: Page, server: Server\n) -> None:\n    await route_iframe(page)\n    await page.goto(server.EMPTY_PAGE)\n    button = page.frame_locator(\"iframe\").frame_locator(\"iframe\").locator(\"button\")\n    await button.wait_for()\n    assert await button.inner_text() == \"Hello nested iframe\"\n    await button.click()\n\n\nasync def test_locators_frame_should_work_with_locator_frame_locator(\n    page: Page, server: Server\n) -> None:\n    await route_iframe(page)\n    await page.goto(server.EMPTY_PAGE)\n    button = page.locator(\"body\").frame_locator(\"iframe\").locator(\"button\")\n    await button.wait_for()\n    assert await button.inner_text() == \"Hello iframe\"\n    await button.click()\n\n\nasync def test_locator_content_frame_should_work(page: Page, server: Server) -> None:\n    await route_iframe(page)\n    await page.goto(server.EMPTY_PAGE)\n    locator = page.locator(\"iframe\")\n    frame_locator = locator.content_frame\n    button = frame_locator.locator(\"button\")\n    assert await button.inner_text() == \"Hello iframe\"\n    await expect(button).to_have_text(\"Hello iframe\")\n    await button.click()\n\n\nasync def test_frame_locator_owner_should_work(page: Page, server: Server) -> None:\n    await route_iframe(page)\n    await page.goto(server.EMPTY_PAGE)\n    frame_locator = page.frame_locator(\"iframe\")\n    locator = frame_locator.owner\n    await expect(locator).to_be_visible()\n    assert await locator.get_attribute(\"name\") == \"frame1\"\n\n\nasync def route_ambiguous(page: Page) -> None:\n    await page.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(\n            body=\"\"\"\n        <iframe src=\"iframe-1.html\"></iframe>\n        <iframe src=\"iframe-2.html\"></iframe>\n        <iframe src=\"iframe-3.html\"></iframe>\n    \"\"\",\n            content_type=\"text/html\",\n        ),\n    )\n    await page.route(\n        \"**/iframe-*\",\n        lambda route: route.fulfill(\n            body=f\"<html><button>Hello from {urlparse(route.request.url).path[1:]}</button></html>\",\n            content_type=\"text/html\",\n        ),\n    )\n\n\nasync def test_locator_frame_locator_should_throw_on_ambiguity(\n    page: Page, server: Server\n) -> None:\n    await route_ambiguous(page)\n    await page.goto(server.EMPTY_PAGE)\n    button = page.locator(\"body\").frame_locator(\"iframe\").locator(\"button\")\n    with pytest.raises(\n        Error,\n        match=r'.*strict mode violation: locator\\(\"body\"\\)\\.locator\\(\"iframe\"\\) resolved to 3 elements.*',\n    ):\n        await button.wait_for()\n\n\nasync def test_locator_frame_locator_should_not_throw_on_first_last_nth(\n    page: Page, server: Server\n) -> None:\n    await route_ambiguous(page)\n    await page.goto(server.EMPTY_PAGE)\n    button1 = page.locator(\"body\").frame_locator(\"iframe\").first.locator(\"button\")\n    assert await button1.text_content() == \"Hello from iframe-1.html\"\n    button2 = page.locator(\"body\").frame_locator(\"iframe\").nth(1).locator(\"button\")\n    assert await button2.text_content() == \"Hello from iframe-2.html\"\n    button3 = page.locator(\"body\").frame_locator(\"iframe\").last.locator(\"button\")\n    assert await button3.text_content() == \"Hello from iframe-3.html\"\n\n\nasync def test_drag_to(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    await page.locator(\"#source\").drag_to(page.locator(\"#target\"))\n    assert (\n        await page.eval_on_selector(\n            \"#target\", \"target => target.contains(document.querySelector('#source'))\"\n        )\n        is True\n    )\n\n\nasync def test_drag_to_with_position(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n      <div style=\"width:100px;height:100px;background:red;\" id=\"red\">\n      </div>\n      <div style=\"width:100px;height:100px;background:blue;\" id=\"blue\">\n      </div>\n    \"\"\"\n    )\n    events_handle = await page.evaluate_handle(\n        \"\"\"\n        () => {\n        const events = [];\n        document.getElementById('red').addEventListener('mousedown', event => {\n            events.push({\n            type: 'mousedown',\n            x: event.offsetX,\n            y: event.offsetY,\n            });\n        });\n        document.getElementById('blue').addEventListener('mouseup', event => {\n            events.push({\n            type: 'mouseup',\n            x: event.offsetX,\n            y: event.offsetY,\n            });\n        });\n        return events;\n        }\n    \"\"\"\n    )\n    await page.locator(\"#red\").drag_to(\n        page.locator(\"#blue\"),\n        source_position={\"x\": 34, \"y\": 7},\n        target_position={\"x\": 10, \"y\": 20},\n    )\n    assert await events_handle.json_value() == [\n        {\"type\": \"mousedown\", \"x\": 34, \"y\": 7},\n        {\"type\": \"mouseup\", \"x\": 10, \"y\": 20},\n    ]\n\n\nasync def test_locator_query_should_filter_by_text(page: Page, server: Server) -> None:\n    await page.set_content(\"<div>Foobar</div><div>Bar</div>\")\n    await expect(page.locator(\"div\", has_text=\"Foo\")).to_have_text(\"Foobar\")\n\n\nasync def test_locator_query_should_filter_by_text_2(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<div>foo <span>hello world</span> bar</div>\")\n    await expect(page.locator(\"div\", has_text=\"hello world\")).to_have_text(\n        \"foo hello world bar\"\n    )\n\n\nasync def test_locator_query_should_filter_by_regex(page: Page, server: Server) -> None:\n    await page.set_content(\"<div>Foobar</div><div>Bar</div>\")\n    await expect(page.locator(\"div\", has_text=re.compile(r\"Foo.*\"))).to_have_text(\n        \"Foobar\"\n    )\n\n\nasync def test_locator_query_should_filter_by_text_with_quotes(\n    page: Page, server: Server\n) -> None:\n    await page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    await expect(page.locator(\"div\", has_text='Hello \"world\"')).to_have_text(\n        'Hello \"world\"'\n    )\n\n\nasync def test_locator_query_should_filter_by_regex_with_quotes(\n    page: Page, server: Server\n) -> None:\n    await page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    await expect(\n        page.locator(\"div\", has_text=re.compile('Hello \"world\"'))\n    ).to_have_text('Hello \"world\"')\n\n\nasync def test_locator_query_should_filter_by_regex_and_regexp_flags(\n    page: Page, server: Server\n) -> None:\n    await page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    await expect(\n        page.locator(\"div\", has_text=re.compile('hElLo \"world', re.IGNORECASE))\n    ).to_have_text('Hello \"world\"')\n\n\nasync def test_locator_should_return_page(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/frames/two-frames.html\")\n    outer = page.locator(\"#outer\")\n    assert outer.page == page\n\n    inner = outer.locator(\"#inner\")\n    assert inner.page == page\n\n    in_frame = page.frames[1].locator(\"div\")\n    assert in_frame.page == page\n\n\nasync def test_locator_should_support_has_locator(page: Page, server: Server) -> None:\n    await page.set_content(\"<div><span>hello</span></div><div><span>world</span></div>\")\n    await expect(page.locator(\"div\", has=page.locator(\"text=world\"))).to_have_count(1)\n    assert (\n        await page.locator(\"div\", has=page.locator(\"text=world\")).evaluate(\n            \"e => e.outerHTML\"\n        )\n        == \"<div><span>world</span></div>\"\n    )\n    await expect(page.locator(\"div\", has=page.locator('text=\"hello\"'))).to_have_count(1)\n    assert (\n        await page.locator(\"div\", has=page.locator('text=\"hello\"')).evaluate(\n            \"e => e.outerHTML\"\n        )\n        == \"<div><span>hello</span></div>\"\n    )\n    await expect(page.locator(\"div\", has=page.locator(\"xpath=./span\"))).to_have_count(2)\n    await expect(page.locator(\"div\", has=page.locator(\"span\"))).to_have_count(2)\n    await expect(\n        page.locator(\"div\", has=page.locator(\"span\", has_text=\"wor\"))\n    ).to_have_count(1)\n    assert (\n        await page.locator(\"div\", has=page.locator(\"span\", has_text=\"wor\")).evaluate(\n            \"e => e.outerHTML\"\n        )\n        == \"<div><span>world</span></div>\"\n    )\n    await expect(\n        page.locator(\n            \"div\",\n            has=page.locator(\"span\"),\n            has_text=\"wor\",\n        )\n    ).to_have_count(1)\n\n\nasync def test_locator_should_enforce_same_frame_for_has_locator(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/two-frames.html\")\n    child = page.frames[1]\n    with pytest.raises(Error) as exc_info:\n        page.locator(\"div\", has=child.locator(\"span\"))\n    assert (\n        'Inner \"has\" locator must belong to the same frame.' in exc_info.value.message\n    )\n\n\nasync def test_locator_should_support_locator_or(page: Page, server: Server) -> None:\n    await page.set_content(\"<div>hello</div><span>world</span>\")\n    await expect(page.locator(\"div\").or_(page.locator(\"span\"))).to_have_count(2)\n    await expect(page.locator(\"div\").or_(page.locator(\"span\"))).to_have_text(\n        [\"hello\", \"world\"]\n    )\n    await expect(\n        page.locator(\"span\").or_(page.locator(\"article\")).or_(page.locator(\"div\"))\n    ).to_have_text([\"hello\", \"world\"])\n    await expect(page.locator(\"article\").or_(page.locator(\"someting\"))).to_have_count(0)\n    await expect(page.locator(\"article\").or_(page.locator(\"div\"))).to_have_text(\"hello\")\n    await expect(page.locator(\"article\").or_(page.locator(\"span\"))).to_have_text(\n        \"world\"\n    )\n    await expect(page.locator(\"div\").or_(page.locator(\"article\"))).to_have_text(\"hello\")\n    await expect(page.locator(\"span\").or_(page.locator(\"article\"))).to_have_text(\n        \"world\"\n    )\n\n\nasync def test_locator_should_support_locator_locator_with_and_or(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div>one <span>two</span> <button>three</button> </div>\n        <span>four</span>\n        <button>five</button>\n        \"\"\"\n    )\n\n    await expect(page.locator(\"div\").locator(page.locator(\"button\"))).to_have_text(\n        [\"three\"]\n    )\n    await expect(\n        page.locator(\"div\").locator(page.locator(\"button\").or_(page.locator(\"span\")))\n    ).to_have_text([\"two\", \"three\"])\n    await expect(page.locator(\"button\").or_(page.locator(\"span\"))).to_have_text(\n        [\"two\", \"three\", \"four\", \"five\"]\n    )\n\n    await expect(\n        page.locator(\"div\").locator(\n            page.locator(\"button\").and_(page.get_by_role(\"button\"))\n        )\n    ).to_have_text([\"three\"])\n    await expect(page.locator(\"button\").and_(page.get_by_role(\"button\"))).to_have_text(\n        [\"three\", \"five\"]\n    )\n\n\nasync def test_locator_highlight_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await page.locator(\".box\").nth(3).highlight()\n    assert await page.locator(\"x-pw-glass\").is_visible()\n\n\nasync def test_should_support_locator_that(page: Page) -> None:\n    await page.set_content(\n        \"<section><div><span>hello</span></div><div><span>world</span></div></section>\"\n    )\n\n    await expect(page.locator(\"div\").filter(has_text=\"hello\")).to_have_count(1)\n    await expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"hello\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(0)\n    await expect(\n        page.locator(\"section\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has_text=\"hello\").locator(\"span\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    await expect(page.locator(\"div\").filter(has=page.locator(\"span\"))).to_have_count(2)\n    await expect(\n        page.locator(\"div\").filter(\n            has=page.locator(\"span\"),\n            has_text=\"world\",\n        )\n    ).to_have_count(1)\n\n\nasync def test_should_filter_by_case_insensitive_regex_in_a_child(page: Page) -> None:\n    await page.set_content('<div class=\"test\"><h5>Title Text</h5></div>')\n    await expect(\n        page.locator(\"div\", has_text=re.compile(r\"^title text$\", re.I))\n    ).to_have_text(\"Title Text\")\n\n\nasync def test_should_filter_by_case_insensitive_regex_in_multiple_children(\n    page: Page,\n) -> None:\n    await page.set_content(\n        '<div class=\"test\"><h5>Title</h5> <h2><i>Text</i></h2></div>'\n    )\n    await expect(\n        page.locator(\"div\", has_text=re.compile(r\"^title text$\", re.I))\n    ).to_have_class(\"test\")\n\n\nasync def test_should_filter_by_regex_with_special_symbols(page: Page) -> None:\n    await page.set_content(\n        '<div class=\"test\"><h5>First/\"and\"</h5><h2><i>Second\\\\</i></h2></div>'\n    )\n    await expect(\n        page.locator(\"div\", has_text=re.compile(r'^first\\/\".*\"second\\\\$', re.S | re.I))\n    ).to_have_class(\"test\")\n\n\nasync def test_should_support_locator_filter(page: Page) -> None:\n    await page.set_content(\n        \"<section><div><span>hello</span></div><div><span>world</span></div></section>\"\n    )\n\n    await expect(page.locator(\"div\").filter(has_text=\"hello\")).to_have_count(1)\n    await expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"hello\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(0)\n    await expect(\n        page.locator(\"section\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has_text=\"hello\").locator(\"span\")\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    await expect(page.locator(\"div\").filter(has=page.locator(\"span\"))).to_have_count(2)\n    await expect(\n        page.locator(\"div\").filter(\n            has=page.locator(\"span\"),\n            has_text=\"world\",\n        )\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has_not=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    await expect(\n        page.locator(\"div\").filter(has_not=page.locator(\"section\"))\n    ).to_have_count(2)\n    await expect(\n        page.locator(\"div\").filter(has_not=page.locator(\"span\"))\n    ).to_have_count(0)\n\n    await expect(page.locator(\"div\").filter(has_not_text=\"hello\")).to_have_count(1)\n    await expect(page.locator(\"div\").filter(has_not_text=\"foo\")).to_have_count(2)\n\n\nasync def test_locators_should_support_locator_and(page: Page, server: Server) -> None:\n    await page.set_content(\n        \"\"\"\n        <div data-testid=foo>hello</div><div data-testid=bar>world</div>\n        <span data-testid=foo>hello2</span><span data-testid=bar>world2</span>\n    \"\"\"\n    )\n    await expect(page.locator(\"div\").and_(page.locator(\"div\"))).to_have_count(2)\n    await expect(page.locator(\"div\").and_(page.get_by_test_id(\"foo\"))).to_have_text(\n        [\"hello\"]\n    )\n    await expect(page.locator(\"div\").and_(page.get_by_test_id(\"bar\"))).to_have_text(\n        [\"world\"]\n    )\n    await expect(page.get_by_test_id(\"foo\").and_(page.locator(\"div\"))).to_have_text(\n        [\"hello\"]\n    )\n    await expect(page.get_by_test_id(\"bar\").and_(page.locator(\"span\"))).to_have_text(\n        [\"world2\"]\n    )\n    await expect(\n        page.locator(\"span\").and_(page.get_by_test_id(re.compile(\"bar|foo\")))\n    ).to_have_count(2)\n\n\nasync def test_locators_has_does_not_encode_unicode(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    locators = [\n        page.locator(\"button\", has_text=\"Драматург\"),\n        page.locator(\"button\", has_text=re.compile(\"Драматург\")),\n        page.locator(\"button\", has=page.locator(\"text=Драматург\")),\n    ]\n    for locator in locators:\n        with pytest.raises(Error) as exc_info:\n            await locator.click(timeout=1_000)\n        assert \"Драматург\" in exc_info.value.message\n\n\nasync def test_locators_should_focus_and_blur_a_button(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    assert not await button.evaluate(\"button => document.activeElement === button\")\n\n    focused = False\n    blurred = False\n\n    async def focus_event() -> None:\n        nonlocal focused\n        focused = True\n\n    async def blur_event() -> None:\n        nonlocal blurred\n        blurred = True\n\n    await page.expose_function(\"focusEvent\", focus_event)\n    await page.expose_function(\"blurEvent\", blur_event)\n    await button.evaluate(\n        \"\"\"button => {\n        button.addEventListener('focus', window['focusEvent']);\n        button.addEventListener('blur', window['blurEvent']);\n    }\"\"\"\n    )\n\n    await button.focus()\n    assert focused\n    assert not blurred\n    assert await button.evaluate(\"button => document.activeElement === button\")\n\n    await button.blur()\n    assert focused\n    assert blurred\n    assert not await button.evaluate(\"button => document.activeElement === button\")\n\n\nasync def test_locator_all_should_work(page: Page) -> None:\n    await page.set_content(\"<div><p>A</p><p>B</p><p>C</p></div>\")\n    texts = []\n    for p in await page.locator(\"p\").all():\n        texts.append(await p.text_content())\n    assert texts == [\"A\", \"B\", \"C\"]\n\n\nasync def test_locator_click_timeout_error_should_contain_call_log(page: Page) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.get_by_role(\"button\", name=\"Hello Python\").click(timeout=42)\n    formatted_exception = \"\".join(\n        traceback.format_exception(type(exc_info.value), value=exc_info.value, tb=None)\n    )\n    assert \"Locator.click: Timeout 42ms exceeded.\" in formatted_exception\n    assert (\n        'waiting for get_by_role(\"button\", name=\"Hello Python\")' in formatted_exception\n    )\n    assert (\n        \"During handling of the above exception, another exception occurred\"\n        not in formatted_exception\n    )\n\n\nasync def test_locator_should_ignore_deprecated_is_hidden_and_visible_timeout(\n    page: Page,\n) -> None:\n    await page.set_content(\"<div>foo</div>\")\n    div = page.locator(\"div\")\n    assert await div.is_hidden(timeout=10) is False\n    assert await div.is_visible(timeout=10) is True\n\n\nasync def test_description_should_return_none_for_locator_without_description(\n    page: Page,\n) -> None:\n    locator = page.locator(\"button\")\n    assert locator.description is None\n\n\nasync def test_description_should_return_description_for_locator_with_simple_description(\n    page: Page,\n) -> None:\n    locator = page.locator(\"button\").describe(\"Submit button\")\n    assert locator.description == \"Submit button\"\n\n\nasync def test_description_should_return_description_with_special_characters(\n    page: Page,\n) -> None:\n    locator = page.locator(\"div\").describe(\"Button with \\\"quotes\\\" and 'apostrophes'\")\n    assert locator.description == \"Button with \\\"quotes\\\" and 'apostrophes'\"\n\n\nasync def test_description_should_return_description_for_chained_locators(\n    page: Page,\n) -> None:\n    locator = page.locator(\"form\").locator(\"input\").describe(\"Form input field\")\n    assert locator.description == \"Form input field\"\n\n\nasync def test_description_should_return_description_for_locator_with_multiple_describe_calls(\n    page: Page,\n) -> None:\n    locator1 = page.locator(\"foo\").describe(\"First description\")\n    assert locator1.description == \"First description\"\n    locator2 = locator1.locator(\"button\").describe(\"Second description\")\n    assert locator2.description == \"Second description\"\n    locator3 = locator2.locator(\"button\")\n    assert locator3.description is None\n"
  },
  {
    "path": "tests/async/test_navigation.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport re\nimport sys\nfrom pathlib import Path\nfrom typing import Any, List, Optional\n\nimport pytest\n\nfrom playwright.async_api import (\n    BrowserContext,\n    Error,\n    Page,\n    Request,\n    Response,\n    Route,\n    TimeoutError,\n)\nfrom tests.server import Server, TestServerRequest\n\n\nasync def test_goto_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n\n\nasync def test_goto_should_work_with_file_URL(page: Page, assetdir: Path) -> None:\n    fileurl = (assetdir / \"frames\" / \"two-frames.html\").as_uri()\n    await page.goto(fileurl)\n    assert page.url.lower() == fileurl.lower()\n    assert len(page.frames) == 3\n\n\nasync def test_goto_should_use_http_for_no_protocol(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE[7:])\n    assert page.url == server.EMPTY_PAGE\n\n\nasync def test_goto_should_work_cross_process(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n\n    url = server.CROSS_PROCESS_PREFIX + \"/empty.html\"\n    request_frames = []\n\n    def on_request(r: Request) -> None:\n        if r.url == url:\n            request_frames.append(r.frame)\n\n    page.on(\"request\", on_request)\n\n    response = await page.goto(url)\n    assert response\n    assert page.url == url\n    assert response.frame == page.main_frame\n    assert request_frames[0] == page.main_frame\n    assert response.url == url\n\n\nasync def test_goto_should_capture_iframe_navigation_request(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n\n    request_frames = []\n\n    def on_request(r: Request) -> None:\n        if r.url == server.PREFIX + \"/frames/frame.html\":\n            request_frames.append(r.frame)\n\n    page.on(\"request\", on_request)\n\n    response = await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n    assert response\n    assert page.url == server.PREFIX + \"/frames/one-frame.html\"\n    assert response.frame == page.main_frame\n    assert response.url == server.PREFIX + \"/frames/one-frame.html\"\n\n    assert len(page.frames) == 2\n    assert request_frames[0] == page.frames[1]\n\n\nasync def test_goto_should_capture_cross_process_iframe_navigation_request(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n\n    request_frames = []\n\n    def on_request(r: Request) -> None:\n        if r.url == server.CROSS_PROCESS_PREFIX + \"/frames/frame.html\":\n            request_frames.append(r.frame)\n\n    page.on(\"request\", on_request)\n\n    response = await page.goto(server.CROSS_PROCESS_PREFIX + \"/frames/one-frame.html\")\n    assert response\n    assert page.url == server.CROSS_PROCESS_PREFIX + \"/frames/one-frame.html\"\n    assert response.frame == page.main_frame\n    assert response.url == server.CROSS_PROCESS_PREFIX + \"/frames/one-frame.html\"\n\n    assert len(page.frames) == 2\n    assert request_frames[0] == page.frames[1]\n\n\nasync def test_goto_should_work_with_anchor_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n    await page.goto(server.EMPTY_PAGE + \"#foo\")\n    assert page.url == server.EMPTY_PAGE + \"#foo\"\n    await page.goto(server.EMPTY_PAGE + \"#bar\")\n    assert page.url == server.EMPTY_PAGE + \"#bar\"\n\n\nasync def test_goto_should_work_with_redirects(page: Page, server: Server) -> None:\n    server.set_redirect(\"/redirect/1.html\", \"/redirect/2.html\")\n    server.set_redirect(\"/redirect/2.html\", \"/empty.html\")\n    response = await page.goto(server.PREFIX + \"/redirect/1.html\")\n    assert response\n    assert response.status == 200\n    assert page.url == server.EMPTY_PAGE\n\n\nasync def test_goto_should_navigate_to_about_blank(page: Page, server: Server) -> None:\n    response = await page.goto(\"about:blank\")\n    assert response is None\n\n\nasync def test_goto_should_return_response_when_page_changes_its_url_after_load(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.PREFIX + \"/historyapi.html\")\n    assert response\n    assert response.status == 200\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_goto_should_work_with_subframes_return_204(\n    page: Page, server: Server\n) -> None:\n    def handle(request: TestServerRequest) -> None:\n        request.setResponseCode(204)\n        request.finish()\n\n    server.set_route(\"/frames/frame.html\", handle)\n\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n\n\nasync def test_goto_should_fail_when_server_returns_204(\n    page: Page, server: Server, is_chromium: bool, is_webkit: bool\n) -> None:\n    # WebKit just loads an empty page.\n    def handle(request: TestServerRequest) -> None:\n        request.setResponseCode(204)\n        request.finish()\n\n    server.set_route(\"/empty.html\", handle)\n\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.EMPTY_PAGE)\n    assert exc_info.value\n    if is_chromium:\n        assert \"net::ERR_ABORTED\" in exc_info.value.message\n    elif is_webkit:\n        assert \"Aborted: 204 No Content\" in exc_info.value.message\n    else:\n        assert \"NS_BINDING_ABORTED\" in exc_info.value.message\n\n\nasync def test_goto_should_navigate_to_empty_page_with_domcontentloaded(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE, wait_until=\"domcontentloaded\")\n    assert response\n    assert response.status == 200\n\n\nasync def test_goto_should_work_when_page_calls_history_api_in_beforeunload(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"() => {\n        window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false)\n    }\"\"\"\n    )\n\n    response = await page.goto(server.PREFIX + \"/grid.html\")\n    assert response\n    assert response.status == 200\n\n\nasync def test_goto_should_fail_when_navigating_to_bad_url(\n    page: Page, is_chromium: bool, is_webkit: bool\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.goto(\"asdfasdf\")\n    if is_chromium or is_webkit:\n        assert \"Cannot navigate to invalid URL\" in exc_info.value.message\n    else:\n        assert \"Invalid url\" in exc_info.value.message\n\n\nasync def test_goto_should_fail_when_navigating_to_bad_ssl(\n    page: Page, https_server: Server, browser_name: str\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.goto(https_server.EMPTY_PAGE)\n    expect_ssl_error(exc_info.value.message, browser_name)\n\n\nasync def test_goto_should_fail_when_navigating_to_bad_ssl_after_redirects(\n    page: Page, server: Server, https_server: Server, browser_name: str\n) -> None:\n    server.set_redirect(\"/redirect/1.html\", \"/redirect/2.html\")\n    server.set_redirect(\"/redirect/2.html\", \"/empty.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.goto(https_server.PREFIX + \"/redirect/1.html\")\n    expect_ssl_error(exc_info.value.message, browser_name)\n\n\nasync def test_goto_should_not_crash_when_navigating_to_bad_ssl_after_a_cross_origin_navigation(\n    page: Page, server: Server, https_server: Server\n) -> None:\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    with pytest.raises(Error):\n        await page.goto(https_server.EMPTY_PAGE)\n\n\nasync def test_goto_should_throw_if_networkidle2_is_passed_as_an_option(\n    page: Page, server: Server\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.EMPTY_PAGE, wait_until=\"networkidle2\")  # type: ignore\n    assert (\n        \"wait_until: expected one of (load|domcontentloaded|networkidle|commit)\"\n        in exc_info.value.message\n    )\n\n\nasync def test_goto_should_fail_when_main_resources_failed_to_load(\n    page: Page, is_chromium: bool, is_webkit: bool, is_win: bool\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.goto(\"http://localhost:44123/non-existing-url\")\n    if is_chromium:\n        assert \"net::ERR_CONNECTION_REFUSED\" in exc_info.value.message\n    elif is_webkit and is_win:\n        assert \"Could not connect to server\" in exc_info.value.message\n    elif is_webkit:\n        assert \"Could not connect\" in exc_info.value.message\n    else:\n        assert \"NS_ERROR_CONNECTION_REFUSED\" in exc_info.value.message\n\n\nasync def test_goto_should_fail_when_exceeding_maximum_navigation_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\", timeout=1)\n    assert \"Timeout 1ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_fail_when_exceeding_default_maximum_navigation_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    page.context.set_default_navigation_timeout(2)\n    page.set_default_navigation_timeout(1)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\")\n    assert \"Timeout 1ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_fail_when_exceeding_browser_context_navigation_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    page.context.set_default_navigation_timeout(2)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\")\n    assert \"Timeout 2ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_fail_when_exceeding_default_maximum_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    page.context.set_default_timeout(2)\n    page.set_default_timeout(1)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\")\n    assert \"Timeout 1ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_fail_when_exceeding_browser_context_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    page.context.set_default_timeout(2)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\")\n    assert \"Timeout 2ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_prioritize_default_navigation_timeout_over_default_timeout(\n    page: Page, server: Server\n) -> None:\n    # Hang for request to the empty.html\n    server.set_route(\"/empty.html\", lambda request: None)\n    page.set_default_timeout(0)\n    page.set_default_navigation_timeout(1)\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/empty.html\")\n    assert \"Timeout 1ms exceeded\" in exc_info.value.message\n    assert server.PREFIX + \"/empty.html\" in exc_info.value.message\n    assert isinstance(exc_info.value, TimeoutError)\n\n\nasync def test_goto_should_disable_timeout_when_its_set_to_0(\n    page: Page, server: Server\n) -> None:\n    loaded: List[bool] = []\n    page.once(\"load\", lambda _: loaded.append(True))\n    await page.goto(server.PREFIX + \"/grid.html\", timeout=0, wait_until=\"load\")\n    assert loaded == [True]\n\n\nasync def test_goto_should_work_when_navigating_to_valid_url(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n\n\nasync def test_goto_should_work_when_navigating_to_data_url(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(\"data:text/html,hello\")\n    assert response is None\n\n\nasync def test_goto_should_work_when_navigating_to_404(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.PREFIX + \"/not-found\")\n    assert response\n    assert response.ok is False\n    assert response.status == 404\n\n\nasync def test_goto_should_return_last_response_in_redirect_chain(\n    page: Page, server: Server\n) -> None:\n    server.set_redirect(\"/redirect/1.html\", \"/redirect/2.html\")\n    server.set_redirect(\"/redirect/2.html\", \"/redirect/3.html\")\n    server.set_redirect(\"/redirect/3.html\", server.EMPTY_PAGE)\n    response = await page.goto(server.PREFIX + \"/redirect/1.html\")\n    assert response\n    assert response.ok\n    assert response.url == server.EMPTY_PAGE\n\n\nasync def test_goto_should_navigate_to_data_url_and_not_fire_dataURL_requests(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda request: requests.append(request))\n    dataURL = \"data:text/html,<div>yo</div>\"\n    response = await page.goto(dataURL)\n    assert response is None\n    assert requests == []\n\n\nasync def test_goto_should_navigate_to_url_with_hash_and_fire_requests_without_hash(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda request: requests.append(request))\n    response = await page.goto(server.EMPTY_PAGE + \"#hash\")\n    assert response\n    assert response.status == 200\n    assert response.url == server.EMPTY_PAGE\n    assert len(requests) == 1\n    assert requests[0].url == server.EMPTY_PAGE\n\n\nasync def test_goto_should_work_with_self_requesting_page(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.PREFIX + \"/self-request.html\")\n    assert response\n    assert response.status == 200\n    assert \"self-request.html\" in response.url\n\n\nasync def test_goto_should_fail_when_navigating_and_show_the_url_at_the_error_message(\n    page: Page, https_server: Server\n) -> None:\n    url = https_server.PREFIX + \"/redirect/1.html\"\n    with pytest.raises(Error) as exc_info:\n        await page.goto(url)\n    assert url in exc_info.value.message\n\n\nasync def test_goto_should_be_able_to_navigate_to_a_page_controlled_by_service_worker(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/serviceworkers/fetch/sw.html\")\n    await page.evaluate(\"window.activationPromise\")\n    await page.goto(server.PREFIX + \"/serviceworkers/fetch/sw.html\")\n\n\nasync def test_goto_should_send_referer(page: Page, server: Server) -> None:\n    [request1, request2, _] = await asyncio.gather(\n        server.wait_for_request(\"/grid.html\"),\n        server.wait_for_request(\"/digits/1.png\"),\n        page.goto(server.PREFIX + \"/grid.html\", referer=\"http://google.com/\"),\n    )\n    assert request1.getHeader(\"referer\") == \"http://google.com/\"\n    # Make sure subresources do not inherit referer.\n    assert request2.getHeader(\"referer\") == server.PREFIX + \"/grid.html\"\n    assert page.url == server.PREFIX + \"/grid.html\"\n\n\nasync def test_goto_should_reject_referer_option_when_set_extra_http_headers_provides_referer(\n    page: Page, server: Server\n) -> None:\n    await page.set_extra_http_headers({\"referer\": \"http://microsoft.com/\"})\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.PREFIX + \"/grid.html\", referer=\"http://google.com/\")\n    assert (\n        '\"referer\" is already specified as extra HTTP header' in exc_info.value.message\n    )\n    assert server.PREFIX + \"/grid.html\" in exc_info.value.message\n\n\nasync def test_goto_should_work_with_commit(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE, wait_until=\"commit\")\n    assert page.url == server.EMPTY_PAGE\n\n\nasync def test_network_idle_should_navigate_to_empty_page_with_networkidle(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE, wait_until=\"networkidle\")\n    assert response\n    assert response.status == 200\n\n\nasync def test_wait_for_nav_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_navigation() as response_info:\n        await page.evaluate(\n            \"url => window.location.href = url\", server.PREFIX + \"/grid.html\"\n        )\n    response = await response_info.value\n    assert response.ok\n    assert \"grid.html\" in response.url\n\n\nasync def test_wait_for_nav_should_respect_timeout(page: Page, server: Server) -> None:\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_navigation(url=\"**/frame.html\", timeout=2500):\n            await page.goto(server.EMPTY_PAGE)\n    assert \"Timeout 2500ms exceeded\" in exc_info.value.message\n\n\nasync def test_wait_for_nav_should_work_with_both_domcontentloaded_and_load(\n    page: Page, server: Server\n) -> None:\n    async with (\n        page.expect_navigation(wait_until=\"domcontentloaded\"),\n        page.expect_navigation(wait_until=\"load\"),\n    ):\n        await page.goto(server.PREFIX + \"/one-style.html\")\n\n\nasync def test_wait_for_nav_should_work_with_clicking_on_anchor_links(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a href=\"#foobar\">foobar</a>')\n    async with page.expect_navigation() as response_info:\n        await page.click(\"a\")\n    response = await response_info.value\n    assert response is None\n    assert page.url == server.EMPTY_PAGE + \"#foobar\"\n\n\nasync def test_wait_for_nav_should_work_with_clicking_on_links_which_do_not_commit_navigation(\n    page: Page, server: Server, https_server: Server, browser_name: str\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(f\"<a href='{https_server.EMPTY_PAGE}'>foobar</a>\")\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_navigation():\n            await page.click(\"a\")\n    expect_ssl_error(exc_info.value.message, browser_name)\n\n\nasync def test_wait_for_nav_should_work_with_history_push_state(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <a onclick='javascript:pushState()'>SPA</a>\n        <script>\n            function pushState() { history.pushState({}, '', 'wow.html') }\n        </script>\n    \"\"\"\n    )\n    async with page.expect_navigation() as response_info:\n        await page.click(\"a\")\n    response = await response_info.value\n    assert response is None\n    assert page.url == server.PREFIX + \"/wow.html\"\n\n\nasync def test_wait_for_nav_should_work_with_history_replace_state(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <a onclick='javascript:replaceState()'>SPA</a>\n        <script>\n            function replaceState() { history.replaceState({}, '', '/replaced.html') }\n        </script>\n    \"\"\"\n    )\n    async with page.expect_navigation() as response_info:\n        await page.click(\"a\")\n    response = await response_info.value\n    assert response is None\n    assert page.url == server.PREFIX + \"/replaced.html\"\n\n\nasync def test_wait_for_nav_should_work_with_dom_history_back_forward(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n      <a id=back onclick='javascript:go_back()'>back</a>\n      <a id=forward onclick='javascript:go_forward()'>forward</a>\n      <script>\n        function go_back() { history.back(); }\n        function go_forward() { history.forward(); }\n        history.pushState({}, '', '/first.html')\n        history.pushState({}, '', '/second.html')\n      </script>\n    \"\"\"\n    )\n    assert page.url == server.PREFIX + \"/second.html\"\n    async with page.expect_navigation() as back_response_info:\n        await page.click(\"a#back\")\n    back_response = await back_response_info.value\n    assert back_response is None\n    assert page.url == server.PREFIX + \"/first.html\"\n    async with page.expect_navigation() as forward_response_info:\n        await page.click(\"a#forward\")\n    forward_response = await forward_response_info.value\n    assert forward_response is None\n    assert page.url == server.PREFIX + \"/second.html\"\n\n\n@pytest.mark.skip_browser(\n    \"webkit\"\n)  # WebKit issues load event in some cases, but not always\nasync def test_wait_for_nav_should_work_when_subframe_issues_window_stop(\n    page: Page, server: Server, is_webkit: bool\n) -> None:\n    server.set_route(\"/frames/style.css\", lambda _: None)\n    done = False\n\n    async def nav_and_mark_done() -> None:\n        nonlocal done\n        await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n        done = True\n\n    task = asyncio.create_task(nav_and_mark_done())\n    await asyncio.sleep(0)\n    async with page.expect_event(\"frameattached\") as frame_info:\n        pass\n    frame = await frame_info.value\n\n    async with page.expect_event(\"framenavigated\", lambda f: f == frame):\n        pass\n    await frame.evaluate(\"() => window.stop()\")\n    await page.wait_for_timeout(2000)  # give it some time to erroneously resolve\n    assert done == (\n        not is_webkit\n    )  # Chromium and Firefox issue load event in this case.\n    if is_webkit:\n        task.cancel()\n\n\nasync def test_wait_for_nav_should_work_with_url_match(\n    page: Page, server: Server\n) -> None:\n    responses: List[Optional[Response]] = [None, None, None]\n\n    async def wait_for_nav(url: Any, index: int) -> None:\n        async with page.expect_navigation(url=url) as response_info:\n            pass\n        responses[index] = await response_info.value\n\n    response0_promise = asyncio.create_task(\n        wait_for_nav(re.compile(r\"one-style\\.html\"), 0)\n    )\n    response1_promise = asyncio.create_task(\n        wait_for_nav(re.compile(r\"\\/frame.html\"), 1)\n    )\n    response2_promise = asyncio.create_task(\n        wait_for_nav(lambda url: \"foo=bar\" in url, 2)\n    )\n    assert responses == [None, None, None]\n    await page.goto(server.EMPTY_PAGE)\n    assert responses == [None, None, None]\n    await page.goto(server.PREFIX + \"/frame.html\")\n    assert responses[0] is None\n    await response1_promise\n    assert responses[1] is not None\n    assert responses[2] is None\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await response0_promise\n    assert responses[0] is not None\n    assert responses[1] is not None\n    assert responses[2] is None\n    await page.goto(server.PREFIX + \"/frame.html?foo=bar\")\n    await response2_promise\n    assert responses[0] is not None\n    assert responses[1] is not None\n    assert responses[2] is not None\n    await page.goto(server.PREFIX + \"/empty.html\")\n    assert responses[0].url == server.PREFIX + \"/one-style.html\"\n    assert responses[1].url == server.PREFIX + \"/frame.html\"\n    assert responses[2].url == server.PREFIX + \"/frame.html?foo=bar\"\n\n\nasync def test_wait_for_nav_should_work_with_url_match_for_same_document_navigations(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_navigation(url=re.compile(r\"third\\.html\")) as response_info:\n        assert not response_info.is_done()\n        await page.evaluate(\"history.pushState({}, '', '/first.html')\")\n        assert not response_info.is_done()\n        await page.evaluate(\"history.pushState({}, '', '/second.html')\")\n        assert not response_info.is_done()\n        await page.evaluate(\"history.pushState({}, '', '/third.html')\")\n    assert response_info.is_done()\n\n\nasync def test_wait_for_nav_should_work_for_cross_process_navigations(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    url = server.CROSS_PROCESS_PREFIX + \"/empty.html\"\n    async with page.expect_navigation(wait_until=\"domcontentloaded\") as response_info:\n        await page.goto(url)\n    response = await response_info.value\n    assert response.url == url\n    assert page.url == url\n    assert await page.evaluate(\"document.location.href\") == url\n\n\nasync def test_expect_navigation_should_work_for_cross_process_navigations(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    url = server.CROSS_PROCESS_PREFIX + \"/empty.html\"\n    async with page.expect_navigation(wait_until=\"domcontentloaded\") as response_info:\n        goto_task = asyncio.create_task(page.goto(url))\n    response = await response_info.value\n    assert response.url == url\n    assert page.url == url\n    assert await page.evaluate(\"document.location.href\") == url\n    await goto_task\n\n\nasync def test_wait_for_nav_should_work_with_commit(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_navigation(wait_until=\"commit\") as response_info:\n        await page.evaluate(\n            \"url => window.location.href = url\", server.PREFIX + \"/grid.html\"\n        )\n    response = await response_info.value\n    assert response.ok\n    assert \"grid.html\" in response.url\n\n\nasync def test_wait_for_load_state_should_respect_timeout(\n    page: Page, server: Server\n) -> None:\n    requests = []\n\n    def handler(request: Any) -> None:\n        requests.append(request)\n\n    server.set_route(\"/one-style.css\", handler)\n\n    await page.goto(server.PREFIX + \"/one-style.html\", wait_until=\"domcontentloaded\")\n    with pytest.raises(Error) as exc_info:\n        await page.wait_for_load_state(\"load\", timeout=1)\n    assert \"Timeout 1ms exceeded.\" in exc_info.value.message\n\n\nasync def test_wait_for_load_state_should_resolve_immediately_if_loaded(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await page.wait_for_load_state()\n\n\nasync def test_wait_for_load_state_should_throw_for_bad_state(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.wait_for_load_state(\"bad\")  # type: ignore\n    assert (\n        \"state: expected one of (load|domcontentloaded|networkidle|commit)\"\n        in exc_info.value.message\n    )\n\n\nasync def test_wait_for_load_state_should_resolve_immediately_if_load_state_matches(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    requests = []\n\n    def handler(request: Any) -> None:\n        requests.append(request)\n\n    server.set_route(\"/one-style.css\", handler)\n\n    await page.goto(server.PREFIX + \"/one-style.html\", wait_until=\"domcontentloaded\")\n    await page.wait_for_load_state(\"domcontentloaded\")\n\n\nasync def test_wait_for_load_state_networkidle(page: Page, server: Server) -> None:\n    wait_for_network_idle_future = asyncio.create_task(\n        page.wait_for_load_state(\"networkidle\")\n    )\n    await page.goto(server.PREFIX + \"/networkidle.html\")\n    await wait_for_network_idle_future\n\n\nasync def test_wait_for_load_state_should_work_with_pages_that_have_loaded_before_being_connected_to(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window._popup = window.open(document.location.href)\")\n\n    # The url is about:blank in FF.\n    popup = await popup_info.value\n    assert popup.url == server.EMPTY_PAGE\n    await popup.wait_for_load_state()\n    assert popup.url == server.EMPTY_PAGE\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_empty_url_popup(\n    page: Page, is_firefox: bool\n) -> None:\n    ready_state = []\n    async with page.expect_popup() as popup_info:\n        ready_state.append(\n            await page.evaluate(\n                \"\"\"() => {\n            popup = window.open('')\n            return popup.document.readyState\n        }\"\"\"\n            )\n        )\n\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert ready_state == [\"uninitialized\"] if is_firefox else [\"complete\"]\n    assert await popup.evaluate(\"() => document.readyState\") == ready_state[0]\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_about_blank_popup_(\n    page: Page,\n) -> None:\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window.open('about:blank') && 1\")\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert await popup.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_about_blank_popup_with_noopener(\n    page: Page,\n) -> None:\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window.open('about:blank', null, 'noopener') && 1\")\n\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert await popup.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_popup_with_network_url_(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"url => window.open(url) && 1\", server.EMPTY_PAGE)\n\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert await popup.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_popup_with_network_url_and_noopener_(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window.open(url, null, 'noopener') && 1\", server.EMPTY_PAGE\n        )\n\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert await popup.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_should_work_with_clicking_target__blank(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=\"opener\" href=\"/one-style.html\">yo</a>'\n    )\n    async with page.expect_popup() as popup_info:\n        await page.click(\"a\")\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert await popup.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_should_wait_for_load_state_of_new_page(\n    context: BrowserContext,\n) -> None:\n    async with context.expect_page() as page_info:\n        await context.new_page()\n    new_page = await page_info.value\n    await new_page.wait_for_load_state()\n    assert await new_page.evaluate(\"document.readyState\") == \"complete\"\n\n\nasync def test_wait_for_load_state_in_popup(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    css_requests = []\n\n    def handle_request(request: TestServerRequest) -> None:\n        css_requests.append(request)\n        request.write(b\"body {}\")\n        request.finish()\n\n    server.set_route(\"/one-style.css\", handle_request)\n\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window.popup = window.open(url)\", server.PREFIX + \"/one-style.html\"\n        )\n\n    popup = await popup_info.value\n    await popup.wait_for_load_state()\n    assert len(css_requests)\n\n\nasync def test_go_back_should_work(page: Page, server: Server) -> None:\n    assert await page.go_back() is None\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.PREFIX + \"/grid.html\")\n\n    response = await page.go_back()\n    assert response\n    assert response.ok\n    assert server.EMPTY_PAGE in response.url\n\n    response = await page.go_forward()\n    assert response\n    assert response.ok\n    assert \"/grid.html\" in response.url\n\n    response = await page.go_forward()\n    assert response is None\n\n\nasync def test_go_back_should_work_with_history_api(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"() => {\n        history.pushState({}, '', '/first.html')\n        history.pushState({}, '', '/second.html')\n    }\"\"\"\n    )\n    assert page.url == server.PREFIX + \"/second.html\"\n\n    await page.go_back()\n    assert page.url == server.PREFIX + \"/first.html\"\n    await page.go_back()\n    assert page.url == server.EMPTY_PAGE\n    await page.go_forward()\n    assert page.url == server.PREFIX + \"/first.html\"\n\n\nasync def test_frame_goto_should_navigate_subframes(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n    assert \"/frames/one-frame.html\" in page.frames[0].url\n    assert \"/frames/frame.html\" in page.frames[1].url\n\n    response = await page.frames[1].goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert response.frame == page.frames[1]\n\n\nasync def test_frame_goto_should_reject_when_frame_detaches(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n\n    server.set_route(\"/one-style.css\", lambda _: None)\n    wait_for_request_task = asyncio.create_task(\n        server.wait_for_request(\"/one-style.css\")\n    )\n    navigation_task = asyncio.create_task(\n        page.frames[1].goto(server.PREFIX + \"/one-style.html\")\n    )\n    await wait_for_request_task\n\n    await page.eval_on_selector(\"iframe\", \"frame => frame.remove()\")\n    with pytest.raises(Error) as exc_info:\n        await navigation_task\n    if browser_name == \"chromium\":\n        assert \"net::ERR_FAILED\" in exc_info.value.message or (\n            \"frame was detached\" in exc_info.value.message.lower()\n        )\n    else:\n        assert \"frame was detached\" in exc_info.value.message.lower()\n\n\nasync def test_frame_goto_should_continue_after_client_redirect(\n    page: Page, server: Server\n) -> None:\n    server.set_route(\"/frames/script.js\", lambda _: None)\n    url = server.PREFIX + \"/frames/child-redirect.html\"\n\n    with pytest.raises(Error) as exc_info:\n        await page.goto(url, timeout=5000, wait_until=\"networkidle\")\n\n    assert \"Timeout 5000ms exceeded.\" in exc_info.value.message\n    assert (\n        f'navigating to \"{url}\", waiting until \"networkidle\"' in exc_info.value.message\n    )\n\n\nasync def test_frame_wait_for_nav_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n    frame = page.frames[1]\n    async with frame.expect_navigation() as response_info:\n        await frame.evaluate(\n            \"url => window.location.href = url\", server.PREFIX + \"/grid.html\"\n        )\n    response = await response_info.value\n    assert response.ok\n    assert \"grid.html\" in response.url\n    assert response.frame == frame\n    assert \"/frames/one-frame.html\" in page.url\n\n\nasync def test_frame_wait_for_nav_should_fail_when_frame_detaches(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n    frame = page.frames[1]\n    server.set_route(\"/empty.html\", lambda _: None)\n    server.set_route(\"/one-style.css\", lambda _: None)\n    with pytest.raises(Error) as exc_info:\n        async with frame.expect_navigation():\n\n            async def after_it() -> None:\n                await server.wait_for_request(\"/one-style.html\")\n                await page.eval_on_selector(\n                    \"iframe\", \"frame => setTimeout(() => frame.remove(), 0)\"\n                )\n\n            await asyncio.gather(\n                page.eval_on_selector(\n                    \"iframe\",\n                    \"frame => frame.contentWindow.location.href = '/one-style.html'\",\n                ),\n                after_it(),\n            )\n    assert \"frame was detached\" in exc_info.value.message\n\n\nasync def test_frame_wait_for_load_state_should_work(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/one-frame.html\")\n    frame = page.frames[1]\n\n    request_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\n        server.PREFIX + \"/one-style.css\",\n        lambda route, request: request_future.set_result(route),\n    )\n\n    await frame.goto(server.PREFIX + \"/one-style.html\", wait_until=\"domcontentloaded\")\n    request = await request_future\n    load_task = asyncio.create_task(frame.wait_for_load_state())\n    # give the promise a chance to resolve, even though it shouldn't\n    await page.evaluate(\"1\")\n    assert not load_task.done()\n    asyncio.create_task(request.continue_())\n    await load_task\n\n\nasync def test_reload_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\"window._foo = 10\")\n    await page.reload()\n    assert await page.evaluate(\"window._foo\") is None\n\n\nasync def test_reload_should_work_with_data_url(page: Page, server: Server) -> None:\n    await page.goto(\"data:text/html,hello\")\n    assert \"hello\" in await page.content()\n    assert await page.reload() is None\n    assert \"hello\" in await page.content()\n\n\nasync def test_should_work_with__blank_target(page: Page, server: Server) -> None:\n    def handler(request: TestServerRequest) -> None:\n        request.write(\n            f'<a href=\"{server.EMPTY_PAGE}\" target=\"_blank\">Click me</a>'.encode()\n        )\n        request.finish()\n\n    server.set_route(\"/empty.html\", handler)\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.click('\"Click me\"')\n\n\nasync def test_should_work_with_cross_process__blank_target(\n    page: Page, server: Server\n) -> None:\n    def handler(request: TestServerRequest) -> None:\n        request.write(\n            f'<a href=\"{server.CROSS_PROCESS_PREFIX}/empty.html\" target=\"_blank\">Click me</a>'.encode()\n        )\n        request.finish()\n\n    server.set_route(\"/empty.html\", handler)\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.click('\"Click me\"')\n\n\ndef expect_ssl_error(error_message: str, browser_name: str) -> None:\n    if browser_name == \"chromium\":\n        assert \"net::ERR_CERT_AUTHORITY_INVALID\" in error_message\n    elif browser_name == \"webkit\":\n        if sys.platform == \"darwin\":\n            assert \"The certificate for this server is invalid\" in error_message\n        elif sys.platform == \"win32\":\n            assert \"SSL peer certificate or SSH remote key was not OK\" in error_message\n        else:\n            assert \"Unacceptable TLS certificate\" in error_message\n    else:\n        assert \"SSL_ERROR_UNKNOWN\" in error_message\n"
  },
  {
    "path": "tests/async/test_network.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nfrom asyncio import Future\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, Union\n\nimport pytest\nfrom twisted.web import http\n\nfrom playwright.async_api import Browser, Error, Page, Request, Response, Route\nfrom tests.server import Server, TestServerRequest\n\nfrom .utils import Utils\n\n\ndef adjust_server_headers(headers: Dict[str, str], browser_name: str) -> Dict[str, str]:\n    if browser_name != \"firefox\":\n        return headers\n    headers = headers.copy()\n    headers.pop(\"priority\", None)\n    return headers\n\n\nasync def test_request_fulfill(page: Page, server: Server) -> None:\n    async def handle_request(route: Route, request: Request) -> None:\n        headers = await route.request.all_headers()\n        assert headers[\"accept\"]\n        assert route.request == request\n        assert repr(route) == f\"<Route request={route.request}>\"\n        assert \"empty.html\" in request.url\n        assert request.headers[\"user-agent\"]\n        assert request.method == \"GET\"\n        assert request.post_data is None\n        assert request.is_navigation_request()\n        assert request.resource_type == \"document\"\n        assert request.frame == page.main_frame\n        assert request.frame.url == \"about:blank\"\n        assert (\n            repr(request) == f\"<Request url={request.url!r} method={request.method!r}>\"\n        )\n        await route.fulfill(body=\"Text\")\n\n    await page.route(\n        \"**/empty.html\",\n        lambda route, request: asyncio.create_task(handle_request(route, request)),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n\n    assert response.ok\n    assert (\n        repr(response) == f\"<Response url={response.url!r} request={response.request}>\"\n    )\n    assert await response.text() == \"Text\"\n\n\nasync def test_request_continue(page: Page, server: Server) -> None:\n    async def handle_request(\n        route: Route, request: Request, intercepted: List[bool]\n    ) -> None:\n        intercepted.append(True)\n        await route.continue_()\n\n    intercepted: List[bool] = []\n    await page.route(\n        \"**/*\",\n        lambda route, request: asyncio.create_task(\n            handle_request(route, request, intercepted)\n        ),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert intercepted == [True]\n    assert await page.title() == \"\"\n\n\nasync def test_page_events_request_should_fire_for_navigation_requests(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    assert len(requests) == 1\n\n\nasync def test_page_events_request_should_accept_method(\n    page: Page, server: Server\n) -> None:\n    class Log:\n        def __init__(self) -> None:\n            self.requests: List[Request] = []\n\n        def handle(self, request: Request) -> None:\n            self.requests.append(request)\n\n    log = Log()\n    page.on(\"request\", log.handle)\n    await page.goto(server.EMPTY_PAGE)\n    assert len(log.requests) == 1\n\n\nasync def test_page_events_request_should_fire_for_iframes(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    assert len(requests) == 2\n\n\nasync def test_page_events_request_should_fire_for_fetches(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate('() => fetch(\"/empty.html\")')\n    assert len(requests) == 2\n\n\nasync def test_page_events_request_should_report_requests_and_responses_handled_by_service_worker(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    await page.evaluate(\"() => window.activationPromise\")\n    sw_response = None\n    async with page.expect_request(\"**/*\") as request_info:\n        sw_response = await page.evaluate('() => fetchDummy(\"foo\")')\n    request = await request_info.value\n    assert sw_response == \"responseFromServiceWorker:foo\"\n    assert request.url == server.PREFIX + \"/serviceworkers/fetchdummy/foo\"\n    response = await request.response()\n    assert response\n    assert response.url == server.PREFIX + \"/serviceworkers/fetchdummy/foo\"\n    assert await response.text() == \"responseFromServiceWorker:foo\"\n\n\nasync def test_request_frame_should_work_for_main_frame_navigation_request(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    assert len(requests) == 1\n    assert requests[0].frame == page.main_frame\n\n\nasync def test_request_frame_should_work_for_subframe_navigation_request(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    assert len(requests) == 1\n    assert requests[0].frame == page.frames[1]\n\n\nasync def test_request_frame_should_work_for_fetch_requests(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    requests: List[Request] = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.evaluate('() => fetch(\"/digits/1.png\")')\n    requests = [r for r in requests if \"favicon\" not in r.url]\n    assert len(requests) == 1\n    assert requests[0].frame == page.main_frame\n\n\nasync def test_request_headers_should_work(\n    page: Page, server: Server, is_chromium: bool, is_firefox: bool, is_webkit: bool\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    if is_chromium:\n        assert \"Chrome\" in response.request.headers[\"user-agent\"]\n    elif is_firefox:\n        assert \"Firefox\" in response.request.headers[\"user-agent\"]\n    elif is_webkit:\n        assert \"WebKit\" in response.request.headers[\"user-agent\"]\n\n\nasync def test_request_headers_should_get_the_same_headers_as_the_server(\n    page: Page,\n    server: Server,\n    is_webkit: bool,\n    is_win: bool,\n    browser_name: str,\n) -> None:\n    if is_webkit and is_win:\n        pytest.xfail(\"Curl does not show accept-encoding and accept-language\")\n    server_request_headers_future: Future[Dict[str, str]] = asyncio.Future()\n\n    def handle(request: http.Request) -> None:\n        normalized_headers = {\n            key.decode().lower(): value[0].decode()\n            for key, value in request.requestHeaders.getAllRawHeaders()\n        }\n        server_request_headers_future.set_result(normalized_headers)\n        request.write(b\"done\")\n        request.finish()\n\n    server.set_route(\"/empty.html\", handle)\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    server_headers = adjust_server_headers(\n        await server_request_headers_future, browser_name\n    )\n    assert await response.request.all_headers() == server_headers\n\n\nasync def test_request_headers_should_get_the_same_headers_as_the_server_cors(\n    page: Page, server: Server, is_webkit: bool, is_win: bool, browser_name: str\n) -> None:\n    if is_webkit and is_win:\n        pytest.xfail(\"Curl does not show accept-encoding and accept-language\")\n    await page.goto(server.PREFIX + \"/empty.html\")\n    server_request_headers_future: Future[Dict[str, str]] = asyncio.Future()\n\n    def handle_something(request: http.Request) -> None:\n        normalized_headers = {\n            key.decode().lower(): value[0].decode()\n            for key, value in request.requestHeaders.getAllRawHeaders()\n        }\n        server_request_headers_future.set_result(normalized_headers)\n        request.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n        request.write(b\"done\")\n        request.finish()\n\n    server.set_route(\"/something\", handle_something)\n\n    text = None\n    async with page.expect_request(\"**/*\") as request_info:\n        text = await page.evaluate(\n            \"\"\"async url => {\n                const data = await fetch(url);\n                return data.text();\n            }\"\"\",\n            server.CROSS_PROCESS_PREFIX + \"/something\",\n        )\n    request = await request_info.value\n    assert text == \"done\"\n    server_headers = adjust_server_headers(\n        await server_request_headers_future, browser_name\n    )\n    assert await request.all_headers() == server_headers\n\n\nasync def test_should_report_request_headers_array(\n    page: Page, server: Server, is_win: bool, browser_name: str\n) -> None:\n    if is_win and browser_name == \"webkit\":\n        pytest.skip(\"libcurl does not support non-set-cookie multivalue headers\")\n    expected_headers = []\n\n    def handle(request: http.Request) -> None:\n        for name, values in request.requestHeaders.getAllRawHeaders():\n            for value in values:\n                if browser_name == \"firefox\" and name.decode().lower() == \"priority\":\n                    continue\n                expected_headers.append(\n                    {\"name\": name.decode().lower(), \"value\": value.decode()}\n                )\n        request.finish()\n\n    server.set_route(\"/headers\", handle)\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\"*/**\") as request_info:\n        await page.evaluate(\n            \"\"\"() => fetch('/headers', {\n            headers: [\n                ['header-a', 'value-a'],\n                ['header-b', 'value-b'],\n                ['header-a', 'value-a-1'],\n                ['header-a', 'value-a-2'],\n            ]\n            })\n        \"\"\"\n        )\n    request = await request_info.value\n    sorted_pw_request_headers = sorted(\n        list(\n            map(\n                lambda header: {\n                    \"name\": header[\"name\"].lower(),\n                    \"value\": header[\"value\"],\n                },\n                await request.headers_array(),\n            )\n        ),\n        key=lambda header: header[\"name\"],\n    )\n    sorted_expected_headers = sorted(\n        expected_headers, key=lambda header: header[\"name\"]\n    )\n    assert sorted_pw_request_headers == sorted_expected_headers\n    assert await request.header_value(\"Header-A\") == \"value-a, value-a-1, value-a-2\"\n    assert await request.header_value(\"not-there\") is None\n\n\nasync def test_should_report_response_headers_array(\n    page: Page, server: Server, is_win: bool, browser_name: str\n) -> None:\n    if is_win and browser_name == \"webkit\":\n        pytest.skip(\"libcurl does not support non-set-cookie multivalue headers\")\n    expected_headers = {\n        \"header-a\": [\"value-a\", \"value-a-1\", \"value-a-2\"],\n        \"header-b\": [\"value-b\"],\n        \"set-cookie\": [\"a=b\", \"c=d\"],\n    }\n\n    def handle(request: http.Request) -> None:\n        for key in expected_headers:\n            for value in expected_headers[key]:\n                request.responseHeaders.addRawHeader(key, value)\n        request.finish()\n\n    server.set_route(\"/headers\", handle)\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_response(\"*/**\") as response_info:\n        await page.evaluate(\n            \"\"\"() => fetch('/headers')\n        \"\"\"\n        )\n    response = await response_info.value\n    actual_headers: Dict[str, List[str]] = {}\n    for header in await response.headers_array():\n        name = header[\"name\"].lower()\n        value = header[\"value\"]\n        if not actual_headers.get(name):\n            actual_headers[name] = []\n        actual_headers[name].append(value)\n\n    for key in [\"Keep-Alive\", \"Connection\", \"Date\", \"Transfer-Encoding\"]:\n        if key in actual_headers:\n            actual_headers.pop(key)\n        if key.lower() in actual_headers:\n            actual_headers.pop(key.lower())\n    assert actual_headers == expected_headers\n    assert await response.header_value(\"not-there\") is None\n    assert await response.header_value(\"set-cookie\") == \"a=b\\nc=d\"\n    assert await response.header_value(\"header-a\") == \"value-a, value-a-1, value-a-2\"\n    assert await response.header_values(\"set-cookie\") == [\"a=b\", \"c=d\"]\n\n\nasync def test_response_headers_should_work(page: Page, server: Server) -> None:\n    server.set_route(\"/empty.html\", lambda r: (r.setHeader(\"foo\", \"bar\"), r.finish()))\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.headers[\"foo\"] == \"bar\"\n    assert (await response.all_headers())[\"foo\"] == \"bar\"\n\n\nasync def test_request_post_data_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\"/post\", lambda r: r.finish())\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.evaluate(\n        '() => fetch(\"./post\", { method: \"POST\", body: JSON.stringify({foo: \"bar\"})})'\n    )\n    assert len(requests) == 1\n    assert requests[0].post_data == '{\"foo\":\"bar\"}'\n\n\nasync def test_request_post_data__should_be_undefined_when_there_is_no_post_data(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.request.post_data is None\n\n\nasync def test_should_parse_the_json_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\"/post\", lambda req: req.finish())\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.evaluate(\n        \"\"\"() => fetch('./post', { method: 'POST', body: JSON.stringify({ foo: 'bar' }) })\"\"\"\n    )\n    assert len(requests) == 1\n    assert requests[0].post_data_json == {\"foo\": \"bar\"}\n\n\nasync def test_should_parse_the_data_if_content_type_is_form_urlencoded(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\"/post\", lambda req: req.finish())\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.set_content(\n        \"\"\"<form method='POST' action='/post'><input type='text' name='foo' value='bar'><input type='number' name='baz' value='123'><input type='submit'></form>\"\"\"\n    )\n    await page.click(\"input[type=submit]\")\n    assert len(requests) == 1\n    assert requests[0].post_data_json == {\"foo\": \"bar\", \"baz\": \"123\"}\n\n\nasync def test_should_be_undefined_when_there_is_no_post_data(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.request.post_data_json is None\n\n\nasync def test_should_return_post_data_without_content_type(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\"**/*\") as request_info:\n        await page.evaluate(\n            \"\"\"({url}) => {\n            const request = new Request(url, {\n                method: 'POST',\n                body: JSON.stringify({ value: 42 }),\n            });\n            request.headers.set('content-type', '');\n            return fetch(request);\n        }\"\"\",\n            {\"url\": server.PREFIX + \"/title.html\"},\n        )\n    request = await request_info.value\n    assert request.post_data_json == {\"value\": 42}\n\n\nasync def test_should_throw_on_invalid_json_in_post_data(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\"**/*\") as request_info:\n        await page.evaluate(\n            \"\"\"({url}) => {\n            const request = new Request(url, {\n                method: 'POST',\n                body: '<not a json>',\n            });\n            request.headers.set('content-type', '');\n            return fetch(request);\n        }\"\"\",\n            {\"url\": server.PREFIX + \"/title.html\"},\n        )\n    request = await request_info.value\n    with pytest.raises(Error) as exc_info:\n        print(request.post_data_json)\n    assert \"POST data is not a valid JSON object: <not a json>\" in str(exc_info.value)\n\n\nasync def test_should_work_with_binary_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\"/post\", lambda req: req.finish())\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.evaluate(\n        \"\"\"async () => {\n        await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })\n    }\"\"\"\n    )\n    assert len(requests) == 1\n    buffer = requests[0].post_data_buffer\n    assert len(buffer) == 256\n    for i in range(256):\n        assert buffer[i] == i\n\n\nasync def test_should_work_with_binary_post_data_and_interception(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\"/post\", lambda req: req.finish())\n    requests = []\n    await page.route(\"/post\", lambda route: asyncio.ensure_future(route.continue_()))\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.evaluate(\n        \"\"\"async () => {\n        await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })\n    }\"\"\"\n    )\n    assert len(requests) == 1\n    buffer = requests[0].post_data_buffer\n    assert len(buffer) == 256\n    for i in range(256):\n        assert buffer[i] == i\n\n\nasync def test_response_text_should_work(page: Page, server: Server) -> None:\n    response = await page.goto(server.PREFIX + \"/simple.json\")\n    assert response\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n\nasync def test_response_text_should_return_uncompressed_text(\n    page: Page, server: Server\n) -> None:\n    server.enable_gzip(\"/simple.json\")\n    response = await page.goto(server.PREFIX + \"/simple.json\")\n    assert response\n    assert response.headers[\"content-encoding\"] == \"gzip\"\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n\nasync def test_response_text_should_throw_when_requesting_body_of_redirected_response(\n    page: Page, server: Server\n) -> None:\n    server.set_redirect(\"/foo.html\", \"/empty.html\")\n    response = await page.goto(server.PREFIX + \"/foo.html\")\n    assert response\n    redirected_from = response.request.redirected_from\n    assert redirected_from\n    redirected = await redirected_from.response()\n    assert redirected\n    assert redirected.status == 302\n    error: Optional[Error] = None\n    try:\n        await redirected.text()\n    except Error as exc:\n        error = exc\n    assert error\n    assert \"Response body is unavailable for redirect responses\" in error.message\n\n\nasync def test_response_json_should_work(page: Page, server: Server) -> None:\n    response = await page.goto(server.PREFIX + \"/simple.json\")\n    assert response\n    assert await response.json() == {\"foo\": \"bar\"}\n\n\nasync def test_response_body_should_work(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    response = await page.goto(server.PREFIX + \"/pptr.png\")\n    assert response\n    with open(\n        assetdir / \"pptr.png\",\n        \"rb\",\n    ) as fd:\n        assert fd.read() == await response.body()\n\n\nasync def test_response_body_should_work_with_compression(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    server.enable_gzip(\"/pptr.png\")\n    response = await page.goto(server.PREFIX + \"/pptr.png\")\n    assert response\n    with open(\n        assetdir / \"pptr.png\",\n        \"rb\",\n    ) as fd:\n        assert fd.read() == await response.body()\n\n\nasync def test_response_status_text_should_work(page: Page, server: Server) -> None:\n    server.set_route(\"/cool\", lambda r: (r.setResponseCode(200, b\"cool!\"), r.finish()))\n\n    response = await page.goto(server.PREFIX + \"/cool\")\n    assert response\n    assert response.status_text == \"cool!\"\n\n\nasync def test_request_resource_type_should_return_event_source(\n    page: Page, server: Server\n) -> None:\n    SSE_MESSAGE = {\"foo\": \"bar\"}\n    # 1. Setup server-sent events on server that immediately sends a message to the client.\n    server.set_route(\n        \"/sse\",\n        lambda r: (\n            r.setHeader(\"Content-Type\", \"text/event-stream\"),\n            r.setHeader(\"Connection\", \"keep-alive\"),\n            r.setHeader(\"Cache-Control\", \"no-cache\"),\n            r.setResponseCode(200),\n            r.write(f\"data: {json.dumps(SSE_MESSAGE)}\\n\\n\".encode()),\n            r.finish(),\n        ),\n    )\n\n    # 2. Subscribe to page request events.\n    await page.goto(server.EMPTY_PAGE)\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    # 3. Connect to EventSource in browser and return first message.\n    assert (\n        await page.evaluate(\n            \"\"\"() => {\n      const eventSource = new EventSource('/sse');\n      return new Promise(resolve => {\n        eventSource.onmessage = e => resolve(JSON.parse(e.data));\n      });\n    }\"\"\"\n        )\n        == SSE_MESSAGE\n    )\n    assert requests[0].resource_type == \"eventsource\"\n\n\nasync def test_network_events_request(page: Page, server: Server) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    assert len(requests) == 1\n    assert requests[0].url == server.EMPTY_PAGE\n    assert requests[0].resource_type == \"document\"\n    assert requests[0].method == \"GET\"\n    assert await requests[0].response()\n    assert requests[0].frame == page.main_frame\n    assert requests[0].frame.url == server.EMPTY_PAGE\n\n\nasync def test_network_events_response(page: Page, server: Server) -> None:\n    responses = []\n    page.on(\"response\", lambda r: responses.append(r))\n    await page.goto(server.EMPTY_PAGE)\n    assert len(responses) == 1\n    assert responses[0].url == server.EMPTY_PAGE\n    assert responses[0].status == 200\n    assert responses[0].ok\n    assert responses[0].request\n\n\nasync def test_network_events_request_failed(\n    page: Page,\n    server: Server,\n    is_chromium: bool,\n    is_webkit: bool,\n    is_mac: bool,\n    is_win: bool,\n) -> None:\n    def handle_request(request: TestServerRequest) -> None:\n        request.setHeader(\"Content-Type\", \"text/css\")\n        request.loseConnection()\n\n    server.set_route(\"/one-style.css\", handle_request)\n\n    failed_requests = []\n    page.on(\"requestfailed\", lambda request: failed_requests.append(request))\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    # TODO: https://github.com/microsoft/playwright/issues/12789\n    assert len(failed_requests) >= 1\n    assert \"one-style.css\" in failed_requests[0].url\n    assert await failed_requests[0].response() is None\n    assert failed_requests[0].resource_type == \"stylesheet\"\n    if is_chromium:\n        assert failed_requests[0].failure == \"net::ERR_EMPTY_RESPONSE\"\n    elif is_webkit:\n        if is_mac:\n            assert failed_requests[0].failure == \"The network connection was lost.\"\n        elif is_win:\n            assert (\n                failed_requests[0].failure\n                == \"Server returned nothing (no headers, no data)\"\n            )\n        else:\n            assert failed_requests[0].failure in [\n                \"Message Corrupt\",\n                \"Connection terminated unexpectedly\",\n            ]\n    else:\n        assert failed_requests[0].failure == \"NS_ERROR_NET_RESET\"\n    assert failed_requests[0].frame\n\n\nasync def test_network_events_request_finished(page: Page, server: Server) -> None:\n    async with page.expect_event(\"requestfinished\") as event_info:\n        await page.goto(server.EMPTY_PAGE)\n    request = await event_info.value\n    assert request.url == server.EMPTY_PAGE\n    assert await request.response()\n    assert request.frame == page.main_frame\n    assert request.frame.url == server.EMPTY_PAGE\n\n\nasync def test_network_events_should_fire_events_in_proper_order(\n    page: Page, server: Server\n) -> None:\n    events = []\n    page.on(\"request\", lambda request: events.append(\"request\"))\n    page.on(\"response\", lambda response: events.append(\"response\"))\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    await response.finished()\n    events.append(\"requestfinished\")\n    assert events == [\"request\", \"response\", \"requestfinished\"]\n\n\nasync def test_network_events_should_support_redirects(\n    page: Page, server: Server\n) -> None:\n    FOO_URL = server.PREFIX + \"/foo.html\"\n    events: Dict[str, List[Union[str, int]]] = {}\n    events[FOO_URL] = []\n    events[server.EMPTY_PAGE] = []\n\n    def _handle_on_request(request: Request) -> None:\n        events[request.url].append(request.method)\n\n    page.on(\"request\", _handle_on_request)\n\n    def _handle_on_response(response: Response) -> None:\n        events[response.url].append(response.status)\n\n    page.on(\"response\", _handle_on_response)\n\n    def _handle_on_requestfinished(request: Request) -> None:\n        events[request.url].append(\"DONE\")\n\n    page.on(\"requestfinished\", _handle_on_requestfinished)\n\n    def _handle_on_requestfailed(request: Request) -> None:\n        events[request.url].append(\"FAIL\")\n\n    page.on(\"requestfailed\", _handle_on_requestfailed)\n    server.set_redirect(\"/foo.html\", \"/empty.html\")\n    response = await page.goto(FOO_URL)\n    assert response\n    await response.finished()\n    expected = {}\n    expected[FOO_URL] = [\"GET\", 302, \"DONE\"]\n    expected[server.EMPTY_PAGE] = [\"GET\", 200, \"DONE\"]\n    assert events == expected\n    redirected_from = response.request.redirected_from\n    assert redirected_from\n    assert \"/foo.html\" in redirected_from.url\n    assert redirected_from.redirected_from is None\n    assert redirected_from.redirected_to == response.request\n\n\nasync def test_request_is_navigation_request_should_work(\n    page: Page, server: Server\n) -> None:\n    requests: Dict[str, Request] = {}\n\n    def handle_request(request: Request) -> None:\n        requests[request.url.split(\"/\").pop()] = request\n\n    page.on(\"request\", handle_request)\n    server.set_redirect(\"/rrredirect\", \"/frames/one-frame.html\")\n    await page.goto(server.PREFIX + \"/rrredirect\")\n    assert requests[\"rrredirect\"].is_navigation_request()\n    assert requests[\"one-frame.html\"].is_navigation_request()\n    assert requests[\"frame.html\"].is_navigation_request()\n    assert requests[\"script.js\"].is_navigation_request() is False\n    assert requests[\"style.css\"].is_navigation_request() is False\n\n\nasync def test_request_is_navigation_request_should_work_when_navigating_to_image(\n    page: Page, server: Server\n) -> None:\n    requests = []\n    page.on(\"request\", lambda r: requests.append(r))\n    await page.goto(server.PREFIX + \"/pptr.png\")\n    assert requests[0].is_navigation_request()\n\n\nasync def test_set_extra_http_headers_should_work(page: Page, server: Server) -> None:\n    await page.set_extra_http_headers({\"foo\": \"bar\"})\n\n    request = (\n        await asyncio.gather(\n            server.wait_for_request(\"/empty.html\"),\n            page.goto(server.EMPTY_PAGE),\n        )\n    )[0]\n    assert request.getHeader(\"foo\") == \"bar\"\n\n\nasync def test_set_extra_http_headers_should_work_with_redirects(\n    page: Page, server: Server\n) -> None:\n    server.set_redirect(\"/foo.html\", \"/empty.html\")\n    await page.set_extra_http_headers({\"foo\": \"bar\"})\n\n    request = (\n        await asyncio.gather(\n            server.wait_for_request(\"/empty.html\"),\n            page.goto(server.PREFIX + \"/foo.html\"),\n        )\n    )[0]\n    assert request.getHeader(\"foo\") == \"bar\"\n\n\nasync def test_set_extra_http_headers_should_work_with_extra_headers_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context()\n    await context.set_extra_http_headers({\"foo\": \"bar\"})\n\n    page = await context.new_page()\n    request = (\n        await asyncio.gather(\n            server.wait_for_request(\"/empty.html\"),\n            page.goto(server.EMPTY_PAGE),\n        )\n    )[0]\n    await context.close()\n    assert request.getHeader(\"foo\") == \"bar\"\n\n\nasync def test_set_extra_http_headers_should_override_extra_headers_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(extra_http_headers={\"fOo\": \"bAr\", \"baR\": \"foO\"})\n\n    page = await context.new_page()\n    await page.set_extra_http_headers({\"Foo\": \"Bar\"})\n\n    request = (\n        await asyncio.gather(\n            server.wait_for_request(\"/empty.html\"),\n            page.goto(server.EMPTY_PAGE),\n        )\n    )[0]\n    await context.close()\n    assert request.getHeader(\"foo\") == \"Bar\"\n    assert request.getHeader(\"bar\") == \"foO\"\n\n\nasync def test_set_extra_http_headers_should_throw_for_non_string_header_values(\n    page: Page,\n) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.set_extra_http_headers({\"foo\": 1})  # type: ignore\n    except Error as exc:\n        error = exc\n    assert error\n    assert (\n        error.message\n        == \"Page.set_extra_http_headers: headers[0].value: expected string, got number\"\n    )\n\n\nasync def test_response_server_addr(page: Page, server: Server) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    server_addr = await response.server_addr()\n    assert server_addr\n    assert server_addr[\"port\"] == server.PORT\n    assert server_addr[\"ipAddress\"] in [\"127.0.0.1\", \"[::1]\"]\n\n\nasync def test_response_security_details(\n    browser: Browser,\n    https_server: Server,\n    browser_name: str,\n    is_win: bool,\n    is_linux: bool,\n) -> None:\n    if (browser_name == \"webkit\" and is_linux) or (browser_name == \"webkit\" and is_win):\n        pytest.skip(\"https://github.com/microsoft/playwright/issues/6759\")\n    page = await browser.new_page(ignore_https_errors=True)\n    response = await page.goto(https_server.EMPTY_PAGE)\n    assert response\n    await response.finished()\n    security_details = await response.security_details()\n    assert security_details\n    if browser_name == \"webkit\" and is_win:\n        assert security_details == {\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": -1,\n        }\n    elif browser_name == \"webkit\":\n        assert security_details == {\n            \"protocol\": \"TLS 1.3\",\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": 33086084863,\n        }\n    else:\n        assert security_details == {\n            \"issuer\": \"puppeteer-tests\",\n            \"protocol\": \"TLS 1.3\",\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": 33086084863,\n        }\n    await page.close()\n\n\nasync def test_response_security_details_none_without_https(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    security_details = await response.security_details()\n    assert security_details is None\n\n\nasync def test_should_report_if_request_was_from_service_worker(\n    page: Page, server: Server\n) -> None:\n    response = await page.goto(server.PREFIX + \"/serviceworkers/fetch/sw.html\")\n    assert response\n    assert not response.from_service_worker\n    await page.evaluate(\"() => window.activationPromise\")\n    async with page.expect_response(\"**/example.txt\") as response_info:\n        await page.evaluate(\"() => fetch('/example.txt')\")\n    response = await response_info.value\n    assert response.from_service_worker\n"
  },
  {
    "path": "tests/async/test_page.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport os\nimport re\nfrom pathlib import Path\nfrom typing import Dict, List, Optional\n\nimport pytest\n\nfrom playwright.async_api import (\n    BrowserContext,\n    Error,\n    JSHandle,\n    Page,\n    Route,\n    TimeoutError,\n)\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE, must\n\n\nasync def test_close_should_reject_all_promises(context: BrowserContext) -> None:\n    new_page = await context.new_page()\n    with pytest.raises(Error) as exc_info:\n        await asyncio.gather(\n            new_page.evaluate(\"() => new Promise(r => {})\"), new_page.close()\n        )\n    assert \" closed\" in exc_info.value.message\n\n\nasync def test_closed_should_not_visible_in_context_pages(\n    context: BrowserContext,\n) -> None:\n    page = await context.new_page()\n    assert page in context.pages\n    await page.close()\n    assert page not in context.pages\n\n\nasync def test_close_should_run_beforeunload_if_asked_for(\n    context: BrowserContext, server: Server, is_chromium: bool, is_webkit: bool\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/beforeunload.html\")\n    # We have to interact with a page so that 'beforeunload' handlers\n    # fire.\n    await page.click(\"body\")\n\n    async with page.expect_event(\"dialog\") as dialog_info:\n        await page.close(run_before_unload=True)\n    dialog = await dialog_info.value\n\n    assert dialog.type == \"beforeunload\"\n    assert dialog.default_value == \"\"\n    if is_chromium:\n        assert dialog.message == \"\"\n    elif is_webkit:\n        assert dialog.message == \"Leave?\"\n    else:\n        assert (\n            \"This page is asking you to confirm that you want to leave\"\n            in dialog.message\n        )\n    async with page.expect_event(\"close\"):\n        await dialog.accept()\n\n\nasync def test_close_should_not_run_beforeunload_by_default(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/beforeunload.html\")\n    # We have to interact with a page so that 'beforeunload' handlers\n    # fire.\n    await page.click(\"body\")\n    await page.close()\n\n\nasync def test_should_be_able_to_navigate_away_from_page_with_before_unload(\n    server: Server, page: Page\n) -> None:\n    await page.goto(server.PREFIX + \"/beforeunload.html\")\n    # We have to interact with a page so that 'beforeunload' handlers\n    # fire.\n    await page.click(\"body\")\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_close_should_set_the_page_close_state(context: BrowserContext) -> None:\n    page = await context.new_page()\n    assert page.is_closed() is False\n    await page.close()\n    assert page.is_closed()\n\n\nasync def test_close_should_terminate_network_waiters(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n\n    async def wait_for_request() -> Error:\n        with pytest.raises(Error) as exc_info:\n            async with page.expect_request(server.EMPTY_PAGE):\n                pass\n        return exc_info.value\n\n    async def wait_for_response() -> Error:\n        with pytest.raises(Error) as exc_info:\n            async with page.expect_response(server.EMPTY_PAGE):\n                pass\n        return exc_info.value\n\n    results = await asyncio.gather(\n        wait_for_request(), wait_for_response(), page.close()\n    )\n    for i in range(2):\n        error = results[i]\n        assert error\n        assert TARGET_CLOSED_ERROR_MESSAGE in error.message\n        assert \"Timeout\" not in error.message\n\n\nasync def test_close_should_be_callable_twice(context: BrowserContext) -> None:\n    page = await context.new_page()\n    await asyncio.gather(\n        page.close(),\n        page.close(),\n    )\n    await page.close()\n\n\nasync def test_load_should_fire_when_expected(page: Page) -> None:\n    async with page.expect_event(\"load\"):\n        await page.goto(\"about:blank\")\n\n\n@pytest.mark.skip(\"FIXME\")\nasync def test_should_work_with_wait_for_loadstate(page: Page, server: Server) -> None:\n    messages = []\n\n    def _handler(request: TestServerRequest) -> None:\n        messages.append(\"route\")\n        request.setHeader(\"Content-Type\", \"text/html\")\n        request.write(b\"<link rel='stylesheet' href='./one-style.css'>\")\n        request.finish()\n\n    server.set_route(\n        \"/empty.html\",\n        _handler,\n    )\n\n    await page.set_content(f'<a id=\"anchor\" href=\"{server.EMPTY_PAGE}\">empty.html</a>')\n\n    async def wait_for_clickload() -> None:\n        await page.click(\"a\")\n        await page.wait_for_load_state(\"load\")\n        messages.append(\"clickload\")\n\n    async def wait_for_page_load() -> None:\n        await page.wait_for_event(\"load\")\n        messages.append(\"load\")\n\n    await asyncio.gather(\n        wait_for_clickload(),\n        wait_for_page_load(),\n    )\n\n    assert messages == [\"route\", \"load\", \"clickload\"]\n\n\nasync def test_async_stacks_should_work(page: Page, server: Server) -> None:\n    await page.route(\n        \"**/empty.html\", lambda route, response: asyncio.create_task(route.abort())\n    )\n    with pytest.raises(Error) as exc_info:\n        await page.goto(server.EMPTY_PAGE)\n    assert exc_info.value.stack\n    assert __file__ in exc_info.value.stack\n\n\nasync def test_opener_should_provide_access_to_the_opener_page(page: Page) -> None:\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window.open('about:blank')\")\n    popup = await popup_info.value\n    opener = await popup.opener()\n    assert opener == page\n\n\nasync def test_opener_should_return_null_if_parent_page_has_been_closed(\n    page: Page,\n) -> None:\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window.open('about:blank')\")\n    popup = await popup_info.value\n    await page.close()\n    opener = await popup.opener()\n    assert opener is None\n\n\nasync def test_domcontentloaded_should_fire_when_expected(\n    page: Page, server: Server\n) -> None:\n    future = asyncio.create_task(page.goto(\"about:blank\"))\n    async with page.expect_event(\"domcontentloaded\"):\n        pass\n    await future\n\n\nasync def test_wait_for_request(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(server.PREFIX + \"/digits/2.png\") as request_info:\n        await page.evaluate(\n            \"\"\"() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }\"\"\"\n        )\n    request = await request_info.value\n    assert request.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_request_should_work_with_predicate(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\n        lambda request: request.url == server.PREFIX + \"/digits/2.png\"\n    ) as request_info:\n        await page.evaluate(\n            \"\"\"() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }\"\"\"\n        )\n    request = await request_info.value\n    assert request.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_request_should_timeout(page: Page, server: Server) -> None:\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_event(\"request\", timeout=1):\n            pass\n    assert exc_info.type is TimeoutError\n\n\nasync def test_wait_for_request_should_respect_default_timeout(\n    page: Page, server: Server\n) -> None:\n    page.set_default_timeout(1)\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_event(\"request\", lambda _: False):\n            pass\n    assert exc_info.type is TimeoutError\n\n\nasync def test_wait_for_request_should_work_with_no_timeout(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\n        server.PREFIX + \"/digits/2.png\", timeout=0\n    ) as request_info:\n        await page.evaluate(\n            \"\"\"() => setTimeout(() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }, 50)\"\"\"\n        )\n    request = await request_info.value\n    assert request.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_request_should_work_with_url_match(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(re.compile(r\"digits\\/\\d\\.png\")) as request_info:\n        await page.evaluate(\"fetch('/digits/1.png')\")\n    request = await request_info.value\n    assert request.url == server.PREFIX + \"/digits/1.png\"\n\n\nasync def test_wait_for_event_should_fail_with_error_upon_disconnect(\n    page: Page,\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_download():\n            await page.close()\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message\n\n\nasync def test_wait_for_response_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_response(server.PREFIX + \"/digits/2.png\") as response_info:\n        await page.evaluate(\n            \"\"\"() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }\"\"\"\n        )\n    response = await response_info.value\n    assert response.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_response_should_respect_timeout(page: Page) -> None:\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_response(\"**/*\", timeout=1):\n            pass\n    assert exc_info.type is TimeoutError\n\n\nasync def test_wait_for_response_should_respect_default_timeout(page: Page) -> None:\n    page.set_default_timeout(1)\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_response(lambda _: False):\n            pass\n    assert exc_info.type is TimeoutError\n\n\nasync def test_wait_for_response_should_work_with_predicate(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_response(\n        lambda response: response.url == server.PREFIX + \"/digits/2.png\"\n    ) as response_info:\n        await page.evaluate(\n            \"\"\"() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }\"\"\"\n        )\n    response = await response_info.value\n    assert response.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_response_should_work_with_no_timeout(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_response(server.PREFIX + \"/digits/2.png\") as response_info:\n        await page.evaluate(\n            \"\"\"() => {\n                fetch('/digits/1.png')\n                fetch('/digits/2.png')\n                fetch('/digits/3.png')\n            }\"\"\"\n        )\n    response = await response_info.value\n    assert response.url == server.PREFIX + \"/digits/2.png\"\n\n\nasync def test_wait_for_response_should_use_context_timeout(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    context.set_default_timeout(1_000)\n    with pytest.raises(Error) as exc_info:\n        async with page.expect_response(\"https://playwright.dev\"):\n            pass\n    assert exc_info.type is TimeoutError\n    assert \"Timeout 1000ms exceeded\" in exc_info.value.message\n\n\nasync def test_expect_response_should_not_hang_when_predicate_throws(\n    page: Page,\n) -> None:\n    with pytest.raises(Exception, match=\"Oops!\"):\n        async with page.expect_response(\"**/*\"):\n            raise Exception(\"Oops!\")\n\n\nasync def test_expose_binding(page: Page) -> None:\n    binding_source = []\n\n    def binding(source: Dict, a: int, b: int) -> int:\n        binding_source.append(source)\n        return a + b\n\n    await page.expose_binding(\"add\", lambda source, a, b: binding(source, a, b))\n\n    result = await page.evaluate(\"add(5, 6)\")\n\n    assert binding_source[0][\"context\"] == page.context\n    assert binding_source[0][\"page\"] == page\n    assert binding_source[0][\"frame\"] == page.main_frame\n    assert result == 11\n\n\nasync def test_expose_function(page: Page, server: Server) -> None:\n    await page.expose_function(\"compute\", lambda a, b: a * b)\n    result = await page.evaluate(\"compute(9, 4)\")\n    assert result == 36\n\n\nasync def test_expose_function_should_throw_exception_in_page_context(\n    page: Page, server: Server\n) -> None:\n    def throw() -> None:\n        raise Exception(\"WOOF WOOF\")\n\n    await page.expose_function(\"woof\", lambda: throw())\n    result = await page.evaluate(\n        \"\"\"async() => {\n            try {\n                await woof()\n            } catch (e) {\n                return {message: e.message, stack: e.stack}\n            }\n        }\"\"\"\n    )\n    assert result[\"message\"] == \"WOOF WOOF\"\n    assert __file__ in result[\"stack\"]\n\n\nasync def test_expose_function_should_be_callable_from_inside_add_init_script(\n    page: Page,\n) -> None:\n    called = []\n    await page.expose_function(\"woof\", lambda: called.append(True))\n    await page.add_init_script(\"woof()\")\n    await page.reload()\n    assert called == [True]\n\n\nasync def test_expose_function_should_survive_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.expose_function(\"compute\", lambda a, b: a * b)\n    await page.goto(server.EMPTY_PAGE)\n    result = await page.evaluate(\"compute(9, 4)\")\n    assert result == 36\n\n\nasync def test_expose_function_should_await_returned_promise(page: Page) -> None:\n    async def mul(a: int, b: int) -> int:\n        return a * b\n\n    await page.expose_function(\"compute\", mul)\n    assert await page.evaluate(\"compute(3, 5)\") == 15\n\n\nasync def test_expose_function_should_work_on_frames(\n    page: Page, server: Server\n) -> None:\n    await page.expose_function(\"compute\", lambda a, b: a * b)\n    await page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    frame = page.frames[1]\n    assert await frame.evaluate(\"compute(3, 5)\") == 15\n\n\nasync def test_expose_function_should_work_on_frames_before_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    await page.expose_function(\"compute\", lambda a, b: a * b)\n    frame = page.frames[1]\n    assert await frame.evaluate(\"compute(3, 5)\") == 15\n\n\nasync def test_expose_function_should_work_after_cross_origin_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.expose_function(\"compute\", lambda a, b: a * b)\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    assert await page.evaluate(\"compute(9, 4)\") == 36\n\n\nasync def test_expose_function_should_work_with_complex_objects(\n    page: Page, server: Server\n) -> None:\n    await page.expose_function(\"complexObject\", lambda a, b: dict(x=a[\"x\"] + b[\"x\"]))\n    result = await page.evaluate(\"complexObject({x: 5}, {x: 2})\")\n    assert result[\"x\"] == 7\n\n\nasync def test_expose_bindinghandle_should_work(page: Page, server: Server) -> None:\n    targets: List[JSHandle] = []\n\n    def logme(t: JSHandle) -> int:\n        targets.append(t)\n        return 17\n\n    await page.expose_binding(\"logme\", lambda source, t: logme(t), handle=True)\n    result = await page.evaluate(\"logme({ foo: 42 })\")\n    assert (await targets[0].evaluate(\"x => x.foo\")) == 42\n    assert result == 17\n\n\nasync def test_page_error_should_fire(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    url = server.PREFIX + \"/error.html\"\n    async with page.expect_event(\"pageerror\") as error_info:\n        await page.goto(url)\n    error = await error_info.value\n    assert error.name == \"Error\"\n    assert error.message == \"Fancy error!\"\n    # Note that WebKit reports the stack of the 'throw' statement instead of the Error constructor call.\n    if browser_name == \"chromium\":\n        assert (\n            error.stack\n            == \"\"\"Error: Fancy error!\n    at c (myscript.js:14:11)\n    at b (myscript.js:10:5)\n    at a (myscript.js:6:5)\n    at myscript.js:3:1\"\"\"\n        )\n    if browser_name == \"firefox\":\n        assert (\n            error.stack\n            == \"\"\"Error: Fancy error!\n    at c (myscript.js:14:11)\n    at b (myscript.js:10:5)\n    at a (myscript.js:6:5)\n    at  (myscript.js:3:1)\"\"\"\n        )\n    if browser_name == \"webkit\":\n        assert (\n            error.stack\n            == f\"\"\"Error: Fancy error!\n    at c ({url}:14:36)\n    at b ({url}:10:6)\n    at a ({url}:6:6)\n    at global code ({url}:3:2)\"\"\"\n        )\n\n\nasync def test_page_error_should_handle_odd_values(page: Page) -> None:\n    cases = [[\"null\", \"null\"], [\"undefined\", \"undefined\"], [\"0\", \"0\"], ['\"\"', \"\"]]\n    for [value, message] in cases:\n        async with page.expect_event(\"pageerror\") as error_info:\n            await page.evaluate(f\"() => setTimeout(() => {{ throw {value}; }}, 0)\")\n        error = await error_info.value\n        assert error.message == message\n\n\nasync def test_page_error_should_handle_object(page: Page, is_chromium: bool) -> None:\n    async with page.expect_event(\"pageerror\") as error_info:\n        await page.evaluate(\"() => setTimeout(() => { throw {}; }, 0)\")\n    error = await error_info.value\n    assert error.message == \"Object\" if is_chromium else \"[object Object]\"\n\n\nasync def test_page_error_should_handle_window(page: Page, is_chromium: bool) -> None:\n    async with page.expect_event(\"pageerror\") as error_info:\n        await page.evaluate(\"() => setTimeout(() => { throw window; }, 0)\")\n    error = await error_info.value\n    assert error.message == \"Window\" if is_chromium else \"[object Window]\"\n\n\nasync def test_page_error_should_pass_error_name_property(page: Page) -> None:\n    async with page.expect_event(\"pageerror\") as error_info:\n        await page.evaluate(\n            \"\"\"() => setTimeout(() => {\n            const error = new Error(\"my-message\");\n            error.name = \"my-name\";\n            throw error;\n        }, 0)\n        \"\"\"\n        )\n    error = await error_info.value\n    assert error.message == \"my-message\"\n    assert error.name == \"my-name\"\n\n\nexpected_output = \"<html><head></head><body><div>hello</div></body></html>\"\n\n\nasync def test_set_content_should_work(page: Page, server: Server) -> None:\n    await page.set_content(\"<div>hello</div>\")\n    result = await page.content()\n    assert result == expected_output\n\n\nasync def test_set_content_should_work_with_domcontentloaded(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<div>hello</div>\", wait_until=\"domcontentloaded\")\n    result = await page.content()\n    assert result == expected_output\n\n\nasync def test_set_content_should_work_with_doctype(page: Page, server: Server) -> None:\n    doctype = \"<!DOCTYPE html>\"\n    await page.set_content(f\"{doctype}<div>hello</div>\")\n    result = await page.content()\n    assert result == f\"{doctype}{expected_output}\"\n\n\nasync def test_set_content_should_work_with_HTML_4_doctype(\n    page: Page, server: Server\n) -> None:\n    doctype = '<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">'\n    await page.set_content(f\"{doctype}<div>hello</div>\")\n    result = await page.content()\n    assert result == f\"{doctype}{expected_output}\"\n\n\nasync def test_set_content_should_respect_timeout(page: Page, server: Server) -> None:\n    img_path = \"/img.png\"\n    # stall for image\n    server.set_route(img_path, lambda request: None)\n    with pytest.raises(Error) as exc_info:\n        await page.set_content(\n            f'<img src=\"{server.PREFIX + img_path}\"></img>', timeout=1\n        )\n    assert exc_info.type is TimeoutError\n\n\nasync def test_set_content_should_respect_default_navigation_timeout(\n    page: Page, server: Server\n) -> None:\n    page.set_default_navigation_timeout(1)\n    img_path = \"/img.png\"\n    # stall for image\n    await page.route(img_path, lambda route, request: None)\n\n    with pytest.raises(Error) as exc_info:\n        await page.set_content(f'<img src=\"{server.PREFIX + img_path}\"></img>')\n    assert \"Timeout 1ms exceeded\" in exc_info.value.message\n    assert exc_info.type is TimeoutError\n\n\nasync def test_set_content_should_await_resources_to_load(\n    page: Page, server: Server\n) -> None:\n    img_route: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\"**/img.png\", lambda route, request: img_route.set_result(route))\n    loaded = []\n\n    async def load() -> None:\n        await page.set_content(f'<img src=\"{server.PREFIX}/img.png\"></img>')\n        loaded.append(True)\n\n    content_promise = asyncio.create_task(load())\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    route = await img_route\n    assert loaded == []\n    asyncio.create_task(route.continue_())\n    await content_promise\n\n\nasync def test_set_content_should_work_with_tricky_content(page: Page) -> None:\n    await page.set_content(\"<div>hello world</div>\" + \"\\x7f\")\n    assert await page.eval_on_selector(\"div\", \"div => div.textContent\") == \"hello world\"\n\n\nasync def test_set_content_should_work_with_accents(page: Page) -> None:\n    await page.set_content(\"<div>aberración</div>\")\n    assert await page.eval_on_selector(\"div\", \"div => div.textContent\") == \"aberración\"\n\n\nasync def test_set_content_should_work_with_emojis(page: Page) -> None:\n    await page.set_content(\"<div>🐥</div>\")\n    assert await page.eval_on_selector(\"div\", \"div => div.textContent\") == \"🐥\"\n\n\nasync def test_set_content_should_work_with_newline(page: Page) -> None:\n    await page.set_content(\"<div>\\n</div>\")\n    assert await page.eval_on_selector(\"div\", \"div => div.textContent\") == \"\\n\"\n\n\nasync def test_add_script_tag_should_work_with_a_url(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    script_handle = await page.add_script_tag(url=\"/injectedfile.js\")\n    assert script_handle.as_element()\n    assert await page.evaluate(\"__injected\") == 42\n\n\nasync def test_add_script_tag_should_work_with_a_url_and_type_module(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.add_script_tag(url=\"/es6/es6import.js\", type=\"module\")\n    assert await page.evaluate(\"__es6injected\") == 42\n\n\nasync def test_add_script_tag_should_work_with_a_path_and_type_module(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.add_script_tag(path=assetdir / \"es6\" / \"es6pathimport.js\", type=\"module\")\n    await page.wait_for_function(\"window.__es6injected\")\n    assert await page.evaluate(\"__es6injected\") == 42\n\n\nasync def test_add_script_tag_should_work_with_a_content_and_type_module(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.add_script_tag(\n        content=\"import num from '/es6/es6module.js';window.__es6injected = num;\",\n        type=\"module\",\n    )\n    await page.wait_for_function(\"window.__es6injected\")\n    assert await page.evaluate(\"__es6injected\") == 42\n\n\nasync def test_add_script_tag_should_throw_an_error_if_loading_from_url_fail(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    with pytest.raises(Error) as exc_info:\n        await page.add_script_tag(url=\"/nonexistfile.js\")\n    assert exc_info.value\n\n\nasync def test_add_script_tag_should_work_with_a_path(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    script_handle = await page.add_script_tag(path=assetdir / \"injectedfile.js\")\n    assert script_handle.as_element()\n    assert await page.evaluate(\"__injected\") == 42\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_add_script_tag_should_include_source_url_when_path_is_provided(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    # Lacking sourceURL support in WebKit\n    await page.goto(server.EMPTY_PAGE)\n    await page.add_script_tag(path=assetdir / \"injectedfile.js\")\n    result = await page.evaluate(\"__injectedError.stack\")\n    assert os.path.join(\"assets\", \"injectedfile.js\") in result\n\n\nasync def test_add_script_tag_should_work_with_content(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    script_handle = await page.add_script_tag(content=\"window.__injected = 35;\")\n    assert script_handle.as_element()\n    assert await page.evaluate(\"__injected\") == 35\n\n\n@pytest.mark.skip_browser(\"firefox\")\nasync def test_add_script_tag_should_throw_when_added_with_content_to_the_csp_page(\n    page: Page, server: Server\n) -> None:\n    # Firefox fires onload for blocked script before it issues the CSP console error.\n    await page.goto(server.PREFIX + \"/csp.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.add_script_tag(content=\"window.__injected = 35;\")\n    assert exc_info.value\n\n\nasync def test_add_script_tag_should_throw_when_added_with_URL_to_the_csp_page(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/csp.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.add_script_tag(url=server.CROSS_PROCESS_PREFIX + \"/injectedfile.js\")\n    assert exc_info.value\n\n\nasync def test_add_script_tag_should_throw_a_nice_error_when_the_request_fails(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    url = server.PREFIX + \"/this_does_not_exist.js\"\n    with pytest.raises(Error) as exc_info:\n        await page.add_script_tag(url=url)\n    assert url in exc_info.value.message\n\n\nasync def test_add_style_tag_should_work_with_a_url(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    style_handle = await page.add_style_tag(url=\"/injectedstyle.css\")\n    assert style_handle.as_element()\n    assert (\n        await page.evaluate(\n            \"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')\"\n        )\n        == \"rgb(255, 0, 0)\"\n    )\n\n\nasync def test_add_style_tag_should_throw_an_error_if_loading_from_url_fail(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    with pytest.raises(Error) as exc_info:\n        await page.add_style_tag(url=\"/nonexistfile.js\")\n    assert exc_info.value\n\n\nasync def test_add_style_tag_should_work_with_a_path(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    style_handle = await page.add_style_tag(path=assetdir / \"injectedstyle.css\")\n    assert style_handle.as_element()\n    assert (\n        await page.evaluate(\n            \"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')\"\n        )\n        == \"rgb(255, 0, 0)\"\n    )\n\n\nasync def test_add_style_tag_should_include_source_url_when_path_is_provided(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.add_style_tag(path=assetdir / \"injectedstyle.css\")\n    style_handle = await page.query_selector(\"style\")\n    style_content = await page.evaluate(\"style => style.innerHTML\", style_handle)\n    assert os.path.join(\"assets\", \"injectedstyle.css\") in style_content\n\n\nasync def test_add_style_tag_should_work_with_content(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    style_handle = await page.add_style_tag(content=\"body { background-color: green; }\")\n    assert style_handle.as_element()\n    assert (\n        await page.evaluate(\n            \"window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')\"\n        )\n        == \"rgb(0, 128, 0)\"\n    )\n\n\nasync def test_add_style_tag_should_throw_when_added_with_content_to_the_CSP_page(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/csp.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.add_style_tag(content=\"body { background-color: green; }\")\n    assert exc_info.value\n\n\nasync def test_add_style_tag_should_throw_when_added_with_URL_to_the_CSP_page(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/csp.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.add_style_tag(url=server.CROSS_PROCESS_PREFIX + \"/injectedstyle.css\")\n    assert exc_info.value\n\n\nasync def test_url_should_work(page: Page, server: Server) -> None:\n    assert page.url == \"about:blank\"\n    await page.goto(server.EMPTY_PAGE)\n    assert page.url == server.EMPTY_PAGE\n\n\nasync def test_url_should_include_hashes(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE + \"#hash\")\n    assert page.url == server.EMPTY_PAGE + \"#hash\"\n    await page.evaluate(\"window.location.hash = 'dynamic'\")\n    assert page.url == server.EMPTY_PAGE + \"#dynamic\"\n\n\nasync def test_title_should_return_the_page_title(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/title.html\")\n    assert await page.title() == \"Woof-Woof\"\n\n\nasync def give_it_a_chance_to_fill(page: Page) -> None:\n    for i in range(5):\n        await page.evaluate(\n            \"() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))\"\n        )\n\n\nasync def test_fill_should_fill_textarea(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.fill(\"textarea\", \"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_should_fill_input(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.fill(\"input\", \"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_should_throw_on_unsupported_inputs(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    for type in [\n        \"button\",\n        \"checkbox\",\n        \"file\",\n        \"image\",\n        \"radio\",\n        \"reset\",\n        \"submit\",\n    ]:\n        await page.eval_on_selector(\n            \"input\", \"(input, type) => input.setAttribute('type', type)\", type\n        )\n        with pytest.raises(Error) as exc_info:\n            await page.fill(\"input\", \"\")\n        assert f'Input of type \"{type}\" cannot be filled' in exc_info.value.message\n\n\nasync def test_fill_should_fill_different_input_types(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    for type in [\"password\", \"search\", \"tel\", \"text\", \"url\"]:\n        await page.eval_on_selector(\n            \"input\", \"(input, type) => input.setAttribute('type', type)\", type\n        )\n        await page.fill(\"input\", \"text \" + type)\n        assert await page.evaluate(\"result\") == \"text \" + type\n\n\nasync def test_fill_should_fill_date_input_after_clicking(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<input type=date>\")\n    await page.click(\"input\")\n    await page.fill(\"input\", \"2020-03-02\")\n    assert await page.eval_on_selector(\"input\", \"input => input.value\") == \"2020-03-02\"\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_fill_should_throw_on_incorrect_date(page: Page, server: Server) -> None:\n    # Disabled as in upstream, we should validate time in the Playwright lib\n    await page.set_content(\"<input type=date>\")\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"input\", \"2020-13-05\")\n    assert \"Malformed value\" in exc_info.value.message\n\n\nasync def test_fill_should_fill_time_input(page: Page, server: Server) -> None:\n    await page.set_content(\"<input type=time>\")\n    await page.fill(\"input\", \"13:15\")\n    assert await page.eval_on_selector(\"input\", \"input => input.value\") == \"13:15\"\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_fill_should_throw_on_incorrect_time(page: Page, server: Server) -> None:\n    # Disabled as in upstream, we should validate time in the Playwright lib\n    await page.set_content(\"<input type=time>\")\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"input\", \"25:05\")\n    assert \"Malformed value\" in exc_info.value.message\n\n\nasync def test_fill_should_fill_datetime_local_input(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<input type=datetime-local>\")\n    await page.fill(\"input\", \"2020-03-02T05:15\")\n    assert (\n        await page.eval_on_selector(\"input\", \"input => input.value\")\n        == \"2020-03-02T05:15\"\n    )\n\n\n@pytest.mark.only_browser(\"chromium\")\nasync def test_fill_should_throw_on_incorrect_datetime_local(page: Page) -> None:\n    await page.set_content(\"<input type=datetime-local>\")\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"input\", \"abc\")\n    assert \"Malformed value\" in exc_info.value.message\n\n\nasync def test_fill_should_fill_contenteditable(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.fill(\"div[contenteditable]\", \"some value\")\n    assert (\n        await page.eval_on_selector(\"div[contenteditable]\", \"div => div.textContent\")\n        == \"some value\"\n    )\n\n\nasync def test_fill_should_fill_elements_with_existing_value_and_selection(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n\n    await page.eval_on_selector(\"input\", \"input => input.value = 'value one'\")\n    await page.fill(\"input\", \"another value\")\n    assert await page.evaluate(\"result\") == \"another value\"\n\n    await page.eval_on_selector(\n        \"input\",\n        \"\"\"input => {\n        input.selectionStart = 1\n        input.selectionEnd = 2\n    }\"\"\",\n    )\n\n    await page.fill(\"input\", \"maybe this one\")\n    assert await page.evaluate(\"result\") == \"maybe this one\"\n\n    await page.eval_on_selector(\n        \"div[contenteditable]\",\n        \"\"\"div => {\n        div.innerHTML = 'some text <span>some more text<span> and even more text'\n        range = document.createRange()\n        range.selectNodeContents(div.querySelector('span'))\n        selection = window.getSelection()\n        selection.removeAllRanges()\n        selection.addRange(range)\n    }\"\"\",\n    )\n\n    await page.fill(\"div[contenteditable]\", \"replace with this\")\n    assert (\n        await page.eval_on_selector(\"div[contenteditable]\", \"div => div.textContent\")\n        == \"replace with this\"\n    )\n\n\nasync def test_fill_should_throw_when_element_is_not_an_input_textarea_or_contenteditable(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"body\", \"\")\n    assert \"Element is not an <input>\" in exc_info.value.message\n\n\nasync def test_fill_should_throw_if_passed_a_non_string_value(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"textarea\", 123)  # type: ignore\n    assert \"expected string, got number\" in exc_info.value.message\n\n\nasync def test_fill_should_retry_on_disabled_element(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.eval_on_selector(\"input\", \"i => i.disabled = true\")\n    done = []\n\n    async def fill() -> None:\n        await page.fill(\"input\", \"some value\")\n        done.append(True)\n\n    promise = asyncio.create_task(fill())\n    await give_it_a_chance_to_fill(page)\n    assert done == []\n    assert await page.evaluate(\"result\") == \"\"\n\n    await page.eval_on_selector(\"input\", \"i => i.disabled = false\")\n    await promise\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_should_retry_on_readonly_element(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.eval_on_selector(\"textarea\", \"i => i.readOnly = true\")\n    done = []\n\n    async def fill() -> None:\n        await page.fill(\"textarea\", \"some value\")\n        done.append(True)\n\n    promise = asyncio.create_task(fill())\n    await give_it_a_chance_to_fill(page)\n    assert done == []\n    assert await page.evaluate(\"result\") == \"\"\n\n    await page.eval_on_selector(\"textarea\", \"i => i.readOnly = false\")\n    await promise\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_should_retry_on_invisible_element(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.eval_on_selector(\"input\", \"i => i.style.display = 'none'\")\n    done = []\n\n    async def fill() -> None:\n        await page.fill(\"input\", \"some value\")\n        done.append(True)\n\n    promise = asyncio.create_task(fill())\n    await give_it_a_chance_to_fill(page)\n    assert done == []\n    assert await page.evaluate(\"result\") == \"\"\n\n    await page.eval_on_selector(\"input\", \"i => i.style.display = 'inline'\")\n    await promise\n    assert await page.evaluate(\"result\") == \"some value\"\n\n\nasync def test_fill_should_be_able_to_fill_the_body(page: Page) -> None:\n    await page.set_content('<body contentEditable=\"true\"></body>')\n    await page.fill(\"body\", \"some value\")\n    assert await page.evaluate(\"document.body.textContent\") == \"some value\"\n\n\nasync def test_fill_should_fill_fixed_position_input(page: Page) -> None:\n    await page.set_content('<input style=\"position: fixed;\" />')\n    await page.fill(\"input\", \"some value\")\n    assert await page.evaluate(\"document.querySelector('input').value\") == \"some value\"\n\n\nasync def test_fill_should_be_able_to_fill_when_focus_is_in_the_wrong_frame(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <div contentEditable=\"true\"></div>\n      <iframe></iframe>\n    \"\"\"\n    )\n    await page.focus(\"iframe\")\n    await page.fill(\"div\", \"some value\")\n    assert await page.eval_on_selector(\"div\", \"d => d.textContent\") == \"some value\"\n\n\nasync def test_fill_should_be_able_to_fill_the_input_type_number_(page: Page) -> None:\n    await page.set_content('<input id=\"input\" type=\"number\"></input>')\n    await page.fill(\"input\", \"42\")\n    assert await page.evaluate(\"input.value\") == \"42\"\n\n\nasync def test_fill_should_be_able_to_fill_exponent_into_the_input_type_number_(\n    page: Page,\n) -> None:\n    await page.set_content('<input id=\"input\" type=\"number\"></input>')\n    await page.fill(\"input\", \"-10e5\")\n    assert await page.evaluate(\"input.value\") == \"-10e5\"\n\n\nasync def test_fill_should_be_able_to_fill_input_type_number__with_empty_string(\n    page: Page,\n) -> None:\n    await page.set_content('<input id=\"input\" type=\"number\" value=\"123\"></input>')\n    await page.fill(\"input\", \"\")\n    assert await page.evaluate(\"input.value\") == \"\"\n\n\nasync def test_fill_should_not_be_able_to_fill_text_into_the_input_type_number_(\n    page: Page,\n) -> None:\n    await page.set_content('<input id=\"input\" type=\"number\"></input>')\n    with pytest.raises(Error) as exc_info:\n        await page.fill(\"input\", \"abc\")\n    assert \"Cannot type text into input[type=number]\" in exc_info.value.message\n\n\nasync def test_fill_should_be_able_to_clear_using_fill(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.fill(\"input\", \"some value\")\n    assert await page.evaluate(\"result\") == \"some value\"\n    await page.fill(\"input\", \"\")\n    assert await page.evaluate(\"result\") == \"\"\n\n\nasync def test_close_event_should_work_with_window_close(\n    page: Page, server: Server\n) -> None:\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"window['newPage'] = window.open('about:blank')\")\n    popup = await popup_info.value\n\n    async with popup.expect_event(\"close\"):\n        await page.evaluate(\"window['newPage'].close()\")\n\n\nasync def test_close_event_should_work_with_page_close(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    async with page.expect_event(\"close\"):\n        await page.close()\n\n\nasync def test_page_context_should_return_the_correct_browser_instance(\n    page: Page, context: BrowserContext\n) -> None:\n    assert page.context == context\n\n\nasync def test_frame_should_respect_name(page: Page, server: Server) -> None:\n    await page.set_content(\"<iframe name=target></iframe>\")\n    assert page.frame(name=\"bogus\") is None\n    frame = page.frame(name=\"target\")\n    assert frame\n    assert frame == page.main_frame.child_frames[0]\n\n\nasync def test_frame_should_respect_url(page: Page, server: Server) -> None:\n    await page.set_content(f'<iframe src=\"{server.EMPTY_PAGE}\"></iframe>')\n    assert page.frame(url=re.compile(r\"bogus\")) is None\n    assert must(page.frame(url=re.compile(r\"empty\"))).url == server.EMPTY_PAGE\n\n\nasync def test_press_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n    await page.press(\"textarea\", \"a\")\n    assert await page.evaluate(\"document.querySelector('textarea').value\") == \"a\"\n\n\nasync def test_frame_press_should_work(page: Page, server: Server) -> None:\n    await page.set_content(\n        f'<iframe name=inner src=\"{server.PREFIX}/input/textarea.html\"></iframe>'\n    )\n    frame = page.frame(\"inner\")\n    assert frame\n    await frame.press(\"textarea\", \"a\")\n    assert await frame.evaluate(\"document.querySelector('textarea').value\") == \"a\"\n\n\nasync def test_should_emulate_reduced_motion(page: Page, server: Server) -> None:\n    assert await page.evaluate(\n        \"matchMedia('(prefers-reduced-motion: no-preference)').matches\"\n    )\n    await page.emulate_media(reduced_motion=\"reduce\")\n    assert await page.evaluate(\"matchMedia('(prefers-reduced-motion: reduce)').matches\")\n    assert not await page.evaluate(\n        \"matchMedia('(prefers-reduced-motion: no-preference)').matches\"\n    )\n    await page.emulate_media(reduced_motion=\"no-preference\")\n    assert not await page.evaluate(\n        \"matchMedia('(prefers-reduced-motion: reduce)').matches\"\n    )\n    assert await page.evaluate(\n        \"matchMedia('(prefers-reduced-motion: no-preference)').matches\"\n    )\n\n\nasync def test_input_value(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/textarea.html\")\n\n    await page.fill(\"input\", \"my-text-content\")\n    assert await page.input_value(\"input\") == \"my-text-content\"\n\n    await page.fill(\"input\", \"\")\n    assert await page.input_value(\"input\") == \"\"\n\n\nasync def test_drag_and_drop_helper_method(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    await page.drag_and_drop(\"#source\", \"#target\")\n    assert (\n        await page.eval_on_selector(\n            \"#target\", \"target => target.contains(document.querySelector('#source'))\"\n        )\n        is True\n    )\n\n\nasync def test_drag_and_drop_with_position(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n      <div style=\"width:100px;height:100px;background:red;\" id=\"red\">\n      </div>\n      <div style=\"width:100px;height:100px;background:blue;\" id=\"blue\">\n      </div>\n    \"\"\"\n    )\n    events_handle = await page.evaluate_handle(\n        \"\"\"\n        () => {\n        const events = [];\n        document.getElementById('red').addEventListener('mousedown', event => {\n            events.push({\n            type: 'mousedown',\n            x: event.offsetX,\n            y: event.offsetY,\n            });\n        });\n        document.getElementById('blue').addEventListener('mouseup', event => {\n            events.push({\n            type: 'mouseup',\n            x: event.offsetX,\n            y: event.offsetY,\n            });\n        });\n        return events;\n        }\n    \"\"\"\n    )\n    await page.drag_and_drop(\n        \"#red\",\n        \"#blue\",\n        source_position={\"x\": 34, \"y\": 7},\n        target_position={\"x\": 10, \"y\": 20},\n    )\n    assert await events_handle.json_value() == [\n        {\"type\": \"mousedown\", \"x\": 34, \"y\": 7},\n        {\"type\": \"mouseup\", \"x\": 10, \"y\": 20},\n    ]\n\n\nasync def test_drag_and_drop_with_tweened_mouse_movement(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <body style=\"margin: 0; padding: 0;\">\n          <div style=\"width:100px;height:100px;background:red;\" id=\"red\"></div>\n          <div style=\"width:300px;height:100px;background:blue;\" id=\"blue\"></div>\n        </body>\n        \"\"\"\n    )\n    events_handle = await page.evaluate_handle(\n        \"\"\"\n        () => {\n            const events = [];\n            document.addEventListener('mousedown', event => {\n                events.push({\n                    type: 'mousedown',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            document.addEventListener('mouseup', event => {\n                events.push({\n                    type: 'mouseup',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            document.addEventListener('mousemove', event => {\n                events.push({\n                    type: 'mousemove',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            return events;\n        }\n        \"\"\"\n    )\n    await page.drag_and_drop(\"#red\", \"#blue\", steps=4)\n    assert await events_handle.json_value() == [\n        {\"type\": \"mousemove\", \"x\": 50, \"y\": 50},\n        {\"type\": \"mousedown\", \"x\": 50, \"y\": 50},\n        {\"type\": \"mousemove\", \"x\": 75, \"y\": 75},\n        {\"type\": \"mousemove\", \"x\": 100, \"y\": 100},\n        {\"type\": \"mousemove\", \"x\": 125, \"y\": 125},\n        {\"type\": \"mousemove\", \"x\": 150, \"y\": 150},\n        {\"type\": \"mouseup\", \"x\": 150, \"y\": 150},\n    ]\n\n\nasync def test_locator_drag_to_with_tweened_mouse_movement(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <body style=\"margin: 0; padding: 0;\">\n          <div style=\"width:100px;height:100px;background:red;\" id=\"red\"></div>\n          <div style=\"width:300px;height:100px;background:blue;\" id=\"blue\"></div>\n        </body>\n        \"\"\"\n    )\n    events_handle = await page.evaluate_handle(\n        \"\"\"\n        () => {\n            const events = [];\n            document.addEventListener('mousedown', event => {\n                events.push({\n                    type: 'mousedown',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            document.addEventListener('mouseup', event => {\n                events.push({\n                    type: 'mouseup',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            document.addEventListener('mousemove', event => {\n                events.push({\n                    type: 'mousemove',\n                    x: event.pageX,\n                    y: event.pageY,\n                });\n            });\n            return events;\n        }\n        \"\"\"\n    )\n    await page.locator(\"#red\").drag_to(page.locator(\"#blue\"), steps=4)\n    assert await events_handle.json_value() == [\n        {\"type\": \"mousemove\", \"x\": 50, \"y\": 50},\n        {\"type\": \"mousedown\", \"x\": 50, \"y\": 50},\n        {\"type\": \"mousemove\", \"x\": 75, \"y\": 75},\n        {\"type\": \"mousemove\", \"x\": 100, \"y\": 100},\n        {\"type\": \"mousemove\", \"x\": 125, \"y\": 125},\n        {\"type\": \"mousemove\", \"x\": 150, \"y\": 150},\n        {\"type\": \"mouseup\", \"x\": 150, \"y\": 150},\n    ]\n\n\nasync def test_should_check_box_using_set_checked(page: Page) -> None:\n    await page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    await page.set_checked(\"input\", True)\n    assert await page.evaluate(\"checkbox.checked\") is True\n    await page.set_checked(\"input\", False)\n    assert await page.evaluate(\"checkbox.checked\") is False\n\n\nasync def test_should_set_bodysize_and_headersize(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\"*/**\") as request_info:\n        await page.evaluate(\n            \"() => fetch('./get', { method: 'POST', body: '12345'}).then(r => r.text())\"\n        )\n    request = await request_info.value\n    sizes = await request.sizes()\n    assert sizes[\"requestBodySize\"] == 5\n    assert sizes[\"requestHeadersSize\"] >= 300\n\n\nasync def test_should_set_bodysize_to_0(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_request(\"*/**\") as request_info:\n        await page.evaluate(\"() => fetch('./get').then(r => r.text())\")\n    request = await request_info.value\n    sizes = await request.sizes()\n    assert sizes[\"requestBodySize\"] == 0\n    assert sizes[\"requestHeadersSize\"] >= 200\n\n\nasync def test_should_emulate_forced_colors(page: Page) -> None:\n    assert await page.evaluate(\"matchMedia('(forced-colors: none)').matches\")\n    await page.emulate_media(forced_colors=\"none\")\n    assert await page.evaluate(\"matchMedia('(forced-colors: none)').matches\")\n    assert not await page.evaluate(\"matchMedia('(forced-colors: active)').matches\")\n    await page.emulate_media(forced_colors=\"active\")\n    assert await page.evaluate(\"matchMedia('(forced-colors: active)').matches\")\n    assert not await page.evaluate(\"matchMedia('(forced-colors: none)').matches\")\n\n\nasync def test_should_emulate_contrast(page: Page) -> None:\n    assert await page.evaluate(\n        \"matchMedia('(prefers-contrast: no-preference)').matches\"\n    )\n    await page.emulate_media(contrast=\"no-preference\")\n    assert await page.evaluate(\n        \"matchMedia('(prefers-contrast: no-preference)').matches\"\n    )\n    assert not await page.evaluate(\"matchMedia('(prefers-contrast: more)').matches\")\n    await page.emulate_media(contrast=\"more\")\n    assert not await page.evaluate(\n        \"matchMedia('(prefers-contrast: no-preference)').matches\"\n    )\n    assert await page.evaluate(\"matchMedia('(prefers-contrast: more)').matches\")\n    await page.emulate_media(contrast=\"null\")\n    assert await page.evaluate(\n        \"matchMedia('(prefers-contrast: no-preference)').matches\"\n    )\n\n\nasync def test_should_not_throw_when_continuing_while_page_is_closing(\n    page: Page, server: Server\n) -> None:\n    done: Optional[asyncio.Future] = None\n\n    def handle_route(route: Route) -> None:\n        nonlocal done\n        done = asyncio.gather(route.continue_(), page.close())\n\n    await page.route(\"**/*\", handle_route)\n    with pytest.raises(Error):\n        await page.goto(server.EMPTY_PAGE)\n    await must(done)\n\n\nasync def test_should_not_throw_when_continuing_after_page_is_closed(\n    page: Page, server: Server\n) -> None:\n    done: \"asyncio.Future[bool]\" = asyncio.Future()\n\n    async def handle_route(route: Route) -> None:\n        await page.close()\n        await route.continue_()\n        done.set_result(True)\n\n    await page.route(\"**/*\", handle_route)\n    with pytest.raises(Error):\n        await page.goto(server.EMPTY_PAGE)\n    await done\n\n\nasync def test_expose_binding_should_serialize_cycles(page: Page) -> None:\n    binding_values = []\n\n    def binding(source: Dict, o: Dict) -> None:\n        binding_values.append(o)\n\n    await page.expose_binding(\"log\", lambda source, o: binding(source, o))\n    await page.evaluate(\"const a = {}; a.b = a; window.log(a)\")\n    assert binding_values[0][\"b\"] == binding_values[0]\n\n\nasync def test_page_pause_should_reset_default_timeouts(\n    page: Page, headless: bool, server: Server\n) -> None:\n    if not headless:\n        pytest.skip()\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.pause()\n    with pytest.raises(Error, match=\"Timeout 30000ms exceeded.\"):\n        await page.get_by_text(\"foo\").click()\n\n\nasync def test_page_pause_should_reset_custom_timeouts(\n    page: Page, headless: bool, server: Server\n) -> None:\n    if not headless:\n        pytest.skip()\n\n    page.set_default_timeout(123)\n    page.set_default_navigation_timeout(456)\n    await page.goto(server.EMPTY_PAGE)\n    await page.pause()\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded.\"):\n        await page.get_by_text(\"foo\").click()\n\n    server.set_route(\"/empty.html\", lambda route: None)\n    with pytest.raises(Error, match=\"Timeout 456ms exceeded.\"):\n        await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_page_should_ignore_deprecated_is_hidden_and_visible_timeout(\n    page: Page,\n) -> None:\n    await page.set_content(\"<div>foo</div>\")\n    assert await page.is_hidden(\"div\", timeout=10) is False\n    assert await page.is_visible(\"div\", timeout=10) is True\n"
  },
  {
    "path": "tests/async/test_page_add_locator_handler.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\n\nimport pytest\n\nfrom playwright.async_api import Error, Locator, Page, expect\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\nasync def test_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    before_count = 0\n    after_count = 0\n\n    original_locator = page.get_by_text(\"This interstitial covers the button\")\n\n    async def handler(locator: Locator) -> None:\n        assert locator == original_locator\n        nonlocal before_count\n        nonlocal after_count\n        before_count += 1\n        await page.locator(\"#close\").click()\n        after_count += 1\n\n    await page.add_locator_handler(original_locator, handler)\n\n    for args in [\n        [\"mouseover\", 1],\n        [\"mouseover\", 1, \"capture\"],\n        [\"mouseover\", 2],\n        [\"mouseover\", 2, \"capture\"],\n        [\"pointerover\", 1],\n        [\"pointerover\", 1, \"capture\"],\n        [\"none\", 1],\n        [\"remove\", 1],\n        [\"hide\", 1],\n    ]:\n        await page.locator(\"#aside\").hover()\n        before_count = 0\n        after_count = 0\n        await page.evaluate(\n            \"(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }\",\n            args,\n        )\n        assert before_count == 0\n        assert after_count == 0\n        await page.locator(\"#target\").click()\n        assert before_count == args[1]\n        assert after_count == args[1]\n        assert await page.evaluate(\"window.clicked\") == 1\n        await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n\n\nasync def test_should_work_with_a_custom_check(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    async def handler() -> None:\n        if await page.get_by_text(\"This interstitial covers the button\").is_visible():\n            await page.locator(\"#close\").click()\n\n    await page.add_locator_handler(page.locator(\"body\"), handler, no_wait_after=True)\n\n    for args in [\n        [\"mouseover\", 2],\n        [\"none\", 1],\n        [\"remove\", 1],\n        [\"hide\", 1],\n    ]:\n        await page.locator(\"#aside\").hover()\n        await page.evaluate(\n            \"(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }\",\n            args,\n        )\n        await page.locator(\"#target\").click()\n        assert await page.evaluate(\"window.clicked\") == 1\n        await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n\n\nasync def test_should_work_with_locator_hover(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    await page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        '() => { window.setupAnnoyingInterstitial(\"pointerover\", 1, \"capture\"); }'\n    )\n    await page.locator(\"#target\").hover()\n    await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert (\n        await page.eval_on_selector(\n            \"#target\", \"e => window.getComputedStyle(e).backgroundColor\"\n        )\n        == \"rgb(255, 255, 0)\"\n    )\n\n\nasync def test_should_not_work_with_force_true(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    await page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n\n    await page.locator(\"#aside\").hover()\n    await page.evaluate('() => { window.setupAnnoyingInterstitial(\"none\", 1); }')\n    await page.locator(\"#target\").click(force=True, timeout=2000)\n    assert await page.locator(\"#interstitial\").is_visible()\n    assert await page.evaluate(\"window.clicked\") is None\n\n\nasync def test_should_throw_when_page_closes(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    await page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), lambda: page.close()\n    )\n\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"mouseover\", 1); }'\n    )\n    with pytest.raises(Error) as exc:\n        await page.locator(\"#target\").click()\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc.value.message\n\n\nasync def test_should_throw_when_handler_times_out(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    called = 0\n    stall_future: asyncio.Future[None] = asyncio.Future()\n\n    async def handler() -> None:\n        nonlocal called\n        called += 1\n        # Deliberately timeout.\n        await stall_future\n\n    await page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), handler\n    )\n\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"mouseover\", 1); }'\n    )\n    with pytest.raises(Error) as exc:\n        await page.locator(\"#target\").click(timeout=3000)\n    assert \"Timeout 3000ms exceeded\" in exc.value.message\n\n    with pytest.raises(Error) as exc:\n        await page.locator(\"#target\").click(timeout=3000)\n    assert \"Timeout 3000ms exceeded\" in exc.value.message\n\n    # Should not enter the same handler while it is still running.\n    assert called == 1\n    stall_future.cancel()\n\n\nasync def test_should_work_with_to_be_visible(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    called = 0\n\n    async def handler() -> None:\n        nonlocal called\n        called += 1\n        await page.locator(\"#close\").click()\n\n    await page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), handler\n    )\n\n    await page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"remove\", 1); }'\n    )\n    await expect(page.locator(\"#target\")).to_be_visible()\n    await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 1\n\n\nasync def test_should_work_when_owner_frame_detaches(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"\"\"\n    () => {\n        const iframe = document.createElement('iframe');\n        iframe.src = 'data:text/html,<body>hello from iframe</body>';\n        document.body.append(iframe);\n\n        const target = document.createElement('button');\n        target.textContent = 'Click me';\n        target.id = 'target';\n        target.addEventListener('click', () => window._clicked = true);\n        document.body.appendChild(target);\n\n        const closeButton = document.createElement('button');\n        closeButton.textContent = 'close';\n        closeButton.id = 'close';\n        closeButton.addEventListener('click', () => iframe.remove());\n        document.body.appendChild(closeButton);\n    }\n    \"\"\"\n    )\n    await page.add_locator_handler(\n        page.frame_locator(\"iframe\").locator(\"body\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n    await page.locator(\"#target\").click()\n    assert await page.query_selector(\"iframe\") is None\n    assert await page.evaluate(\"window._clicked\") is True\n\n\nasync def test_should_work_with_times_option(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler() -> None:\n        nonlocal called\n        called += 1\n\n    await page.add_locator_handler(\n        page.locator(\"body\"), _handler, no_wait_after=True, times=2\n    )\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('mouseover', 4);\n    }\n    \"\"\"\n    )\n    with pytest.raises(Error) as exc_info:\n        await page.locator(\"#target\").click(timeout=3000)\n    assert called == 2\n    assert await page.evaluate(\"window.clicked\") == 0\n    await expect(page.locator(\"#interstitial\")).to_be_visible()\n    assert \"Timeout 3000ms exceeded\" in exc_info.value.message\n    assert (\n        '<div>This interstitial covers the button</div> from <div class=\"visible\" id=\"interstitial\">…</div> subtree intercepts pointer events'\n        in exc_info.value.message\n    )\n\n\nasync def test_should_wait_for_hidden_by_default(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    async def _handler(button: Locator) -> None:\n        nonlocal called\n        called += 1\n        await button.click()\n\n    await page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('timeout', 1);\n    }\n    \"\"\"\n    )\n    await page.locator(\"#target\").click()\n    assert await page.evaluate(\"window.clicked\") == 1\n    await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 1\n\n\nasync def test_should_wait_for_hidden_by_default_2(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler() -> None:\n        nonlocal called\n        called += 1\n\n    await page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    with pytest.raises(Error) as exc_info:\n        await page.locator(\"#target\").click(timeout=3000)\n    assert await page.evaluate(\"window.clicked\") == 0\n    assert await page.locator(\"#interstitial\").is_visible()\n    assert called == 1\n    assert (\n        'locator handler has finished, waiting for get_by_role(\"button\", name=\"close\") to be hidden'\n        in exc_info.value.message\n    )\n\n\nasync def test_should_work_with_noWaitAfter(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    async def _handler(button: Locator) -> None:\n        nonlocal called\n        called += 1\n        if called == 1:\n            await button.click()\n        else:\n            await page.locator(\"#interstitial\").wait_for(state=\"hidden\")\n\n    await page.add_locator_handler(\n        page.get_by_role(\"button\", name=\"close\"), _handler, no_wait_after=True\n    )\n    await page.locator(\"#aside\").hover()\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('timeout', 1);\n    }\n    \"\"\"\n    )\n    await page.locator(\"#target\").click()\n    assert await page.evaluate(\"window.clicked\") == 1\n    await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 2\n\n\nasync def test_should_removeLocatorHandler(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    async def _handler(locator: Locator) -> None:\n        nonlocal called\n        called += 1\n        await locator.click()\n\n    await page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    await page.locator(\"#target\").click()\n    assert called == 1\n    assert await page.evaluate(\"window.clicked\") == 1\n    await expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    await page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    await page.remove_locator_handler(page.get_by_role(\"button\", name=\"close\"))\n    with pytest.raises(Error) as error:\n        await page.locator(\"#target\").click(timeout=3000)\n    assert called == 1\n    assert await page.evaluate(\"window.clicked\") == 0\n    await expect(page.locator(\"#interstitial\")).to_be_visible()\n    assert \"Timeout 3000ms exceeded\" in error.value.message\n"
  },
  {
    "path": "tests/async/test_page_aria_snapshot.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nimport pytest\n\nfrom playwright.async_api import Locator, Page, expect\n\n\ndef _unshift(snapshot: str) -> str:\n    lines = snapshot.split(\"\\n\")\n    whitespace_prefix_length = 100\n    for line in lines:\n        if not line.strip():\n            continue\n        match = re.match(r\"^(\\s*)\", line)\n        if match and len(match[1]) < whitespace_prefix_length:\n            whitespace_prefix_length = len(match[1])\n    return \"\\n\".join(\n        [line[whitespace_prefix_length:] for line in lines if line.strip()]\n    )\n\n\nasync def check_and_match_snapshot(locator: Locator, snapshot: str) -> None:\n    assert await locator.aria_snapshot() == _unshift(snapshot)\n    await expect(locator).to_match_aria_snapshot(snapshot, timeout=1000)\n\n\nasync def test_should_snapshot(page: Page) -> None:\n    await page.set_content(\"<h1>title</h1>\")\n    await check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - heading \"title\" [level=1]\n    \"\"\",\n    )\n\n\nasync def test_should_snapshot_list(page: Page) -> None:\n    await page.set_content(\"<h1>title</h1><h1>title 2</h1>\")\n    await check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - heading \"title\" [level=1]\n      - heading \"title 2\" [level=1]\n    \"\"\",\n    )\n\n\nasync def test_should_snapshot_list_with_list(page: Page) -> None:\n    await page.set_content(\"<ul><li>one</li><li>two</li></ul>\")\n    await check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list:\n        - listitem: one\n        - listitem: two\n    \"\"\",\n    )\n\n\nasync def test_should_snapshot_list_with_accessible_name(page: Page) -> None:\n    await page.set_content('<ul aria-label=\"my list\"><li>one</li><li>two</li></ul>')\n    await check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list \"my list\":\n        - listitem: one\n        - listitem: two\n    \"\"\",\n    )\n\n\nasync def test_should_snapshot_complex(page: Page) -> None:\n    await page.set_content('<ul><li><a href=\"about:blank\">link</a></li></ul>')\n    await check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list:\n        - listitem:\n          - link \"link\":\n            - /url: about:blank\n    \"\"\",\n    )\n\n\nasync def test_should_snapshot_with_unexpected_children_equal(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <ul>\n        <li>One</li>\n        <li>Two</li>\n        <li>Three</li>\n      </ul>\n    \"\"\"\n    )\n    await expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n      - list:\n        - listitem: One\n        - listitem: Three\n    \"\"\",\n    )\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n        - list:\n          - /children: equal\n          - listitem: One\n          - listitem: Three\n      \"\"\",\n            timeout=1000,\n        )\n\n\nasync def test_should_snapshot_with_unexpected_children_deep_equal(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n      <ul>\n        <li>\n          <ul>\n            <li>1.1</li>\n            <li>1.2</li>\n          </ul>\n        </li>\n      </ul>\n    \"\"\"\n    )\n    await expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n      - list:\n        - listitem:\n          - list:\n            - listitem: 1.1\n    \"\"\",\n    )\n    await expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - list:\n          - /children: equal\n          - listitem:\n            - list:\n              - listitem: 1.1\n      \"\"\",\n    )\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n          - list:\n            - /children: deep-equal\n            - listitem:\n              - list:\n                - listitem: 1.1\n        \"\"\",\n            timeout=1000,\n        )\n\n\nasync def test_should_snapshot_with_restored_contain_mode_inside_deep_equal(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n      <ul>\n        <li>\n          <ul>\n            <li>1.1</li>\n            <li>1.2</li>\n          </ul>\n        </li>\n      </ul>\n    \"\"\"\n    )\n    with pytest.raises(AssertionError):\n        await expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n        - list:\n          - /children: deep-equal\n          - listitem:\n            - list:\n              - listitem: 1.1\n      \"\"\",\n            timeout=1000,\n        )\n    await expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - list:\n          - /children: deep-equal\n          - listitem:\n            - list:\n              - /children: contain\n              - listitem: 1.1\n      \"\"\",\n    )\n\n\nasync def test_match_values_both_against_regex_and_string(page: Page) -> None:\n    await page.set_content('<a href=\"/auth?r=/\">Log in</a>')\n    await expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - link \"Log in\":\n          - /url: /auth?r=/\n      \"\"\",\n    )\n"
  },
  {
    "path": "tests/async/test_page_base_url.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import Dict\n\nfrom playwright.async_api import Browser, BrowserType\nfrom tests.server import Server\nfrom tests.utils import must\n\n\nasync def test_should_construct_a_new_url_when_a_base_url_in_browser_new_context_is_passed(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(base_url=server.PREFIX)\n    page = await context.new_page()\n    assert (must(await page.goto(\"/empty.html\"))).url == server.EMPTY_PAGE\n    await context.close()\n\n\nasync def test_should_construct_a_new_url_when_a_base_url_in_browser_new_page_is_passed(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=server.PREFIX)\n    assert (must(await page.goto(\"/empty.html\"))).url == server.EMPTY_PAGE\n    await page.close()\n\n\nasync def test_should_construct_a_new_url_when_a_base_url_in_browser_new_persistent_context_is_passed(\n    browser_type: BrowserType, tmp_path: Path, server: Server, launch_arguments: Dict\n) -> None:\n    context = await browser_type.launch_persistent_context(\n        tmp_path, **launch_arguments, base_url=server.PREFIX\n    )\n    page = await context.new_page()\n    assert (must(await page.goto(\"/empty.html\"))).url == server.EMPTY_PAGE\n    await context.close()\n\n\nasync def test_should_construct_correctly_when_a_baseurl_without_a_trailing_slash_is_passed(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=server.PREFIX + \"/url-construction\")\n    assert (must(await page.goto(\"mypage.html\"))).url == server.PREFIX + \"/mypage.html\"\n    assert (\n        must(await page.goto(\"./mypage.html\"))\n    ).url == server.PREFIX + \"/mypage.html\"\n    assert (must(await page.goto(\"/mypage.html\"))).url == server.PREFIX + \"/mypage.html\"\n    await page.close()\n\n\nasync def test_should_construct_correctly_when_a_baseurl_with_a_trailing_slash_is_passed(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=server.PREFIX + \"/url-construction/\")\n    assert (\n        must(await page.goto(\"mypage.html\"))\n    ).url == server.PREFIX + \"/url-construction/mypage.html\"\n    assert (\n        must(await page.goto(\"./mypage.html\"))\n    ).url == server.PREFIX + \"/url-construction/mypage.html\"\n    assert (must(await page.goto(\"/mypage.html\"))).url == server.PREFIX + \"/mypage.html\"\n    assert (must(await page.goto(\".\"))).url == server.PREFIX + \"/url-construction/\"\n    assert (must(await page.goto(\"/\"))).url == server.PREFIX + \"/\"\n    await page.close()\n\n\nasync def test_should_not_construct_a_new_url_when_valid_urls_are_passed(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=\"http://microsoft.com\")\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.url == server.EMPTY_PAGE\n\n    await page.goto(\"data:text/html,Hello world\")\n    assert page.url == \"data:text/html,Hello world\"\n\n    await page.goto(\"about:blank\")\n    assert page.url == \"about:blank\"\n\n    await page.close()\n\n\nasync def test_should_be_able_to_match_a_url_relative_to_its_given_url_with_urlmatcher(\n    browser: Browser, server: Server\n) -> None:\n    page = await browser.new_page(base_url=server.PREFIX + \"/foobar/\")\n\n    await page.goto(\"/kek/index.html\")\n    await page.wait_for_url(\"/kek/index.html\")\n    assert page.url == server.PREFIX + \"/kek/index.html\"\n\n    await page.route(\n        \"./kek/index.html\", lambda route: route.fulfill(body=\"base-url-matched-route\")\n    )\n\n    async with page.expect_request(\"./kek/index.html\") as request_info:\n        async with page.expect_response(\"./kek/index.html\") as response_info:\n            await page.goto(\"./kek/index.html\")\n    request = await request_info.value\n    response = await response_info.value\n    assert request.url == server.PREFIX + \"/foobar/kek/index.html\"\n    assert response.url == server.PREFIX + \"/foobar/kek/index.html\"\n    assert await response.body() == b\"base-url-matched-route\"\n\n    await page.close()\n"
  },
  {
    "path": "tests/async/test_page_clock.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport datetime\nfrom typing import Any, AsyncGenerator, List\n\nimport pytest\n\nfrom playwright.async_api import Error, Page\nfrom tests.server import Server\n\n\n@pytest.fixture(autouse=True)\nasync def calls(page: Page) -> List[Any]:\n    calls: List[Any] = []\n    await page.expose_function(\"stub\", lambda *args: calls.append(list(args)))\n    return calls\n\n\nclass TestRunFor:\n    @pytest.fixture(autouse=True)\n    async def before_each(self, page: Page) -> AsyncGenerator[None, None]:\n        await page.clock.install(time=0)\n        await page.clock.pause_at(1000)\n        yield\n\n    async def test_run_for_triggers_immediately_without_specified_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(window.stub)\")\n        await page.clock.run_for(0)\n        assert len(calls) == 1\n\n    async def test_run_for_does_not_trigger_without_sufficient_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(window.stub, 100)\")\n        await page.clock.run_for(10)\n        assert len(calls) == 0\n\n    async def test_run_for_triggers_after_sufficient_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(window.stub, 100)\")\n        await page.clock.run_for(100)\n        assert len(calls) == 1\n\n    async def test_run_for_triggers_simultaneous_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"setTimeout(window.stub, 100); setTimeout(window.stub, 100)\"\n        )\n        await page.clock.run_for(100)\n        assert len(calls) == 2\n\n    async def test_run_for_triggers_multiple_simultaneous_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"setTimeout(window.stub, 100); setTimeout(window.stub, 100); setTimeout(window.stub, 99); setTimeout(window.stub, 100)\"\n        )\n        await page.clock.run_for(100)\n        assert len(calls) == 4\n\n    async def test_run_for_waits_after_setTimeout_was_called(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(window.stub, 150)\")\n        await page.clock.run_for(50)\n        assert len(calls) == 0\n        await page.clock.run_for(100)\n        assert len(calls) == 1\n\n    async def test_run_for_triggers_event_when_some_throw(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"setTimeout(() => { throw new Error(); }, 100); setTimeout(window.stub, 120)\"\n        )\n        with pytest.raises(Error):\n            await page.clock.run_for(120)\n        assert len(calls) == 1\n\n    async def test_run_for_creates_updated_Date_while_ticking(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.clock.set_system_time(0)\n        await page.evaluate(\n            \"setInterval(() => { window.stub(new Date().getTime()); }, 10)\"\n        )\n        await page.clock.run_for(100)\n        assert calls == [\n            [10],\n            [20],\n            [30],\n            [40],\n            [50],\n            [60],\n            [70],\n            [80],\n            [90],\n            [100],\n        ]\n\n    async def test_run_for_passes_8_seconds(self, page: Page, calls: List[Any]) -> None:\n        await page.evaluate(\"setInterval(window.stub, 4000)\")\n        await page.clock.run_for(\"08\")\n        assert len(calls) == 2\n\n    async def test_run_for_passes_1_minute(self, page: Page, calls: List[Any]) -> None:\n        await page.evaluate(\"setInterval(window.stub, 6000)\")\n        await page.clock.run_for(\"01:00\")\n        assert len(calls) == 10\n\n    async def test_run_for_passes_2_hours_34_minutes_and_10_seconds(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setInterval(window.stub, 10000)\")\n        await page.clock.run_for(\"02:34:10\")\n        assert len(calls) == 925\n\n    async def test_run_for_throws_for_invalid_format(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setInterval(window.stub, 10000)\")\n        with pytest.raises(Error):\n            await page.clock.run_for(\"12:02:34:10\")\n        assert len(calls) == 0\n\n    async def test_run_for_returns_the_current_now_value(self, page: Page) -> None:\n        await page.clock.set_system_time(0)\n        value = 200\n        await page.clock.run_for(value)\n        assert await page.evaluate(\"Date.now()\") == value\n\n\nclass TestFastForward:\n    @pytest.fixture(autouse=True)\n    async def before_each(self, page: Page) -> AsyncGenerator[None, None]:\n        await page.clock.install(time=0)\n        await page.clock.pause_at(1)\n        yield\n\n    async def test_ignores_timers_which_wouldnt_be_run(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"setTimeout(() => { window.stub('should not be logged'); }, 1000)\"\n        )\n        await page.clock.fast_forward(500)\n        assert len(calls) == 0\n\n    async def test_pushes_back_execution_time_for_skipped_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(() => { window.stub(Date.now()); }, 1000)\")\n        await page.clock.fast_forward(2000)\n        assert calls == [[1000 + 2000]]\n\n    async def test_supports_string_time_arguments(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"setTimeout(() => { window.stub(Date.now()); }, 100000)\"\n        )  # 100000 = 1:40\n        await page.clock.fast_forward(\"01:50\")\n        assert calls == [[1000 + 110000]]\n\n\nclass TestStubTimers:\n    @pytest.fixture(autouse=True)\n    async def before_each(self, page: Page) -> AsyncGenerator[None, None]:\n        await page.clock.install(time=0)\n        await page.clock.pause_at(1)\n        yield\n\n    async def test_sets_initial_timestamp(self, page: Page) -> None:\n        await page.clock.set_system_time(1.4)\n        assert await page.evaluate(\"Date.now()\") == 1400\n\n    async def test_replaces_global_setTimeout(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setTimeout(window.stub, 1000)\")\n        await page.clock.run_for(1000)\n        assert len(calls) == 1\n\n    async def test_global_fake_setTimeout_should_return_id(self, page: Page) -> None:\n        to = await page.evaluate(\"setTimeout(window.stub, 1000)\")\n        assert isinstance(to, int)\n\n    async def test_replaces_global_clearTimeout(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"\"\"\n            const to = setTimeout(window.stub, 1000);\n            clearTimeout(to);\n        \"\"\"\n        )\n        await page.clock.run_for(1000)\n        assert len(calls) == 0\n\n    async def test_replaces_global_setInterval(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\"setInterval(window.stub, 500)\")\n        await page.clock.run_for(1000)\n        assert len(calls) == 2\n\n    async def test_replaces_global_clearInterval(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.evaluate(\n            \"\"\"\n            const to = setInterval(window.stub, 500);\n            clearInterval(to);\n        \"\"\"\n        )\n        await page.clock.run_for(1000)\n        assert len(calls) == 0\n\n    async def test_replaces_global_performance_now(self, page: Page) -> None:\n        promise = asyncio.create_task(\n            page.evaluate(\n                \"\"\"async () => {\n            const prev = performance.now();\n            await new Promise(f => setTimeout(f, 1000));\n            const next = performance.now();\n            return { prev, next };\n        }\"\"\"\n            )\n        )\n        await asyncio.sleep(0)  # Make sure the promise is scheduled.\n        await page.clock.run_for(1000)\n        assert await promise == {\"prev\": 1000, \"next\": 2000}\n\n    async def test_fakes_Date_constructor(self, page: Page) -> None:\n        now = await page.evaluate(\"new Date().getTime()\")\n        assert now == 1000\n\n\nclass TestStubTimersPerformance:\n    async def test_replaces_global_performance_time_origin(self, page: Page) -> None:\n        await page.clock.install(time=1)\n        await page.clock.pause_at(2)\n        promise = asyncio.create_task(\n            page.evaluate(\n                \"\"\"async () => {\n            const prev = performance.now();\n            await new Promise(f => setTimeout(f, 1000));\n            const next = performance.now();\n            return { prev, next };\n        }\"\"\"\n            )\n        )\n        await asyncio.sleep(0)  # Make sure the promise is scheduled.\n        await page.clock.run_for(1000)\n        assert await page.evaluate(\"performance.timeOrigin\") == 1000\n        assert await promise == {\"prev\": 1000, \"next\": 2000}\n\n\nclass TestPopup:\n    async def test_should_tick_after_popup(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        now = datetime.datetime.fromisoformat(\"2015-09-25\")\n        await page.clock.pause_at(now)\n        popup, _ = await asyncio.gather(\n            page.wait_for_event(\"popup\"), page.evaluate(\"window.open('about:blank')\")\n        )\n        popup_time = await popup.evaluate(\"Date.now()\")\n        assert popup_time == now.timestamp() * 1000\n        await page.clock.run_for(1000)\n        popup_time_after = await popup.evaluate(\"Date.now()\")\n        assert popup_time_after == now.timestamp() * 1000 + 1000\n\n    async def test_should_tick_before_popup(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        now = datetime.datetime.fromisoformat(\"2015-09-25\")\n        await page.clock.pause_at(now)\n        await page.clock.run_for(1000)\n        popup, _ = await asyncio.gather(\n            page.wait_for_event(\"popup\"), page.evaluate(\"window.open('about:blank')\")\n        )\n        popup_time = await popup.evaluate(\"Date.now()\")\n        assert popup_time == int(now.timestamp() * 1000 + 1000)\n        assert datetime.datetime.fromtimestamp(popup_time / 1_000).year == 2015\n\n    async def test_should_run_time_before_popup(\n        self, page: Page, server: Server\n    ) -> None:\n        server.set_route(\n            \"/popup.html\",\n            lambda res: (\n                res.setHeader(\"Content-Type\", \"text/html\"),\n                res.write(b\"<script>window.time = Date.now()</script>\"),\n                res.finish(),\n            ),\n        )\n        await page.goto(server.EMPTY_PAGE)\n        # Wait for 2 second in real life to check that it is past in popup.\n        await page.wait_for_timeout(2000)\n        popup, _ = await asyncio.gather(\n            page.wait_for_event(\"popup\"),\n            page.evaluate(\"window.open('{}')\".format(server.PREFIX + \"/popup.html\")),\n        )\n        popup_time = await popup.evaluate(\"window.time\")\n        assert popup_time >= 2000\n\n    async def test_should_not_run_time_before_popup_on_pause(\n        self, page: Page, server: Server\n    ) -> None:\n        server.set_route(\n            \"/popup.html\",\n            lambda res: (\n                res.setHeader(\"Content-Type\", \"text/html\"),\n                res.write(b\"<script>window.time = Date.now()</script>\"),\n                res.finish(),\n            ),\n        )\n        await page.clock.install(time=0)\n        await page.clock.pause_at(1)\n        await page.goto(server.EMPTY_PAGE)\n        # Wait for 2 second in real life to check that it is past in popup.\n        await page.wait_for_timeout(2000)\n        popup, _ = await asyncio.gather(\n            page.wait_for_event(\"popup\"),\n            page.evaluate(\"window.open('{}')\".format(server.PREFIX + \"/popup.html\")),\n        )\n        popup_time = await popup.evaluate(\"window.time\")\n        assert popup_time == 1000\n\n\nclass TestSetFixedTime:\n    async def test_does_not_fake_methods(self, page: Page) -> None:\n        await page.clock.set_fixed_time(0)\n        # Should not stall.\n        await page.evaluate(\"new Promise(f => setTimeout(f, 1))\")\n\n    async def test_allows_setting_time_multiple_times(self, page: Page) -> None:\n        await page.clock.set_fixed_time(0.1)\n        assert await page.evaluate(\"Date.now()\") == 100\n        await page.clock.set_fixed_time(0.2)\n        assert await page.evaluate(\"Date.now()\") == 200\n\n    async def test_fixed_time_is_not_affected_by_clock_manipulation(\n        self, page: Page\n    ) -> None:\n        await page.clock.set_fixed_time(0.1)\n        assert await page.evaluate(\"Date.now()\") == 100\n        await page.clock.fast_forward(20)\n        assert await page.evaluate(\"Date.now()\") == 100\n\n    async def test_allows_installing_fake_timers_after_setting_time(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.clock.set_fixed_time(0.1)\n        assert await page.evaluate(\"Date.now()\") == 100\n        await page.clock.set_fixed_time(0.2)\n        await page.evaluate(\"setTimeout(() => window.stub(Date.now()))\")\n        await page.clock.run_for(0)\n        assert calls == [[200]]\n\n\nclass TestWhileRunning:\n    async def test_should_progress_time(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.wait_for_timeout(1000)\n        now = await page.evaluate(\"Date.now()\")\n        assert 1000 <= now <= 2000\n\n    async def test_should_run_for(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.run_for(10000)\n        now = await page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    async def test_should_fast_forward(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.fast_forward(10000)\n        now = await page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    async def test_should_fast_forward_to(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.fast_forward(10000)\n        now = await page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    async def test_should_pause(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1)\n        await page.wait_for_timeout(1000)\n        now = await page.evaluate(\"Date.now()\")\n        assert 0 <= now <= 1000\n\n    async def test_should_pause_and_fast_forward(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1)\n        await page.clock.fast_forward(1000)\n        now = await page.evaluate(\"Date.now()\")\n        assert now == 2000\n\n    async def test_should_set_system_time_on_pause(self, page: Page) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1)\n        now = await page.evaluate(\"Date.now()\")\n        assert now == 1000\n\n\nclass TestWhileOnPause:\n    async def test_fast_forward_should_not_run_nested_immediate(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1000)\n        await page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                setTimeout(() => window.stub('inner'), 0);\n            }, 1000);\n        \"\"\"\n        )\n        await page.clock.fast_forward(1000)\n        assert calls == [[\"outer\"]]\n        await page.clock.fast_forward(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n\n    async def test_run_for_should_not_run_nested_immediate(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1000)\n        await page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                setTimeout(() => window.stub('inner'), 0);\n            }, 1000);\n        \"\"\"\n        )\n        await page.clock.run_for(1000)\n        assert calls == [[\"outer\"]]\n        await page.clock.run_for(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n\n    async def test_run_for_should_not_run_nested_immediate_from_microtask(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        await page.clock.install(time=0)\n        await page.goto(\"data:text/html,\")\n        await page.clock.pause_at(1000)\n        await page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                void Promise.resolve().then(() => setTimeout(() => window.stub('inner'), 0));\n            }, 1000);\n        \"\"\"\n        )\n        await page.clock.run_for(1000)\n        assert calls == [[\"outer\"]]\n        await page.clock.run_for(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n"
  },
  {
    "path": "tests/async/test_page_evaluate.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport math\nfrom datetime import datetime, timedelta, timezone\nfrom typing import Optional\nfrom urllib.parse import ParseResult, urlparse\n\nfrom playwright.async_api import Error, Page\n\n\nasync def test_evaluate_work(page: Page) -> None:\n    result = await page.evaluate(\"7 * 3\")\n    assert result == 21\n\n\nasync def test_evaluate_return_none_for_null(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", None)\n    assert result is None\n\n\nasync def test_evaluate_transfer_nan(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", float(\"nan\"))\n    assert math.isnan(result)\n\n\nasync def test_evaluate_transfer_neg_zero(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", -0)\n    assert result == float(\"-0\")\n\n\nasync def test_evaluate_transfer_infinity(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", float(\"Infinity\"))\n    assert result == float(\"Infinity\")\n\n\nasync def test_evaluate_transfer_neg_infinity(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", float(\"-Infinity\"))\n    assert result == float(\"-Infinity\")\n\n\nasync def test_evaluate_roundtrip_unserializable_values(page: Page) -> None:\n    value = {\n        \"infinity\": float(\"Infinity\"),\n        \"nInfinity\": float(\"-Infinity\"),\n        \"nZero\": float(\"-0\"),\n    }\n    result = await page.evaluate(\"a => a\", value)\n    assert result == value\n\n\nasync def test_evaluate_transfer_arrays(page: Page) -> None:\n    result = await page.evaluate(\"a => a\", [1, 2, 3])\n    assert result == [1, 2, 3]\n\n\nasync def test_evaluate_transfer_typed_arrays(page: Page) -> None:\n    async def test_typed_array(\n        typed_array: str, expected: list[float], value_suffix: Optional[str]\n    ) -> None:\n        value_suffix = \"\" if value_suffix is None else value_suffix\n        result = await page.evaluate(\n            f\"() => new {typed_array}([1{value_suffix}, 2{value_suffix}, 3{value_suffix}])\"\n        )\n        assert result == expected\n\n    await test_typed_array(\"Int8Array\", [1, 2, 3], None)\n    await test_typed_array(\"Uint8Array\", [1, 2, 3], None)\n    await test_typed_array(\"Uint8ClampedArray\", [1, 2, 3], None)\n    await test_typed_array(\"Int16Array\", [1, 2, 3], None)\n    await test_typed_array(\"Uint16Array\", [1, 2, 3], None)\n    await test_typed_array(\"Int32Array\", [1, 2, 3], None)\n    await test_typed_array(\"Uint32Array\", [1, 2, 3], None)\n    await test_typed_array(\"Float32Array\", [1.5, 2.5, 3.5], \".5\")\n    await test_typed_array(\"Float64Array\", [1.5, 2.5, 3.5], \".5\")\n    await test_typed_array(\"BigInt64Array\", [1, 2, 3], \"n\")\n    await test_typed_array(\"BigUint64Array\", [1, 2, 3], \"n\")\n\n\nasync def test_evaluate_transfer_bigint(page: Page) -> None:\n    assert await page.evaluate(\"() => 42n\") == 42\n    assert await page.evaluate(\"a => a\", 17) == 17\n\n\nasync def test_evaluate_return_undefined_for_objects_with_symbols(page: Page) -> None:\n    assert await page.evaluate('[Symbol(\"foo4\")]') == [None]\n    assert (\n        await page.evaluate(\n            \"\"\"() => {\n                const a = { };\n                a[Symbol('foo4')] = 42;\n                return a;\n            }\"\"\"\n        )\n        == {}\n    )\n    assert (\n        await page.evaluate(\n            \"\"\"() => {\n                return { foo: [{ a: Symbol('foo4') }] };\n            }\"\"\"\n        )\n        == {\"foo\": [{\"a\": None}]}\n    )\n\n\nasync def test_evaluate_work_with_unicode_chars(page: Page) -> None:\n    result = await page.evaluate('a => a[\"中文字符\"]', {\"中文字符\": 42})\n    assert result == 42\n\n\nasync def test_evaluate_throw_when_evaluation_triggers_reload(page: Page) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.evaluate(\n            \"() => { location.reload(); return new Promise(() => {}); }\"\n        )\n    except Error as e:\n        error = e\n    assert error\n    assert \"navigation\" in error.message\n\n\nasync def test_evaluate_work_with_exposed_function(page: Page) -> None:\n    await page.expose_function(\"callController\", lambda a, b: a * b)\n    result = await page.evaluate(\"callController(9, 3)\")\n    assert result == 27\n\n\nasync def test_evaluate_reject_promise_with_exception(page: Page) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.evaluate(\"not_existing_object.property\")\n    except Error as e:\n        error = e\n    assert error\n    assert \"not_existing_object\" in error.message\n\n\nasync def test_evaluate_support_thrown_strings(page: Page) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.evaluate('throw \"qwerty\"')\n    except Error as e:\n        error = e\n    assert error\n    assert \"qwerty\" in error.message\n\n\nasync def test_evaluate_support_thrown_numbers(page: Page) -> None:\n    error: Optional[Error] = None\n    try:\n        await page.evaluate(\"throw 100500\")\n    except Error as e:\n        error = e\n    assert error\n    assert \"100500\" in error.message\n\n\nasync def test_evaluate_return_complex_objects(page: Page) -> None:\n    obj = {\"foo\": \"bar!\"}\n    result = await page.evaluate(\"a => a\", obj)\n    assert result == obj\n\n\nasync def test_evaluate_accept_none_as_one_of_multiple_parameters(page: Page) -> None:\n    result = await page.evaluate(\n        '({ a, b }) => Object.is(a, null) && Object.is(b, \"foo\")',\n        {\"a\": None, \"b\": \"foo\"},\n    )\n    assert result\n\n\nasync def test_evaluate_properly_serialize_none_arguments(page: Page) -> None:\n    assert await page.evaluate(\"x => ({a: x})\", None) == {\"a\": None}\n\n\nasync def test_should_alias_window_document_and_node(page: Page) -> None:\n    object = await page.evaluate(\"[window, document, document.body]\")\n    assert object == [\"ref: <Window>\", \"ref: <Document>\", \"ref: <Node>\"]\n\n\nasync def test_evaluate_should_work_for_circular_object(page: Page) -> None:\n    a = await page.evaluate(\n        \"\"\"() => {\n                const a = {x: 47};\n                const b = {a};\n                a.b = b;\n                return a;\n            }\"\"\"\n    )\n\n    assert a[\"b\"][\"a\"][\"b\"][\"a\"][\"x\"] == 47\n    assert a[\"b\"][\"a\"] == a\n\n\nasync def test_evaluate_accept_string(page: Page) -> None:\n    assert await page.evaluate(\"1 + 2\") == 3\n\n\nasync def test_evaluate_accept_element_handle_as_an_argument(page: Page) -> None:\n    await page.set_content(\"<section>42</section>\")\n    element = await page.query_selector(\"section\")\n    text = await page.evaluate(\"e => e.textContent\", element)\n    assert text == \"42\"\n\n\nasync def test_evaluate_throw_if_underlying_element_was_disposed(page: Page) -> None:\n    await page.set_content(\"<section>39</section>\")\n    element = await page.query_selector(\"section\")\n    assert element\n    await element.dispose()\n    error: Optional[Error] = None\n    try:\n        await page.evaluate(\"e => e.textContent\", element)\n    except Error as e:\n        error = e\n    assert error\n    assert \"no object with guid\" in error.message\n\n\nasync def test_evaluate_evaluate_exception(page: Page) -> None:\n    error = await page.evaluate(\n        \"\"\"() => {\n        function innerFunction() {\n        const e = new Error('error message');\n        e.name = 'foobar';\n        return e;\n        }\n        return innerFunction();\n    }\"\"\"\n    )\n    assert isinstance(error, Error)\n    assert error.message == \"error message\"\n    assert error.name == \"foobar\"\n    assert error.stack\n    assert \"innerFunction\" in error.stack\n\n\nasync def test_should_pass_exception_argument(page: Page) -> None:\n    def _raise_and_get_exception(exception: Exception) -> Exception:\n        try:\n            raise exception\n        except Exception as e:\n            return e\n\n    error_for_roundtrip = Error(\"error message\")\n    error_for_roundtrip._name = \"foobar\"\n    error_for_roundtrip._stack = \"test stack\"\n    error = await page.evaluate(\n        \"\"\"e => {\n            return { message: e.message, name: e.name, stack: e.stack };\n        }\"\"\",\n        error_for_roundtrip,\n    )\n    assert error[\"message\"] == \"error message\"\n    assert error[\"name\"] == \"foobar\"\n    assert \"test stack\" in error[\"stack\"]\n\n    error = await page.evaluate(\n        \"\"\"e => {\n            return { message: e.message, name: e.name, stack: e.stack };\n        }\"\"\",\n        _raise_and_get_exception(Exception(\"error message\")),\n    )\n    assert error[\"message\"] == \"error message\"\n    assert error[\"name\"] == \"Exception\"\n    assert \"error message\" in error[\"stack\"]\n\n\nasync def test_evaluate_evaluate_date(page: Page) -> None:\n    result = await page.evaluate(\n        '() => ({ date: new Date(\"2020-05-27T01:31:38.506Z\") })'\n    )\n    assert result == {\n        \"date\": datetime.fromisoformat(\"2020-05-27T01:31:38.506\").replace(\n            tzinfo=timezone.utc\n        )\n    }\n\n\nasync def test_evaluate_roundtrip_date_without_tzinfo(page: Page) -> None:\n    date = datetime.fromisoformat(\"2020-05-27T01:31:38.506\")\n    result = await page.evaluate(\"date => date\", date)\n    assert result.timestamp() == date.timestamp()\n\n\nasync def test_evaluate_roundtrip_date(page: Page) -> None:\n    date = datetime.fromisoformat(\"2020-05-27T01:31:38.506\").replace(\n        tzinfo=timezone.utc\n    )\n    result = await page.evaluate(\"date => date\", date)\n    assert result == date\n\n\nasync def test_evaluate_roundtrip_date_with_tzinfo(page: Page) -> None:\n    date = datetime.fromisoformat(\"2020-05-27T01:31:38.506\")\n    date = date.astimezone(timezone(timedelta(hours=4)))\n    result = await page.evaluate(\"date => date\", date)\n    assert result == date\n\n\nasync def test_evaluate_jsonvalue_date(page: Page) -> None:\n    date = datetime.fromisoformat(\"2020-05-27T01:31:38.506\").replace(\n        tzinfo=timezone.utc\n    )\n    result = await page.evaluate(\n        '() => ({ date: new Date(\"2020-05-27T01:31:38.506Z\") })'\n    )\n    assert result == {\"date\": date}\n\n\nasync def test_should_evaluate_url(page: Page) -> None:\n    out = await page.evaluate(\n        \"() => ({ someKey: new URL('https://user:pass@example.com/?foo=bar#hi') })\"\n    )\n    assert out[\"someKey\"] == ParseResult(\n        scheme=\"https\",\n        netloc=\"user:pass@example.com\",\n        path=\"/\",\n        query=\"foo=bar\",\n        params=\"\",\n        fragment=\"hi\",\n    )\n\n\nasync def test_should_roundtrip_url(page: Page) -> None:\n    in_ = urlparse(\"https://user:pass@example.com/?foo=bar#hi\")\n    out = await page.evaluate(\"url => url\", in_)\n    assert in_ == out\n\n\nasync def test_should_roundtrip_complex_url(page: Page) -> None:\n    in_ = urlparse(\n        \"https://user:password@www.contoso.com:80/Home/Index.htm?q1=v1&q2=v2#FragmentName\"\n    )\n    out = await page.evaluate(\"url => url\", in_)\n    assert in_ == out\n\n\nasync def test_evaluate_jsonvalue_url(page: Page) -> None:\n    url = urlparse(\"https://example.com/\")\n    result = await page.evaluate('() => ({ someKey: new URL(\"https://example.com/\") })')\n    assert result == {\"someKey\": url}\n"
  },
  {
    "path": "tests/async/test_page_event_console.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright._impl._page import Page\n\n\nasync def test_console_messages_should_work(page: Page) -> None:\n    await page.evaluate(\n        \"\"\"() => {\n            for (let i = 0; i < 301; i++)\n                console.log('message' + i);\n        }\"\"\"\n    )\n\n    messages = await page.console_messages()\n    objects = [{\"text\": m.text, \"type\": m.type, \"page\": m.page} for m in messages]\n\n    expected = []\n    for i in range(201, 301):\n        expected.append({\"text\": f\"message{i}\", \"type\": \"log\", \"page\": page})\n\n    assert len(objects) >= 100, \"should be at least 100 messages\"\n    message_count = len(messages) - len(expected)\n    assert objects[message_count:] == expected, \"should return last messages\"\n"
  },
  {
    "path": "tests/async/test_page_event_pageerror.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page\n\n\nasync def test_page_errors_should_work(page: Page) -> None:\n    await page.evaluate(\n        \"\"\"async () => {\n            for (let i = 0; i < 301; i++)\n                window.setTimeout(() => { throw new Error('error' + i); }, 0);\n            await new Promise(f => window.setTimeout(f, 100));\n        }\"\"\"\n    )\n\n    errors = await page.page_errors()\n    messages = [e.message for e in errors]\n\n    expected = []\n    for i in range(201, 301):\n        expected.append(f\"error{i}\")\n\n    assert len(messages) >= 100, \"should be at least 100 errors\"\n    message_count = len(messages) - len(expected)\n    assert messages[message_count:] == expected, \"should return last errors\"\n"
  },
  {
    "path": "tests/async/test_page_event_request.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\n\nfrom playwright._impl._network import Request, Route\nfrom playwright._impl._page import Page\nfrom tests.server import Server\n\n\nasync def test_should_return_last_requests(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/title.html\")\n    for i in range(200):\n\n        def _handle_route(route: Route) -> None:\n            asyncio.ensure_future(\n                route.fulfill(\n                    status=200,\n                    body=f\"url:{route.request.url}\",\n                )\n            )\n\n        await page.route(f\"**/fetch?{i}\", _handle_route)\n\n    # #0 is the navigation request, so start with #1.\n    for i in range(1, 100):\n        await page.evaluate(\"url => fetch(url)\", server.PREFIX + f\"/fetch?{i}\")\n    first_100_requests_with_goto = await page.requests()\n    first_100_requests = first_100_requests_with_goto[1:]\n\n    for i in range(100, 200):\n        await page.evaluate(\"url => fetch(url)\", server.PREFIX + f\"/fetch?{i}\")\n    last_100_requests = await page.requests()\n\n    all_requests = first_100_requests + last_100_requests\n\n    async def gather_response(request: Request) -> dict:\n        response = await request.response()\n        assert response\n        return {\"text\": await response.text(), \"url\": request.url}\n\n    # All 199 requests are fully functional.\n    received = await asyncio.gather(\n        *[gather_response(request) for request in all_requests]\n    )\n\n    expected = []\n    for i in range(1, 200):\n        url = server.PREFIX + f\"/fetch?{i}\"\n        expected.append({\"url\": url, \"text\": f\"url:{url}\"})\n    assert received == expected\n"
  },
  {
    "path": "tests/async/test_page_network_request.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, Request\nfrom tests.server import Server\n\n\nasync def test_should_not_allow_to_access_frame_on_popup_main_request(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(f'<a target=_blank href=\"{server.EMPTY_PAGE}\">click me</a>')\n    request_promise = asyncio.ensure_future(page.context.wait_for_event(\"request\"))\n    popup_promise = asyncio.ensure_future(page.context.wait_for_event(\"page\"))\n    clicked = asyncio.ensure_future(page.get_by_text(\"click me\").click())\n    request: Request = await request_promise\n\n    assert request.is_navigation_request()\n\n    with pytest.raises(Error) as exc_info:\n        request.frame\n    assert (\n        \"Frame for this navigation request is not available\" in exc_info.value.message\n    )\n\n    response = await request.response()\n    assert response\n    await response.finished()\n    await popup_promise\n    await clicked\n\n\nasync def test_should_parse_the_data_if_content_type_is_application_x_www_form_urlencoded_charset_UTF_8(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_event(\"request\") as request_info:\n        await page.evaluate(\n            \"\"\"() => fetch('./post', {\n            method: 'POST',\n            headers: {\n            'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',\n            },\n            body: 'foo=bar&baz=123'\n        })\"\"\"\n        )\n    request = await request_info.value\n    assert request\n    assert request.post_data_json == {\"foo\": \"bar\", \"baz\": \"123\"}\n"
  },
  {
    "path": "tests/async/test_page_network_response.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\n\nimport pytest\n\nfrom playwright.async_api import Error, Page\nfrom tests.server import Server, TestServerRequest\n\n\nasync def test_should_reject_response_finished_if_page_closes(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    def handle_get(request: TestServerRequest) -> None:\n        # In Firefox, |fetch| will be hanging until it receives |Content-Type| header\n        # from server.\n        request.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\")\n        request.write(b\"hello \")\n\n    server.set_route(\"/get\", handle_get)\n    # send request and wait for server response\n    [page_response, _] = await asyncio.gather(\n        page.wait_for_event(\"response\"),\n        page.evaluate(\"() => fetch('./get', { method: 'GET' })\"),\n    )\n\n    finish_coroutine = page_response.finished()\n    await page.close()\n    with pytest.raises(Error) as exc_info:\n        await finish_coroutine\n    error = exc_info.value\n    assert \"closed\" in error.message\n\n\nasync def test_should_reject_response_finished_if_context_closes(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    def handle_get(request: TestServerRequest) -> None:\n        # In Firefox, |fetch| will be hanging until it receives |Content-Type| header\n        # from server.\n        request.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\")\n        request.write(b\"hello \")\n\n    server.set_route(\"/get\", handle_get)\n    # send request and wait for server response\n    [page_response, _] = await asyncio.gather(\n        page.wait_for_event(\"response\"),\n        page.evaluate(\"() => fetch('./get', { method: 'GET' })\"),\n    )\n\n    finish_coroutine = page_response.finished()\n    await page.context.close()\n    with pytest.raises(Error) as exc_info:\n        await finish_coroutine\n    error = exc_info.value\n    assert \"closed\" in error.message\n"
  },
  {
    "path": "tests/async/test_page_request_fallback.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Any, Callable, Coroutine, cast\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, Request, Route\nfrom tests.server import Server\n\n\nasync def test_should_work(page: Page, server: Server) -> None:\n    await page.route(\"**/*\", lambda route: asyncio.create_task(route.fallback()))\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_should_fall_back(page: Page, server: Server) -> None:\n    intercepted = []\n\n    def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler1)\n\n    def _handler2(route: Route) -> None:\n        intercepted.append(2)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler2)\n\n    def _handler3(route: Route) -> None:\n        intercepted.append(3)\n        asyncio.create_task(route.fallback())\n\n    await page.route(\"**/empty.html\", _handler3)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_fall_back_async_delayed(page: Page, server: Server) -> None:\n    intercepted = []\n\n    def create_handler(i: int) -> Callable[[Route], Coroutine]:\n        async def handler(route: Route) -> None:\n            intercepted.append(i)\n            await asyncio.sleep(0.1)\n            await route.fallback()\n\n        return handler\n\n    await page.route(\"**/empty.html\", create_handler(1))\n    await page.route(\"**/empty.html\", create_handler(2))\n    await page.route(\"**/empty.html\", create_handler(3))\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_chain_once(page: Page, server: Server) -> None:\n    await page.route(\n        \"**/madeup.txt\",\n        lambda route: asyncio.create_task(\n            route.fulfill(status=200, body=\"fulfilled one\")\n        ),\n        times=1,\n    )\n    await page.route(\n        \"**/madeup.txt\", lambda route: asyncio.create_task(route.fallback()), times=1\n    )\n\n    resp = await page.goto(server.PREFIX + \"/madeup.txt\")\n    assert resp\n    body = await resp.body()\n    assert body == b\"fulfilled one\"\n\n\nasync def test_should_not_chain_fulfill(page: Page, server: Server) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    await page.route(\"**/empty.html\", handler)\n    await page.route(\n        \"**/empty.html\",\n        lambda route: asyncio.create_task(route.fulfill(status=200, body=\"fulfilled\")),\n    )\n    await page.route(\n        \"**/empty.html\", lambda route: asyncio.create_task(route.fallback())\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    body = await response.body()\n    assert body == b\"fulfilled\"\n    assert not failed[0]\n\n\nasync def test_should_not_chain_abort(\n    page: Page, server: Server, is_webkit: bool, is_firefox: bool\n) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    await page.route(\"**/empty.html\", handler)\n    await page.route(\"**/empty.html\", lambda route: asyncio.create_task(route.abort()))\n    await page.route(\n        \"**/empty.html\", lambda route: asyncio.create_task(route.fallback())\n    )\n\n    with pytest.raises(Error) as excinfo:\n        await page.goto(server.EMPTY_PAGE)\n    if is_webkit:\n        assert \"Blocked by Web Inspector\" in excinfo.value.message\n    elif is_firefox:\n        assert \"NS_ERROR_FAILURE\" in excinfo.value.message\n    else:\n        assert \"net::ERR_FAILED\" in excinfo.value.message\n    assert not failed[0]\n\n\nasync def test_should_fall_back_after_exception(page: Page, server: Server) -> None:\n    await page.route(\"**/empty.html\", lambda route: route.continue_())\n\n    async def handler(route: Route) -> None:\n        try:\n            await route.fulfill(response=cast(Any, {}))\n        except Exception:\n            await route.fallback()\n\n    await page.route(\"**/empty.html\", handler)\n\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_should_amend_http_headers(page: Page, server: Server) -> None:\n    values = []\n\n    async def handler(route: Route) -> None:\n        values.append(route.request.headers.get(\"foo\"))\n        values.append(await route.request.header_value(\"FOO\"))\n        await route.continue_()\n\n    await page.route(\"**/sleep.zzz\", handler)\n\n    async def handler_with_header_mods(route: Route) -> None:\n        await route.fallback(headers={**route.request.headers, \"FOO\": \"bar\"})\n\n    await page.route(\"**/*\", handler_with_header_mods)\n\n    await page.goto(server.EMPTY_PAGE)\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        await page.evaluate(\"() => fetch('/sleep.zzz')\")\n    values.append(server_request_info.value.getHeader(\"foo\"))\n    assert values == [\"bar\", \"bar\", \"bar\"]\n\n\nasync def test_should_delete_header_with_undefined_value(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\n        \"/something\",\n        lambda r: (\n            r.setHeader(\"Acces-Control-Allow-Origin\", \"*\"),\n            r.write(b\"done\"),\n            r.finish(),\n        ),\n    )\n\n    intercepted_request = []\n\n    async def capture_and_continue(route: Route, request: Request) -> None:\n        intercepted_request.append(request)\n        await route.continue_()\n\n    await page.route(\"**/*\", capture_and_continue)\n\n    async def delete_foo_header(route: Route, request: Request) -> None:\n        headers = await request.all_headers()\n        del headers[\"foo\"]\n        await route.fallback(headers=headers)\n\n    await page.route(server.PREFIX + \"/something\", delete_foo_header)\n\n    [server_req, text] = await asyncio.gather(\n        server.wait_for_request(\"/something\"),\n        page.evaluate(\n            \"\"\"\n            async url => {\n                const data = await fetch(url, {\n                    headers: {\n                    foo: 'a',\n                    bar: 'b',\n                    }\n                });\n                return data.text();\n                }\n            \"\"\",\n            server.PREFIX + \"/something\",\n        ),\n    )\n\n    assert text == \"done\"\n    assert not intercepted_request[0].headers.get(\"foo\")\n    assert intercepted_request[0].headers.get(\"bar\") == \"b\"\n    assert not server_req.getHeader(\"foo\")\n    assert server_req.getHeader(\"bar\") == \"b\"\n\n\nasync def test_should_amend_method(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    method = []\n\n    def _handler(route: Route) -> None:\n        method.append(route.request.method)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handler)\n    await page.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fallback(method=\"POST\"))\n    )\n\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"() => fetch('/sleep.zzz')\"),\n    )\n\n    assert method == [\"POST\"]\n    assert request.method == b\"POST\"\n\n\nasync def test_should_override_request_url(page: Page, server: Server) -> None:\n    url = []\n\n    def _handler1(route: Route) -> None:\n        url.append(route.request.url)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/global-var.html\", _handler1)\n\n    def _handler2(route: Route) -> None:\n        asyncio.create_task(route.fallback(url=server.PREFIX + \"/global-var.html\"))\n\n    await page.route(\"**/foo\", _handler2)\n\n    [server_request, response, _] = await asyncio.gather(\n        server.wait_for_request(\"/global-var.html\"),\n        page.wait_for_event(\"response\"),\n        page.goto(server.PREFIX + \"/foo\"),\n    )\n\n    assert url == [server.PREFIX + \"/global-var.html\"]\n    assert response.url == server.PREFIX + \"/global-var.html\"\n    assert response.request.url == server.PREFIX + \"/global-var.html\"\n    assert await page.evaluate(\"() => window['globalVar']\") == 123\n    assert server_request.uri == b\"/global-var.html\"\n    assert server_request.method == b\"GET\"\n\n\nasync def test_should_amend_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    post_data = []\n\n    def _handler(route: Route) -> None:\n        post_data.append(route.request.post_data)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handler)\n    await page.route(\n        \"**/*\", lambda route: asyncio.create_task(route.fallback(post_data=\"doggo\"))\n    )\n    [server_request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    assert post_data == [\"doggo\"]\n    assert server_request.post_body == b\"doggo\"\n\n\nasync def test_should_amend_binary_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    post_data_buffer = []\n\n    def _handler1(route: Route) -> None:\n        post_data_buffer.append(route.request.post_data)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handler1)\n\n    async def _handler2(route: Route) -> None:\n        await route.fallback(post_data=b\"\\x00\\x01\\x02\\x03\\x04\")\n\n    await page.route(\"**/*\", _handler2)\n\n    [server_request, result] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    # FIXME: should this be bytes?\n    assert post_data_buffer == [\"\\x00\\x01\\x02\\x03\\x04\"]\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body == b\"\\x00\\x01\\x02\\x03\\x04\"\n\n\nasync def test_should_chain_fallback_with_dynamic_url(\n    server: Server, page: Page\n) -> None:\n    intercepted = []\n\n    def _handler1(route: Route) -> None:\n        intercepted.append(1)\n        asyncio.create_task(route.fallback(url=server.EMPTY_PAGE))\n\n    await page.route(\"**/bar\", _handler1)\n\n    def _handler2(route: Route, request: Request) -> None:\n        intercepted.append(2)\n        asyncio.create_task(route.fallback(url=\"http://localhost/bar\"))\n\n    await page.route(\"**/foo\", _handler2)\n\n    def _handler3(route: Route, request: Request) -> None:\n        intercepted.append(3)\n        asyncio.create_task(route.fallback(url=\"http://localhost/foo\"))\n\n    await page.route(\"**/empty.html\", _handler3)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\nasync def test_should_amend_json_post_data(server: Server, page: Page) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    post_data = []\n\n    def _handle1(route: Route, request: Request) -> None:\n        post_data.append(route.request.post_data)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handle1)\n    await page.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(route.fallback(post_data={\"foo\": \"bar\"})),\n    )\n\n    [server_request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    assert post_data == ['{\"foo\": \"bar\"}']\n    assert server_request.post_body == b'{\"foo\": \"bar\"}'\n"
  },
  {
    "path": "tests/async/test_page_request_gc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page\nfrom tests.server import Server\n\n\nasync def test_should_work(page: Page, server: Server) -> None:\n    await page.evaluate(\n        \"\"\"() => {\n        globalThis.objectToDestroy = { hello: 'world' };\n        globalThis.weakRef = new WeakRef(globalThis.objectToDestroy);\n    }\"\"\"\n    )\n    await page.request_gc()\n    assert await page.evaluate(\"() => globalThis.weakRef.deref()\") == {\"hello\": \"world\"}\n\n    await page.request_gc()\n    assert await page.evaluate(\"() => globalThis.weakRef.deref()\") == {\"hello\": \"world\"}\n\n    await page.evaluate(\"() => globalThis.objectToDestroy = null\")\n    await page.request_gc()\n    assert await page.evaluate(\"() => globalThis.weakRef.deref()\") is None\n"
  },
  {
    "path": "tests/async/test_page_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import cast\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, Route, expect\nfrom tests.server import Server, TestServerRequest\n\n\nasync def test_should_support_timeout_option_in_route_fetch(\n    server: Server, page: Page\n) -> None:\n    def _handler(request: TestServerRequest) -> None:\n        request.responseHeaders.addRawHeader(\"Content-Length\", \"4096\")\n        request.responseHeaders.addRawHeader(\"Content-Type\", \"text/html\")\n        request.write(b\"\")\n\n    server.set_route(\"/slow\", _handler)\n\n    async def handle(route: Route) -> None:\n        with pytest.raises(Error) as error:\n            await route.fetch(timeout=1000)\n        assert \"Route.fetch: Timeout 1000ms exceeded.\" in error.value.message\n\n    await page.route(\"**/*\", lambda route: handle(route))\n    with pytest.raises(Error) as error:\n        await page.goto(server.PREFIX + \"/slow\", timeout=2000)\n    assert \"Timeout 2000ms exceeded\" in error.value.message\n\n\nasync def test_should_not_follow_redirects_when_max_redirects_is_set_to_0_in_route_fetch(\n    server: Server, page: Page\n) -> None:\n    server.set_redirect(\"/foo\", \"/empty.html\")\n\n    async def handle(route: Route) -> None:\n        response = await route.fetch(max_redirects=0)\n        assert response.headers[\"location\"] == \"/empty.html\"\n        assert response.status == 302\n        await route.fulfill(body=\"hello\")\n\n    await page.route(\"**/*\", lambda route: handle(route))\n    await page.goto(server.PREFIX + \"/foo\")\n    assert \"hello\" in await page.content()\n\n\nasync def test_should_intercept_with_url_override(server: Server, page: Page) -> None:\n    async def handle(route: Route) -> None:\n        response = await route.fetch(url=server.PREFIX + \"/one-style.html\")\n        await route.fulfill(response=response)\n\n    await page.route(\"**/*.html\", lambda route: handle(route))\n    response = await page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 200\n    assert \"one-style.css\" in (await response.body()).decode(\"utf-8\")\n\n\nasync def test_should_intercept_with_post_data_override(\n    server: Server, page: Page\n) -> None:\n    request_promise = asyncio.create_task(server.wait_for_request(\"/empty.html\"))\n\n    async def handle(route: Route) -> None:\n        response = await route.fetch(post_data={\"foo\": \"bar\"})\n        await route.fulfill(response=response)\n\n    await page.route(\"**/*.html\", lambda route: handle(route))\n    await page.goto(server.PREFIX + \"/empty.html\")\n    request = await request_promise\n    assert request.post_body\n    assert request.post_body.decode(\"utf-8\") == '{\"foo\": \"bar\"}'\n\n\nasync def test_should_fulfill_popup_main_request_using_alias(\n    page: Page, server: Server\n) -> None:\n    async def route_handler(route: Route) -> None:\n        response = await route.fetch()\n        await route.fulfill(response=response, body=\"hello\")\n\n    await page.context.route(\"**/*\", route_handler)\n    await page.set_content(f'<a target=_blank href=\"{server.EMPTY_PAGE}\">click me</a>')\n    [popup, _] = await asyncio.gather(\n        page.wait_for_event(\"popup\"), page.get_by_text(\"click me\").click()\n    )\n    await expect(cast(Page, popup).locator(\"body\")).to_have_text(\"hello\")\n"
  },
  {
    "path": "tests/async/test_page_route.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport json\nimport re\nfrom pathlib import Path\nfrom typing import Callable, List, Optional\n\nimport pytest\n\nfrom playwright._impl._glob import glob_to_regex_pattern\nfrom playwright._impl._helper import url_matches\nfrom playwright.async_api import (\n    Browser,\n    BrowserContext,\n    Error,\n    Page,\n    Playwright,\n    Request,\n    Route,\n    expect,\n)\nfrom tests.server import Server, TestServerRequest\nfrom tests.utils import must\n\n\nasync def test_page_route_should_intercept(page: Page, server: Server) -> None:\n    intercepted = []\n\n    async def handle_request(route: Route, request: Request) -> None:\n        assert route.request == request\n        assert \"empty.html\" in request.url\n        assert request.headers[\"user-agent\"]\n        assert request.method == \"GET\"\n        assert request.post_data is None\n        assert request.is_navigation_request()\n        assert request.resource_type == \"document\"\n        assert request.frame == page.main_frame\n        assert request.frame.url == \"about:blank\"\n        await route.continue_()\n        intercepted.append(True)\n\n    await page.route(\"**/empty.html\", handle_request)\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n    assert len(intercepted) == 1\n\n\nasync def test_page_route_should_unroute(page: Page, server: Server) -> None:\n    intercepted = []\n\n    def _handle1(route: Route) -> None:\n        intercepted.append(1)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handle1)\n\n    def _handle2(route: Route, request: Request) -> None:\n        intercepted.append(2)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/empty.html\", _handle2)\n\n    def _handle3(route: Route, request: Request) -> None:\n        intercepted.append(3)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/empty.html\",\n        _handle3,\n    )\n\n    def handler4(route: Route) -> None:\n        intercepted.append(4)\n        asyncio.create_task(route.continue_())\n\n    await page.route(re.compile(\"empty.html\"), handler4)\n\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [4]\n\n    intercepted = []\n    await page.unroute(re.compile(\"empty.html\"), handler4)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3]\n\n    intercepted = []\n    await page.unroute(\"**/empty.html\")\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [1]\n\n\nasync def test_page_route_should_work_when_POST_is_redirected_with_302(\n    page: Page, server: Server\n) -> None:\n    server.set_redirect(\"/rredirect\", \"/empty.html\")\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\"**/*\", lambda route: route.continue_())\n    await page.set_content(\n        \"\"\"\n      <form action='/rredirect' method='post'>\n        <input type=\"hidden\" id=\"foo\" name=\"foo\" value=\"FOOBAR\">\n      </form>\n    \"\"\"\n    )\n    async with page.expect_navigation():\n        await page.eval_on_selector(\"form\", \"form => form.submit()\")\n\n\n# @see https://github.com/GoogleChrome/puppeteer/issues/3973\nasync def test_page_route_should_work_when_header_manipulation_headers_with_redirect(\n    page: Page, server: Server\n) -> None:\n    server.set_redirect(\"/rrredirect\", \"/empty.html\")\n    await page.route(\n        \"**/*\",\n        lambda route: route.continue_(headers={**route.request.headers, \"foo\": \"bar\"}),\n    )\n\n    await page.goto(server.PREFIX + \"/rrredirect\")\n\n\n# @see https://github.com/GoogleChrome/puppeteer/issues/4743\nasync def test_page_route_should_be_able_to_remove_headers(\n    page: Page, server: Server\n) -> None:\n    async def handle_request(route: Route) -> None:\n        headers = route.request.headers\n        if \"origin\" in headers:\n            del headers[\"origin\"]\n        await route.continue_(headers=headers)\n\n    await page.route(\n        \"**/*\",  # remove \"origin\" header\n        handle_request,\n    )\n\n    [serverRequest, _] = await asyncio.gather(\n        server.wait_for_request(\"/empty.html\"), page.goto(server.PREFIX + \"/empty.html\")\n    )\n    assert serverRequest.getHeader(\"origin\") is None\n\n\nasync def test_page_route_should_contain_referer_header(\n    page: Page, server: Server\n) -> None:\n    requests = []\n\n    def _handle(route: Route, request: Request) -> None:\n        requests.append(route.request)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    assert \"/one-style.css\" in requests[1].url\n    assert \"/one-style.html\" in requests[1].headers[\"referer\"]\n\n\nasync def test_page_route_should_properly_return_navigation_response_when_URL_has_cookies(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    # Setup cookie.\n    await page.goto(server.EMPTY_PAGE)\n    await context.add_cookies(\n        [{\"url\": server.EMPTY_PAGE, \"name\": \"foo\", \"value\": \"bar\"}]\n    )\n\n    # Setup request interception.\n    await page.route(\"**/*\", lambda route: route.continue_())\n    response = await page.reload()\n    assert response\n    assert response.status == 200\n\n\nasync def test_page_route_should_show_custom_HTTP_headers(\n    page: Page, server: Server\n) -> None:\n    await page.set_extra_http_headers({\"foo\": \"bar\"})\n\n    def assert_headers(request: Request) -> None:\n        assert request.headers[\"foo\"] == \"bar\"\n\n    def _handle(route: Route) -> None:\n        assert_headers(route.request)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n\n\n# @see https://github.com/GoogleChrome/puppeteer/issues/4337\nasync def test_page_route_should_work_with_redirect_inside_sync_XHR(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_redirect(\"/logo.png\", \"/pptr.png\")\n    await page.route(\"**/*\", lambda route: route.continue_())\n    status = await page.evaluate(\n        \"\"\"async() => {\n      const request = new XMLHttpRequest();\n      request.open('GET', '/logo.png', false);  // `false` makes the request synchronous\n      request.send(null);\n      return request.status;\n    }\"\"\"\n    )\n\n    assert status == 200\n\n\nasync def test_page_route_should_work_with_custom_referer_headers(\n    page: Page, server: Server\n) -> None:\n    await page.set_extra_http_headers({\"referer\": server.EMPTY_PAGE})\n\n    def assert_headers(route: Route) -> None:\n        assert route.request.headers[\"referer\"] == server.EMPTY_PAGE\n\n    def _handle(route: Route, request: Request) -> None:\n        assert_headers(route)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.ok\n\n\nasync def test_page_route_should_be_abortable(page: Page, server: Server) -> None:\n    await page.route(r\"/\\.css$/\", lambda route: asyncio.create_task(route.abort()))\n    failed = []\n\n    def handle_request(request: Request) -> None:\n        if \".css\" in request.url:\n            failed.append(True)\n\n    page.on(\"requestfailed\", handle_request)\n\n    response = await page.goto(server.PREFIX + \"/one-style.html\")\n    assert response\n    assert response.ok\n    assert response.request.failure is None\n    assert len(failed) == 0\n\n\nasync def test_page_route_should_be_abortable_with_custom_error_codes(\n    page: Page, server: Server, is_webkit: bool, is_firefox: bool\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.abort(\"internetdisconnected\"),\n    )\n    failed_requests = []\n    page.on(\"requestfailed\", lambda request: failed_requests.append(request))\n    with pytest.raises(Error):\n        await page.goto(server.EMPTY_PAGE)\n    assert len(failed_requests) == 1\n    failed_request = failed_requests[0]\n    if is_webkit:\n        assert failed_request.failure == \"Blocked by Web Inspector\"\n    elif is_firefox:\n        assert failed_request.failure == \"NS_ERROR_OFFLINE\"\n    else:\n        assert failed_request.failure == \"net::ERR_INTERNET_DISCONNECTED\"\n\n\nasync def test_page_route_should_send_referer(page: Page, server: Server) -> None:\n    await page.set_extra_http_headers({\"referer\": \"http://google.com/\"})\n\n    await page.route(\"**/*\", lambda route: route.continue_())\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/grid.html\"),\n        page.goto(server.PREFIX + \"/grid.html\"),\n    )\n    assert request.getHeader(\"referer\") == \"http://google.com/\"\n\n\nasync def test_page_route_should_fail_navigation_when_aborting_main_resource(\n    page: Page, server: Server, is_webkit: bool, is_firefox: bool\n) -> None:\n    await page.route(\"**/*\", lambda route: route.abort())\n    with pytest.raises(Error) as exc:\n        await page.goto(server.EMPTY_PAGE)\n    assert exc\n    if is_webkit:\n        assert \"Blocked by Web Inspector\" in exc.value.message\n    elif is_firefox:\n        assert \"NS_ERROR_FAILURE\" in exc.value.message\n    else:\n        assert \"net::ERR_FAILED\" in exc.value.message\n\n\nasync def test_page_route_should_not_work_with_redirects(\n    page: Page, server: Server\n) -> None:\n    intercepted = []\n\n    def _handle(route: Route, request: Request) -> None:\n        asyncio.create_task(route.continue_())\n        intercepted.append(route.request)\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    server.set_redirect(\"/non-existing-page.html\", \"/non-existing-page-2.html\")\n    server.set_redirect(\"/non-existing-page-2.html\", \"/non-existing-page-3.html\")\n    server.set_redirect(\"/non-existing-page-3.html\", \"/non-existing-page-4.html\")\n    server.set_redirect(\"/non-existing-page-4.html\", \"/empty.html\")\n\n    response = await page.goto(server.PREFIX + \"/non-existing-page.html\")\n    assert response\n    assert response.status == 200\n    assert \"empty.html\" in response.url\n\n    assert len(intercepted) == 1\n    assert intercepted[0].resource_type == \"document\"\n    assert intercepted[0].is_navigation_request()\n    assert \"/non-existing-page.html\" in intercepted[0].url\n\n    chain = []\n    r: Optional[Request] = response.request\n    while r:\n        chain.append(r)\n        assert r.is_navigation_request()\n        r = r.redirected_from\n\n    assert len(chain) == 5\n    assert \"/empty.html\" in chain[0].url\n    assert \"/non-existing-page-4.html\" in chain[1].url\n    assert \"/non-existing-page-3.html\" in chain[2].url\n    assert \"/non-existing-page-2.html\" in chain[3].url\n    assert \"/non-existing-page.html\" in chain[4].url\n    for idx, _ in enumerate(chain):\n        assert chain[idx].redirected_to == (chain[idx - 1] if idx > 0 else None)\n\n\nasync def test_page_route_should_work_with_redirects_for_subresources(\n    page: Page, server: Server\n) -> None:\n    intercepted: List[Request] = []\n\n    def _handle(route: Route) -> None:\n        asyncio.create_task(route.continue_())\n        intercepted.append(route.request)\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    server.set_redirect(\"/one-style.css\", \"/two-style.css\")\n    server.set_redirect(\"/two-style.css\", \"/three-style.css\")\n    server.set_redirect(\"/three-style.css\", \"/four-style.css\")\n    server.set_route(\n        \"/four-style.css\",\n        lambda req: (req.write(b\"body {box-sizing: border-box; }\"), req.finish()),\n    )\n\n    response = await page.goto(server.PREFIX + \"/one-style.html\")\n    assert response\n    assert response.status == 200\n    assert \"one-style.html\" in response.url\n\n    # TODO: https://github.com/microsoft/playwright/issues/12789\n    assert len(intercepted) >= 2\n    assert intercepted[0].resource_type == \"document\"\n    assert \"one-style.html\" in intercepted[0].url\n\n    r: Optional[Request] = intercepted[1]\n    for url in [\n        \"/one-style.css\",\n        \"/two-style.css\",\n        \"/three-style.css\",\n        \"/four-style.css\",\n    ]:\n        assert r\n        assert r.resource_type == \"stylesheet\"\n        assert url in r.url\n        r = r.redirected_to\n    assert r is None\n\n\nasync def test_page_route_should_work_with_equal_requests(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    hits = [True]\n\n    def handle_request(request: TestServerRequest, hits: List[bool]) -> None:\n        request.write(str(len(hits) * 11).encode())\n        request.finish()\n        hits.append(True)\n\n    server.set_route(\"/zzz\", lambda r: handle_request(r, hits))\n\n    spinner: List[bool] = []\n\n    async def handle_route(route: Route) -> None:\n        if len(spinner) == 1:\n            await route.abort()\n            spinner.pop(0)\n        else:\n            await route.continue_()\n            spinner.append(True)\n\n    # Cancel 2nd request.\n    await page.route(\"**/*\", handle_route)\n\n    results = []\n    for idx in range(3):\n        results.append(\n            await page.evaluate(\n                \"\"\"() => fetch('/zzz').then(response => response.text()).catch(e => 'FAILED')\"\"\"\n            )\n        )\n    assert results == [\"11\", \"FAILED\", \"22\"]\n\n\nasync def test_page_route_should_navigate_to_dataURL_and_not_fire_dataURL_requests(\n    page: Page, server: Server\n) -> None:\n    requests = []\n\n    def _handle(route: Route) -> None:\n        requests.append(route.request)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    data_URL = \"data:text/html,<div>yo</div>\"\n    response = await page.goto(data_URL)\n    assert response is None\n    assert len(requests) == 0\n\n\nasync def test_page_route_should_be_able_to_fetch_dataURL_and_not_fire_dataURL_requests(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    requests = []\n\n    def _handle(route: Route) -> None:\n        requests.append(route.request)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\"**/*\", _handle)\n\n    data_URL = \"data:text/html,<div>yo</div>\"\n    text = await page.evaluate(\"url => fetch(url).then(r => r.text())\", data_URL)\n    assert text == \"<div>yo</div>\"\n    assert len(requests) == 0\n\n\nasync def test_page_route_should_navigate_to_URL_with_hash_and_and_fire_requests_without_hash(\n    page: Page, server: Server\n) -> None:\n    requests = []\n\n    def _handle(route: Route) -> None:\n        requests.append(route.request)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        \"**/*\",\n        _handle,\n    )\n\n    response = await page.goto(server.EMPTY_PAGE + \"#hash\")\n    assert response\n    assert response.status == 200\n    assert response.url == server.EMPTY_PAGE\n    assert len(requests) == 1\n    assert requests[0].url == server.EMPTY_PAGE\n\n\nasync def test_page_route_should_work_with_encoded_server(\n    page: Page, server: Server\n) -> None:\n    # The requestWillBeSent will report encoded URL, whereas interception will\n    # report URL as-is. @see crbug.com/759388\n    await page.route(\"**/*\", lambda route: route.continue_())\n    response = await page.goto(server.PREFIX + \"/some nonexisting page\")\n    assert response\n    assert response.status == 404\n\n\nasync def test_page_route_should_work_with_encoded_server___2(\n    page: Page, server: Server\n) -> None:\n    # The requestWillBeSent will report URL as-is, whereas interception will\n    # report encoded URL for stylesheet. @see crbug.com/759388\n    requests: List[Request] = []\n\n    def _handle(route: Route) -> None:\n        asyncio.create_task(route.continue_())\n        requests.append(route.request)\n\n    await page.route(\"**/*\", _handle)\n\n    await page.set_content(\n        f\"\"\"data:text/html,<link rel=\"stylesheet\" href=\"{server.PREFIX}/fonts?helvetica|arial\"/>\"\"\"\n    )\n    assert len(requests) == 1\n    assert (must(await requests[0].response())).status == 404\n\n\nasync def test_page_route_should_not_throw_Invalid_Interception_Id_if_the_request_was_cancelled(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\"<iframe></iframe>\")\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\"**/*\", lambda r, _: route_future.set_result(r))\n\n    async with page.expect_request(\"**/*\"):\n        await page.eval_on_selector(\n            \"iframe\", \"\"\"(frame, url) => frame.src = url\"\"\", server.EMPTY_PAGE\n        )\n    # Delete frame to cause request to be canceled.\n    await page.eval_on_selector(\"iframe\", \"frame => frame.remove()\")\n    route = await route_future\n    await route.continue_()\n\n\nasync def test_page_route_should_intercept_main_resource_during_cross_process_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    intercepted = []\n\n    def _handle(route: Route) -> None:\n        intercepted.append(True)\n        asyncio.create_task(route.continue_())\n\n    await page.route(\n        server.CROSS_PROCESS_PREFIX + \"/empty.html\",\n        _handle,\n    )\n\n    response = await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    assert response\n    assert response.ok\n    assert len(intercepted) == 1\n\n\n@pytest.mark.skip_browser(\"webkit\")\nasync def test_page_route_should_create_a_redirect(page: Page, server: Server) -> None:\n    await page.goto(server.PREFIX + \"/empty.html\")\n\n    async def handle_route(route: Route, request: Request) -> None:\n        if request.url != (server.PREFIX + \"/redirect_this\"):\n            return await route.continue_()\n        await route.fulfill(status=301, headers={\"location\": \"/empty.html\"})\n\n    await page.route(\n        \"**/*\",\n        handle_route,\n    )\n\n    text = await page.evaluate(\n        \"\"\"async url => {\n      const data = await fetch(url);\n      return data.text();\n    }\"\"\",\n        server.PREFIX + \"/redirect_this\",\n    )\n    assert text == \"\"\n\n\nasync def test_page_route_should_support_cors_with_GET(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    async def handle_route(route: Route, request: Request) -> None:\n        headers = {\n            \"access-control-allow-origin\": (\n                \"*\" if request.url.endswith(\"allow\") else \"none\"\n            )\n        }\n        await route.fulfill(\n            content_type=\"application/json\",\n            headers=headers,\n            status=200,\n            body=json.dumps([\"electric\", \"gas\"]),\n        )\n\n    await page.route(\n        \"**/cars*\",\n        handle_route,\n    )\n    # Should succeed\n    resp = await page.evaluate(\n        \"\"\"async () => {\n        const response = await fetch('https://example.com/cars?allow', { mode: 'cors' });\n        return response.json();\n      }\"\"\"\n    )\n\n    assert resp == [\"electric\", \"gas\"]\n\n    # Should be rejected\n    with pytest.raises(Error) as exc:\n        await page.evaluate(\n            \"\"\"async () => {\n            const response = await fetch('https://example.com/cars?reject', { mode: 'cors' });\n            return response.json();\n        }\"\"\"\n        )\n    if browser_name == \"chromium\":\n        assert \"Failed\" in exc.value.message\n    elif browser_name == \"webkit\":\n        assert \"TypeError\" in exc.value.message\n    elif browser_name == \"firefox\":\n        assert \"NetworkError\" in exc.value.message\n\n\nasync def test_page_route_should_support_cors_with_POST(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/cars\",\n        lambda route: route.fulfill(\n            content_type=\"application/json\",\n            headers={\"Access-Control-Allow-Origin\": \"*\"},\n            status=200,\n            body=json.dumps([\"electric\", \"gas\"]),\n        ),\n    )\n\n    resp = await page.evaluate(\n        \"\"\"async () => {\n      const response = await fetch('https://example.com/cars', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        mode: 'cors',\n        body: JSON.stringify({ 'number': 1 })\n      });\n      return response.json();\n    }\"\"\"\n    )\n\n    assert resp == [\"electric\", \"gas\"]\n\n\nasync def test_page_route_should_support_cors_for_different_methods(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/cars\",\n        lambda route, request: route.fulfill(\n            content_type=\"application/json\",\n            headers={\"Access-Control-Allow-Origin\": \"*\"},\n            status=200,\n            body=json.dumps([request.method, \"electric\", \"gas\"]),\n        ),\n    )\n\n    # First POST\n    resp = await page.evaluate(\n        \"\"\"async () => {\n        const response = await fetch('https://example.com/cars', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/json' },\n          mode: 'cors',\n          body: JSON.stringify({ 'number': 1 })\n        });\n        return response.json();\n      }\"\"\"\n    )\n\n    assert resp == [\"POST\", \"electric\", \"gas\"]\n    # Then DELETE\n    resp = await page.evaluate(\n        \"\"\"async () => {\n        const response = await fetch('https://example.com/cars', {\n          method: 'DELETE',\n          headers: {},\n          mode: 'cors',\n          body: ''\n        });\n        return response.json();\n      }\"\"\"\n    )\n\n    assert resp == [\"DELETE\", \"electric\", \"gas\"]\n\n\nasync def test_request_fulfill_should_work_a(page: Page, server: Server) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            status=201,\n            headers={\"foo\": \"bar\"},\n            content_type=\"text/html\",\n            body=\"Yo, page!\",\n        ),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.headers[\"foo\"] == \"bar\"\n    assert await page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\nasync def test_request_fulfill_should_work_with_status_code_422(\n    page: Page, server: Server\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(status=422, body=\"Yo, page!\"),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 422\n    assert response.status_text == \"Unprocessable Entity\"\n    assert await page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\nasync def test_request_fulfill_should_allow_mocking_binary_responses(\n    page: Page,\n    server: Server,\n    assert_to_be_golden: Callable[[bytes, str], None],\n    assetdir: Path,\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            content_type=\"image/png\",\n            body=(assetdir / \"pptr.png\").read_bytes(),\n        ),\n    )\n\n    await page.evaluate(\n        \"\"\"PREFIX => {\n      const img = document.createElement('img');\n      img.src = PREFIX + '/does-not-exist.png';\n      document.body.appendChild(img);\n      return new Promise(fulfill => img.onload = fulfill);\n    }\"\"\",\n        server.PREFIX,\n    )\n    img = await page.query_selector(\"img\")\n    assert img\n    assert_to_be_golden(await img.screenshot(), \"mock-binary-response.png\")\n\n\nasync def test_request_fulfill_should_allow_mocking_svg_with_charset(\n    page: Page, server: Server, assert_to_be_golden: Callable[[bytes, str], None]\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            content_type=\"image/svg+xml ; charset=utf-8\",\n            body='<svg width=\"50\" height=\"50\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"><rect x=\"10\" y=\"10\" width=\"30\" height=\"30\" stroke=\"black\" fill=\"transparent\" stroke-width=\"5\"/></svg>',\n        ),\n    )\n\n    await page.evaluate(\n        \"\"\"PREFIX => {\n      const img = document.createElement('img');\n      img.src = PREFIX + '/does-not-exist.svg';\n      document.body.appendChild(img);\n      return new Promise((f, r) => { img.onload = f; img.onerror = r; });\n    }\"\"\",\n        server.PREFIX,\n    )\n    img = await page.query_selector(\"img\")\n    assert img\n    assert_to_be_golden(await img.screenshot(), \"mock-svg.png\")\n\n\nasync def test_request_fulfill_should_work_with_file_path(\n    page: Page,\n    server: Server,\n    assert_to_be_golden: Callable[[bytes, str], None],\n    assetdir: Path,\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            content_type=\"shouldBeIgnored\", path=assetdir / \"pptr.png\"\n        ),\n    )\n    await page.evaluate(\n        \"\"\"PREFIX => {\n      const img = document.createElement('img');\n      img.src = PREFIX + '/does-not-exist.png';\n      document.body.appendChild(img);\n      return new Promise(fulfill => img.onload = fulfill);\n    }\"\"\",\n        server.PREFIX,\n    )\n    img = await page.query_selector(\"img\")\n    assert img\n    assert_to_be_golden(await img.screenshot(), \"mock-binary-response.png\")\n\n\nasync def test_request_fulfill_should_stringify_intercepted_request_response_headers(\n    page: Page, server: Server\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            status=200, headers={\"foo\": True}, body=\"Yo, page!\"  # type: ignore\n        ),\n    )\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    headers = response.headers\n    assert headers[\"foo\"] == \"True\"\n    assert await page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\nasync def test_request_fulfill_should_not_modify_the_headers_sent_to_the_server(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/empty.html\")\n    interceptedRequests = []\n\n    # this is just to enable request interception, which disables caching in chromium\n    await page.route(server.PREFIX + \"/unused\", lambda route, req: None)\n\n    def _handler1(response: TestServerRequest) -> None:\n        interceptedRequests.append(response)\n        response.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n        response.write(b\"done\")\n        response.finish()\n\n    server.set_route(\"/something\", _handler1)\n\n    text = await page.evaluate(\n        \"\"\"async url => {\n      const data = await fetch(url);\n      return data.text();\n    }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/something\",\n    )\n    assert text == \"done\"\n\n    playwrightRequest: \"asyncio.Future[Request]\" = asyncio.Future()\n\n    def _handler2(route: Route, request: Request) -> None:\n        playwrightRequest.set_result(request)\n        asyncio.create_task(route.continue_(headers={**request.headers}))\n\n    await page.route(\n        server.CROSS_PROCESS_PREFIX + \"/something\",\n        _handler2,\n    )\n\n    textAfterRoute = await page.evaluate(\n        \"\"\"async url => {\n      const data = await fetch(url);\n      return data.text();\n    }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/something\",\n    )\n    assert textAfterRoute == \"done\"\n\n    assert len(interceptedRequests) == 2\n    assert (\n        interceptedRequests[0].requestHeaders == interceptedRequests[1].requestHeaders\n    )\n\n\nasync def test_request_fulfill_should_include_the_origin_header(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/empty.html\")\n    interceptedRequest = []\n\n    def _handle(route: Route, request: Request) -> None:\n        interceptedRequest.append(request)\n        asyncio.create_task(\n            route.fulfill(\n                headers={\"Access-Control-Allow-Origin\": \"*\"},\n                content_type=\"text/plain\",\n                body=\"done\",\n            )\n        )\n\n    await page.route(server.CROSS_PROCESS_PREFIX + \"/something\", _handle)\n\n    text = await page.evaluate(\n        \"\"\"async url => {\n      const data = await fetch(url);\n      return data.text();\n    }\"\"\",\n        server.CROSS_PROCESS_PREFIX + \"/something\",\n    )\n    assert text == \"done\"\n    assert len(interceptedRequest) == 1\n    assert interceptedRequest[0].headers[\"origin\"] == server.PREFIX\n\n\nasync def test_request_fulfill_should_work_with_request_interception(\n    page: Page, server: Server\n) -> None:\n    requests = {}\n\n    async def _handle_route(route: Route) -> None:\n        requests[route.request.url.split(\"/\").pop()] = route.request\n        await route.continue_()\n\n    await page.route(\"**/*\", _handle_route)\n\n    server.set_redirect(\"/rrredirect\", \"/frames/one-frame.html\")\n    await page.goto(server.PREFIX + \"/rrredirect\")\n    assert requests[\"rrredirect\"].is_navigation_request()\n    assert requests[\"frame.html\"].is_navigation_request()\n    assert requests[\"script.js\"].is_navigation_request() is False\n    assert requests[\"style.css\"].is_navigation_request() is False\n\n\nasync def test_Interception_should_work_with_request_interception(\n    browser: Browser, https_server: Server\n) -> None:\n    context = await browser.new_context(ignore_https_errors=True)\n    page = await context.new_page()\n\n    await page.route(\"**/*\", lambda route: asyncio.ensure_future(route.continue_()))\n    response = await page.goto(https_server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    await context.close()\n\n\nasync def test_ignore_http_errors_service_worker_should_intercept_after_a_service_worker(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    await page.evaluate(\"() => window.activationPromise\")\n\n    # Sanity check.\n    sw_response = await page.evaluate('() => fetchDummy(\"foo\")')\n    assert sw_response == \"responseFromServiceWorker:foo\"\n\n    def _handle_route(route: Route) -> None:\n        asyncio.ensure_future(\n            route.fulfill(\n                status=200,\n                content_type=\"text/css\",\n                body=\"responseFromInterception:\" + route.request.url.split(\"/\")[-1],\n            )\n        )\n\n    await page.route(\"**/foo\", _handle_route)\n\n    # Page route is applied after service worker fetch event.\n    sw_response2 = await page.evaluate('() => fetchDummy(\"foo\")')\n    assert sw_response2 == \"responseFromServiceWorker:foo\"\n\n    # Page route is not applied to service worker initiated fetch.\n    non_intercepted_response = await page.evaluate('() => fetchDummy(\"passthrough\")')\n    assert non_intercepted_response == \"FAILURE: Not Found\"\n\n\nasync def test_page_route_should_support_times_parameter(\n    page: Page, server: Server\n) -> None:\n    intercepted = []\n\n    async def handle_request(route: Route) -> None:\n        await route.continue_()\n        intercepted.append(True)\n\n    await page.route(\"**/empty.html\", handle_request, times=1)\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.EMPTY_PAGE)\n    assert len(intercepted) == 1\n\n\nasync def test_should_work_if_handler_with_times_parameter_was_removed_from_another_handler(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted = []\n\n    async def handler(route: Route) -> None:\n        intercepted.append(\"first\")\n        await route.continue_()\n\n    await page.route(\"**/*\", handler, times=1)\n\n    async def handler2(route: Route) -> None:\n        intercepted.append(\"second\")\n        await page.unroute(\"**/*\", handler)\n        await route.fallback()\n\n    await page.route(\"**/*\", handler2)\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [\"second\"]\n    intercepted.clear()\n    await page.goto(server.EMPTY_PAGE)\n    assert intercepted == [\"second\"]\n\n\nasync def test_should_fulfill_with_global_fetch_result(\n    page: Page, playwright: Playwright, server: Server\n) -> None:\n    async def handle_request(route: Route) -> None:\n        request = await playwright.request.new_context()\n        response = await request.get(server.PREFIX + \"/simple.json\")\n        await route.fulfill(response=response)\n        await request.dispose()\n\n    await page.route(\"**/*\", handle_request)\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    assert await response.json() == {\"foo\": \"bar\"}\n\n\nasync def test_should_work_with_glob() -> None:\n    def glob_to_regex(pattern: str) -> re.Pattern:\n        return re.compile(glob_to_regex_pattern(pattern))\n\n    assert glob_to_regex(\"**/*.js\").match(\"https://localhost:8080/foo.js\")\n    assert not glob_to_regex(\"**/*.css\").match(\"https://localhost:8080/foo.js\")\n    assert not glob_to_regex(\"*.js\").match(\n        \"https://localhost:8080/foo.js\"\n    )  # Doesn\"t match path separator\n    assert glob_to_regex(\"https://**/*.js\").match(\"https://localhost:8080/foo.js\")\n    assert glob_to_regex(\"http://localhost:8080/simple/path.js\").match(\n        \"http://localhost:8080/simple/path.js\"\n    )\n    assert glob_to_regex(\"**/{a,b}.js\").match(\"https://localhost:8080/a.js\")\n    assert glob_to_regex(\"**/{a,b}.js\").match(\"https://localhost:8080/b.js\")\n    assert not glob_to_regex(\"**/{a,b}.js\").match(\"https://localhost:8080/c.js\")\n\n    assert glob_to_regex(\"**/*.{png,jpg,jpeg}\").match(\"https://localhost:8080/c.jpg\")\n    assert glob_to_regex(\"**/*.{png,jpg,jpeg}\").match(\"https://localhost:8080/c.jpeg\")\n    assert glob_to_regex(\"**/*.{png,jpg,jpeg}\").match(\"https://localhost:8080/c.png\")\n    assert not glob_to_regex(\"**/*.{png,jpg,jpeg}\").match(\n        \"https://localhost:8080/c.css\"\n    )\n    assert glob_to_regex(\"foo*\").match(\"foo.js\")\n    assert not glob_to_regex(\"foo*\").match(\"foo/bar.js\")\n    assert not glob_to_regex(\"http://localhost:3000/signin-oidc*\").match(\n        \"http://localhost:3000/signin-oidc/foo\"\n    )\n    assert glob_to_regex(\"http://localhost:3000/signin-oidc*\").match(\n        \"http://localhost:3000/signin-oidcnice\"\n    )\n\n    assert glob_to_regex(\"**/*.js\").match(\"/foo.js\")\n    assert not glob_to_regex(\"asd/**.js\").match(\"/foo.js\")\n    assert not glob_to_regex(\"**/*.js\").match(\"bar_foo.js\")\n\n    # range [] is NOT supported\n    assert glob_to_regex(\"**/api/v[0-9]\").fullmatch(\"http://example.com/api/v[0-9]\")\n    assert not glob_to_regex(\"**/api/v[0-9]\").fullmatch(\n        \"http://example.com/api/version\"\n    )\n    assert not glob_to_regex(\"**/api/v[0-9]\").fullmatch(\n        \"http://example.com/api/v1\"\n    )  # Should not match if [] is literal\n\n    # query params\n    assert glob_to_regex(\"**/api\\\\?param\").match(\"http://example.com/api?param\")\n    assert not glob_to_regex(\"**/api\\\\?param\").match(\"http://example.com/api-param\")\n\n    assert glob_to_regex(\"**/three-columns/settings.html\\\\?**id=settings-**\").match(\n        \"http://mydomain:8080/blah/blah/three-columns/settings.html?id=settings-e3c58efe-02e9-44b0-97ac-dd138100cf7c&blah\"\n    )\n\n    assert glob_to_regex(\"\\\\?\").pattern == r\"^\\?$\"\n    assert glob_to_regex(\"\\\\\").pattern == r\"^\\\\$\"\n    assert glob_to_regex(\"\\\\\\\\\").pattern == r\"^\\\\$\"\n    assert glob_to_regex(\"\\\\[\").pattern == r\"^\\[$\"\n    assert glob_to_regex(\"[a-z]\").pattern == r\"^\\[a-z\\]$\"\n    assert (\n        glob_to_regex(\"$^+.\\\\*()|\\\\?\\\\{\\\\}\\\\[\\\\]\").pattern\n        == r\"^\\$\\^\\+\\.\\*\\(\\)\\|\\?\\{\\}\\[\\]$\"\n    )\n\n    # --- url_matches tests ---\n    # Basic exact and wildcard matching\n    assert url_matches(None, \"http://playwright.dev/\", \"http://playwright.dev\")\n    assert url_matches(None, \"http://playwright.dev/?a=b\", \"http://playwright.dev?a=b\")\n    assert url_matches(None, \"http://playwright.dev/\", \"h*://playwright.dev\")\n    assert url_matches(\n        None, \"http://api.playwright.dev/?x=y\", \"http://*.playwright.dev?x=y\"\n    )\n    assert url_matches(None, \"http://playwright.dev/foo/bar\", \"**/foo/**\")\n\n    # Relative path matching with base URL\n    assert url_matches(\"http://playwright.dev\", \"http://playwright.dev/?x=y\", \"?x=y\")\n    assert url_matches(\n        \"http://playwright.dev/foo/\", \"http://playwright.dev/foo/bar?x=y\", \"./bar?x=y\"\n    )\n\n    # Case insensitive matching\n    assert url_matches(\n        None, \"https://playwright.dev/fooBAR\", \"HtTpS://pLaYwRiGhT.dEv/fooBAR\"\n    )\n    assert url_matches(\n        \"http://ignored\",\n        \"https://playwright.dev/fooBAR\",\n        \"HtTpS://pLaYwRiGhT.dEv/fooBAR\",\n    )\n    # Path and search query are case-sensitive\n    assert not url_matches(\n        None, \"https://playwright.dev/foobar\", \"https://playwright.dev/fooBAR\"\n    )\n    assert not url_matches(\n        None, \"https://playwright.dev/foobar?a=b\", \"https://playwright.dev/foobar?A=B\"\n    )\n\n    assert url_matches(None, \"https://localhost:3000/?a=b\", \"**/?a=b\")\n    assert url_matches(None, \"https://localhost:3000/?a=b\", \"**?a=b\")\n    assert url_matches(None, \"https://localhost:3000/?a=b\", \"**=b\")\n\n    # Custom schema.\n    assert url_matches(None, \"my.custom.protocol://foo\", \"my.custom.protocol://**\")\n    assert not url_matches(None, \"my.p://foo\", \"my.{p,y}://**\")\n    assert url_matches(None, \"my.p://foo/\", \"my.{p,y}://**\")\n    assert url_matches(None, \"file:///foo/\", \"f*e://**\")\n\n    # This is not supported, we treat ? as a query separator.\n    assert not url_matches(\n        None,\n        \"http://localhost:8080/Simple/path.js\",\n        \"http://localhost:8080/?imple/path.js\",\n    )\n    assert not url_matches(None, \"http://playwright.dev/\", \"http://playwright.?ev\")\n    assert url_matches(None, \"http://playwright./?ev\", \"http://playwright.?ev\")\n    assert not url_matches(\n        None, \"http://playwright.dev/foo\", \"http://playwright.dev/f??\"\n    )\n    assert url_matches(None, \"http://playwright.dev/f??\", \"http://playwright.dev/f??\")\n    assert url_matches(\n        None, \"http://playwright.dev/?x=y\", r\"http://playwright.dev\\?x=y\"\n    )\n    assert url_matches(\n        None, \"http://playwright.dev/?x=y\", r\"http://playwright.dev/\\?x=y\"\n    )\n    assert url_matches(\n        \"http://playwright.dev/foo\", \"http://playwright.dev/foo?bar\", \"?bar\"\n    )\n    assert url_matches(\n        \"http://playwright.dev/foo\", \"http://playwright.dev/foo?bar\", r\"\\\\?bar\"\n    )\n    assert url_matches(\"http://first.host/\", \"http://second.host/foo\", \"**/foo\")\n    assert url_matches(\"http://playwright.dev/\", \"http://localhost/\", \"*//localhost/\")\n\n    # /**/ should match /.\n    assert url_matches(None, \"https://foo/bar.js\", \"https://foo/**/bar.js\")\n    assert url_matches(None, \"https://foo/bar.js\", \"https://foo/**/**/bar.js\")\n\n    custom_prefixes = [\"about\", \"data\", \"chrome\", \"edge\", \"file\"]\n    for prefix in custom_prefixes:\n        assert url_matches(\n            \"http://playwright.dev/\", f\"{prefix}:blank\", f\"{prefix}:blank\"\n        )\n        assert not url_matches(\n            \"http://playwright.dev/\", f\"{prefix}:blank\", \"http://playwright.dev/\"\n        )\n        assert url_matches(None, f\"{prefix}:blank\", f\"{prefix}:blank\")\n        assert url_matches(None, f\"{prefix}:blank\", f\"{prefix}:*\")\n        assert not url_matches(None, f\"not{prefix}:blank\", f\"{prefix}:*\")\n\n    # Added for Python implementation\n    assert url_matches(\n        None,\n        \"custom://example.com/foo/bar?id=123\",\n        \"{custom,another}://example.com/foo/bar?id=123\",\n    )\n    assert url_matches(\n        None, \"custom://example.com/foo/bar?id=123\", \"**example.com/foo/bar?id=123\"\n    )\n\n\nasync def test_should_not_support_question_in_glob_pattern(\n    page: Page, playwright: Playwright, server: Server\n) -> None:\n    server.set_route(\"/index\", lambda req: (req.write(b\"index-no-hello\"), req.finish()))\n    server.set_route(\n        \"/index123hello\", lambda req: (req.write(b\"index123hello\"), req.finish())\n    )\n    server.set_route(\n        \"/index?hello\", lambda req: (req.write(b\"index?hello\"), req.finish())\n    )\n    server.set_route(\n        \"/index1hello\", lambda req: (req.write(b\"index1hello\"), req.finish())\n    )\n\n    async def handle_any_char(route: Route) -> None:\n        await route.fulfill(body=\"intercepted any character\")\n\n    await page.route(\"**/index?hello\", handle_any_char)\n\n    async def handle_question_mark(route: Route) -> None:\n        await route.fulfill(body=\"intercepted question mark\")\n\n    await page.route(r\"**/index\\?hello\", handle_question_mark)\n\n    await page.goto(server.PREFIX + \"/index?hello\")\n    await expect(page.locator(\"body\")).to_have_text(\"intercepted question mark\")\n\n    await page.goto(server.PREFIX + \"/index\")\n    await expect(page.locator(\"body\")).to_have_text(\"index-no-hello\")\n\n    await page.goto(server.PREFIX + \"/index1hello\")\n    await expect(page.locator(\"body\")).to_have_text(\"index1hello\")\n\n    await page.goto(server.PREFIX + \"/index123hello\")\n    await expect(page.locator(\"body\")).to_have_text(\"index123hello\")\n"
  },
  {
    "path": "tests/async/test_page_select_option.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, TimeoutError\nfrom tests.server import Server\n\n\nasync def test_select_option_should_select_single_option(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", \"blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_select_option_should_select_single_option_by_value(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", \"blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_select_option_should_select_single_option_by_label(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", label=\"Indigo\")\n    assert await page.evaluate(\"result.onInput\") == [\"indigo\"]\n    assert await page.evaluate(\"result.onChange\") == [\"indigo\"]\n\n\nasync def test_select_option_should_select_single_option_by_empty_label(\n    page: Page, server: Server\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"indigo\">Indigo</option>\n            <option value=\"violet\"></option>\n        </select>\n    \"\"\"\n    )\n    assert await page.locator(\"select\").input_value() == \"indigo\"\n    await page.select_option(\"select\", label=\"\")\n    assert await page.locator(\"select\").input_value() == \"violet\"\n\n\nasync def test_select_option_should_select_single_option_by_handle(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\n        \"select\", element=await page.query_selector(\"[id=whiteOption]\")\n    )\n    assert await page.evaluate(\"result.onInput\") == [\"white\"]\n    assert await page.evaluate(\"result.onChange\") == [\"white\"]\n\n\nasync def test_select_option_should_select_single_option_by_index(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", index=2)\n    assert await page.evaluate(\"result.onInput\") == [\"brown\"]\n    assert await page.evaluate(\"result.onChange\") == [\"brown\"]\n\n\nasync def test_select_option_should_select_single_option_by_index_0(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", index=0)\n    assert await page.evaluate(\"result.onInput\") == [\"black\"]\n\n\nasync def test_select_option_should_select_only_first_option(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", [\"blue\", \"green\", \"red\"])\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_select_option_should_not_throw_when_select_causes_navigation(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.eval_on_selector(\n        \"select\",\n        \"select => select.addEventListener('input', () => window.location = '/empty.html')\",\n    )\n    async with page.expect_navigation():\n        await page.select_option(\"select\", \"blue\")\n    assert \"empty.html\" in page.url\n\n\nasync def test_select_option_should_select_multiple_options(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    await page.select_option(\"select\", [\"blue\", \"green\", \"red\"])\n    assert await page.evaluate(\"result.onInput\") == [\"blue\", \"green\", \"red\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\", \"green\", \"red\"]\n\n\nasync def test_select_option_should_select_multiple_options_with_attributes(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    await page.select_option(\n        \"select\",\n        value=\"blue\",\n        label=\"Green\",\n        index=4,\n    )\n    assert await page.evaluate(\"result.onInput\") == [\"blue\", \"gray\", \"green\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\", \"gray\", \"green\"]\n\n\nasync def test_select_option_should_select_option_with_empty_value(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"first\">First</option>\n            <option value=\"\">Second</option>\n        </select>\n    \"\"\"\n    )\n    assert await page.locator(\"select\").input_value() == \"first\"\n    await page.select_option(\"select\", value=\"\")\n    assert await page.locator(\"select\").input_value() == \"\"\n\n\nasync def test_select_option_should_respect_event_bubbling(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", \"blue\")\n    assert await page.evaluate(\"result.onBubblingInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onBubblingChange\") == [\"blue\"]\n\n\nasync def test_select_option_should_throw_when_element_is_not_a__select_(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    with pytest.raises(Error) as exc_info:\n        await page.select_option(\"body\", \"\")\n    assert \"Element is not a <select> element\" in exc_info.value.message\n\n\nasync def test_select_option_should_return_on_no_matched_values(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    with pytest.raises(TimeoutError) as exc_info:\n        await page.select_option(\"select\", [\"42\", \"abc\"], timeout=1000)\n    assert \"Timeout 1000\" in exc_info.value.message\n\n\nasync def test_select_option_should_return_an_array_of_matched_values(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    result = await page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    assert result == [\"black\", \"blue\", \"magenta\"]\n\n\nasync def test_select_option_should_return_an_array_of_one_element_when_multiple_is_not_set(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    result = await page.select_option(\"select\", [\"42\", \"blue\", \"black\", \"magenta\"])\n    assert len(result) == 1\n\n\nasync def test_select_option_should_return_on_no_values(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    result = await page.select_option(\"select\", [])\n    assert result == []\n\n\nasync def test_select_option_should_not_allow_null_items(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    with pytest.raises(Error) as exc_info:\n        await page.select_option(\"select\", [\"blue\", None, \"black\", \"magenta\"])  # type: ignore\n    assert \"expected string, got object\" in exc_info.value.message\n\n\nasync def test_select_option_should_unselect_with_null(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    result = await page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    assert result == [\"black\", \"blue\", \"magenta\"]\n    await page.select_option(\"select\", None)\n    assert await page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\nasync def test_select_option_should_deselect_all_options_when_passed_no_values_for_a_multiple_select(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"makeMultiple()\")\n    await page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    await page.select_option(\"select\", [])\n    assert await page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\nasync def test_select_option_should_deselect_all_options_when_passed_no_values_for_a_select_without_multiple(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    await page.select_option(\"select\", [])\n    assert await page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\nasync def test_select_option_should_work_when_re_defining_top_level_event_class(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.evaluate(\"window.Event = null\")\n    await page.select_option(\"select\", \"blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\nasync def test_select_options_should_fall_back_to_selecting_by_label(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.PREFIX + \"/input/select.html\")\n    await page.select_option(\"select\", \"Blue\")\n    assert await page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert await page.evaluate(\"result.onChange\") == [\"blue\"]\n"
  },
  {
    "path": "tests/async/test_pdf.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.async_api import Page\nfrom tests.server import Server\n\npytestmark = pytest.mark.only_browser(\"chromium\")\n\n\nasync def test_should_be_able_to_save_pdf_file(page: Page, tmp_path: Path) -> None:\n    output_file = tmp_path / \"foo.png\"\n    await page.pdf(path=str(output_file))\n    assert os.path.getsize(output_file) > 0\n\n\nasync def test_should_be_able_capture_pdf_without_path(page: Page) -> None:\n    buffer = await page.pdf()\n    assert buffer\n\n\nasync def test_should_be_able_to_generate_outline(\n    page: Page, server: Server, tmp_path: Path\n) -> None:\n    await page.goto(server.PREFIX + \"/headings.html\")\n    output_file_no_outline = tmp_path / \"outputNoOutline.pdf\"\n    output_file_outline = tmp_path / \"outputOutline.pdf\"\n    await page.pdf(path=output_file_no_outline)\n    await page.pdf(path=output_file_outline, tagged=True, outline=True)\n    assert os.path.getsize(output_file_outline) > os.path.getsize(\n        output_file_no_outline\n    )\n"
  },
  {
    "path": "tests/async/test_popup.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import List, Optional\n\nfrom playwright.async_api import Browser, BrowserContext, Request, Route\nfrom tests.server import Server\nfrom tests.utils import must\n\n\nasync def test_link_navigation_inherit_user_agent_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(user_agent=\"hey\")\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=noopener href=\"/popup/popup.html\">link</a>'\n    )\n    request_waitable = asyncio.create_task(server.wait_for_request(\"/popup/popup.html\"))\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    async with context.expect_page() as page_info:\n        await page.click(\"a\")\n    popup = await page_info.value\n    await popup.wait_for_load_state(\"domcontentloaded\")\n    user_agent = await popup.evaluate(\"window.initialUserAgent\")\n    request = await request_waitable\n    assert user_agent == \"hey\"\n    assert request.requestHeaders.getRawHeaders(\"user-agent\") == [\"hey\"]\n    await context.close()\n\n\nasync def test_link_navigation_respect_routes_from_browser_context(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a target=_blank rel=noopener href=\"empty.html\">link</a>')\n\n    intercepted: List[bool] = []\n\n    async def handle_request(route: Route) -> None:\n        intercepted.append(True)\n        await route.continue_()\n\n    await context.route(\"**/empty.html\", handle_request)\n    async with context.expect_page():\n        await page.click(\"a\")\n    assert intercepted == [True]\n\n\nasync def test_window_open_inherit_user_agent_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(user_agent=\"hey\")\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    request_promise = asyncio.create_task(server.wait_for_request(\"/dummy.html\"))\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    user_agent = await page.evaluate(\n        \"\"\"url => {\n            win = window.open(url)\n            return win.navigator.userAgent\n        }\"\"\",\n        server.PREFIX + \"/dummy.html\",\n    )\n    request = await request_promise\n    assert user_agent == \"hey\"\n    assert request.requestHeaders.getRawHeaders(\"user-agent\") == [\"hey\"]\n    await context.close()\n\n\nasync def test_should_inherit_extra_headers_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(extra_http_headers={\"foo\": \"bar\"})\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    request_promise = asyncio.create_task(server.wait_for_request(\"/dummy.html\"))\n    await asyncio.sleep(0)  # execute scheduled tasks, but don't await them\n    await page.evaluate(\n        \"url => window._popup = window.open(url)\", server.PREFIX + \"/dummy.html\"\n    )\n    request = await request_promise\n    assert request.requestHeaders.getRawHeaders(\"foo\") == [\"bar\"]\n    await context.close()\n\n\nasync def test_should_inherit_offline_from_browser_context(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await context.set_offline(True)\n    online = await page.evaluate(\n        \"\"\"url => {\n            win = window.open(url)\n            return win.navigator.onLine\n        }\"\"\",\n        server.PREFIX + \"/dummy.html\",\n    )\n    assert online is False\n\n\nasync def test_should_inherit_http_credentials_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    server.set_auth(\"/title.html\", \"user\", \"pass\")\n    context = await browser.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window._popup = window.open(url)\", server.PREFIX + \"/title.html\"\n        )\n    popup = await popup_info.value\n    await popup.wait_for_load_state(\"domcontentloaded\")\n    assert await popup.title() == \"Woof-Woof\"\n    await context.close()\n\n\nasync def test_should_inherit_touch_support_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(\n        viewport={\"width\": 400, \"height\": 500}, has_touch=True\n    )\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    has_touch = await page.evaluate(\n        \"\"\"() => {\n            win = window.open('')\n            return 'ontouchstart' in win\n        }\"\"\"\n    )\n\n    assert has_touch\n    await context.close()\n\n\nasync def test_should_inherit_viewport_size_from_browser_context(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(viewport={\"width\": 400, \"height\": 500})\n\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    size = await page.evaluate(\n        \"\"\"() => {\n            win = window.open('about:blank')\n            return { width: win.innerWidth, height: win.innerHeight }\n        }\"\"\"\n    )\n\n    assert size == {\"width\": 400, \"height\": 500}\n    await context.close()\n\n\nasync def test_should_use_viewport_size_from_window_features(\n    browser: Browser, server: Server\n) -> None:\n    context = await browser.new_context(viewport={\"width\": 700, \"height\": 700})\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    size = None\n    async with page.expect_popup() as popup_info:\n        size = await page.evaluate(\n            \"\"\"async () => {\n                const win = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=600,height=300,top=0,left=0');\n                await new Promise(resolve => {\n                    const interval = setInterval(() => {\n                    if (win.innerWidth === 600 && win.innerHeight === 300) {\n                        clearInterval(interval);\n                        resolve();\n                    }\n                    }, 10);\n                });\n                return { width: win.innerWidth, height: win.innerHeight }\n            }\"\"\"\n        )\n    popup = await popup_info.value\n    await popup.set_viewport_size({\"width\": 500, \"height\": 400})\n    await popup.wait_for_load_state()\n    resized = await popup.evaluate(\n        \"() => ({ width: window.innerWidth, height: window.innerHeight })\"\n    )\n    await context.close()\n    assert size == {\"width\": 600, \"height\": 300}\n    assert resized == {\"width\": 500, \"height\": 400}\n\n\nasync def test_should_respect_routes_from_browser_context(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n\n    def handle_request(route: Route, request: Request, intercepted: List[bool]) -> None:\n        asyncio.create_task(route.continue_())\n        intercepted.append(True)\n\n    intercepted: List[bool] = []\n    await context.route(\n        \"**/empty.html\",\n        lambda route, request: handle_request(route, request, intercepted),\n    )\n\n    async with page.expect_popup():\n        await page.evaluate(\n            \"url => window.__popup = window.open(url)\", server.EMPTY_PAGE\n        )\n    assert len(intercepted) == 1\n\n\nasync def test_browser_context_add_init_script_should_apply_to_an_in_process_popup(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.add_init_script(\"window.injected = 123\")\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    injected = await page.evaluate(\n        \"\"\"() => {\n            const win = window.open('about:blank');\n            return win.injected;\n        }\"\"\"\n    )\n\n    assert injected == 123\n\n\nasync def test_browser_context_add_init_script_should_apply_to_a_cross_process_popup(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.add_init_script(\"window.injected = 123\")\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"url => window.open(url)\", server.CROSS_PROCESS_PREFIX + \"/title.html\"\n        )\n    popup = await popup_info.value\n    assert await popup.evaluate(\"injected\") == 123\n    await popup.reload()\n    assert await popup.evaluate(\"injected\") == 123\n\n\nasync def test_should_expose_function_from_browser_context(\n    context: BrowserContext, server: Server\n) -> None:\n    await context.expose_function(\"add\", lambda a, b: a + b)\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    added = await page.evaluate(\n        \"\"\"async () => {\n            win = window.open('about:blank')\n            return win.add(9, 4)\n        }\"\"\"\n    )\n\n    assert added == 13\n\n\nasync def test_should_work(context: BrowserContext) -> None:\n    page = await context.new_page()\n    async with page.expect_popup() as popup_info:\n        await page.evaluate('window.__popup = window.open(\"about:blank\")')\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\")\n\n\nasync def test_should_work_with_window_features(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            'window.__popup = window.open(window.location.href, \"Title\", \"toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0\")'\n        )\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\")\n\n\nasync def test_window_open_emit_for_immediately_closed_popups(\n    context: BrowserContext,\n) -> None:\n    page = await context.new_page()\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"\"\"() => {\n                win = window.open('about:blank')\n                win.close()\n            }\"\"\"\n        )\n    popup = await popup_info.value\n    assert popup\n\n\nasync def test_should_emit_for_immediately_closed_popups(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            \"\"\"() => {\n                win = window.open(window.location.href)\n                win.close()\n            }\"\"\"\n        )\n    popup = await popup_info.value\n    assert popup\n\n\nasync def test_should_be_able_to_capture_alert(context: BrowserContext) -> None:\n    page = await context.new_page()\n    evaluate_task: Optional[asyncio.Future] = None\n\n    async def evaluate() -> None:\n        nonlocal evaluate_task\n        evaluate_task = asyncio.create_task(\n            page.evaluate(\n                \"\"\"() => {\n                const win = window.open('')\n                win.alert('hello')\n            }\"\"\"\n            )\n        )\n\n    [popup, dialog, _] = await asyncio.gather(\n        page.wait_for_event(\"popup\"), context.wait_for_event(\"dialog\"), evaluate()\n    )\n\n    assert dialog.message == \"hello\"\n    assert dialog.page == popup\n    await dialog.dismiss()\n    await must(evaluate_task)\n\n\nasync def test_should_work_with_empty_url(context: BrowserContext) -> None:\n    page = await context.new_page()\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\"() => window.__popup = window.open('')\")\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\")\n\n\nasync def test_should_work_with_noopener_and_no_url(context: BrowserContext) -> None:\n    page = await context.new_page()\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            '() => window.__popup = window.open(undefined, null, \"noopener\")'\n        )\n    popup = await popup_info.value\n    # Chromium reports 'about:blank#blocked' here.\n    assert popup.url.split(\"#\")[0] == \"about:blank\"\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\") is False\n\n\nasync def test_should_work_with_noopener_and_about_blank(\n    context: BrowserContext,\n) -> None:\n    page = await context.new_page()\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            '() => window.__popup = window.open(\"about:blank\", null, \"noopener\")'\n        )\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\") is False\n\n\nasync def test_should_work_with_noopener_and_url(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_popup() as popup_info:\n        await page.evaluate(\n            'url => window.__popup = window.open(url, null, \"noopener\")',\n            server.EMPTY_PAGE,\n        )\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\") is False\n\n\nasync def test_should_work_with_clicking_target__blank(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=\"opener\" href=\"/one-style.html\">yo</a>'\n    )\n    async with page.expect_popup() as popup_info:\n        await page.click(\"a\")\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\")\n    assert popup.main_frame.page == popup\n\n\nasync def test_should_work_with_fake_clicking_target__blank_and_rel_noopener(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=noopener href=\"/one-style.html\">yo</a>'\n    )\n    async with page.expect_popup() as popup_info:\n        await page.eval_on_selector(\"a\", \"a => a.click()\")\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\") is False\n\n\nasync def test_should_work_with_clicking_target__blank_and_rel_noopener(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=noopener href=\"/one-style.html\">yo</a>'\n    )\n    async with page.expect_popup() as popup_info:\n        await page.click(\"a\")\n    popup = await popup_info.value\n    assert await page.evaluate(\"!!window.opener\") is False\n    assert await popup.evaluate(\"!!window.opener\") is False\n\n\nasync def test_should_not_treat_navigations_as_new_popups(\n    context: BrowserContext, server: Server\n) -> None:\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        '<a target=_blank rel=noopener href=\"/one-style.html\">yo</a>'\n    )\n    async with page.expect_popup() as popup_info:\n        await page.click(\"a\")\n    popup = await popup_info.value\n    handled_popups = []\n    page.on(\n        \"popup\",\n        lambda popup: handled_popups.append(True),\n    )\n\n    await popup.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    assert len(handled_popups) == 0\n"
  },
  {
    "path": "tests/async/test_proxy.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport base64\nfrom typing import Callable\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error\nfrom tests.server import Server, TestServerRequest\n\n\nasync def test_should_throw_for_bad_server_value(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\",\n) -> None:\n    with pytest.raises(Error) as exc_info:\n        await browser_factory(proxy={\"server\": 123})\n    assert \"proxy.server: expected string, got number\" in exc_info.value.message\n\n\nasync def test_should_use_proxy(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    browser = await browser_factory(proxy={\"server\": f\"localhost:{server.PORT}\"})\n    page = await browser.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_proxy_should_allow_none_for_optional_settings(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    browser = await browser_factory(\n        proxy={\n            \"server\": f\"localhost:{server.PORT}\",\n            \"username\": None,\n            \"password\": None,\n            \"bypass\": None,\n        }\n    )\n    page = await browser.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_should_use_proxy_for_second_page(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    browser = await browser_factory(proxy={\"server\": f\"localhost:{server.PORT}\"})\n\n    page1 = await browser.new_page()\n    await page1.goto(\"http://non-existent.com/target.html\")\n    assert await page1.title() == \"Served by the proxy\"\n\n    page2 = await browser.new_page()\n    await page2.goto(\"http://non-existent.com/target.html\")\n    assert await page2.title() == \"Served by the proxy\"\n\n\nasync def test_should_work_with_ip_port_notion(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    server.set_route(\n        \"/target.html\",\n        lambda r: (\n            r.write(b\"<html><title>Served by the proxy</title></html>\"),\n            r.finish(),\n        ),\n    )\n    browser = await browser_factory(proxy={\"server\": f\"127.0.0.1:{server.PORT}\"})\n    page = await browser.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Served by the proxy\"\n\n\nasync def test_should_authenticate(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    def handler(req: TestServerRequest) -> None:\n        auth = req.getHeader(\"proxy-authorization\")\n        if not auth:\n            req.setHeader(\n                b\"Proxy-Authenticate\", b'Basic realm=\"Access to internal site\"'\n            )\n            req.setResponseCode(407)\n        else:\n            req.write(f\"<html><title>{auth}</title></html>\".encode(\"utf-8\"))\n        req.finish()\n\n    server.set_route(\"/target.html\", handler)\n\n    browser = await browser_factory(\n        proxy={\n            \"server\": f\"localhost:{server.PORT}\",\n            \"username\": \"user\",\n            \"password\": \"secret\",\n        }\n    )\n    page = await browser.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Basic \" + base64.b64encode(b\"user:secret\").decode(\n        \"utf-8\"\n    )\n\n\nasync def test_should_authenticate_with_empty_password(\n    browser_factory: \"Callable[..., asyncio.Future[Browser]]\", server: Server\n) -> None:\n    def handler(req: TestServerRequest) -> None:\n        auth = req.getHeader(\"proxy-authorization\")\n        if not auth:\n            req.setHeader(\n                b\"Proxy-Authenticate\", b'Basic realm=\"Access to internal site\"'\n            )\n            req.setResponseCode(407)\n        else:\n            req.write(f\"<html><title>{auth}</title></html>\".encode(\"utf-8\"))\n        req.finish()\n\n    server.set_route(\"/target.html\", handler)\n\n    browser = await browser_factory(\n        proxy={\"server\": f\"localhost:{server.PORT}\", \"username\": \"user\", \"password\": \"\"}\n    )\n    page = await browser.new_page()\n    await page.goto(\"http://non-existent.com/target.html\")\n    assert await page.title() == \"Basic \" + base64.b64encode(b\"user:\").decode(\"utf-8\")\n"
  },
  {
    "path": "tests/async/test_queryselector.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.async_api import Browser, Error, Page, Selectors\n\nfrom .utils import Utils\n\n\nasync def test_selectors_register_should_work(\n    selectors: Selectors, browser: Browser, browser_name: str\n) -> None:\n    tag_selector = \"\"\"\n        {\n            create(root, target) {\n                return target.nodeName;\n            },\n            query(root, selector) {\n                return root.querySelector(selector);\n            },\n            queryAll(root, selector) {\n                return Array.from(root.querySelectorAll(selector));\n            }\n        }\"\"\"\n\n    selector_name = f\"tag_{browser_name}\"\n    selector2_name = f\"tag2_{browser_name}\"\n\n    # Register one engine before creating context.\n    await selectors.register(selector_name, tag_selector)\n\n    context = await browser.new_context()\n    # Register another engine after creating context.\n    await selectors.register(selector2_name, tag_selector)\n\n    page = await context.new_page()\n    await page.set_content(\"<div><span></span></div><div></div>\")\n\n    assert (\n        await page.eval_on_selector(f\"{selector_name}=DIV\", \"e => e.nodeName\") == \"DIV\"\n    )\n    assert (\n        await page.eval_on_selector(f\"{selector_name}=SPAN\", \"e => e.nodeName\")\n        == \"SPAN\"\n    )\n    assert (\n        await page.eval_on_selector_all(f\"{selector_name}=DIV\", \"es => es.length\") == 2\n    )\n\n    assert (\n        await page.eval_on_selector(f\"{selector2_name}=DIV\", \"e => e.nodeName\") == \"DIV\"\n    )\n    assert (\n        await page.eval_on_selector(f\"{selector2_name}=SPAN\", \"e => e.nodeName\")\n        == \"SPAN\"\n    )\n    assert (\n        await page.eval_on_selector_all(f\"{selector2_name}=DIV\", \"es => es.length\") == 2\n    )\n\n    # Selector names are case-sensitive.\n    with pytest.raises(Error) as exc:\n        await page.query_selector(\"tAG=DIV\")\n    assert 'Unknown engine \"tAG\" while parsing selector tAG=DIV' in exc.value.message\n\n    await context.close()\n\n\nasync def test_selectors_register_should_work_with_path(\n    selectors: Selectors, page: Page, utils: Utils, assetdir: Path\n) -> None:\n    await utils.register_selector_engine(\n        selectors, \"foo\", path=assetdir / \"sectionselectorengine.js\"\n    )\n    await page.set_content(\"<section></section>\")\n    assert await page.eval_on_selector(\"foo=whatever\", \"e => e.nodeName\") == \"SECTION\"\n\n\nasync def test_selectors_register_should_work_in_main_and_isolated_world(\n    selectors: Selectors, page: Page, utils: Utils\n) -> None:\n    dummy_selector_script = \"\"\"{\n      create(root, target) { },\n      query(root, selector) {\n        return window.__answer;\n      },\n      queryAll(root, selector) {\n        return window['__answer'] ? [window['__answer'], document.body, document.documentElement] : [];\n      }\n    }\"\"\"\n\n    await utils.register_selector_engine(selectors, \"main\", dummy_selector_script)\n    await utils.register_selector_engine(\n        selectors, \"isolated\", dummy_selector_script, content_script=True\n    )\n    await page.set_content(\"<div><span><section></section></span></div>\")\n    await page.evaluate('() => window.__answer = document.querySelector(\"span\")')\n    # Works in main if asked.\n    assert await page.eval_on_selector(\"main=ignored\", \"e => e.nodeName\") == \"SPAN\"\n    assert (\n        await page.eval_on_selector(\"css=div >> main=ignored\", \"e => e.nodeName\")\n        == \"SPAN\"\n    )\n    assert await page.eval_on_selector_all(\n        \"main=ignored\", \"es => window.__answer !== undefined\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"main=ignored\", \"es => es.filter(e => e).length\"\n        )\n        == 3\n    )\n    # Works in isolated by default.\n    assert await page.query_selector(\"isolated=ignored\") is None\n    assert await page.query_selector(\"css=div >> isolated=ignored\") is None\n    # $$eval always works in main, to avoid adopting nodes one by one.\n    assert await page.eval_on_selector_all(\n        \"isolated=ignored\", \"es => window.__answer !== undefined\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"isolated=ignored\", \"es => es.filter(e => e).length\"\n        )\n        == 3\n    )\n    # At least one engine in main forces all to be in main.\n    assert (\n        await page.eval_on_selector(\n            \"main=ignored >> isolated=ignored\", \"e => e.nodeName\"\n        )\n        == \"SPAN\"\n    )\n    assert (\n        await page.eval_on_selector(\n            \"isolated=ignored >> main=ignored\", \"e => e.nodeName\"\n        )\n        == \"SPAN\"\n    )\n    # Can be chained to css.\n    assert (\n        await page.eval_on_selector(\"main=ignored >> css=section\", \"e => e.nodeName\")\n        == \"SECTION\"\n    )\n\n\nasync def test_selectors_register_should_handle_errors(\n    selectors: Selectors, page: Page, utils: Utils\n) -> None:\n    with pytest.raises(Error) as exc:\n        await page.query_selector(\"neverregister=ignored\")\n    assert (\n        'Unknown engine \"neverregister\" while parsing selector neverregister=ignored'\n        in exc.value.message\n    )\n\n    dummy_selector_engine_script = \"\"\"{\n      create(root, target) {\n        return target.nodeName;\n      },\n      query(root, selector) {\n        return root.querySelector('dummy');\n      },\n      queryAll(root, selector) {\n        return Array.from(root.query_selector_all('dummy'));\n      }\n    }\"\"\"\n\n    with pytest.raises(Error) as exc:\n        await selectors.register(\"$\", dummy_selector_engine_script)\n    assert (\n        exc.value.message\n        == \"Selectors.register: Selector engine name may only contain [a-zA-Z0-9_] characters\"\n    )\n\n    # Selector names are case-sensitive.\n    await utils.register_selector_engine(\n        selectors, \"dummy\", dummy_selector_engine_script\n    )\n    await utils.register_selector_engine(\n        selectors, \"duMMy\", dummy_selector_engine_script\n    )\n\n    with pytest.raises(Error) as exc:\n        await selectors.register(\"dummy\", dummy_selector_engine_script)\n    assert (\n        exc.value.message\n        == 'Selectors.register: \"dummy\" selector engine has been already registered'\n    )\n\n    with pytest.raises(Error) as exc:\n        await selectors.register(\"css\", dummy_selector_engine_script)\n    assert (\n        exc.value.message == 'Selectors.register: \"css\" is a predefined selector engine'\n    )\n\n\nasync def test_should_work_with_layout_selectors(page: Page) -> None:\n    #        +--+  +--+\n    #        | 1|  | 2|\n    #        +--+  ++-++\n    #        | 3|   | 4|\n    #   +-------+  ++-++\n    #   |   0   |  | 5|\n    #   | +--+  +--+--+\n    #   | | 6|  | 7|\n    #   | +--+  +--+\n    #   |       |\n    #   O-------+\n    #           +--+\n    #           | 8|\n    #           +--++--+\n    #               | 9|\n    #               +--+\n\n    boxes = [\n        # x, y, width, height\n        [0, 0, 150, 150],\n        [100, 200, 50, 50],\n        [200, 200, 50, 50],\n        [100, 150, 50, 50],\n        [201, 150, 50, 50],\n        [200, 100, 50, 50],\n        [50, 50, 50, 50],\n        [150, 50, 50, 50],\n        [150, -51, 50, 50],\n        [201, -101, 50, 50],\n    ]\n    await page.set_content(\n        '<container style=\"width: 500px; height: 500px; position: relative;\"></container>'\n    )\n    await page.eval_on_selector(\n        \"container\",\n        \"\"\"(container, boxes) => {\n    for (let i = 0; i < boxes.length; i++) {\n      const div = document.createElement('div');\n      div.style.position = 'absolute';\n      div.style.overflow = 'hidden';\n      div.style.boxSizing = 'border-box';\n      div.style.border = '1px solid black';\n      div.id = 'id' + i;\n      div.textContent = 'id' + i;\n      const box = boxes[i];\n      div.style.left = box[0] + 'px';\n      // Note that top is a flipped y coordinate.\n      div.style.top = (250 - box[1] - box[3]) + 'px';\n      div.style.width = box[2] + 'px';\n      div.style.height = box[3] + 'px';\n      container.appendChild(div);\n      const span = document.createElement('span');\n      span.textContent = '' + i;\n      div.appendChild(span);\n    }\n  }\"\"\",\n        boxes,\n    )\n\n    assert await page.eval_on_selector(\"div:right-of(#id6)\", \"e => e.id\") == \"id7\"\n    assert await page.eval_on_selector(\"div:right-of(#id1)\", \"e => e.id\") == \"id2\"\n    assert await page.eval_on_selector(\"div:right-of(#id3)\", \"e => e.id\") == \"id4\"\n    assert await page.query_selector(\"div:right-of(#id4)\") is None\n    assert await page.eval_on_selector(\"div:right-of(#id0)\", \"e => e.id\") == \"id7\"\n    assert await page.eval_on_selector(\"div:right-of(#id8)\", \"e => e.id\") == \"id9\"\n    assert (\n        await page.eval_on_selector_all(\n            \"div:right-of(#id3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id2,id5,id7,id8,id9\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:right-of(#id3, 50)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id2,id5,id7,id8\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:right-of(#id3, 49)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id8\"\n    )\n\n    assert await page.eval_on_selector(\"div:left-of(#id2)\", \"e => e.id\") == \"id1\"\n    assert await page.query_selector(\"div:left-of(#id0)\") is None\n    assert await page.eval_on_selector(\"div:left-of(#id5)\", \"e => e.id\") == \"id0\"\n    assert await page.eval_on_selector(\"div:left-of(#id9)\", \"e => e.id\") == \"id8\"\n    assert await page.eval_on_selector(\"div:left-of(#id4)\", \"e => e.id\") == \"id3\"\n    assert (\n        await page.eval_on_selector_all(\n            \"div:left-of(#id5)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id7,id3,id1,id6,id8\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:left-of(#id5, 3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id8\"\n    )\n\n    assert await page.eval_on_selector(\"div:above(#id0)\", \"e => e.id\") == \"id3\"\n    assert await page.eval_on_selector(\"div:above(#id5)\", \"e => e.id\") == \"id4\"\n    assert await page.eval_on_selector(\"div:above(#id7)\", \"e => e.id\") == \"id5\"\n    assert await page.eval_on_selector(\"div:above(#id8)\", \"e => e.id\") == \"id0\"\n    assert await page.eval_on_selector(\"div:above(#id9)\", \"e => e.id\") == \"id8\"\n    assert await page.query_selector(\"div:above(#id2)\") is None\n    assert (\n        await page.eval_on_selector_all(\n            \"div:above(#id5)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id2,id3,id1\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:above(#id5, 20)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id3\"\n    )\n\n    assert await page.eval_on_selector(\"div:below(#id4)\", \"e => e.id\") == \"id5\"\n    assert await page.eval_on_selector(\"div:below(#id3)\", \"e => e.id\") == \"id0\"\n    assert await page.eval_on_selector(\"div:below(#id2)\", \"e => e.id\") == \"id4\"\n    assert await page.eval_on_selector(\"div:below(#id6)\", \"e => e.id\") == \"id8\"\n    assert await page.eval_on_selector(\"div:below(#id7)\", \"e => e.id\") == \"id8\"\n    assert await page.eval_on_selector(\"div:below(#id8)\", \"e => e.id\") == \"id9\"\n    assert await page.query_selector(\"div:below(#id9)\") is None\n    assert (\n        await page.eval_on_selector_all(\n            \"div:below(#id3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id6,id7,id8,id9\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:below(#id3, 105)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id6,id7\"\n    )\n\n    assert await page.eval_on_selector(\"div:near(#id0)\", \"e => e.id\") == \"id3\"\n    assert (\n        await page.eval_on_selector_all(\n            \"div:near(#id7)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id3,id6\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:near(#id0)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id3,id6,id7,id8,id1,id5\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:near(#id6)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id3,id7\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:near(#id6, 10)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"div:near(#id0, 100)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id3,id6,id7,id8,id1,id5,id4,id2\"\n    )\n\n    assert (\n        await page.eval_on_selector_all(\n            \"div:below(#id5):above(#id8)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id6\"\n    )\n    assert (\n        await page.eval_on_selector(\"div:below(#id5):above(#id8)\", \"e => e.id\") == \"id7\"\n    )\n\n    assert (\n        await page.eval_on_selector_all(\n            \"div:right-of(#id0) + div:above(#id8)\",\n            \"els => els.map(e => e.id).join(',')\",\n        )\n        == \"id5,id6,id3\"\n    )\n\n    with pytest.raises(Error) as exc_info:\n        await page.query_selector(\":near(50)\")\n    assert (\n        '\"near\" engine expects a selector list and optional maximum distance in pixels'\n        in exc_info.value.message\n    )\n    with pytest.raises(Error) as exc_info:\n        await page.query_selector('left-of=\"div\"')\n    assert '\"left-of\" selector cannot be first' in exc_info.value.message\n"
  },
  {
    "path": "tests/async/test_request_continue.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Optional\n\nfrom playwright.async_api import Page, Route\nfrom tests.server import Server, TestServerRequest\n\n\nasync def test_request_continue_should_work(page: Page, server: Server) -> None:\n    await page.route(\"**/*\", lambda route: asyncio.create_task(route.continue_()))\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_request_continue_should_amend_http_headers(\n    page: Page, server: Server\n) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(\n            route.continue_(headers={**route.request.headers, \"FOO\": \"bar\"})\n        ),\n    )\n\n    await page.goto(server.EMPTY_PAGE)\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate('() => fetch(\"/sleep.zzz\")'),\n    )\n    assert request.getHeader(\"foo\") == \"bar\"\n\n\nasync def test_request_continue_should_amend_method(page: Page, server: Server) -> None:\n    server_request = asyncio.create_task(server.wait_for_request(\"/sleep.zzz\"))\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/*\", lambda route: asyncio.create_task(route.continue_(method=\"POST\"))\n    )\n    [request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate('() => fetch(\"/sleep.zzz\")'),\n    )\n    assert request.method.decode() == \"POST\"\n    assert (await server_request).method.decode() == \"POST\"\n\n\nasync def test_request_continue_should_amend_method_on_main_request(\n    page: Page, server: Server\n) -> None:\n    request = asyncio.create_task(server.wait_for_request(\"/empty.html\"))\n    await page.route(\n        \"**/*\", lambda route: asyncio.create_task(route.continue_(method=\"POST\"))\n    )\n    await page.goto(server.EMPTY_PAGE)\n    assert (await request).method.decode() == \"POST\"\n\n\nasync def test_request_continue_should_amend_post_data(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(route.continue_(post_data=b\"doggo\")),\n    )\n\n    [server_request, _] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\n            \"\"\"\n            () => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\n            \"\"\"\n        ),\n    )\n    assert server_request.post_body\n    assert server_request.post_body.decode() == \"doggo\"\n\n\nasync def test_should_override_request_url(page: Page, server: Server) -> None:\n    request = asyncio.create_task(server.wait_for_request(\"/empty.html\"))\n    await page.route(\n        \"**/foo\",\n        lambda route: asyncio.create_task(route.continue_(url=server.EMPTY_PAGE)),\n    )\n\n    await page.goto(server.PREFIX + \"/foo\")\n    assert (await request).method == b\"GET\"\n\n\nasync def test_should_raise_except(page: Page, server: Server) -> None:\n    exc_fut: \"asyncio.Future[Optional[Exception]]\" = asyncio.Future()\n\n    async def capture_exception(route: Route) -> None:\n        try:\n            await route.continue_(url=\"file:///tmp/does-not-exist\")\n            exc_fut.set_result(None)\n        except Exception as e:\n            exc_fut.set_result(e)\n\n    await page.route(\"**/*\", capture_exception)\n    asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    assert \"New URL must have same protocol as overridden URL\" in str(await exc_fut)\n\n\nasync def test_should_amend_utf8_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(route.continue_(post_data=\"пушкин\")),\n    )\n\n    [server_request, result] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body\n    assert server_request.post_body.decode(\"utf8\") == \"пушкин\"\n\n\nasync def test_should_amend_binary_post_data(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"**/*\",\n        lambda route: asyncio.create_task(\n            route.continue_(post_data=b\"\\x00\\x01\\x02\\x03\\x04\")\n        ),\n    )\n\n    [server_request, result] = await asyncio.gather(\n        server.wait_for_request(\"/sleep.zzz\"),\n        page.evaluate(\"fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\"),\n    )\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body == b\"\\x00\\x01\\x02\\x03\\x04\"\n\n\nasync def test_continue_should_not_change_multipart_form_data_body(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    server.set_route(\n        \"/upload\",\n        lambda context: (\n            context.write(b\"done\"),\n            context.setHeader(\"Content-Type\", \"text/plain\"),\n            context.finish(),\n        ),\n    )\n\n    async def send_form_data() -> TestServerRequest:\n        req_task = asyncio.create_task(server.wait_for_request(\"/upload\"))\n        status = await page.evaluate(\n            \"\"\"async () => {\n            const newFile = new File(['file content'], 'file.txt');\n            const formData = new FormData();\n            formData.append('file', newFile);\n            const response = await fetch('/upload', {\n                method: 'POST',\n                credentials: 'include',\n                body: formData,\n            });\n            return response.status;\n        }\"\"\"\n        )\n        req = await req_task\n        assert status == 200\n        return req\n\n    req_before = await send_form_data()\n    await page.route(\"**/*\", lambda route: route.continue_())\n    req_after = await send_form_data()\n\n    file_content = (\n        'Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\"\\r\\n'\n        \"Content-Type: application/octet-stream\\r\\n\"\n        \"\\r\\n\"\n        \"file content\\r\\n\"\n        \"------\"\n    )\n    assert req_before.post_body\n    assert req_after.post_body\n    assert file_content in req_before.post_body.decode()\n    assert file_content in req_after.post_body.decode()\n"
  },
  {
    "path": "tests/async/test_request_fulfill.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.async_api import Page, Route\nfrom tests.server import Server\n\n\nasync def test_should_fetch_original_request_and_fulfill(\n    page: Page, server: Server\n) -> None:\n    async def handle(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(response=response)\n\n    await page.route(\"**/*\", handle)\n    response = await page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 200\n    assert await page.title() == \"Woof-Woof\"\n\n\nasync def test_should_fulfill_json(page: Page, server: Server) -> None:\n    async def handle(route: Route) -> None:\n        await route.fulfill(status=201, headers={\"foo\": \"bar\"}, json={\"bar\": \"baz\"})\n\n    await page.route(\"**/*\", handle)\n\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert await response.json() == {\"bar\": \"baz\"}\n\n\nasync def test_should_fulfill_json_overriding_existing_response(\n    page: Page, server: Server\n) -> None:\n    server.set_route(\n        \"/tags\",\n        lambda request: (\n            request.setHeader(\"foo\", \"bar\"),\n            request.write('{\"tags\": [\"a\", \"b\"]}'.encode()),\n            request.finish(),\n        ),\n    )\n\n    original = {}\n\n    async def handle(route: Route) -> None:\n        response = await route.fetch()\n        json = await response.json()\n        original[\"tags\"] = json[\"tags\"]\n        json[\"tags\"] = [\"c\"]\n        await route.fulfill(response=response, json=json)\n\n    await page.route(\"**/*\", handle)\n\n    response = await page.goto(server.PREFIX + \"/tags\")\n    assert response\n    assert response.status == 200\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert response.headers[\"foo\"] == \"bar\"\n    assert original[\"tags\"] == [\"a\", \"b\"]\n    assert await response.json() == {\"tags\": [\"c\"]}\n"
  },
  {
    "path": "tests/async/test_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom pathlib import Path\n\nfrom twisted.web import http\n\nfrom playwright.async_api import Page, Route\nfrom tests.server import Server\n\n\nasync def test_should_fulfill_intercepted_response(page: Page, server: Server) -> None:\n    async def handle(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(\n            response=response,\n            status=201,\n            headers={\"foo\": \"bar\"},\n            content_type=\"text/plain\",\n            body=\"Yo, page!\",\n        )\n\n    await page.route(\"**/*\", handle)\n    response = await page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 201\n    assert response.headers[\"foo\"] == \"bar\"\n    assert response.headers[\"content-type\"] == \"text/plain\"\n    assert await page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\nasync def test_should_fulfill_response_with_empty_body(\n    page: Page, server: Server\n) -> None:\n    async def handle(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(\n            response=response, status=201, body=\"\", headers={\"content-length\": \"0\"}\n        )\n\n    await page.route(\"**/*\", handle)\n    response = await page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"\"\n\n\nasync def test_should_override_with_defaults_when_intercepted_response_not_provided(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"my content\".encode())\n        request.finish()\n\n    server.set_route(\"/empty.html\", server_handler)\n\n    async def handle(route: Route) -> None:\n        await page.request.fetch(route.request)\n        await route.fulfill(status=201)\n\n    await page.route(\"**/*\", handle)\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"\"\n    if browser_name == \"webkit\":\n        assert response.headers == {\"content-type\": \"text/plain\"}\n    else:\n        assert response.headers == {}\n\n\nasync def test_should_fulfill_with_any_response(page: Page, server: Server) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"Woo-hoo\".encode())\n        request.finish()\n\n    server.set_route(\"/sample\", server_handler)\n    sample_response = await page.request.get(server.PREFIX + \"/sample\")\n    await page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            response=sample_response, status=201, content_type=\"text/plain\"\n        ),\n    )\n    response = await page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert await response.text() == \"Woo-hoo\"\n    assert response.headers[\"foo\"] == \"bar\"\n\n\nasync def test_should_support_fulfill_after_intercept(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    request_future = asyncio.create_task(server.wait_for_request(\"/title.html\"))\n\n    async def handle_route(route: Route) -> None:\n        response = await page.request.fetch(route.request)\n        await route.fulfill(response=response)\n\n    await page.route(\"**\", handle_route)\n    response = await page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    request = await request_future\n    assert request.uri.decode() == \"/title.html\"\n    original = (assetdir / \"title.html\").read_text()\n    assert await response.text() == original\n\n\nasync def test_should_give_access_to_the_intercepted_response(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_task: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\"**/title.html\", lambda route: route_task.set_result(route))\n\n    eval_task = asyncio.create_task(\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + \"/title.html\")\n    )\n\n    route = await route_task\n    response = await page.request.fetch(route.request)\n\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.url.endswith(\"/title.html\") is True\n    assert response.headers[\"content-type\"] == \"text/html; charset=utf-8\"\n    assert list(\n        filter(\n            lambda header: header[\"name\"].lower() == \"content-type\",\n            response.headers_array,\n        )\n    ) == [{\"name\": \"Content-Type\", \"value\": \"text/html; charset=utf-8\"}]\n\n    await asyncio.gather(\n        route.fulfill(response=response),\n        eval_task,\n    )\n\n\nasync def test_should_give_access_to_the_intercepted_response_body(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_task: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\"**/simple.json\", lambda route: route_task.set_result(route))\n\n    eval_task = asyncio.create_task(\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + \"/simple.json\")\n    )\n\n    route = await route_task\n    response = await page.request.fetch(route.request)\n\n    assert await response.text() == '{\"foo\": \"bar\"}\\n'\n\n    await asyncio.gather(\n        route.fulfill(response=response),\n        eval_task,\n    )\n\n\nasync def test_should_intercept_by_glob(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.route(\n        \"http://localhos**?*oo\",\n        lambda route: route.fulfill(body=\"intercepted\", status=200),\n    )\n\n    result = await page.evaluate(\n        \"url => fetch(url).then(r => r.text())\", server.PREFIX + \"/?foo\"\n    )\n\n    assert result == \"intercepted\"\n"
  },
  {
    "path": "tests/async/test_resource_timing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.async_api import Browser, Page\nfrom tests.server import Server\n\n\nasync def test_should_work(page: Page, server: Server) -> None:\n    async with page.expect_event(\"requestfinished\") as request_info:\n        await page.goto(server.EMPTY_PAGE)\n    request = await request_info.value\n    timing = request.timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= timing[\"connectEnd\"]\n    assert timing[\"responseStart\"] >= timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n\n\nasync def test_should_work_for_subresource(\n    page: Page, server: Server, is_win: bool, is_mac: bool, is_webkit: bool\n) -> None:\n    if is_webkit and (is_mac or is_win):\n        pytest.skip()\n    requests = []\n    page.on(\"requestfinished\", lambda request: requests.append(request))\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    assert len(requests) >= 2\n    timing = requests[1].timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= 0\n    assert timing[\"responseStart\"] > timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n\n\nasync def test_should_work_for_ssl(browser: Browser, https_server: Server) -> None:\n    page = await browser.new_page(ignore_https_errors=True)\n    async with page.expect_event(\"requestfinished\") as request_info:\n        await page.goto(https_server.EMPTY_PAGE)\n    request = await request_info.value\n    timing = request.timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= timing[\"connectEnd\"]\n    assert timing[\"responseStart\"] >= timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n    await page.close()\n\n\n@pytest.mark.skip_browser(\"webkit\")  # In WebKit, redirects don\"t carry the timing info\nasync def test_should_work_for_redirect(page: Page, server: Server) -> None:\n    server.set_redirect(\"/foo.html\", \"/empty.html\")\n    responses = []\n    page.on(\"response\", lambda response: responses.append(response))\n    await page.goto(server.PREFIX + \"/foo.html\")\n    for r in responses:\n        await r.finished()\n\n    assert len(responses) == 2\n    assert responses[0].url == server.PREFIX + \"/foo.html\"\n    assert responses[1].url == server.PREFIX + \"/empty.html\"\n\n    timing1 = responses[0].request.timing\n    verify_connections_timing_consistency(timing1)\n    assert timing1[\"requestStart\"] >= timing1[\"connectEnd\"]\n    assert timing1[\"responseStart\"] > timing1[\"requestStart\"]\n    assert timing1[\"responseEnd\"] >= timing1[\"responseStart\"]\n    assert timing1[\"responseEnd\"] < 10000\n\n    timing2 = responses[1].request.timing\n    verify_connections_timing_consistency(timing2)\n    assert timing2[\"requestStart\"] >= 0\n    assert timing2[\"responseStart\"] > timing2[\"requestStart\"]\n    assert timing2[\"responseEnd\"] >= timing2[\"responseStart\"]\n    assert timing2[\"responseEnd\"] < 10000\n\n\ndef verify_timing_value(value: float, previous: float) -> None:\n    assert value == -1 or value > 0 and value >= previous\n\n\ndef verify_connections_timing_consistency(timing: Dict) -> None:\n    verify_timing_value(timing[\"domainLookupStart\"], -1)\n    verify_timing_value(timing[\"domainLookupEnd\"], timing[\"domainLookupStart\"])\n    verify_timing_value(timing[\"connectStart\"], timing[\"domainLookupEnd\"])\n    verify_timing_value(timing[\"secureConnectionStart\"], timing[\"connectStart\"])\n    verify_timing_value(timing[\"connectEnd\"], timing[\"secureConnectionStart\"])\n"
  },
  {
    "path": "tests/async/test_route_web_socket.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport re\nfrom typing import Any, Awaitable, Callable, Literal, Tuple, Union\n\nfrom playwright.async_api import Frame, Page, WebSocketRoute\nfrom playwright.async_api._generated import Browser\nfrom tests.server import Server, WebSocketProtocol\n\n\nasync def assert_equal(\n    actual_cb: Callable[[], Union[Any, Awaitable[Any]]], expected: Any\n) -> None:\n    __tracebackhide__ = True\n    start_time = asyncio.get_event_loop().time()\n    attempts = 0\n    while True:\n        actual = actual_cb()\n        if asyncio.iscoroutine(actual):\n            actual = await actual\n        if actual == expected:\n            return\n        attempts += 1\n        if asyncio.get_event_loop().time() - start_time > 5:\n            raise TimeoutError(f\"Timed out after 10 seconds. Last actual was: {actual}\")\n        await asyncio.sleep(0.2)\n\n\nasync def setup_ws(\n    target: Union[Page, Frame],\n    port: int,\n    protocol: Union[Literal[\"blob\"], Literal[\"arraybuffer\"]],\n) -> None:\n    await target.goto(\"about:blank\")\n    await target.evaluate(\n        \"\"\"({ port, binaryType }) => {\n    window.log = [];\n    window.ws = new WebSocket('ws://localhost:' + port + '/ws');\n    window.ws.binaryType = binaryType;\n    window.ws.addEventListener('open', () => window.log.push('open'));\n    window.ws.addEventListener('close', event => window.log.push(`close code=${event.code} reason=${event.reason} wasClean=${event.wasClean}`));\n    window.ws.addEventListener('error', event => window.log.push(`error`));\n    window.ws.addEventListener('message', async event => {\n      let data;\n      if (typeof event.data === 'string')\n        data = event.data;\n      else if (event.data instanceof Blob)\n        data = 'blob:' + await event.data.text();\n      else\n        data = 'arraybuffer:' + await (new Blob([event.data])).text();\n      window.log.push(`message: data=${data} origin=${event.origin} lastEventId=${event.lastEventId}`);\n    });\n    window.wsOpened = new Promise(f => window.ws.addEventListener('open', () => f()));\n  }\"\"\",\n        {\"port\": port, \"binaryType\": protocol},\n    )\n\n\nasync def test_should_work_with_ws_close(page: Page, server: Server) -> None:\n    future: asyncio.Future[WebSocketRoute] = asyncio.Future()\n\n    def _handle_ws(ws: WebSocketRoute) -> None:\n        ws.connect_to_server()\n        future.set_result(ws)\n\n    await page.route_web_socket(re.compile(\".*\"), _handle_ws)\n\n    ws_task = server.wait_for_web_socket()\n    await setup_ws(page, server.PORT, \"blob\")\n    ws = await ws_task\n\n    route = await future\n    route.send(\"hello\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    closed_promise: asyncio.Future[Tuple[int, str]] = asyncio.Future()\n    ws.events.once(\n        \"close\", lambda code, reason: closed_promise.set_result((code, reason))\n    )\n    await route.close(code=3009, reason=\"oops\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=\",\n            \"close code=3009 reason=oops wasClean=true\",\n        ],\n    )\n    assert await closed_promise == (3009, \"oops\")\n\n\nasync def test_should_pattern_match(page: Page, server: Server) -> None:\n    await page.route_web_socket(\n        re.compile(r\".*/ws$\"), lambda ws: ws.connect_to_server()\n    )\n    await page.route_web_socket(\n        \"**/mock-ws\", lambda ws: ws.on_message(lambda message: ws.send(\"mock-response\"))\n    )\n\n    ws_task = server.wait_for_web_socket()\n    await page.goto(\"about:blank\")\n    await page.evaluate(\n        \"\"\"async ({ port }) => {\n        window.log = [];\n        window.ws1 = new WebSocket('ws://localhost:' + port + '/ws');\n        window.ws1.addEventListener('message', event => window.log.push(`ws1:${event.data}`));\n        window.ws2 = new WebSocket('ws://localhost:' + port + '/something/something/mock-ws');\n        window.ws2.addEventListener('message', event => window.log.push(`ws2:${event.data}`));\n        await Promise.all([\n            new Promise(f => window.ws1.addEventListener('open', f)),\n            new Promise(f => window.ws2.addEventListener('open', f)),\n        ]);\n    }\"\"\",\n        {\"port\": server.PORT},\n    )\n\n    ws = await ws_task\n    ws.events.on(\"message\", lambda payload, isBinary: ws.sendMessage(b\"response\"))\n\n    await page.evaluate(\"window.ws1.send('request')\")\n    await assert_equal(lambda: page.evaluate(\"window.log\"), [\"ws1:response\"])\n\n    await page.evaluate(\"window.ws2.send('request')\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"), [\"ws1:response\", \"ws2:mock-response\"]\n    )\n\n\nasync def test_should_work_with_server(page: Page, server: Server) -> None:\n    future: asyncio.Future[WebSocketRoute] = asyncio.Future()\n\n    async def _handle_ws(ws: WebSocketRoute) -> None:\n        server = ws.connect_to_server()\n\n        def _ws_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-respond\":\n                ws.send(\"response\")\n                return\n            if message == \"to-block\":\n                return\n            if message == \"to-modify\":\n                server.send(\"modified\")\n                return\n            server.send(message)\n\n        ws.on_message(_ws_on_message)\n\n        def _server_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-block\":\n                return\n            if message == \"to-modify\":\n                ws.send(\"modified\")\n                return\n            ws.send(message)\n\n        server.on_message(_server_on_message)\n        server.send(\"fake\")\n        future.set_result(ws)\n\n    await page.route_web_socket(re.compile(\".*\"), _handle_ws)\n    ws_task = server.wait_for_web_socket()\n    log = []\n\n    def _once_web_socket_connection(ws: WebSocketProtocol) -> None:\n        ws.events.on(\n            \"message\", lambda data, is_binary: log.append(f\"message: {data.decode()}\")\n        )\n        ws.events.on(\n            \"close\",\n            lambda code, reason: log.append(f\"close: code={code} reason={reason}\"),\n        )\n\n    server.once_web_socket_connection(_once_web_socket_connection)\n\n    await setup_ws(page, server.PORT, \"blob\")\n    ws = await ws_task\n    await assert_equal(lambda: log, [\"message: fake\"])\n\n    ws.sendMessage(b\"to-modify\")\n    ws.sendMessage(b\"to-block\")\n    ws.sendMessage(b\"pass-server\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    await page.evaluate(\n        \"\"\"() => {\n        window.ws.send('to-respond');\n        window.ws.send('to-modify');\n        window.ws.send('to-block');\n        window.ws.send('pass-client');\n    }\"\"\"\n    )\n    await assert_equal(\n        lambda: log, [\"message: fake\", \"message: modified\", \"message: pass-client\"]\n    )\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    route = await future\n    route.send(\"another\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=another origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    await page.evaluate(\n        \"\"\"() => {\n        window.ws.send('pass-client-2');\n    }\"\"\"\n    )\n    await assert_equal(\n        lambda: log,\n        [\n            \"message: fake\",\n            \"message: modified\",\n            \"message: pass-client\",\n            \"message: pass-client-2\",\n        ],\n    )\n\n    await page.evaluate(\n        \"\"\"() => {\n        window.ws.close(3009, 'problem');\n    }\"\"\"\n    )\n    await assert_equal(\n        lambda: log,\n        [\n            \"message: fake\",\n            \"message: modified\",\n            \"message: pass-client\",\n            \"message: pass-client-2\",\n            \"close: code=3009 reason=problem\",\n        ],\n    )\n\n\nasync def test_should_work_without_server(page: Page, server: Server) -> None:\n    future: asyncio.Future[WebSocketRoute] = asyncio.Future()\n\n    async def _handle_ws(ws: WebSocketRoute) -> None:\n        def _ws_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-respond\":\n                ws.send(\"response\")\n\n        ws.on_message(_ws_on_message)\n        future.set_result(ws)\n\n    await page.route_web_socket(re.compile(\".*\"), _handle_ws)\n    await setup_ws(page, server.PORT, \"blob\")\n\n    await page.evaluate(\n        \"\"\"async () => {\n        await window.wsOpened;\n        window.ws.send('to-respond');\n        window.ws.send('to-block');\n        window.ws.send('to-respond');\n    }\"\"\"\n    )\n\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    route = await future\n    route.send(\"another\")\n    # wait for the message to be processed\n    await page.wait_for_timeout(100)\n    await route.close(code=3008, reason=\"oops\")\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=another origin=ws://localhost:{server.PORT} lastEventId=\",\n            \"close code=3008 reason=oops wasClean=true\",\n        ],\n    )\n\n\nasync def test_should_work_with_base_url(browser: Browser, server: Server) -> None:\n    context = await browser.new_context(base_url=f\"http://localhost:{server.PORT}\")\n    page = await context.new_page()\n\n    async def _handle_ws(ws: WebSocketRoute) -> None:\n        ws.on_message(lambda message: ws.send(message))\n\n    await page.route_web_socket(\"/ws\", _handle_ws)\n    await setup_ws(page, server.PORT, \"blob\")\n\n    await page.evaluate(\n        \"\"\"async () => {\n        await window.wsOpened;\n        window.ws.send('echo');\n    }\"\"\"\n    )\n\n    await assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=echo origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n\nasync def test_should_work_with_no_trailing_slash(page: Page, server: Server) -> None:\n    log: list[str] = []\n\n    async def handle_ws(ws: WebSocketRoute) -> None:\n        def on_message(message: Union[str, bytes]) -> None:\n            assert isinstance(message, str)\n            log.append(message)\n            ws.send(\"response\")\n\n        ws.on_message(on_message)\n\n    # No trailing slash in the route pattern\n    await page.route_web_socket(f\"ws://localhost:{server.PORT}\", handle_ws)\n\n    await page.goto(\"about:blank\")\n    await page.evaluate(\n        \"\"\"({ port }) => {\n            window.log = [];\n            // No trailing slash in WebSocket URL\n            window.ws = new WebSocket('ws://localhost:' + port);\n            window.ws.addEventListener('message', event => window.log.push(event.data));\n        }\"\"\",\n        {\"port\": server.PORT},\n    )\n\n    await assert_equal(\n        lambda: page.evaluate(\"window.ws.readyState\"), 1  # WebSocket.OPEN\n    )\n    await page.evaluate(\"window.ws.send('query')\")\n    await assert_equal(lambda: log, [\"query\"])\n    await assert_equal(lambda: page.evaluate(\"window.log\"), [\"response\"])\n"
  },
  {
    "path": "tests/async/test_screenshot.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import Callable\n\nfrom PIL import Image\n\nfrom playwright.async_api import Page\nfrom tests.server import Server\nfrom tests.utils import must\n\n\ndef assert_image_file_format(path: Path, image_format: str) -> None:\n    with Image.open(path) as img:\n        assert img.format == image_format\n\n\nasync def test_should_screenshot_with_mask(\n    page: Page, server: Server, assert_to_be_golden: Callable[[bytes, str], None]\n) -> None:\n    await page.set_viewport_size(\n        {\n            \"width\": 500,\n            \"height\": 500,\n        }\n    )\n    await page.goto(server.PREFIX + \"/grid.html\")\n    assert_to_be_golden(\n        await page.screenshot(mask=[page.locator(\"div\").nth(5)]),\n        \"mask-should-work-with-page.png\",\n    )\n    assert_to_be_golden(\n        await page.locator(\"body\").screenshot(mask=[page.locator(\"div\").nth(5)]),\n        \"mask-should-work-with-locator.png\",\n    )\n    assert_to_be_golden(\n        await must(await page.query_selector(\"body\")).screenshot(\n            mask=[page.locator(\"div\").nth(5)]\n        ),\n        \"mask-should-work-with-element-handle.png\",\n    )\n\n\nasync def test_should_infer_screenshot_type_from_path(\n    page: Page, tmp_path: Path\n) -> None:\n    output_png_file = tmp_path / \"foo.png\"\n    await page.screenshot(path=output_png_file)\n    assert_image_file_format(output_png_file, \"PNG\")\n\n    output_jpeg_file = tmp_path / \"bar.jpeg\"\n    await page.screenshot(path=output_jpeg_file)\n    assert_image_file_format(output_jpeg_file, \"JPEG\")\n\n    output_jpg_file = tmp_path / \"bar.jpg\"\n    await page.screenshot(path=output_jpg_file)\n    assert_image_file_format(output_jpg_file, \"JPEG\")\n\n\nasync def test_should_screenshot_with_type_argument(page: Page, tmp_path: Path) -> None:\n    output_jpeg_with_png_extension = tmp_path / \"foo_jpeg.png\"\n    await page.screenshot(path=output_jpeg_with_png_extension, type=\"jpeg\")\n    assert_image_file_format(output_jpeg_with_png_extension, \"JPEG\")\n\n    output_png_with_jpeg_extension = tmp_path / \"bar_png.jpeg\"\n    await page.screenshot(path=output_png_with_jpeg_extension, type=\"png\")\n    assert_image_file_format(output_png_with_jpeg_extension, \"PNG\")\n"
  },
  {
    "path": "tests/async/test_selector_generator.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, Playwright\n\n\nasync def test_should_use_data_test_id_in_strict_errors(\n    page: Page, playwright: Playwright\n) -> None:\n    playwright.selectors.set_test_id_attribute(\"data-custom-id\")\n    try:\n        await page.set_content(\n            \"\"\"\n          <div>\n            <div></div>\n            <div>\n              <div></div>\n              <div></div>\n            </div>\n          </div>\n          <div>\n            <div class='foo bar:0' data-custom-id='One'>\n            </div>\n            <div class='foo bar:1' data-custom-id='Two'>\n            </div>\n          </div>\n        \"\"\"\n        )\n        with pytest.raises(Error) as exc_info:\n            await page.locator(\".foo\").hover()\n        assert \"strict mode violation\" in exc_info.value.message\n        assert '<div class=\"foo bar:0' in exc_info.value.message\n        assert '<div class=\"foo bar:1' in exc_info.value.message\n        assert 'aka get_by_test_id(\"One\")' in exc_info.value.message\n        assert 'aka get_by_test_id(\"Two\")' in exc_info.value.message\n    finally:\n        playwright.selectors.set_test_id_attribute(\"data-testid\")\n"
  },
  {
    "path": "tests/async/test_selectors_get_by.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nfrom playwright.async_api import Page, expect\n\n\nasync def test_get_by_escaping(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <label id=label for=control>Hello my\nwo\"rld</label><input id=control />\"\"\"\n    )\n    await page.eval_on_selector(\n        \"input\",\n        \"\"\"input => {\n        input.setAttribute('placeholder', 'hello my\\\\nwo\"rld');\n        input.setAttribute('title', 'hello my\\\\nwo\"rld');\n        input.setAttribute('alt', 'hello my\\\\nwo\"rld');\n    }\"\"\",\n    )\n    await expect(page.get_by_text('hello my\\nwo\"rld')).to_have_attribute(\"id\", \"label\")\n    await expect(page.get_by_text('hello       my     wo\"rld')).to_have_attribute(\n        \"id\", \"label\"\n    )\n    await expect(page.get_by_label('hello my\\nwo\"rld')).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_placeholder('hello my\\nwo\"rld')).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_alt_text('hello my\\nwo\"rld')).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_title('hello my\\nwo\"rld')).to_have_attribute(\n        \"id\", \"control\"\n    )\n\n    await page.set_content(\n        \"\"\"\n        <label id=label for=control>Hello my\nworld</label><input id=control />\"\"\"\n    )\n    await page.eval_on_selector(\n        \"input\",\n        \"\"\"input => {\n        input.setAttribute('placeholder', 'hello my\\\\nworld');\n        input.setAttribute('title', 'hello my\\\\nworld');\n        input.setAttribute('alt', 'hello my\\\\nworld');\n    }\"\"\",\n    )\n    await expect(page.get_by_text(\"hello my\\nworld\")).to_have_attribute(\"id\", \"label\")\n    await expect(page.get_by_text(\"hello        my    world\")).to_have_attribute(\n        \"id\", \"label\"\n    )\n    await expect(page.get_by_label(\"hello my\\nworld\")).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_placeholder(\"hello my\\nworld\")).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_alt_text(\"hello my\\nworld\")).to_have_attribute(\n        \"id\", \"control\"\n    )\n    await expect(page.get_by_title(\"hello my\\nworld\")).to_have_attribute(\n        \"id\", \"control\"\n    )\n\n    await page.set_content(\"\"\"<div id=target title=\"my title\">Text here</div>\"\"\")\n    await expect(page.get_by_title(\"my title\", exact=True)).to_have_count(\n        1, timeout=500\n    )\n    await expect(page.get_by_title(\"my t\\\\itle\", exact=True)).to_have_count(\n        0, timeout=500\n    )\n    await expect(page.get_by_title(\"my t\\\\\\\\itle\", exact=True)).to_have_count(\n        0, timeout=500\n    )\n\n    await page.set_content(\n        \"\"\"<label for=target>foo &gt;&gt; bar</label><input id=target>\"\"\"\n    )\n    await page.eval_on_selector(\n        \"input\",\n        \"\"\"input => {\n        input.setAttribute('placeholder', 'foo >> bar');\n        input.setAttribute('title', 'foo >> bar');\n        input.setAttribute('alt', 'foo >> bar');\n    }\"\"\",\n    )\n    assert await page.get_by_text(\"foo >> bar\").text_content() == \"foo >> bar\"\n    await expect(page.locator(\"label\")).to_have_text(\"foo >> bar\")\n    await expect(page.get_by_text(\"foo >> bar\")).to_have_text(\"foo >> bar\")\n    assert (\n        await page.get_by_text(re.compile(\"foo >> bar\")).text_content() == \"foo >> bar\"\n    )\n    await expect(page.get_by_label(\"foo >> bar\")).to_have_attribute(\"id\", \"target\")\n    await expect(page.get_by_label(re.compile(\"foo >> bar\"))).to_have_attribute(\n        \"id\", \"target\"\n    )\n    await expect(page.get_by_placeholder(\"foo >> bar\")).to_have_attribute(\n        \"id\", \"target\"\n    )\n    await expect(page.get_by_alt_text(\"foo >> bar\")).to_have_attribute(\"id\", \"target\")\n    await expect(page.get_by_title(\"foo >> bar\")).to_have_attribute(\"id\", \"target\")\n    await expect(page.get_by_placeholder(re.compile(\"foo >> bar\"))).to_have_attribute(\n        \"id\", \"target\"\n    )\n    await expect(page.get_by_alt_text(re.compile(\"foo >> bar\"))).to_have_attribute(\n        \"id\", \"target\"\n    )\n    await expect(page.get_by_title(re.compile(\"foo >> bar\"))).to_have_attribute(\n        \"id\", \"target\"\n    )\n\n\nasync def test_get_by_role_escaping(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <a href=\"https://playwright.dev\">issues 123</a>\n        <a href=\"https://playwright.dev\">he llo 56</a>\n        <button>Click me</button>\n    \"\"\"\n    )\n    assert await page.get_by_role(\"button\").evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"<button>Click me</button>\",\n    ]\n    assert await page.get_by_role(\"link\").evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"\"\"<a href=\"https://playwright.dev\">issues 123</a>\"\"\",\n        \"\"\"<a href=\"https://playwright.dev\">he llo 56</a>\"\"\",\n    ]\n\n    assert await page.get_by_role(\"link\", name=\"issues 123\").evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"\"\"<a href=\"https://playwright.dev\">issues 123</a>\"\"\",\n    ]\n    assert await page.get_by_role(\"link\", name=\"sues\").evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"\"\"<a href=\"https://playwright.dev\">issues 123</a>\"\"\",\n    ]\n    assert await page.get_by_role(\"link\", name=\"  he    \\n  llo \").evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"\"\"<a href=\"https://playwright.dev\">he llo 56</a>\"\"\",\n    ]\n    assert (\n        await page.get_by_role(\"button\", name=\"issues\").evaluate_all(\n            \"els => els.map(e => e.outerHTML)\"\n        )\n        == []\n    )\n\n    assert (\n        await page.get_by_role(\"link\", name=\"sues\", exact=True).evaluate_all(\n            \"els => els.map(e => e.outerHTML)\"\n        )\n        == []\n    )\n    assert await page.get_by_role(\n        \"link\", name=\"   he \\n llo 56 \", exact=True\n    ).evaluate_all(\"els => els.map(e => e.outerHTML)\") == [\n        \"\"\"<a href=\"https://playwright.dev\">he llo 56</a>\"\"\",\n    ]\n\n    assert await page.get_by_role(\"button\", name=\"Click me\", exact=True).evaluate_all(\n        \"els => els.map(e => e.outerHTML)\"\n    ) == [\n        \"<button>Click me</button>\",\n    ]\n    assert (\n        await page.get_by_role(\"button\", name=\"Click \\\\me\", exact=True).evaluate_all(\n            \"els => els.map(e => e.outerHTML)\"\n        )\n        == []\n    )\n    assert (\n        await page.get_by_role(\"button\", name=\"Click \\\\\\\\me\", exact=True).evaluate_all(\n            \"els => els.map(e => e.outerHTML)\"\n        )\n        == []\n    )\n\n\nasync def test_include_hidden_should_work(\n    page: Page,\n) -> None:\n    await page.set_content(\"\"\"<button style=\"display: none\">Hidden</button>\"\"\")\n    assert (\n        await page.get_by_role(\"button\", name=\"Hidden\").evaluate_all(\n            \"els => els.map(e => e.outerHTML)\"\n        )\n        == []\n    )\n    assert await page.get_by_role(\n        \"button\", name=\"Hidden\", include_hidden=True\n    ).evaluate_all(\"els => els.map(e => e.outerHTML)\") == [\n        \"\"\"<button style=\"display: none\">Hidden</button>\"\"\",\n    ]\n"
  },
  {
    "path": "tests/async/test_selectors_misc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright._impl._browser import Browser\nfrom playwright._impl._errors import Error\nfrom playwright._impl._selectors import Selectors\nfrom playwright.async_api import Page\n\n\nasync def test_should_work_with_internal_and(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div class=foo>hello</div><div class=bar>world</div>\n        <span class=foo>hello2</span><span class=bar>world2</span>\n    \"\"\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            'div >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == []\n    assert (\n        await page.eval_on_selector_all(\n            'div >> internal:and=\".foo\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello\"]\n    assert (\n        await page.eval_on_selector_all(\n            'div >> internal:and=\".bar\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"world\"]\n    assert (\n        await page.eval_on_selector_all(\n            'span >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello2\", \"world2\"]\n    assert (\n        await page.eval_on_selector_all(\n            '.foo >> internal:and=\"div\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello\"]\n    assert (\n        await page.eval_on_selector_all(\n            '.bar >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"world2\"]\n\n\nasync def test_should_throw_already_registered_error_when_registering(\n    selectors: Selectors,\n    browser: Browser,\n) -> None:\n    create_tag_selector = \"\"\"\n    () => ({\n        query(root, selector) {\n            return root.querySelector(selector);\n        },\n        queryAll(root, selector) {\n            return Array.from(root.querySelectorAll(selector));\n        }\n    })\n    \"\"\"\n    name = f\"alreadyRegistered-{browser.browser_type.name}\"\n    await selectors.register(name, create_tag_selector)\n    with pytest.raises(\n        Error,\n        match=f'Selectors.register: \"{name}\" selector engine has been already registered',\n    ):\n        await selectors.register(name, create_tag_selector)\n"
  },
  {
    "path": "tests/async/test_selectors_text.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport re\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, expect\n\n\nasync def test_has_text_and_internal_text_should_match_full_node_text_in_strict_mode(\n    page: Page,\n) -> None:\n    await page.set_content(\n        \"\"\"\n        <div id=div1>hello<span>world</span></div>\n        <div id=div2>hello</div>\n    \"\"\"\n    )\n    await expect(page.get_by_text(\"helloworld\", exact=True)).to_have_id(\"div1\")\n    await expect(page.get_by_text(\"hello\", exact=True)).to_have_id(\"div2\")\n    await expect(page.locator(\"div\", has_text=re.compile(\"^helloworld$\"))).to_have_id(\n        \"div1\"\n    )\n    await expect(page.locator(\"div\", has_text=re.compile(\"^hello$\"))).to_have_id(\"div2\")\n\n    await page.set_content(\n        \"\"\"\n        <div id=div1><span id=span1>hello</span>world</div>\n        <div id=div2><span id=span2>hello</span></div>\n    \"\"\"\n    )\n    await expect(page.get_by_text(\"helloworld\", exact=True)).to_have_id(\"div1\")\n    assert await page.get_by_text(\"hello\", exact=True).evaluate_all(\n        \"els => els.map(e => e.id)\"\n    ) == [\"span1\", \"span2\"]\n    await expect(page.locator(\"div\", has_text=re.compile(\"^helloworld$\"))).to_have_id(\n        \"div1\"\n    )\n    await expect(page.locator(\"div\", has_text=re.compile(\"^hello$\"))).to_have_id(\"div2\")\n\n\nasync def test_should_work(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div>yo</div><div>ya</div><div>\\nye  </div>\n    \"\"\"\n    )\n    assert await page.eval_on_selector(\"text=ya\", \"e => e.outerHTML\") == \"<div>ya</div>\"\n    assert (\n        await page.eval_on_selector('text=\"ya\"', \"e => e.outerHTML\") == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=/^[ay]+$/\", \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=/Ya/i\", \"e => e.outerHTML\") == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=ye\", \"e => e.outerHTML\")\n        == \"<div>\\nye  </div>\"\n    )\n    assert \">\\nye  </div>\" in await page.get_by_text(\"ye\").evaluate(\"e => e.outerHTML\")\n\n    await page.set_content(\n        \"\"\"\n        <div> ye </div><div>ye</div>\n    \"\"\"\n    )\n    assert (\n        await page.eval_on_selector('text=\"ye\"', \"e => e.outerHTML\")\n        == \"<div> ye </div>\"\n    )\n    assert \"> ye </div>\" in await page.get_by_text(\"ye\", exact=True).first.evaluate(\n        \"e => e.outerHTML\"\n    )\n\n    await page.set_content(\n        \"\"\"\n        <div>yo</div><div>\"ya</div><div> hello world! </div>\n    \"\"\"\n    )\n    assert (\n        await page.eval_on_selector('text=\"\\\\\"ya\"', \"e => e.outerHTML\")\n        == '<div>\"ya</div>'\n    )\n    assert (\n        await page.eval_on_selector(\"text=/hello/\", \"e => e.outerHTML\")\n        == \"<div> hello world! </div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=/^\\\\s*heLLo/i\", \"e => e.outerHTML\")\n        == \"<div> hello world! </div>\"\n    )\n\n    await page.set_content(\n        \"\"\"\n        <div>yo<div>ya</div>hey<div>hey</div></div>\n    \"\"\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=hey\", \"e => e.outerHTML\") == \"<div>hey</div>\"\n    )\n    assert (\n        await page.eval_on_selector('text=yo>>text=\"ya\"', \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector('text=yo>> text=\"ya\"', \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=yo >>text='ya'\", \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=yo >> text='ya'\", \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"'yo'>>\\\"ya\\\"\", \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"\\\"yo\\\" >> 'ya'\", \"e => e.outerHTML\")\n        == \"<div>ya</div>\"\n    )\n\n    await page.set_content(\n        \"\"\"\n        <div>yo<span id=\"s1\"></span></div><div>yo<span id=\"s2\"></span><span id=\"s3\"></span></div>\n        \"\"\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"text=yo\", \"es => es.map(e => e.outerHTML).join('\\\\n')\"\n        )\n        == '<div>yo<span id=\"s1\"></span></div>\\n<div>yo<span id=\"s2\"></span><span id=\"s3\"></span></div>'\n    )\n\n    await page.set_content(\"<div>'</div><div>\\\"</div><div>\\\\</div><div>x</div>\")\n    assert (\n        await page.eval_on_selector(\"text='\\\\''\", \"e => e.outerHTML\") == \"<div>'</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text='\\\"'\", \"e => e.outerHTML\") == '<div>\"</div>'\n    )\n    assert (\n        await page.eval_on_selector('text=\"\\\\\"\"', \"e => e.outerHTML\") == '<div>\"</div>'\n    )\n    assert (\n        await page.eval_on_selector('text=\"\\'\"', \"e => e.outerHTML\") == \"<div>'</div>\"\n    )\n    assert (\n        await page.eval_on_selector('text=\"\\\\x\"', \"e => e.outerHTML\") == \"<div>x</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text='\\\\x'\", \"e => e.outerHTML\") == \"<div>x</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text='\\\\\\\\'\", \"e => e.outerHTML\")\n        == \"<div>\\\\</div>\"\n    )\n    assert (\n        await page.eval_on_selector('text=\"\\\\\\\\\"', \"e => e.outerHTML\")\n        == \"<div>\\\\</div>\"\n    )\n    assert await page.eval_on_selector('text=\"', \"e => e.outerHTML\") == '<div>\"</div>'\n    assert await page.eval_on_selector(\"text='\", \"e => e.outerHTML\") == \"<div>'</div>\"\n    assert await page.eval_on_selector('\"x\"', \"e => e.outerHTML\") == \"<div>x</div>\"\n    assert await page.eval_on_selector(\"'x'\", \"e => e.outerHTML\") == \"<div>x</div>\"\n    with pytest.raises(Error):\n        await page.query_selector_all('\"')\n    with pytest.raises(Error):\n        await page.query_selector_all(\"'\")\n\n    await page.set_content(\"<div> ' </div><div> \\\" </div>\")\n    assert await page.eval_on_selector('text=\"', \"e => e.outerHTML\") == '<div> \" </div>'\n    assert await page.eval_on_selector(\"text='\", \"e => e.outerHTML\") == \"<div> ' </div>\"\n\n    await page.set_content(\"<div>Hi''&gt;&gt;foo=bar</div>\")\n    assert (\n        await page.eval_on_selector(\"text=\\\"Hi''>>foo=bar\\\"\", \"e => e.outerHTML\")\n        == \"<div>Hi''&gt;&gt;foo=bar</div>\"\n    )\n    await page.set_content(\"<div>Hi'\\\"&gt;&gt;foo=bar</div>\")\n    assert (\n        await page.eval_on_selector('text=\"Hi\\'\\\\\">>foo=bar\"', \"e => e.outerHTML\")\n        == \"<div>Hi'\\\"&gt;&gt;foo=bar</div>\"\n    )\n\n    await page.set_content(\"<div>Hi&gt;&gt;<span></span></div>\")\n    assert (\n        await page.eval_on_selector('text=\"Hi>>\">>span', \"e => e.outerHTML\")\n        == \"<span></span>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=/Hi\\\\>\\\\>/ >> span\", \"e => e.outerHTML\")\n        == \"<span></span>\"\n    )\n\n    await page.set_content(\"<div>a<br>b</div><div>a</div>\")\n    assert (\n        await page.eval_on_selector(\"text=a\", \"e => e.outerHTML\") == \"<div>a<br>b</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=b\", \"e => e.outerHTML\") == \"<div>a<br>b</div>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=ab\", \"e => e.outerHTML\")\n        == \"<div>a<br>b</div>\"\n    )\n    assert await page.query_selector(\"text=abc\") is None\n    assert await page.eval_on_selector_all(\"text=a\", \"els => els.length\") == 2\n    assert await page.eval_on_selector_all(\"text=b\", \"els => els.length\") == 1\n    assert await page.eval_on_selector_all(\"text=ab\", \"els => els.length\") == 1\n    assert await page.eval_on_selector_all(\"text=abc\", \"els => els.length\") == 0\n\n    await page.set_content(\"<div></div><span></span>\")\n    await page.eval_on_selector(\n        \"div\",\n        \"\"\"div => {\n        div.appendChild(document.createTextNode('hello'))\n        div.appendChild(document.createTextNode('world'))\n    }\"\"\",\n    )\n    await page.eval_on_selector(\n        \"span\",\n        \"\"\"span => {\n        span.appendChild(document.createTextNode('hello'))\n        span.appendChild(document.createTextNode('world'))\n    }\"\"\",\n    )\n    assert (\n        await page.eval_on_selector(\"text=lowo\", \"e => e.outerHTML\")\n        == \"<div>helloworld</div>\"\n    )\n    assert (\n        await page.eval_on_selector_all(\n            \"text=lowo\", \"els => els.map(e => e.outerHTML).join('')\"\n        )\n        == \"<div>helloworld</div><span>helloworld</span>\"\n    )\n\n    await page.set_content(\"<span>Sign&nbsp;in</span><span>Hello\\n \\nworld</span>\")\n    assert (\n        await page.eval_on_selector(\"text=Sign in\", \"e => e.outerHTML\")\n        == \"<span>Sign&nbsp;in</span>\"\n    )\n    assert len((await page.query_selector_all(\"text=Sign \\tin\"))) == 1\n    assert len((await page.query_selector_all('text=\"Sign in\"'))) == 1\n    assert (\n        await page.eval_on_selector(\"text=lo wo\", \"e => e.outerHTML\")\n        == \"<span>Hello\\n \\nworld</span>\"\n    )\n    assert (\n        await page.eval_on_selector('text=\"Hello world\"', \"e => e.outerHTML\")\n        == \"<span>Hello\\n \\nworld</span>\"\n    )\n    assert await page.query_selector('text=\"lo wo\"') is None\n    assert len((await page.query_selector_all(\"text=lo \\nwo\"))) == 1\n    assert len(await page.query_selector_all('text=\"lo \\nwo\"')) == 0\n\n    await page.set_content(\"<div>let's<span>hello</span></div>\")\n    assert (\n        await page.eval_on_selector(\"text=/let's/i >> span\", \"e => e.outerHTML\")\n        == \"<span>hello</span>\"\n    )\n    assert (\n        await page.eval_on_selector(\"text=/let\\\\'s/i >> span\", \"e => e.outerHTML\")\n        == \"<span>hello</span>\"\n    )\n"
  },
  {
    "path": "tests/async/test_tap.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import AsyncGenerator, Optional, cast\n\nimport pytest\n\nfrom playwright.async_api import Browser, BrowserContext, ElementHandle, JSHandle, Page\n\n\n@pytest.fixture\nasync def context(browser: Browser) -> AsyncGenerator[BrowserContext, None]:\n    context = await browser.new_context(has_touch=True)\n    yield context\n    await context.close()\n\n\nasync def test_should_send_all_of_the_correct_events(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n            <div id=\"a\" style=\"background: lightblue; width: 50px; height: 50px\">a</div>\n            <div id=\"b\" style=\"background: pink; width: 50px; height: 50px\">b</div>\n        \"\"\"\n    )\n    await page.tap(\"#a\")\n    element_handle = await track_events(await page.query_selector(\"#b\"))\n    await page.tap(\"#b\")\n    assert await element_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n        \"mouseover\",\n        \"mouseenter\",\n        \"mousemove\",\n        \"mousedown\",\n        \"mouseup\",\n        \"click\",\n    ]\n\n\nasync def test_should_not_send_mouse_events_touchstart_is_canceled(page: Page) -> None:\n    await page.set_content(\"hello world\")\n    await page.evaluate(\n        \"\"\"() => {\n            // touchstart is not cancelable unless passive is false\n            document.addEventListener('touchstart', t => t.preventDefault(), {passive: false});\n        }\"\"\"\n    )\n    events_handle = await track_events(await page.query_selector(\"body\"))\n    await page.tap(\"body\")\n    assert await events_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n    ]\n\n\nasync def test_should_not_send_mouse_events_touchend_is_canceled(page: Page) -> None:\n    await page.set_content(\"hello world\")\n    await page.evaluate(\n        \"\"\"() => {\n            // touchstart is not cancelable unless passive is false\n            document.addEventListener('touchend', t => t.preventDefault());\n        }\"\"\"\n    )\n    events_handle = await track_events(await page.query_selector(\"body\"))\n    await page.tap(\"body\")\n    assert await events_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n    ]\n\n\nasync def test_should_work_with_modifiers(page: Page) -> None:\n    await page.set_content(\"hello world\")\n    alt_key_promise = asyncio.create_task(\n        page.evaluate(\n            \"\"\"() => new Promise(resolve => {\n                document.addEventListener('touchstart', event => {\n                    resolve(event.altKey);\n                }, {passive: false});\n            })\"\"\"\n        )\n    )\n    await asyncio.sleep(0)  # make sure the evals hit the page\n    await page.evaluate(\"\"\"() => void 0\"\"\")\n    await page.tap(\"body\", modifiers=[\"Alt\"])\n    assert await alt_key_promise is True\n\n\nasync def test_should_send_well_formed_touch_points(page: Page) -> None:\n    promises = asyncio.gather(\n        page.evaluate(\n            \"\"\"() => new Promise(resolve => {\n                    document.addEventListener('touchstart', event => {\n                        resolve([...event.touches].map(t => ({\n                        identifier: t.identifier,\n                        clientX: t.clientX,\n                        clientY: t.clientY,\n                        pageX: t.pageX,\n                        pageY: t.pageY,\n                        radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],\n                        radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],\n                        rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],\n                        force: 'force' in t ? t.force : t['webkitForce'],\n                        })));\n                    }, false);\n                })\"\"\"\n        ),\n        page.evaluate(\n            \"\"\"() => new Promise(resolve => {\n                    document.addEventListener('touchend', event => {\n                        resolve([...event.touches].map(t => ({\n                        identifier: t.identifier,\n                        clientX: t.clientX,\n                        clientY: t.clientY,\n                        pageX: t.pageX,\n                        pageY: t.pageY,\n                        radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'],\n                        radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'],\n                        rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'],\n                        force: 'force' in t ? t.force : t['webkitForce'],\n                        })));\n                    }, false);\n                })\"\"\"\n        ),\n    )\n    # make sure the evals hit the page\n    await page.evaluate(\"\"\"() => void 0\"\"\")\n    await page.touchscreen.tap(40, 60)\n    [touchstart, touchend] = await promises\n    assert touchstart == [\n        {\n            \"clientX\": 40,\n            \"clientY\": 60,\n            \"force\": 1,\n            \"identifier\": 0,\n            \"pageX\": 40,\n            \"pageY\": 60,\n            \"radiusX\": 1,\n            \"radiusY\": 1,\n            \"rotationAngle\": 0,\n        }\n    ]\n    assert touchend == []\n\n\nasync def test_should_wait_until_an_element_is_visible_to_tap_it(page: Page) -> None:\n    div = cast(\n        ElementHandle,\n        await page.evaluate_handle(\n            \"\"\"() => {\n            const button = document.createElement('button');\n            button.textContent = 'not clicked';\n            document.body.appendChild(button);\n            button.style.display = 'none';\n            return button;\n        }\"\"\"\n        ),\n    )\n    tap_promise = asyncio.create_task(div.tap())\n    await asyncio.sleep(0)  # issue tap\n    await div.evaluate(\"\"\"div => div.onclick = () => div.textContent = 'clicked'\"\"\")\n    await div.evaluate(\"\"\"div => div.style.display = 'block'\"\"\")\n    await tap_promise\n    assert await div.text_content() == \"clicked\"\n\n\nasync def test_locators_tap(page: Page) -> None:\n    await page.set_content(\n        \"\"\"\n        <div id=\"a\" style=\"background: lightblue; width: 50px; height: 50px\">a</div>\n        <div id=\"b\" style=\"background: pink; width: 50px; height: 50px\">b</div>\n    \"\"\"\n    )\n    await page.locator(\"#a\").tap()\n    element_handle = await track_events(await page.query_selector(\"#b\"))\n    await page.locator(\"#b\").tap()\n    assert await element_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n        \"mouseover\",\n        \"mouseenter\",\n        \"mousemove\",\n        \"mousedown\",\n        \"mouseup\",\n        \"click\",\n    ]\n\n\nasync def track_events(target: Optional[ElementHandle]) -> JSHandle:\n    assert target\n    return await target.evaluate_handle(\n        \"\"\"target => {\n            const events = [];\n            for (const event of [\n                'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click',\n                'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerup',\n                'touchstart', 'touchend', 'touchmove', 'touchcancel',])\n                target.addEventListener(event, () => events.push(event), false);\n            return events;\n        }\"\"\"\n    )\n"
  },
  {
    "path": "tests/async/test_tracing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport re\nfrom pathlib import Path\nfrom typing import AsyncContextManager, Callable\n\nfrom playwright.async_api import (\n    Browser,\n    BrowserContext,\n    BrowserType,\n    Page,\n    Response,\n    expect,\n)\nfrom tests.server import Server\n\nfrom .conftest import TraceViewerPage\n\n\nasync def test_browser_context_output_trace(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    context = await browser.new_context()\n    await context.tracing.start(screenshots=True, snapshots=True)\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await context.tracing.stop(path=tmp_path / \"trace.zip\")\n    assert Path(tmp_path / \"trace.zip\").exists()\n\n\nasync def test_start_stop(browser: Browser) -> None:\n    context = await browser.new_context()\n    await context.tracing.start()\n    await context.tracing.stop()\n    await context.close()\n\n\nasync def test_browser_context_should_not_throw_when_stopping_without_start_but_not_exporting(\n    context: BrowserContext,\n) -> None:\n    await context.tracing.stop()\n\n\nasync def test_browser_context_output_trace_chunk(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    context = await browser.new_context()\n    await context.tracing.start(screenshots=True, snapshots=True)\n    page = await context.new_page()\n    await page.goto(server.PREFIX + \"/grid.html\")\n    button = page.locator(\".box\").first\n\n    await context.tracing.start_chunk(title=\"foo\")\n    await button.click()\n    await context.tracing.stop_chunk(path=tmp_path / \"trace1.zip\")\n    assert Path(tmp_path / \"trace1.zip\").exists()\n\n    await context.tracing.start_chunk(title=\"foo\")\n    await button.click()\n    await context.tracing.stop_chunk(path=tmp_path / \"trace2.zip\")\n    assert Path(tmp_path / \"trace2.zip\").exists()\n\n\nasync def test_should_collect_sources(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start(sources=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button>Click</button>\")\n\n    async def my_method_outer() -> None:\n        async def my_method_inner() -> None:\n            await page.get_by_text(\"Click\").click()\n\n        await my_method_inner()\n\n    await my_method_outer()\n    path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=path)\n\n    async with show_trace_viewer(path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n            ]\n        )\n        await trace_viewer.show_source_tab()\n        # Check that stack frames are shown (they might be anonymous in Python)\n        await expect(trace_viewer.stack_frames).to_contain_text(\n            [\n                re.compile(r\"my_method_inner\"),\n                re.compile(r\"my_method_outer\"),\n                re.compile(r\"test_should_collect_sources\"),\n            ]\n        )\n\n        await trace_viewer.select_action(\"Set content\")\n        # Check that the source file is shown\n        await expect(\n            trace_viewer.page.locator(\".source-tab-file-name\")\n        ).to_have_attribute(\"title\", re.compile(r\".*test_.*\\.py\"))\n        await expect(trace_viewer.page.locator(\".source-line-running\")).to_contain_text(\n            'page.set_content(\"<button>Click</button>\")'\n        )\n\n\nasync def test_should_collect_trace_with_resources_but_no_js(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start(screenshots=True, snapshots=True)\n    await page.goto(server.PREFIX + \"/frames/frame.html\")\n    await page.set_content(\"<button>Click</button>\")\n    await page.click('\"Click\"')\n    await page.mouse.move(20, 20)\n    await page.mouse.dblclick(30, 30)\n    await page.request.get(server.EMPTY_PAGE)\n    await page.keyboard.insert_text(\"abc\")\n    await page.wait_for_timeout(2000)  # Give it some time to produce screenshots.\n    await page.route(\"**/empty.html\", lambda route: route.continue_())\n    await page.goto(server.EMPTY_PAGE)\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await page.close()\n    trace_file_path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=trace_file_path)\n\n    async with show_trace_viewer(trace_file_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/frames/frame\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n                re.compile(r\"Mouse move\"),\n                re.compile(r\"Double click\"),\n                re.compile(r\"GET \\\"/empty\\.html\\\"\"),\n                re.compile(r'Insert \"abc\"'),\n                re.compile(r\"Wait for timeout\"),\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r'Navigate to \"/one-style\\.html\"'),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n        await trace_viewer.select_action(\"Set content\")\n        await expect(\n            trace_viewer.page.locator(\".browser-frame-address-bar\")\n        ).to_have_text(server.PREFIX + \"/frames/frame.html\")\n        frame = await trace_viewer.snapshot_frame(\"Set content\", 0, False)\n        await expect(frame.locator(\"button\")).to_have_text(\"Click\")\n\n\nasync def test_should_correctly_determine_sync_apiname(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable,\n) -> None:\n    await context.tracing.start(screenshots=True, snapshots=True)\n\n    received_response: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handle_response(response: Response) -> None:\n        await response.request.all_headers()\n        await response.text()\n        received_response.set_result(None)\n\n    page.once(\"response\", _handle_response)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await received_response\n    await page.close()\n    trace_file_path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=trace_file_path)\n\n    async with show_trace_viewer(trace_file_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/grid\\.html\"'),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n\nasync def test_should_collect_two_traces(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start(screenshots=True, snapshots=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button>Click</button>\")\n    await page.click('\"Click\"')\n    tracing1_path = tmp_path / \"trace1.zip\"\n    await context.tracing.stop(path=tracing1_path)\n\n    await context.tracing.start(screenshots=True, snapshots=True)\n    await page.dblclick('\"Click\"')\n    await page.close()\n    tracing2_path = tmp_path / \"trace2.zip\"\n    await context.tracing.stop(path=tracing2_path)\n\n    async with show_trace_viewer(tracing1_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n            ]\n        )\n\n    async with show_trace_viewer(tracing2_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r\"Double click\"),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n\nasync def test_should_work_with_playwright_context_managers(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start(screenshots=True, snapshots=True)\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\"<button>Click</button>\")\n    async with page.expect_console_message() as message_info:\n        await page.evaluate('() => console.log(\"hello\")')\n        await page.click('\"Click\"')\n    assert (await message_info.value).text == \"hello\"\n\n    async with page.expect_popup():\n        await page.evaluate(\"window._popup = window.open(document.location.href)\")\n    trace_file_path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=trace_file_path)\n\n    async with show_trace_viewer(trace_file_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r'Wait for event \"page\\.expect_event\\(console\\)\"'),\n                re.compile(r\"Evaluate\"),\n                re.compile(r\"Click\"),\n                re.compile(r'Wait for event \"page\\.expect_event\\(popup\\)\"'),\n                re.compile(r\"Evaluate\"),\n            ]\n        )\n\n\nasync def test_should_display_wait_for_load_state_even_if_did_not_wait_for_it(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start(screenshots=True, snapshots=True)\n\n    await page.goto(server.EMPTY_PAGE)\n    await page.wait_for_load_state(\"load\")\n    await page.wait_for_load_state(\"load\")\n\n    trace_file_path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=trace_file_path)\n\n    async with show_trace_viewer(trace_file_path) as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r'Wait for event \"frame\\.wait_for_load_state\"'),\n                re.compile(r'Wait for event \"frame\\.wait_for_load_state\"'),\n            ]\n        )\n\n\nasync def test_should_respect_traces_dir_and_name(\n    browser_type: BrowserType,\n    server: Server,\n    tmp_path: Path,\n    launch_arguments: dict,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    traces_dir = tmp_path / \"traces\"\n    browser = await browser_type.launch(traces_dir=traces_dir, **launch_arguments)\n    context = await browser.new_context()\n    page = await context.new_page()\n\n    await context.tracing.start(name=\"name1\", snapshots=True)\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    await context.tracing.stop_chunk(path=tmp_path / \"trace1.zip\")\n    assert (traces_dir / \"name1.trace\").exists()\n    assert (traces_dir / \"name1.network\").exists()\n\n    await context.tracing.start_chunk(name=\"name2\")\n    await page.goto(server.PREFIX + \"/har.html\")\n    await context.tracing.stop(path=tmp_path / \"trace2.zip\")\n    assert (traces_dir / \"name2.trace\").exists()\n    assert (traces_dir / \"name2.network\").exists()\n\n    await browser.close()\n\n    async with show_trace_viewer(tmp_path / \"trace1.zip\") as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile('Navigate to \"/one-style\\\\.html\"'),\n            ]\n        )\n        frame = await trace_viewer.snapshot_frame(\n            'Navigate to \"/one-style.html\"', 0, False\n        )\n        await expect(frame.locator(\"body\")).to_have_css(\n            \"background-color\", \"rgb(255, 192, 203)\"\n        )\n        await expect(frame.locator(\"body\")).to_have_text(\"hello, world!\")\n\n    async with show_trace_viewer(tmp_path / \"trace2.zip\") as trace_viewer:\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/har\\.html\"'),\n            ]\n        )\n        frame = await trace_viewer.snapshot_frame('Navigate to \"/har.html\"', 0, False)\n        await expect(frame.locator(\"body\")).to_have_css(\n            \"background-color\", \"rgb(255, 192, 203)\"\n        )\n        await expect(frame.locator(\"body\")).to_have_text(\"hello, world!\")\n\n\nasync def test_should_show_tracing_group_in_action_list(\n    context: BrowserContext,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], AsyncContextManager[TraceViewerPage]],\n) -> None:\n    await context.tracing.start()\n    page = await context.new_page()\n\n    await context.tracing.group(\"outer group\")\n    await page.goto(\"data:text/html,<!DOCTYPE html><body><div>Hello world</div></body>\")\n    await context.tracing.group(\"inner group 1\")\n    await page.locator(\"body\").click()\n    await context.tracing.group_end()\n    await context.tracing.group(\"inner group 2\")\n    await page.get_by_text(\"Hello\").is_visible()\n    await context.tracing.group_end()\n    await context.tracing.group_end()\n\n    trace_path = tmp_path / \"trace.zip\"\n    await context.tracing.stop(path=trace_path)\n\n    async with show_trace_viewer(trace_path) as trace_viewer:\n        await trace_viewer.expand_action(\"inner group 1\")\n        await expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r\"Create page\"),\n                re.compile(r\"outer group\"),\n                re.compile(r\"Navigate to \\\"data:\\\"\"),\n                re.compile(r\"inner group 1\"),\n                re.compile(r\"Click\"),\n                re.compile(r\"inner group 2\"),\n            ]\n        )\n"
  },
  {
    "path": "tests/async/test_unroute_behavior.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nimport re\n\nfrom playwright.async_api import BrowserContext, Error, Page, Route\nfrom tests.server import Server\nfrom tests.utils import must\n\n\nasync def test_context_unroute_should_not_wait_for_pending_handlers_to_complete(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.continue_()\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        await route.fallback()\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    await context.unroute(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    route_barrier_future.set_result(None)\n    await navigation_task\n    assert second_handler_called\n\n\nasync def test_context_unroute_all_removes_all_handlers(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await context.route(\n        \"**/*\",\n        lambda route: route.abort(),\n    )\n    await context.route(\n        \"**/empty.html\",\n        lambda route: route.abort(),\n    )\n    await context.unroute_all()\n    await page.goto(server.EMPTY_PAGE)\n\n\nasync def test_context_unroute_all_should_not_wait_for_pending_handlers_to_complete(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.abort()\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        await route.fallback()\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    did_unroute = False\n\n    async def _unroute_promise() -> None:\n        nonlocal did_unroute\n        await context.unroute_all(behavior=\"wait\")\n        did_unroute = True\n\n    unroute_task = asyncio.create_task(_unroute_promise())\n    await asyncio.sleep(0.5)\n    assert did_unroute is False\n    route_barrier_future.set_result(None)\n    await unroute_task\n    assert did_unroute\n    await navigation_task\n    assert second_handler_called is False\n\n\nasync def test_context_unroute_all_should_not_wait_for_pending_handlers_to_complete_if_behavior_is_ignore_errors(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.abort()\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        raise Exception(\"Handler error\")\n\n    await context.route(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    did_unroute = False\n\n    async def _unroute_promise() -> None:\n        await context.unroute_all(behavior=\"ignoreErrors\")\n        nonlocal did_unroute\n        did_unroute = True\n\n    unroute_task = asyncio.create_task(_unroute_promise())\n    await asyncio.sleep(0.5)\n    await unroute_task\n    assert did_unroute\n    route_barrier_future.set_result(None)\n    try:\n        await navigation_task\n    except Error:\n        pass\n    # The error in the unrouted handler should be silently caught and remaining handler called.\n    assert not second_handler_called\n\n\nasync def test_page_close_should_not_wait_for_active_route_handlers_on_the_owning_context(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await context.route(\n        re.compile(\".*\"),\n        lambda route: route_future.set_result(route),\n    )\n    await page.route(\n        re.compile(\".*\"),\n        lambda route: route.fallback(),\n    )\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    await route_future\n    await page.close()\n\n\nasync def test_context_close_should_not_wait_for_active_route_handlers_on_the_owned_pages(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\n        re.compile(\".*\"),\n        lambda route: route_future.set_result(route),\n    )\n    await page.route(re.compile(\".*\"), lambda route: route.fallback())\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    await route_future\n    await context.close()\n\n\nasync def test_page_unroute_should_not_wait_for_pending_handlers_to_complete(\n    page: Page, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.continue_()\n\n    await page.route(\n        re.compile(\".*\"),\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        await route.fallback()\n\n    await page.route(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    await page.unroute(\n        re.compile(\".*\"),\n        _handler2,\n    )\n    route_barrier_future.set_result(None)\n    await navigation_task\n    assert second_handler_called\n\n\nasync def test_page_unroute_all_removes_all_routes(page: Page, server: Server) -> None:\n    await page.route(\n        \"**/*\",\n        lambda route: route.abort(),\n    )\n    await page.route(\n        \"**/empty.html\",\n        lambda route: route.abort(),\n    )\n    await page.unroute_all()\n    response = must(await page.goto(server.EMPTY_PAGE))\n    assert response.ok\n\n\nasync def test_page_unroute_should_wait_for_pending_handlers_to_complete(\n    page: Page, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.abort()\n\n    await page.route(\n        \"**/*\",\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        await route.fallback()\n\n    await page.route(\n        \"**/*\",\n        _handler2,\n    )\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    did_unroute = False\n\n    async def _unroute_promise() -> None:\n        await page.unroute_all(behavior=\"wait\")\n        nonlocal did_unroute\n        did_unroute = True\n\n    unroute_task = asyncio.create_task(_unroute_promise())\n    await asyncio.sleep(0.5)\n    assert did_unroute is False\n    route_barrier_future.set_result(None)\n    await unroute_task\n    assert did_unroute\n    await navigation_task\n    assert second_handler_called is False\n\n\nasync def test_page_unroute_all_should_not_wait_for_pending_handlers_to_complete_if_behavior_is_ignore_errors(\n    page: Page, server: Server\n) -> None:\n    second_handler_called = False\n\n    async def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n        await route.abort()\n\n    await page.route(re.compile(\".*\"), _handler1)\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    route_barrier_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await route_barrier_future\n        raise Exception(\"Handler error\")\n\n    await page.route(re.compile(\".*\"), _handler2)\n    navigation_task = asyncio.create_task(page.goto(server.EMPTY_PAGE))\n    await route_future\n    did_unroute = False\n\n    async def _unroute_promise() -> None:\n        await page.unroute_all(behavior=\"ignoreErrors\")\n        nonlocal did_unroute\n        did_unroute = True\n\n    unroute_task = asyncio.create_task(_unroute_promise())\n    await asyncio.sleep(0.5)\n    await unroute_task\n    assert did_unroute\n    route_barrier_future.set_result(None)\n    try:\n        await navigation_task\n    except Error:\n        pass\n    # The error in the unrouted handler should be silently caught.\n    assert not second_handler_called\n\n\nasync def test_page_close_does_not_wait_for_active_route_handlers(\n    page: Page, server: Server\n) -> None:\n    stalling_future: \"asyncio.Future[None]\" = asyncio.Future()\n    second_handler_called = False\n\n    def _handler1(route: Route) -> None:\n        nonlocal second_handler_called\n        second_handler_called = True\n\n    await page.route(\n        \"**/*\",\n        _handler1,\n    )\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n\n    async def _handler2(route: Route) -> None:\n        route_future.set_result(route)\n        await stalling_future\n\n    await page.route(\n        \"**/*\",\n        _handler2,\n    )\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    await route_future\n    await page.close()\n    await asyncio.sleep(0.5)\n    assert not second_handler_called\n    stalling_future.cancel()\n\n\nasync def test_route_continue_should_not_throw_if_page_has_been_closed(\n    page: Page, server: Server\n) -> None:\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\n        re.compile(\".*\"),\n        lambda route: route_future.set_result(route),\n    )\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    route = await route_future\n    await page.close()\n    # Should not throw.\n    await route.continue_()\n\n\nasync def test_route_fallback_should_not_throw_if_page_has_been_closed(\n    page: Page, server: Server\n) -> None:\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\n        re.compile(\".*\"),\n        lambda route: route_future.set_result(route),\n    )\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    route = await route_future\n    await page.close()\n    # Should not throw.\n    await route.fallback()\n\n\nasync def test_route_fulfill_should_not_throw_if_page_has_been_closed(\n    page: Page, server: Server\n) -> None:\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n    await page.route(\n        \"**/*\",\n        lambda route: route_future.set_result(route),\n    )\n\n    async def _goto_ignore_exceptions() -> None:\n        try:\n            await page.goto(server.EMPTY_PAGE)\n        except Error:\n            pass\n\n    asyncio.create_task(_goto_ignore_exceptions())\n    route = await route_future\n    await page.close()\n    # Should not throw.\n    await route.fulfill()\n\n\nasync def test_should_not_continue_requests_in_flight_page(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n\n    async def handle(route: Route) -> None:\n        route_future.set_result(route)\n        await asyncio.sleep(3)\n        await route.fulfill(status=200)\n\n    async def _evaluate_ignore_exceptions() -> None:\n        try:\n            await page.evaluate(\"() => fetch('/')\")\n        except Error:\n            pass\n\n    await page.route(\n        \"**/*\",\n        handle,\n    )\n\n    asyncio.create_task(_evaluate_ignore_exceptions())\n    await route_future\n    await page.unroute_all(behavior=\"wait\")\n\n\nasync def test_should_not_continue_requests_in_flight_context(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n\n    route_future: \"asyncio.Future[Route]\" = asyncio.Future()\n\n    async def handle(route: Route) -> None:\n        route_future.set_result(route)\n        await asyncio.sleep(3)\n        await route.fulfill(status=200)\n\n    async def _evaluate_ignore_exceptions() -> None:\n        try:\n            await page.evaluate(\"() => fetch('/')\")\n        except Error:\n            pass\n\n    await context.route(\n        \"**/*\",\n        handle,\n    )\n\n    asyncio.create_task(_evaluate_ignore_exceptions())\n    await route_future\n    await context.unroute_all(behavior=\"wait\")\n"
  },
  {
    "path": "tests/async/test_video.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\nfrom typing import Dict\n\nfrom playwright.async_api import Browser, BrowserType\nfrom tests.server import Server\n\n\nasync def test_should_expose_video_path(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = await browser.new_page(record_video_dir=tmp_path)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    assert page.video\n    path = await page.video.path()\n    assert str(tmp_path) in str(path)\n    await page.context.close()\n\n\nasync def test_short_video_should_throw(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = await browser.new_page(record_video_dir=tmp_path)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    assert page.video\n    path = await page.video.path()\n    assert str(tmp_path) in str(path)\n    await page.wait_for_timeout(1000)\n    await page.context.close()\n    assert os.path.exists(path)\n\n\nasync def test_short_video_should_throw_persistent_context(\n    browser_type: BrowserType, tmp_path: Path, launch_arguments: Dict, server: Server\n) -> None:\n    context = await browser_type.launch_persistent_context(\n        str(tmp_path),\n        **launch_arguments,\n        viewport={\"width\": 320, \"height\": 240},\n        record_video_dir=str(tmp_path) + \"1\",\n    )\n    page = context.pages[0]\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await page.wait_for_timeout(1000)\n    await context.close()\n\n    assert page.video\n    path = await page.video.path()\n    assert str(tmp_path) in str(path)\n\n\nasync def test_should_not_error_if_page_not_closed_before_save_as(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = await browser.new_page(record_video_dir=tmp_path)\n    await page.goto(server.PREFIX + \"/grid.html\")\n    await page.wait_for_timeout(1000)  # make sure video has some data\n    out_path = tmp_path / \"some-video.webm\"\n    assert page.video\n    saved = page.video.save_as(out_path)\n    await page.close()\n    await saved\n    await page.context.close()\n    assert os.path.exists(out_path)\n\n\nasync def test_should_be_None_if_not_recording(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = await browser.new_page()\n    assert page.video is None\n    await page.close()\n"
  },
  {
    "path": "tests/async/test_wait_for_function.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom datetime import datetime\n\nimport pytest\n\nfrom playwright.async_api import ConsoleMessage, Error, Page\n\n\nasync def test_should_timeout(page: Page) -> None:\n    start_time = datetime.now()\n    timeout = 42\n    await page.wait_for_timeout(timeout)\n    assert ((datetime.now() - start_time).microseconds * 1000) >= timeout / 2\n\n\nasync def test_should_accept_a_string(page: Page) -> None:\n    watchdog = page.wait_for_function(\"window.__FOO === 1\")\n    await page.evaluate(\"window['__FOO'] = 1\")\n    await watchdog\n\n\nasync def test_should_work_when_resolved_right_before_execution_context_disposal(\n    page: Page,\n) -> None:\n    await page.add_init_script(\"window['__RELOADED'] = true\")\n    await page.wait_for_function(\n        \"\"\"() => {\n            if (!window['__RELOADED'])\n                window.location.reload();\n            return true;\n        }\"\"\"\n    )\n\n\nasync def test_should_poll_on_interval(page: Page) -> None:\n    polling = 100\n    time_delta = await page.wait_for_function(\n        \"\"\"() => {\n            if (!window['__startTime']) {\n                window['__startTime'] = Date.now();\n                return false;\n            }\n            return Date.now() - window['__startTime'];\n        }\"\"\",\n        polling=polling,\n    )\n    assert await time_delta.json_value() >= polling\n\n\nasync def test_should_avoid_side_effects_after_timeout(page: Page) -> None:\n    counter = 0\n\n    async def on_console(message: ConsoleMessage) -> None:\n        nonlocal counter\n        counter += 1\n\n    page.on(\"console\", on_console)\n    with pytest.raises(Error) as exc_info:\n        await page.wait_for_function(\n            \"\"\"() => {\n            window['counter'] = (window['counter'] || 0) + 1;\n            console.log(window['counter']);\n        }\"\"\",\n            polling=1,\n            timeout=1000,\n        )\n\n    saved_counter = counter\n    await page.wait_for_timeout(2000)  # Give it some time to produce more logs.\n\n    assert \"Timeout 1000ms exceeded\" in exc_info.value.message\n    assert counter == saved_counter\n\n\nasync def test_should_throw_on_polling_mutation(page: Page) -> None:\n    with pytest.raises(Error) as exc_info:\n        await page.wait_for_function(\"() => true\", polling=\"mutation\")  # type: ignore\n    assert \"Unknown polling option: mutation\" in exc_info.value.message\n"
  },
  {
    "path": "tests/async/test_wait_for_url.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nimport pytest\n\nfrom playwright.async_api import Error, Page\nfrom tests.server import Server\n\n\nasync def test_wait_for_url_should_work(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"url => window.location.href = url\", server.PREFIX + \"/grid.html\"\n    )\n    await page.wait_for_url(\"**/grid.html\")\n    assert \"grid.html\" in page.url\n\n\nasync def test_wait_for_url_should_respect_timeout(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    with pytest.raises(Error) as exc_info:\n        await page.wait_for_url(\"**/frame.html\", timeout=2500)\n    assert \"Timeout 2500ms exceeded\" in exc_info.value.message\n\n\nasync def test_wait_for_url_should_work_with_both_domcontentloaded_and_load(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.wait_for_url(\"**/*\", wait_until=\"domcontentloaded\")\n    await page.wait_for_url(\"**/*\", wait_until=\"load\")\n\n\nasync def test_wait_for_url_should_work_with_clicking_on_anchor_links(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content('<a href=\"#foobar\">foobar</a>')\n    await page.click(\"a\")\n    await page.wait_for_url(\"**/*#foobar\")\n    assert page.url == server.EMPTY_PAGE + \"#foobar\"\n\n\nasync def test_wait_for_url_should_work_with_history_push_state(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <a onclick='javascript:pushState()'>SPA</a>\n        <script>\n            function pushState() { history.pushState({}, '', 'wow.html') }\n        </script>\n    \"\"\"\n    )\n    await page.click(\"a\")\n    await page.wait_for_url(\"**/wow.html\")\n    assert page.url == server.PREFIX + \"/wow.html\"\n\n\nasync def test_wait_for_url_should_work_with_history_replace_state(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n        <a onclick='javascript:replaceState()'>SPA</a>\n        <script>\n            function replaceState() { history.replaceState({}, '', '/replaced.html') }\n        </script>\n    \"\"\"\n    )\n    await page.click(\"a\")\n    await page.wait_for_url(\"**/replaced.html\")\n    assert page.url == server.PREFIX + \"/replaced.html\"\n\n\nasync def test_wait_for_url_should_work_with_dom_history_back_forward(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.set_content(\n        \"\"\"\n      <a id=back onclick='javascript:go_back()'>back</a>\n      <a id=forward onclick='javascript:go_forward()'>forward</a>\n      <script>\n        function go_back() { history.back(); }\n        function go_forward() { history.forward(); }\n        history.pushState({}, '', '/first.html')\n        history.pushState({}, '', '/second.html')\n      </script>\n    \"\"\"\n    )\n\n    assert page.url == server.PREFIX + \"/second.html\"\n\n    await page.click(\"a#back\")\n    await page.wait_for_url(\"**/first.html\")\n    assert page.url == server.PREFIX + \"/first.html\"\n\n    await page.click(\"a#forward\")\n    await page.wait_for_url(\"**/second.html\")\n    assert page.url == server.PREFIX + \"/second.html\"\n\n\nasync def test_wait_for_url_should_work_with_url_match_for_same_document_navigations(\n    page: Page, server: Server\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\"history.pushState({}, '', '/first.html')\")\n    await page.evaluate(\"history.pushState({}, '', '/second.html')\")\n    await page.evaluate(\"history.pushState({}, '', '/third.html')\")\n    await page.wait_for_url(re.compile(r\"third\\.html\"))\n    assert \"/third.html\" in page.url\n\n\nasync def test_wait_for_url_should_work_with_commit(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    await page.evaluate(\n        \"url => window.location.href = url\", server.PREFIX + \"/grid.html\"\n    )\n    await page.wait_for_url(\"**/grid.html\", wait_until=\"commit\")\n    assert \"grid.html\" in page.url\n"
  },
  {
    "path": "tests/async/test_websocket.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom typing import Union\n\nimport pytest\n\nfrom playwright.async_api import Error, Page, WebSocket\nfrom tests.server import Server, WebSocketProtocol\n\n\nasync def test_should_work(page: Page, server: Server) -> None:\n    server.send_on_web_socket_connection(b\"incoming\")\n    value = await page.evaluate(\n        \"\"\"port => {\n        let cb;\n        const result = new Promise(f => cb = f);\n        const ws = new WebSocket('ws://localhost:' + port + '/ws');\n        ws.addEventListener('message', data => { ws.close(); cb(data.data); });\n        return result;\n    }\"\"\",\n        server.PORT,\n    )\n    assert value == \"incoming\"\n    pass\n\n\nasync def test_should_emit_close_events(page: Page, server: Server) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    close_future: asyncio.Future[None] = asyncio.Future()\n    async with page.expect_websocket() as ws_info:\n        await page.evaluate(\n            \"\"\"port => {\n            const ws = new WebSocket('ws://localhost:' + port + '/ws');\n            ws.addEventListener('open', data => ws.close());\n        }\"\"\",\n            server.PORT,\n        )\n    ws = await ws_info.value\n    ws.on(\"close\", lambda ws: close_future.set_result(None))\n    assert ws.url == f\"ws://localhost:{server.PORT}/ws\"\n    assert repr(ws) == f\"<WebSocket url={ws.url!r}>\"\n    await close_future\n    assert ws.is_closed()\n\n\nasync def test_should_emit_frame_events(page: Page, server: Server) -> None:\n    def _handle_ws_connection(ws: WebSocketProtocol) -> None:\n        def _onMessage(payload: bytes, isBinary: bool) -> None:\n            ws.sendMessage(b\"incoming\", False)\n            ws.sendClose()\n\n        setattr(ws, \"onMessage\", _onMessage)\n\n    server.once_web_socket_connection(_handle_ws_connection)\n    log = []\n    socket_close_future: \"asyncio.Future[None]\" = asyncio.Future()\n\n    def on_web_socket(ws: WebSocket) -> None:\n        log.append(\"open\")\n\n        def _on_framesent(payload: Union[bytes, str]) -> None:\n            assert isinstance(payload, str)\n            log.append(f\"sent<{payload}>\")\n\n        ws.on(\"framesent\", _on_framesent)\n\n        def _on_framereceived(payload: Union[bytes, str]) -> None:\n            assert isinstance(payload, str)\n            log.append(f\"received<{payload}>\")\n\n        ws.on(\"framereceived\", _on_framereceived)\n\n        def _handle_close(ws: WebSocket) -> None:\n            log.append(\"close\")\n            socket_close_future.set_result(None)\n\n        ws.on(\"close\", _handle_close)\n\n    page.on(\"websocket\", on_web_socket)\n    async with page.expect_event(\"websocket\"):\n        await page.evaluate(\n            \"\"\"port => {\n            const ws = new WebSocket('ws://localhost:' + port + '/ws');\n            ws.addEventListener('open', () => ws.send('outgoing'));\n            ws.addEventListener('message', () => ws.close())\n        }\"\"\",\n            server.PORT,\n        )\n    await socket_close_future\n    assert log[0] == \"open\"\n    assert log[3] == \"close\"\n    log.sort()\n    assert log == [\"close\", \"open\", \"received<incoming>\", \"sent<outgoing>\"]\n\n\nasync def test_should_emit_binary_frame_events(page: Page, server: Server) -> None:\n    def _handle_ws_connection(ws: WebSocketProtocol) -> None:\n        ws.sendMessage(b\"incoming\")\n\n        def _onMessage(payload: bytes, isBinary: bool) -> None:\n            if payload == b\"echo-bin\":\n                ws.sendMessage(b\"\\x04\\x02\", True)\n                ws.sendClose()\n            if payload == b\"echo-text\":\n                ws.sendMessage(b\"text\", False)\n                ws.sendClose()\n\n        setattr(ws, \"onMessage\", _onMessage)\n\n    server.once_web_socket_connection(_handle_ws_connection)\n    done_task: \"asyncio.Future[None]\" = asyncio.Future()\n    sent = []\n    received = []\n\n    def on_web_socket(ws: WebSocket) -> None:\n        ws.on(\"framesent\", lambda payload: sent.append(payload))\n        ws.on(\"framereceived\", lambda payload: received.append(payload))\n        ws.on(\"close\", lambda _: done_task.set_result(None))\n\n    page.on(\"websocket\", on_web_socket)\n    async with page.expect_event(\"websocket\"):\n        await page.evaluate(\n            \"\"\"port => {\n            const ws = new WebSocket('ws://localhost:' + port + '/ws');\n            ws.addEventListener('open', () => {\n                const binary = new Uint8Array(5);\n                for (let i = 0; i < 5; ++i)\n                    binary[i] = i;\n                ws.send(binary);\n                ws.send('echo-bin');\n            });\n        }\"\"\",\n            server.PORT,\n        )\n    await done_task\n    assert sent == [b\"\\x00\\x01\\x02\\x03\\x04\", \"echo-bin\"]\n    assert received == [\"incoming\", b\"\\x04\\x02\"]\n\n\nasync def test_should_reject_wait_for_event_on_close_and_error(\n    page: Page, server: Server\n) -> None:\n    server.send_on_web_socket_connection(b\"incoming\")\n    async with page.expect_event(\"websocket\") as ws_info:\n        await page.evaluate(\n            \"\"\"port => {\n            window.ws = new WebSocket('ws://localhost:' + port + '/ws');\n        }\"\"\",\n            server.PORT,\n        )\n    ws = await ws_info.value\n    await ws.wait_for_event(\"framereceived\")\n    with pytest.raises(Error) as exc_info:\n        async with ws.expect_event(\"framesent\"):\n            await page.evaluate(\"window.ws.close()\")\n    assert exc_info.value.message == \"Socket closed\"\n\n\nasync def test_should_emit_error_event(\n    page: Page, server: Server, browser_name: str, browser_channel: str\n) -> None:\n    future: \"asyncio.Future[str]\" = asyncio.Future()\n\n    def _on_ws_socket_error(err: str) -> None:\n        future.set_result(err)\n\n    def _on_websocket(websocket: WebSocket) -> None:\n        websocket.on(\"socketerror\", _on_ws_socket_error)\n\n    page.on(\n        \"websocket\",\n        _on_websocket,\n    )\n    await page.evaluate(\n        \"\"\"port => new WebSocket(`ws://localhost:${port}/bogus-ws`)\"\"\",\n        server.PORT,\n    )\n    err = await future\n    if browser_name == \"firefox\":\n        assert err == \"CLOSE_ABNORMAL\"\n    else:\n        assert (\"\" if browser_channel == \"msedge\" else \": 404\") in err\n"
  },
  {
    "path": "tests/async/test_worker.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport asyncio\nfrom asyncio.futures import Future\n\nimport pytest\n\nfrom playwright.async_api import Browser, ConsoleMessage, Error, Page, Worker\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\nasync def test_workers_page_workers(page: Page, server: Server) -> None:\n    async with page.expect_worker() as worker_info:\n        await page.goto(server.PREFIX + \"/worker/worker.html\")\n    worker = await worker_info.value\n    assert \"worker.js\" in worker.url\n    assert repr(worker) == f\"<Worker url={worker.url!r}>\"\n\n    assert (\n        await worker.evaluate('() => self[\"workerFunction\"]()')\n        == \"worker function result\"\n    )\n\n    await page.goto(server.EMPTY_PAGE)\n    assert len(page.workers) == 0\n\n\nasync def test_workers_should_emit_created_and_destroyed_events(page: Page) -> None:\n    worker_obj = None\n    async with page.expect_event(\"worker\") as event_info:\n        worker_obj = await page.evaluate_handle(\n            \"() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'})))\"\n        )\n    worker = await event_info.value\n    worker_this_obj = await worker.evaluate_handle(\"() => this\")\n    worker_destroyed_promise: Future[Worker] = asyncio.Future()\n    worker.once(\"close\", lambda w: worker_destroyed_promise.set_result(w))\n    await page.evaluate(\"workerObj => workerObj.terminate()\", worker_obj)\n    assert await worker_destroyed_promise == worker\n    with pytest.raises(Error) as exc:\n        await worker_this_obj.get_property(\"self\")\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc.value.message\n\n\nasync def test_workers_should_report_console_logs(page: Page) -> None:\n    async with page.expect_console_message() as message_info:\n        await page.evaluate(\n            '() => new Worker(URL.createObjectURL(new Blob([\"console.log(1)\"], {type: \"application/javascript\"})))'\n        )\n    message = await message_info.value\n    assert message.text == \"1\"\n\n\nasync def test_workers_should_have_JSHandles_for_console_logs(\n    page: Page, browser_name: str\n) -> None:\n    log_promise: \"asyncio.Future[ConsoleMessage]\" = asyncio.Future()\n    page.on(\"console\", lambda m: log_promise.set_result(m))\n    await page.evaluate(\n        \"() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'})))\"\n    )\n    log = await log_promise\n    if browser_name != \"firefox\":\n        assert log.text == \"1 2 3 DedicatedWorkerGlobalScope\"\n    else:\n        assert log.text == \"1 2 3 JSHandle@object\"\n    assert len(log.args) == 4\n    assert await (await log.args[3].get_property(\"origin\")).json_value() == \"null\"\n\n\nasync def test_workers_should_evaluate(page: Page) -> None:\n    async with page.expect_event(\"worker\") as event_info:\n        await page.evaluate(\n            \"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))\"\n        )\n    worker = await event_info.value\n    assert await worker.evaluate(\"1+1\") == 2\n\n\nasync def test_workers_should_report_errors(page: Page) -> None:\n    error_promise: \"asyncio.Future[Error]\" = asyncio.Future()\n    page.on(\"pageerror\", lambda e: error_promise.set_result(e))\n    await page.evaluate(\n        \"\"\"() => new Worker(URL.createObjectURL(new Blob([`\n      setTimeout(() => {\n        // Do a console.log just to check that we do not confuse it with an error.\n        console.log('hey');\n        throw new Error('this is my error');\n      })\n    `], {type: 'application/javascript'})))\"\"\"\n    )\n    error_log = await error_promise\n    assert \"this is my error\" in error_log.message\n\n\nasync def test_workers_should_clear_upon_navigation(server: Server, page: Page) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_event(\"worker\") as event_info:\n        await page.evaluate(\n            '() => new Worker(URL.createObjectURL(new Blob([\"console.log(1)\"], {type: \"application/javascript\"})))'\n        )\n    worker = await event_info.value\n    assert len(page.workers) == 1\n    destroyed = []\n    worker.once(\"close\", lambda _: destroyed.append(True))\n    await page.goto(server.PREFIX + \"/one-style.html\")\n    assert destroyed == [True]\n    assert len(page.workers) == 0\n\n\nasync def test_workers_should_clear_upon_cross_process_navigation(\n    server: Server, page: Page\n) -> None:\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_event(\"worker\") as event_info:\n        await page.evaluate(\n            \"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))\"\n        )\n    worker = await event_info.value\n    assert len(page.workers) == 1\n    destroyed = []\n    worker.once(\"close\", lambda _: destroyed.append(True))\n    await page.goto(server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    assert destroyed == [True]\n    assert len(page.workers) == 0\n\n\n@pytest.mark.skip_browser(\n    \"firefox\"\n)  # https://github.com/microsoft/playwright/issues/21760\nasync def test_workers_should_report_network_activity(\n    page: Page, server: Server\n) -> None:\n    async with page.expect_worker() as worker_info:\n        await page.goto(server.PREFIX + \"/worker/worker.html\")\n    worker = await worker_info.value\n    url = server.PREFIX + \"/one-style.css\"\n    async with (\n        page.expect_request(url) as request_info,\n        page.expect_response(url) as response_info,\n    ):\n        await worker.evaluate(\n            \"url => fetch(url).then(response => response.text()).then(console.log)\", url\n        )\n    request = await request_info.value\n    response = await response_info.value\n    assert request.url == url\n    assert response.request == request\n    assert response.ok\n\n\n@pytest.mark.skip_browser(\n    \"firefox\"\n)  # https://github.com/microsoft/playwright/issues/21760\nasync def test_workers_should_report_network_activity_on_worker_creation(\n    page: Page, server: Server\n) -> None:\n    # Chromium needs waitForDebugger enabled for this one.\n    await page.goto(server.EMPTY_PAGE)\n    url = server.PREFIX + \"/one-style.css\"\n    async with (\n        page.expect_request(url) as request_info,\n        page.expect_response(url) as response_info,\n    ):\n        await page.evaluate(\n            \"\"\"url => new Worker(URL.createObjectURL(new Blob([`\n        fetch(\"${url}\").then(response => response.text()).then(console.log);\n        `], {type: 'application/javascript'})))\"\"\",\n            url,\n        )\n    request = await request_info.value\n    response = await response_info.value\n    assert request.url == url\n    assert response.request == request\n    assert response.ok\n\n\nasync def test_workers_should_format_number_using_context_locale(\n    browser: Browser, server: Server, browser_name: str\n) -> None:\n    context = await browser.new_context(locale=\"ru-RU\")\n    page = await context.new_page()\n    await page.goto(server.EMPTY_PAGE)\n    async with page.expect_worker() as worker_info:\n        await page.evaluate(\n            \"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))\"\n        )\n    worker = await worker_info.value\n    # https://github.com/microsoft/playwright/issues/38919\n    expected = \"10,000.2\" if browser_name == \"firefox\" else \"10\\u00a0000,2\"\n    assert await worker.evaluate(\"() => (10000.20).toLocaleString()\") == expected\n    await context.close()\n\n\nasync def test_worker_should_report_console_event(page: Page) -> None:\n    async with page.expect_worker() as worker_info:\n        await page.evaluate(\n            \"() => { window.worker = new Worker(URL.createObjectURL(new Blob(['42'], { type: 'application/javascript' }))); }\"\n        )\n    worker = await worker_info.value\n\n    async with worker.expect_event(\"console\") as message1_info:\n        async with page.expect_console_message() as message2_info:\n            async with page.context.expect_console_message() as message3_info:\n                await worker.evaluate(\"() => { console.log('hello from worker'); }\")\n\n    message1 = await message1_info.value\n    message2 = await message2_info.value\n    message3 = await message3_info.value\n\n    assert message1.text == \"hello from worker\"\n    assert message1 is message2\n    assert message1 is message3\n    assert message1.worker is worker\n\n\nasync def test_worker_should_report_console_event_when_not_listening_on_page_or_context(\n    page: Page,\n) -> None:\n    async with page.expect_worker() as worker_info:\n        await page.evaluate(\n            \"() => { window.worker = new Worker(URL.createObjectURL(new Blob(['42'], { type: 'application/javascript' }))); }\"\n        )\n    worker = await worker_info.value\n\n    async with worker.expect_event(\"console\") as message_info:\n        await worker.evaluate(\"() => { console.log('hello from worker'); }\")\n\n    message = await message_info.value\n    assert message.text == \"hello from worker\"\n    assert message.worker is worker\n"
  },
  {
    "path": "tests/async/utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom typing import Any, List, cast\n\nfrom playwright.async_api import (\n    ElementHandle,\n    Error,\n    Frame,\n    Page,\n    Selectors,\n    ViewportSize,\n)\n\n\nclass Utils:\n    async def attach_frame(self, page: Page, frame_id: str, url: str) -> Frame:\n        handle = await page.evaluate_handle(\n            \"\"\"async ({ frame_id, url }) => {\n                const frame = document.createElement('iframe');\n                frame.src = url;\n                frame.id = frame_id;\n                document.body.appendChild(frame);\n                await new Promise(x => frame.onload = x);\n                return frame;\n            }\"\"\",\n            {\"frame_id\": frame_id, \"url\": url},\n        )\n        frame = await cast(ElementHandle, handle.as_element()).content_frame()\n        assert frame\n        return frame\n\n    async def detach_frame(self, page: Page, frame_id: str) -> None:\n        await page.evaluate(\n            \"frame_id => document.getElementById(frame_id).remove()\", frame_id\n        )\n\n    def dump_frames(self, frame: Frame, indentation: str = \"\") -> List[str]:\n        indentation = indentation or \"\"\n        description = re.sub(r\":\\d+/\", \":<PORT>/\", frame.url)\n        if frame.name:\n            description += \" (\" + frame.name + \")\"\n        result = [indentation + description]\n        sorted_frames = sorted(\n            frame.child_frames, key=lambda frame: frame.url + frame.name\n        )\n        for child in sorted_frames:\n            result = result + utils.dump_frames(child, \"    \" + indentation)\n        return result\n\n    async def verify_viewport(self, page: Page, width: int, height: int) -> None:\n        assert cast(ViewportSize, page.viewport_size)[\"width\"] == width\n        assert cast(ViewportSize, page.viewport_size)[\"height\"] == height\n        assert await page.evaluate(\"window.innerWidth\") == width\n        assert await page.evaluate(\"window.innerHeight\") == height\n\n    async def register_selector_engine(\n        self, selectors: Selectors, *args: Any, **kwargs: Any\n    ) -> None:\n        try:\n            await selectors.register(*args, **kwargs)\n        except Error as exc:\n            if \"has been already registered\" not in exc.message:\n                raise exc\n\n\nutils = Utils()\n"
  },
  {
    "path": "tests/common/__init__.py",
    "content": ""
  },
  {
    "path": "tests/common/test_collect_handles.py",
    "content": ""
  },
  {
    "path": "tests/common/test_events.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import sync_playwright\nfrom tests.server import Server\n\n\ndef test_events(browser_name: str, launch_arguments: Dict, server: Server) -> None:\n    with pytest.raises(Exception, match=\"fail\"):\n\n        def fail() -> None:\n            raise Exception(\"fail\")\n\n        with sync_playwright() as p:\n            with p[browser_name].launch(**launch_arguments) as browser:\n                with browser.new_page() as page:\n                    page.on(\"response\", lambda _: fail())\n                    page.goto(server.PREFIX + \"/grid.html\")\n"
  },
  {
    "path": "tests/common/test_signals.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nimport asyncio\nimport multiprocessing\nimport os\nimport signal\nimport sys\nfrom typing import Any, Dict\n\nimport pytest\n\nfrom playwright.async_api import async_playwright\nfrom playwright.sync_api import sync_playwright\n\n\ndef _test_signals_async(\n    browser_name: str, launch_arguments: Dict, wait_queue: \"multiprocessing.Queue[str]\"\n) -> None:\n    # On Windows, hint to mypy and pyright that they shouldn't check this function\n    if sys.platform == \"win32\":\n        return\n\n    os.setpgrp()\n    sigint_received = False\n\n    def my_sig_handler(signum: int, frame: Any) -> None:\n        nonlocal sigint_received\n        sigint_received = True\n\n    signal.signal(signal.SIGINT, my_sig_handler)\n\n    async def main() -> None:\n        playwright = await async_playwright().start()\n        browser = await playwright[browser_name].launch(\n            **launch_arguments,\n            handle_sigint=False,\n        )\n        context = await browser.new_context()\n        page = await context.new_page()\n        notified = False\n        try:\n            while not sigint_received:\n                if not notified:\n                    wait_queue.put(\"ready\")\n                    notified = True\n                await page.wait_for_timeout(100)\n        finally:\n            wait_queue.put(\"close context\")\n            await context.close()\n            wait_queue.put(\"close browser\")\n            await browser.close()\n            wait_queue.put(\"close playwright\")\n            await playwright.stop()\n            wait_queue.put(\"all done\")\n\n    asyncio.run(main())\n\n\ndef _test_signals_sync(\n    browser_name: str, launch_arguments: Dict, wait_queue: \"multiprocessing.Queue[str]\"\n) -> None:\n    # On Windows, hint to mypy and pyright that they shouldn't check this function\n    if sys.platform == \"win32\":\n        return\n\n    os.setpgrp()\n    sigint_received = False\n\n    def my_sig_handler(signum: int, frame: Any) -> None:\n        nonlocal sigint_received\n        sigint_received = True\n\n    signal.signal(signal.SIGINT, my_sig_handler)\n\n    playwright = sync_playwright().start()\n    browser = playwright[browser_name].launch(\n        **launch_arguments,\n        handle_sigint=False,\n    )\n    context = browser.new_context()\n    page = context.new_page()\n    notified = False\n    try:\n        while not sigint_received:\n            if not notified:\n                wait_queue.put(\"ready\")\n                notified = True\n            page.wait_for_timeout(100)\n    finally:\n        wait_queue.put(\"close context\")\n        context.close()\n        wait_queue.put(\"close browser\")\n        browser.close()\n        wait_queue.put(\"close playwright\")\n        playwright.stop()\n        wait_queue.put(\"all done\")\n\n\ndef _create_signals_test(\n    target: Any, browser_name: str, launch_arguments: Dict\n) -> None:\n    # On Windows, hint to mypy and pyright that they shouldn't check this function\n    if sys.platform == \"win32\":\n        return\n\n    wait_queue: \"multiprocessing.Queue[str]\" = multiprocessing.Queue()\n    process = multiprocessing.Process(\n        target=target, args=[browser_name, launch_arguments, wait_queue]\n    )\n    process.start()\n    assert process.pid is not None\n    logs = [wait_queue.get()]\n    os.killpg(os.getpgid(process.pid), signal.SIGINT)\n    process.join()\n    while not wait_queue.empty():\n        logs.append(wait_queue.get())\n    assert logs == [\n        \"ready\",\n        \"close context\",\n        \"close browser\",\n        \"close playwright\",\n        \"all done\",\n    ]\n    assert process.exitcode == 0\n\n\n@pytest.mark.skipif(sys.platform == \"win32\", reason=\"there is no SIGINT on Windows\")\ndef test_signals_sync(browser_name: str, launch_arguments: Dict) -> None:\n    _create_signals_test(_test_signals_sync, browser_name, launch_arguments)\n\n\n@pytest.mark.skipif(sys.platform == \"win32\", reason=\"there is no SIGINT on Windows\")\ndef test_signals_async(browser_name: str, launch_arguments: Dict) -> None:\n    _create_signals_test(_test_signals_async, browser_name, launch_arguments)\n"
  },
  {
    "path": "tests/common/test_threads.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport threading\nfrom typing import Dict\n\nfrom playwright.sync_api import sync_playwright\n\n\ndef test_running_in_thread(browser_name: str, launch_arguments: Dict) -> None:\n    result = []\n\n    class TestThread(threading.Thread):\n        def run(self) -> None:\n            with sync_playwright() as playwright:\n                browser = playwright[browser_name].launch(**launch_arguments)\n                # This should not throw ^^.\n                browser.new_page()\n                browser.close()\n                result.append(\"Success\")\n\n    test_thread = TestThread()\n    test_thread.start()\n    test_thread.join()\n    assert \"Success\" in result\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport inspect\nimport io\nimport json\nimport os\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom typing import Any, Callable, Dict, Generator, List, Optional, cast\n\nimport pytest\nfrom PIL import Image\nfrom pixelmatch import pixelmatch\nfrom pixelmatch.contrib.PIL import from_PIL_to_raw_data\n\nimport playwright\nfrom playwright._impl._path_utils import get_file_dirname\n\nfrom .server import Server, test_server\n\n_dirname = get_file_dirname()\n\n\ndef pytest_configure(config: pytest.Config) -> None:\n    if os.environ.get(\"CI\"):\n        config.option.reruns = 3\n\n\ndef pytest_generate_tests(metafunc: pytest.Metafunc) -> None:\n    if \"browser_name\" in metafunc.fixturenames:\n        browsers = metafunc.config.option.browser or [\"chromium\", \"firefox\", \"webkit\"]\n        metafunc.parametrize(\"browser_name\", browsers, scope=\"session\")\n\n\n@pytest.fixture(scope=\"session\")\ndef assetdir() -> Path:\n    return _dirname / \"assets\"\n\n\n@pytest.fixture(scope=\"session\")\ndef headless(pytestconfig: pytest.Config) -> bool:\n    return not (pytestconfig.getoption(\"--headed\") or os.getenv(\"HEADFUL\", False))\n\n\n@pytest.fixture(scope=\"session\")\ndef launch_arguments(pytestconfig: pytest.Config, headless: bool) -> Dict:\n    args: Dict = {\n        \"headless\": headless,\n    }\n    if pytestconfig.getoption(\"--browser-channel\"):\n        args[\"channel\"] = pytestconfig.getoption(\"--browser-channel\")\n    return args\n\n\n@pytest.fixture\ndef server() -> Generator[Server, None, None]:\n    yield test_server.server\n\n\n@pytest.fixture\ndef https_server() -> Generator[Server, None, None]:\n    yield test_server.https_server\n\n\n@pytest.fixture(autouse=True, scope=\"session\")\ndef start_server() -> Generator[None, None, None]:\n    test_server.start()\n    yield\n    test_server.stop()\n\n\n@pytest.fixture(autouse=True)\ndef after_each_hook() -> Generator[None, None, None]:\n    yield\n    test_server.reset()\n\n\n@pytest.fixture(scope=\"session\")\ndef browser_name(pytestconfig: pytest.Config) -> str:\n    return cast(str, pytestconfig.getoption(\"browser\"))\n\n\n@pytest.fixture(scope=\"session\")\ndef browser_channel(pytestconfig: pytest.Config) -> Optional[str]:\n    return cast(Optional[str], pytestconfig.getoption(\"--browser-channel\"))\n\n\n@pytest.fixture(scope=\"session\")\ndef is_headless_shell(browser_name: str, browser_channel: str, headless: bool) -> bool:\n    return browser_name == \"chromium\" and (\n        browser_channel == \"chromium-headless-shell\"\n        or (not browser_channel and headless)\n    )\n\n\n@pytest.fixture(scope=\"session\")\ndef is_webkit(browser_name: str) -> bool:\n    return browser_name == \"webkit\"\n\n\n@pytest.fixture(scope=\"session\")\ndef is_firefox(browser_name: str) -> bool:\n    return browser_name == \"firefox\"\n\n\n@pytest.fixture(scope=\"session\")\ndef is_chromium(browser_name: str) -> bool:\n    return browser_name == \"chromium\"\n\n\n@pytest.fixture(scope=\"session\")\ndef is_win() -> bool:\n    return sys.platform == \"win32\"\n\n\n@pytest.fixture(scope=\"session\")\ndef is_linux() -> bool:\n    return sys.platform == \"linux\"\n\n\n@pytest.fixture(scope=\"session\")\ndef is_mac() -> bool:\n    return sys.platform == \"darwin\"\n\n\ndef _get_skiplist(\n    request: pytest.FixtureRequest, values: List[str], value_name: str\n) -> List[str]:\n    skipped_values = []\n    # Allowlist\n    only_marker = request.node.get_closest_marker(f\"only_{value_name}\")\n    if only_marker:\n        skipped_values = values\n        skipped_values.remove(only_marker.args[0])\n\n    # Denylist\n    skip_marker = request.node.get_closest_marker(f\"skip_{value_name}\")\n    if skip_marker:\n        skipped_values.append(skip_marker.args[0])\n\n    return skipped_values\n\n\n@pytest.fixture(autouse=True)\ndef skip_by_browser(request: pytest.FixtureRequest, browser_name: str) -> None:\n    skip_browsers_names = _get_skiplist(\n        request, [\"chromium\", \"firefox\", \"webkit\"], \"browser\"\n    )\n\n    if browser_name in skip_browsers_names:\n        pytest.skip(f\"skipped for this browser: {browser_name}\")\n\n\n@pytest.fixture(autouse=True)\ndef skip_by_platform(request: pytest.FixtureRequest) -> None:\n    skip_platform_names = _get_skiplist(\n        request, [\"win32\", \"linux\", \"darwin\"], \"platform\"\n    )\n\n    if sys.platform in skip_platform_names:\n        pytest.skip(f\"skipped on this platform: {sys.platform}\")\n\n\ndef pytest_addoption(parser: pytest.Parser) -> None:\n    group = parser.getgroup(\"playwright\", \"Playwright\")\n    group.addoption(\n        \"--browser\",\n        action=\"append\",\n        default=[],\n        help=\"Browsers which should be used. By default on all the browsers.\",\n    )\n    group.addoption(\n        \"--browser-channel\",\n        action=\"store\",\n        default=None,\n        help=\"Browser channel to be used.\",\n    )\n    parser.addoption(\n        \"--headed\",\n        action=\"store_true\",\n        default=False,\n        help=\"Run tests in headed mode.\",\n    )\n\n\n@pytest.fixture(scope=\"session\")\ndef assert_to_be_golden(browser_name: str) -> Callable[[bytes, str], None]:\n    def compare(received_raw: bytes, golden_name: str) -> None:\n        golden_file_path = _dirname / f\"golden-{browser_name}\" / golden_name\n        try:\n            golden_file = golden_file_path.read_bytes()\n            received_image = Image.open(io.BytesIO(received_raw))\n            golden_image = Image.open(io.BytesIO(golden_file))\n\n            if golden_image.size != received_image.size:\n                pytest.fail(\"Image size differs to golden image\")\n                return\n            diff_pixels = pixelmatch(\n                from_PIL_to_raw_data(received_image),\n                from_PIL_to_raw_data(golden_image),\n                golden_image.size[0],\n                golden_image.size[1],\n                threshold=0.2,\n            )\n            assert diff_pixels == 0\n        except Exception:\n            if os.getenv(\"PW_WRITE_SCREENSHOT\"):\n                golden_file_path.parent.mkdir(parents=True, exist_ok=True)\n                golden_file_path.write_bytes(received_raw)\n                print(f\"Wrote {golden_file_path}\")\n            raise\n\n    return compare\n\n\nclass RemoteServer:\n    def __init__(\n        self, browser_name: str, launch_server_options: Dict, tmpfile: Path\n    ) -> None:\n        driver_dir = Path(inspect.getfile(playwright)).parent / \"driver\"\n        if sys.platform == \"win32\":\n            node_executable = driver_dir / \"node.exe\"\n        else:\n            node_executable = driver_dir / \"node\"\n        cli_js = driver_dir / \"package\" / \"cli.js\"\n        tmpfile.write_text(json.dumps(launch_server_options))\n        self.process = subprocess.Popen(\n            [\n                str(node_executable),\n                str(cli_js),\n                \"launch-server\",\n                \"--browser\",\n                browser_name,\n                \"--config\",\n                str(tmpfile),\n            ],\n            stdout=subprocess.PIPE,\n            stderr=sys.stderr,\n            cwd=driver_dir,\n        )\n        assert self.process.stdout\n        self.ws_endpoint = self.process.stdout.readline().decode().strip()\n        self.process.stdout.close()\n\n    def kill(self) -> None:\n        # Send the signal to all the process groups\n        if self.process.poll() is not None:\n            return\n        self.process.kill()\n        self.process.wait()\n\n\n@pytest.fixture\ndef launch_server(\n    browser_name: str, launch_arguments: Dict, tmp_path: Path\n) -> Generator[Callable[..., RemoteServer], None, None]:\n    remotes: List[RemoteServer] = []\n\n    def _launch_server(**kwargs: Dict[str, Any]) -> RemoteServer:\n        remote = RemoteServer(\n            browser_name,\n            {\n                **launch_arguments,\n                **kwargs,\n            },\n            tmp_path / f\"settings-{len(remotes)}.json\",\n        )\n        remotes.append(remote)\n        return remote\n\n    yield _launch_server\n\n    for remote in remotes:\n        remote.kill()\n"
  },
  {
    "path": "tests/server.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport abc\nimport asyncio\nimport contextlib\nimport gzip\nimport mimetypes\nimport pathlib\nimport socket\nimport threading\nfrom contextlib import closing\nfrom http import HTTPStatus\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    Generator,\n    Generic,\n    List,\n    Optional,\n    Set,\n    Tuple,\n    TypeVar,\n    Union,\n    cast,\n)\nfrom urllib.parse import urlparse\n\nfrom autobahn.twisted.resource import WebSocketResource\nfrom autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol\nfrom OpenSSL import crypto\nfrom pyee import EventEmitter\nfrom twisted.internet import reactor as _twisted_reactor\nfrom twisted.internet import ssl\nfrom twisted.internet.selectreactor import SelectReactor\nfrom twisted.web import http\n\nfrom playwright._impl._path_utils import get_file_dirname\n\n_dirname = get_file_dirname()\nreactor = cast(SelectReactor, _twisted_reactor)\n\n\ndef find_free_port() -> int:\n    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n        s.bind((\"\", 0))\n        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n        return s.getsockname()[1]\n\n\nT = TypeVar(\"T\")\n\n\nclass ExpectResponse(Generic[T]):\n    def __init__(self) -> None:\n        self._value: T\n\n    @property\n    def value(self) -> T:\n        if not hasattr(self, \"_value\"):\n            raise ValueError(\"no received value\")\n        return self._value\n\n\nclass TestServerRequest(http.Request):\n    __test__ = False\n    channel: \"TestServerHTTPChannel\"\n    post_body: Optional[bytes] = None\n\n    def process(self) -> None:\n        server = self.channel.factory.server_instance\n        if self.content:\n            self.post_body = self.content.read()\n            self.content.seek(0, 0)\n        else:\n            self.post_body = None\n        path = urlparse(self.uri.decode()).path\n\n        request_subscriber = server.request_subscribers.get(path)\n        if request_subscriber:\n            request_subscriber._loop.call_soon_threadsafe(\n                request_subscriber.set_result, self\n            )\n            server.request_subscribers.pop(path)\n\n        if path == \"/ws\":\n            server._ws_resource.render(self)\n            return\n\n        if server.auth.get(path):\n            authorization_header = self.requestHeaders.getRawHeaders(\"authorization\")\n            creds_correct = False\n            if authorization_header:\n                creds_correct = server.auth.get(path) == (\n                    self.getUser().decode(),\n                    self.getPassword().decode(),\n                )\n            if not creds_correct:\n                self.setHeader(b\"www-authenticate\", 'Basic realm=\"Secure Area\"')\n                self.setResponseCode(HTTPStatus.UNAUTHORIZED)\n                self.write(b\"HTTP Error 401 Unauthorized: Access is denied\")\n                self.finish()\n                return\n        if server.csp.get(path):\n            self.setHeader(b\"Content-Security-Policy\", server.csp[path])\n        if server.routes.get(path):\n            server.routes[path](self)\n            return\n\n        self._serve_file(server.static_path / path[1:], path)\n\n    def serve_file(self, path: pathlib.Path) -> None:\n        return self._serve_file(path, urlparse(self.uri.decode()).path)\n\n    def _serve_file(self, path: pathlib.Path, request_path: str) -> None:\n        server = self.channel.factory.server_instance\n        file_content = None\n        try:\n            file_content = path.read_bytes()\n            content_type = mimetypes.guess_type(path)[0]\n            if content_type and content_type.startswith(\"text/\"):\n                content_type += \"; charset=utf-8\"\n            self.setHeader(b\"Content-Type\", content_type)\n            self.setHeader(b\"Cache-Control\", \"no-cache, no-store\")\n            if request_path in server.gzip_routes:\n                self.setHeader(\"Content-Encoding\", \"gzip\")\n                self.write(gzip.compress(file_content))\n            else:\n                self.setHeader(b\"Content-Length\", str(len(file_content)))\n                self.write(file_content)\n            self.setResponseCode(HTTPStatus.OK)\n        except (FileNotFoundError, IsADirectoryError, PermissionError):\n            self.setHeader(b\"Content-Type\", \"text/plain\")\n            self.setResponseCode(HTTPStatus.NOT_FOUND)\n            if self.method != \"HEAD\":\n                self.write(f\"File not found: {path}\".encode())\n        self.finish()\n\n\nclass TestServerHTTPChannel(http.HTTPChannel):\n    factory: \"TestServerFactory\"\n    requestFactory = TestServerRequest\n\n\nclass TestServerFactory(http.HTTPFactory):\n    server_instance: \"Server\"\n    protocol = TestServerHTTPChannel\n\n\nclass Server:\n    protocol = \"http\"\n\n    def __init__(self) -> None:\n        self.PORT = find_free_port()\n        self.EMPTY_PAGE = f\"{self.protocol}://localhost:{self.PORT}/empty.html\"\n        self.PREFIX = f\"{self.protocol}://localhost:{self.PORT}\"\n        self.CROSS_PROCESS_PREFIX = f\"{self.protocol}://127.0.0.1:{self.PORT}\"\n        # On Windows, this list can be empty, reporting text/plain for scripts.\n        mimetypes.add_type(\"text/html\", \".html\")\n        mimetypes.add_type(\"text/css\", \".css\")\n        mimetypes.add_type(\"application/javascript\", \".js\")\n        mimetypes.add_type(\"image/png\", \".png\")\n        mimetypes.add_type(\"font/woff2\", \".woff2\")\n\n    def __repr__(self) -> str:\n        return self.PREFIX\n\n    @abc.abstractmethod\n    def listen(self, factory: TestServerFactory) -> None:\n        pass\n\n    def start(self, static_path: pathlib.Path = _dirname / \"assets\") -> None:\n        request_subscribers: Dict[str, asyncio.Future] = {}\n        auth: Dict[str, Tuple[str, str]] = {}\n        csp: Dict[str, str] = {}\n        routes: Dict[str, Callable[[TestServerRequest], Any]] = {}\n        gzip_routes: Set[str] = set()\n        self.request_subscribers = request_subscribers\n        self.auth = auth\n        self.csp = csp\n        self.routes = routes\n        self._ws_handlers: List[Callable[[\"WebSocketProtocol\"], None]] = []\n        self.gzip_routes = gzip_routes\n        self.static_path = static_path\n        factory = TestServerFactory()\n        factory.server_instance = self\n\n        ws_factory = WebSocketServerFactory()\n        ws_factory.protocol = WebSocketProtocol\n        setattr(ws_factory, \"server_instance\", self)\n        self._ws_resource = WebSocketResource(ws_factory)\n\n        self.listen(factory)\n\n    async def wait_for_request(self, path: str) -> TestServerRequest:\n        if path in self.request_subscribers:\n            return await self.request_subscribers[path]\n        future: asyncio.Future[\"TestServerRequest\"] = asyncio.Future()\n        self.request_subscribers[path] = future\n        return await future\n\n    def wait_for_web_socket(self) -> 'asyncio.Future[\"WebSocketProtocol\"]':\n        future: asyncio.Future[WebSocketProtocol] = asyncio.Future()\n        self.once_web_socket_connection(future.set_result)\n        return future\n\n    @contextlib.contextmanager\n    def expect_request(\n        self, path: str\n    ) -> Generator[ExpectResponse[TestServerRequest], None, None]:\n        future = asyncio.create_task(self.wait_for_request(path))\n\n        cb_wrapper: ExpectResponse[TestServerRequest] = ExpectResponse()\n\n        def done_cb(task: asyncio.Task) -> None:\n            cb_wrapper._value = future.result()\n\n        future.add_done_callback(done_cb)\n        yield cb_wrapper\n\n    @contextlib.contextmanager\n    def expect_websocket(\n        self,\n    ) -> Generator[ExpectResponse[\"WebSocketProtocol\"], None, None]:\n        future = self.wait_for_web_socket()\n\n        cb_wrapper: ExpectResponse[\"WebSocketProtocol\"] = ExpectResponse()\n\n        def done_cb(_: asyncio.Future) -> None:\n            cb_wrapper._value = future.result()\n\n        future.add_done_callback(done_cb)\n        yield cb_wrapper\n\n    def set_auth(self, path: str, username: str, password: str) -> None:\n        self.auth[path] = (username, password)\n\n    def set_csp(self, path: str, value: str) -> None:\n        self.csp[path] = value\n\n    def reset(self) -> None:\n        self.request_subscribers.clear()\n        self.auth.clear()\n        self.csp.clear()\n        self.gzip_routes.clear()\n        self.routes.clear()\n        self._ws_handlers.clear()\n\n    def set_route(\n        self, path: str, callback: Callable[[TestServerRequest], Any]\n    ) -> None:\n        self.routes[path] = callback\n\n    def enable_gzip(self, path: str) -> None:\n        self.gzip_routes.add(path)\n\n    def set_redirect(self, from_: str, to: str) -> None:\n        def handle_redirect(request: http.Request) -> None:\n            request.setResponseCode(HTTPStatus.FOUND)\n            request.setHeader(\"location\", to)\n            request.finish()\n\n        self.set_route(from_, handle_redirect)\n\n    def send_on_web_socket_connection(self, data: bytes) -> None:\n        self.once_web_socket_connection(lambda ws: ws.sendMessage(data))\n\n    def once_web_socket_connection(\n        self, handler: Callable[[\"WebSocketProtocol\"], None]\n    ) -> None:\n        self._ws_handlers.append(handler)\n\n\nclass HTTPServer(Server):\n    def __init__(self) -> None:\n        self._listeners: list[Any] = []\n        super().__init__()\n\n    def listen(self, factory: http.HTTPFactory) -> None:\n        self._listeners.append(\n            reactor.listenTCP(self.PORT, factory, interface=\"127.0.0.1\")\n        )\n        try:\n            self._listeners.append(\n                reactor.listenTCP(self.PORT, factory, interface=\"::1\")\n            )\n        except Exception:\n            pass\n\n    def stop(self) -> None:\n        for listener in self._listeners:\n            listener.stopListening()\n        self._listeners.clear()\n\n\nclass HTTPSServer(Server):\n    protocol = \"https\"\n\n    def __init__(self) -> None:\n        self._listeners: list[Any] = []\n        super().__init__()\n\n    def listen(self, factory: http.HTTPFactory) -> None:\n        cert = ssl.PrivateCertificate.fromCertificateAndKeyPair(\n            ssl.Certificate.loadPEM(\n                (_dirname / \"testserver\" / \"cert.pem\").read_bytes()\n            ),\n            ssl.KeyPair.load(\n                (_dirname / \"testserver\" / \"key.pem\").read_bytes(), crypto.FILETYPE_PEM\n            ),\n        )\n        contextFactory = cert.options()\n        self._listeners.append(\n            reactor.listenSSL(self.PORT, factory, contextFactory, interface=\"127.0.0.1\")\n        )\n        try:\n            self._listeners.append(\n                reactor.listenSSL(self.PORT, factory, contextFactory, interface=\"::1\")\n            )\n        except Exception:\n            pass\n\n    def stop(self) -> None:\n        for listener in self._listeners:\n            listener.stopListening()\n        self._listeners.clear()\n\n\nclass WebSocketProtocol(WebSocketServerProtocol):\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        super().__init__(*args, **kwargs)\n        self.events = EventEmitter()\n\n    def onClose(self, wasClean: bool, code: int, reason: str) -> None:\n        super().onClose(wasClean, code, reason)\n        self.events.emit(\n            \"close\",\n            code,\n            reason,\n        )\n\n    def onMessage(self, payload: Union[str, bytes], isBinary: bool) -> None:\n        self.events.emit(\"message\", payload, isBinary)\n\n    def onOpen(self) -> None:\n        for handler in getattr(self.factory, \"server_instance\")._ws_handlers.copy():\n            getattr(self.factory, \"server_instance\")._ws_handlers.remove(handler)\n            handler(self)\n\n\nclass TestServer:\n    def __init__(self) -> None:\n        self.server = HTTPServer()\n        self.https_server = HTTPSServer()\n\n    def start(self) -> None:\n        self.server.start()\n        self.https_server.start()\n        self.thread = threading.Thread(\n            target=lambda: reactor.run(installSignalHandlers=False)\n        )\n        self.thread.start()\n\n    def stop(self) -> None:\n        reactor.stop()\n        self.thread.join()\n\n    def reset(self) -> None:\n        self.server.reset()\n        self.https_server.reset()\n\n\ntest_server = TestServer()\n"
  },
  {
    "path": "tests/sync/__init__.py",
    "content": ""
  },
  {
    "path": "tests/sync/conftest.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport asyncio\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom typing import Any, Callable, Dict, Generator, List\n\nimport pytest\nfrom greenlet import greenlet\n\nfrom playwright._impl._driver import compute_driver_executable\nfrom playwright.sync_api import (\n    Browser,\n    BrowserContext,\n    BrowserType,\n    FrameLocator,\n    Locator,\n    Page,\n    Playwright,\n    Selectors,\n    sync_playwright,\n)\nfrom tests.server import HTTPServer\n\nfrom .utils import Utils\nfrom .utils import utils as utils_object\n\n\n@pytest.fixture\ndef utils() -> Generator[Utils, None, None]:\n    yield utils_object\n\n\n@pytest.fixture(scope=\"session\")\ndef playwright() -> Generator[Playwright, None, None]:\n    with sync_playwright() as p:\n        yield p\n\n\n@pytest.fixture(scope=\"session\")\ndef browser_type(\n    playwright: Playwright, browser_name: str\n) -> Generator[BrowserType, None, None]:\n    browser_type = None\n    if browser_name == \"chromium\":\n        browser_type = playwright.chromium\n    elif browser_name == \"firefox\":\n        browser_type = playwright.firefox\n    elif browser_name == \"webkit\":\n        browser_type = playwright.webkit\n    assert browser_type, f\"Unkown browser name '{browser_name}'\"\n    yield browser_type\n\n\n@pytest.fixture(scope=\"session\")\ndef browser(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> Generator[Browser, None, None]:\n    browser = browser_type.launch(**launch_arguments)\n    yield browser\n    browser.close()\n\n\n@pytest.fixture\ndef context(browser: Browser) -> Generator[BrowserContext, None, None]:\n    context = browser.new_context()\n    yield context\n    context.close()\n\n\n@pytest.fixture\ndef page(context: BrowserContext) -> Generator[Page, None, None]:\n    page = context.new_page()\n    yield page\n    page.close()\n\n\n@pytest.fixture(scope=\"session\")\ndef selectors(playwright: Playwright) -> Selectors:\n    return playwright.selectors\n\n\n@pytest.fixture(scope=\"session\")\ndef sync_gather(playwright: Playwright) -> Generator[Callable, None, None]:\n    def _sync_gather_impl(*actions: Callable) -> List[Any]:\n        g_self = greenlet.getcurrent()\n        results: Dict[Callable, Any] = {}\n        exceptions: List[Exception] = []\n\n        def action_wrapper(action: Callable) -> Callable:\n            def body() -> Any:\n                try:\n                    results[action] = action()\n                except Exception as e:\n                    results[action] = e\n                    exceptions.append(e)\n                g_self.switch()\n\n            return body\n\n        async def task() -> None:\n            for action in actions:\n                g = greenlet(action_wrapper(action))\n                g.switch()\n\n        asyncio.create_task(task())\n\n        while len(results) < len(actions):\n            playwright._dispatcher_fiber.switch()\n\n        if exceptions:\n            raise exceptions[0]\n\n        return list(map(lambda action: results[action], actions))\n\n    yield _sync_gather_impl\n\n\nclass TraceViewerPage:\n    def __init__(self, page: Page):\n        self.page = page\n\n    @property\n    def actions_tree(self) -> Locator:\n        return self.page.get_by_test_id(\"actions-tree\")\n\n    @property\n    def action_titles(self) -> Locator:\n        return self.page.locator(\".action-title\")\n\n    @property\n    def stack_frames(self) -> Locator:\n        return self.page.get_by_role(\"list\", name=\"Stack trace\").get_by_role(\"listitem\")\n\n    def select_action(self, title: str, ordinal: int = 0) -> None:\n        self.page.locator(\".action-title\", has_text=title).nth(ordinal).click()\n\n    def select_snapshot(self, name: str) -> None:\n        self.page.click(f'.snapshot-tab .tabbed-pane-tab-label:has-text(\"{name}\")')\n\n    def snapshot_frame(\n        self, action_name: str, ordinal: int = 0, has_subframe: bool = False\n    ) -> FrameLocator:\n        self.select_action(action_name, ordinal)\n        expected_frames = 4 if has_subframe else 3\n        while len(self.page.frames) < expected_frames:\n            self.page.wait_for_event(\"frameattached\")\n        return self.page.frame_locator(\"iframe.snapshot-visible[name=snapshot]\")\n\n    def show_source_tab(self) -> None:\n        self.page.click(\"text='Source'\")\n\n    def expand_action(self, title: str, ordinal: int = 0) -> None:\n        self.actions_tree.locator(\".tree-view-entry\", has_text=title).nth(\n            ordinal\n        ).locator(\".codicon-chevron-right\").click()\n\n\n@pytest.fixture\ndef show_trace_viewer(browser: Browser) -> Generator[Callable, None, None]:\n    \"\"\"Fixture that provides a function to show trace viewer for a trace file.\"\"\"\n\n    @contextmanager\n    def _show_trace_viewer(\n        trace_path: Path,\n    ) -> Generator[TraceViewerPage, None, None]:\n        trace_viewer_path = (\n            Path(compute_driver_executable()[0]) / \"../package/lib/vite/traceViewer\"\n        ).resolve()\n\n        server = HTTPServer()\n        server.start(trace_viewer_path)\n        server.set_route(\"/trace.zip\", lambda request: request.serve_file(trace_path))\n\n        page = browser.new_page()\n\n        try:\n            page.goto(f\"{server.PREFIX}/index.html?trace={server.PREFIX}/trace.zip\")\n            yield TraceViewerPage(page)\n        finally:\n            page.close()\n            server.stop()\n\n    yield _show_trace_viewer\n"
  },
  {
    "path": "tests/sync/test_add_init_script.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, Error, Page\n\n\ndef test_add_init_script_evaluate_before_anything_else_on_the_page(page: Page) -> None:\n    page.add_init_script(\"window.injected = 123\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_work_with_a_path(page: Page, assetdir: Path) -> None:\n    page.add_init_script(path=assetdir / \"injectedfile.js\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_work_with_content(page: Page) -> None:\n    page.add_init_script(\"window.injected = 123\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_throw_without_path_and_content(page: Page) -> None:\n    with pytest.raises(\n        Error, match=\"Either path or script parameter must be specified\"\n    ):\n        page.add_init_script({\"foo\": \"bar\"})  # type: ignore\n\n\ndef test_add_init_script_work_with_browser_context_scripts(\n    page: Page, context: BrowserContext\n) -> None:\n    context.add_init_script(\"window.temp = 123\")\n    page = context.new_page()\n    page.add_init_script(\"window.injected = window.temp\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_work_with_browser_context_scripts_with_a_path(\n    page: Page, context: BrowserContext, assetdir: Path\n) -> None:\n    context.add_init_script(path=assetdir / \"injectedfile.js\")\n    page = context.new_page()\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_work_with_browser_context_scripts_for_already_created_pages(\n    page: Page, context: BrowserContext\n) -> None:\n    context.add_init_script(\"window.temp = 123\")\n    page.add_init_script(\"window.injected = window.temp\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.result\") == 123\n\n\ndef test_add_init_script_support_multiple_scripts(page: Page) -> None:\n    page.add_init_script(\"window.script1 = 1\")\n    page.add_init_script(\"window.script2 = 2\")\n    page.goto(\"data:text/html,<script>window.result = window.injected</script>\")\n    assert page.evaluate(\"window.script1\") == 1\n    assert page.evaluate(\"window.script2\") == 2\n\n\ndef test_should_work_with_trailing_comments(page: Page) -> None:\n    page.add_init_script(\"// comment\")\n    page.add_init_script(\"window.secret = 42;\")\n    page.goto(\"data:text/html,<html></html>\")\n    assert page.evaluate(\"secret\") == 42\n"
  },
  {
    "path": "tests/sync/test_assertions.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nimport re\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Error, Page, expect\nfrom tests.server import Server\n\n\ndef test_assertions_page_to_have_title(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<title>new title</title>\")\n    expect(page).to_have_title(\"new title\")\n    expect(page).to_have_title(re.compile(\"new title\"))\n    with pytest.raises(AssertionError):\n        expect(page).to_have_title(\"not the current title\", timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).to_have_title(re.compile(\"not the current title\"), timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).not_to_have_title(re.compile(\"new title\"), timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).not_to_have_title(\"new title\", timeout=750)\n    expect(page).not_to_have_title(\"great title\", timeout=750)\n    page.evaluate(\n        \"\"\"\n        setTimeout(() => {\n            document.title = 'great title';\n        }, 2000);\n    \"\"\"\n    )\n    expect(page).to_have_title(\"great title\")\n    expect(page).to_have_title(re.compile(\"great title\"))\n\n\ndef test_assertions_page_to_have_url(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    expect(page).to_have_url(server.EMPTY_PAGE)\n    expect(page).to_have_url(re.compile(r\".*/empty\\.html\"))\n    with pytest.raises(AssertionError):\n        expect(page).to_have_url(\"nooooo\", timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).to_have_url(re.compile(\"not-the-url\"), timeout=750)\n    page.evaluate(\n        \"\"\"\n        setTimeout(() => {\n            window.location = window.location.origin + '/grid.html';\n        }, 2000);\n    \"\"\"\n    )\n    expect(page).to_have_url(server.PREFIX + \"/grid.html\")\n    expect(page).not_to_have_url(server.EMPTY_PAGE, timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).not_to_have_url(re.compile(r\".*/grid\\.html\"), timeout=750)\n    with pytest.raises(AssertionError):\n        expect(page).not_to_have_url(server.PREFIX + \"/grid.html\", timeout=750)\n    expect(page).to_have_url(re.compile(r\".*/grid\\.html\"))\n    expect(page).not_to_have_url(\"**/empty.html\", timeout=750)\n\n\ndef test_assertions_page_to_have_url_with_base_url(\n    browser: Browser, server: Server\n) -> None:\n    page = browser.new_page(base_url=server.PREFIX)\n    page.goto(\"/empty.html\")\n    expect(page).to_have_url(\"/empty.html\")\n    expect(page).to_have_url(re.compile(r\".*/empty\\.html\"))\n    page.close()\n\n\ndef test_assertions_locator_to_contain_text(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div id=foobar>kek</div>\")\n    expect(page.locator(\"div#foobar\")).to_contain_text(\"kek\")\n    expect(page.locator(\"div#foobar\")).not_to_contain_text(\"bar\", timeout=100)\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div#foobar\")).to_contain_text(\"bar\", timeout=100)\n\n    page.set_content(\"<div>Text \\n1</div><div>Text2</div><div>Text3</div>\")\n    expect(page.locator(\"div\")).to_contain_text([\"ext     1\", re.compile(\"ext3\")])\n\n\ndef test_assertions_locator_to_have_attribute(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div id=foobar>kek</div>\")\n    expect(page.locator(\"div#foobar\")).to_have_attribute(\"id\", \"foobar\")\n    expect(page.locator(\"div#foobar\")).to_have_attribute(\"id\", re.compile(\"foobar\"))\n    expect(page.locator(\"div#foobar\")).not_to_have_attribute(\"id\", \"kek\", timeout=100)\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div#foobar\")).to_have_attribute(\"id\", \"koko\", timeout=100)\n\n\ndef test_assertions_locator_to_have_attribute_ignore_case(\n    page: Page, server: Page\n) -> None:\n    page.set_content(\"<div id=NoDe>Text content</div>\")\n    locator = page.locator(\"#NoDe\")\n    expect(locator).to_have_attribute(\"id\", \"node\", ignore_case=True)\n    expect(locator).not_to_have_attribute(\"id\", \"node\")\n\n\ndef test_assertions_locator_to_have_class(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div class=foobar>kek</div>\")\n    expect(page.locator(\"div.foobar\")).to_have_class(\"foobar\")\n    expect(page.locator(\"div.foobar\")).to_have_class([\"foobar\"])\n    expect(page.locator(\"div.foobar\")).to_have_class(re.compile(\"foobar\"))\n    expect(page.locator(\"div.foobar\")).to_have_class([re.compile(\"foobar\")])\n    expect(page.locator(\"div.foobar\")).not_to_have_class(\"kekstar\", timeout=100)\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div.foobar\")).to_have_class(\"oh-no\", timeout=100)\n\n\ndef test_assertions_locator_to_contain_class(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div class='foo bar baz'></div>\")\n    locator = page.locator(\"div\")\n    expect(locator).to_contain_class(\"\")\n    expect(locator).to_contain_class(\"bar\")\n    expect(locator).to_contain_class(\"baz bar\")\n    expect(locator).to_contain_class(\"  bar   foo \")\n    expect(locator).not_to_contain_class(\n        \"  baz   not-matching \"\n    )  # Strip whitespace and match individual classes\n    with pytest.raises(AssertionError) as excinfo:\n        expect(locator).to_contain_class(\"does-not-exist\", timeout=100)\n\n    assert excinfo.match(\"Locator expected to contain class 'does-not-exist'\")\n    assert excinfo.match(\"Actual value: foo bar baz\")\n    assert excinfo.match('Expect \"to_contain_class\" with timeout 100ms')\n\n    page.set_content(\n        '<div class=\"foo\"></div><div class=\"hello bar\"></div><div class=\"baz\"></div>'\n    )\n    expect(locator).to_contain_class([\"foo\", \"hello\", \"baz\"])\n    expect(locator).not_to_contain_class([\"not-there\", \"hello\", \"baz\"])\n    expect(locator).not_to_contain_class([\"foo\", \"hello\"])\n\n\ndef test_assertions_locator_to_have_count(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div class=foobar>kek</div><div class=foobar>kek</div>\")\n    expect(page.locator(\"div.foobar\")).to_have_count(2)\n    expect(page.locator(\"div.foobar\")).not_to_have_count(42, timeout=100)\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div.foobar\")).to_have_count(42, timeout=100)\n\n\ndef test_assertions_locator_to_have_css(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div class=foobar style='color: rgb(234, 74, 90);'>kek</div>\")\n    expect(page.locator(\"div.foobar\")).to_have_css(\"color\", \"rgb(234, 74, 90)\")\n    expect(page.locator(\"div.foobar\")).not_to_have_css(\n        \"color\", \"rgb(42, 42, 42)\", timeout=100\n    )\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div.foobar\")).to_have_css(\n            \"color\", \"rgb(42, 42, 42)\", timeout=100\n        )\n\n\ndef test_assertions_locator_to_have_id(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div class=foobar id=kek>kek</div>\")\n    expect(page.locator(\"div.foobar\")).to_have_id(\"kek\")\n    expect(page.locator(\"div.foobar\")).not_to_have_id(\"top\", timeout=100)\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"div.foobar\")).to_have_id(\"top\", timeout=100)\n\n\ndef test_assertions_locator_to_have_js_property(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\n        \"div\", \"e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }\"\n    )\n    expect(page.locator(\"div\")).to_have_js_property(\n        \"foo\",\n        {\n            \"a\": 1,\n            \"b\": \"string\",\n            \"c\": datetime.datetime.fromtimestamp(1627503992000 / 1000),\n        },\n    )\n\n\ndef test_to_have_js_property_pass_string(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = 'string'\")\n    locator = page.locator(\"div\")\n    expect(locator).to_have_js_property(\"foo\", \"string\")\n\n\ndef test_to_have_js_property_fail_string(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = 'string'\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        expect(locator).to_have_js_property(\"foo\", \"error\", timeout=500)\n\n\ndef test_to_have_js_property_pass_number(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = 2021\")\n    locator = page.locator(\"div\")\n    expect(locator).to_have_js_property(\"foo\", 2021)\n\n\ndef test_to_have_js_property_fail_number(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = 2021\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        expect(locator).to_have_js_property(\"foo\", 1, timeout=500)\n\n\ndef test_to_have_js_property_pass_boolean(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = true\")\n    locator = page.locator(\"div\")\n    expect(locator).to_have_js_property(\"foo\", True)\n\n\ndef test_to_have_js_property_fail_boolean(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        expect(locator).to_have_js_property(\"foo\", True, timeout=500)\n\n\ndef test_to_have_js_property_pass_boolean_2(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    expect(locator).to_have_js_property(\"foo\", False)\n\n\ndef test_to_have_js_property_fail_boolean_2(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = false\")\n    locator = page.locator(\"div\")\n    with pytest.raises(AssertionError):\n        expect(locator).to_have_js_property(\"foo\", True, timeout=500)\n\n\ndef test_to_have_js_property_pass_null(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\"div\", \"e => e.foo = null\")\n    locator = page.locator(\"div\")\n    expect(locator).to_have_js_property(\"foo\", None)\n\n\ndef test_assertions_locator_to_have_text(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div id=foobar>kek</div>\")\n    expect(page.locator(\"div#foobar\")).to_have_text(\"kek\")\n    expect(page.locator(\"div#foobar\")).not_to_have_text(\"top\", timeout=100)\n\n    page.set_content(\"<div>Text    \\n1</div><div>Text   2a</div>\")\n    # Should only normalize whitespace in the first item.\n    expect(page.locator(\"div\")).to_have_text([\"Text  1\", re.compile(r\"Text   \\d+a\")])\n\n\n@pytest.mark.parametrize(\n    \"method\",\n    [\"to_have_text\", \"to_contain_text\"],\n)\ndef test_ignore_case(page: Page, server: Server, method: str) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div id=target>apple BANANA</div><div>orange</div>\")\n    getattr(expect(page.locator(\"div#target\")), method)(\"apple BANANA\")\n    getattr(expect(page.locator(\"div#target\")), method)(\n        \"apple banana\", ignore_case=True\n    )\n    # defaults false\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div#target\")), method)(\n            \"apple banana\",\n            timeout=300,\n        )\n    expected_error_msg = method.replace(\"_\", \" \")\n    assert expected_error_msg in str(excinfo.value)\n\n    # Array Variants\n    getattr(expect(page.locator(\"div\")), method)([\"apple BANANA\", \"orange\"])\n    getattr(expect(page.locator(\"div\")), method)(\n        [\"apple banana\", \"ORANGE\"], ignore_case=True\n    )\n    # defaults false\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div\")), method)(\n            [\"apple banana\", \"ORANGE\"],\n            timeout=300,\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # not variant\n    getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\"apple banana\")\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n            \"apple banana\",\n            ignore_case=True,\n            timeout=300,\n        )\n    assert f\"not {expected_error_msg}\" in str(excinfo)\n\n\n@pytest.mark.parametrize(\n    \"method\",\n    [\"to_have_text\", \"to_contain_text\"],\n)\ndef test_ignore_case_regex(page: Page, server: Server, method: str) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div id=target>apple BANANA</div><div>orange</div>\")\n    getattr(expect(page.locator(\"div#target\")), method)(re.compile(\"apple BANANA\"))\n    getattr(expect(page.locator(\"div#target\")), method)(\n        re.compile(\"apple banana\"), ignore_case=True\n    )\n    # defaults to regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div#target\")), method)(\n            re.compile(\"apple banana\"), timeout=300\n        )\n    expected_error_msg = method.replace(\"_\", \" \")\n    assert expected_error_msg in str(excinfo.value)\n    # overrides regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div#target\")), method)(\n            re.compile(\"apple banana\", re.IGNORECASE),\n            ignore_case=False,\n            timeout=300,\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # Array Variants\n    getattr(expect(page.locator(\"div\")), method)(\n        [re.compile(\"apple BANANA\"), re.compile(\"orange\")]\n    )\n    getattr(expect(page.locator(\"div\")), method)(\n        [re.compile(\"apple banana\"), re.compile(\"ORANGE\")], ignore_case=True\n    )\n    # defaults regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div\")), method)(\n            [re.compile(\"apple banana\"), re.compile(\"ORANGE\")],\n            timeout=300,\n        )\n    assert expected_error_msg in str(excinfo.value)\n    # overrides regex flag\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div\")), method)(\n            [\n                re.compile(\"apple banana\", re.IGNORECASE),\n                re.compile(\"ORANGE\", re.IGNORECASE),\n            ],\n            ignore_case=False,\n            timeout=300,\n        )\n    assert expected_error_msg in str(excinfo.value)\n\n    # not variant\n    getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n        re.compile(\"apple banana\")\n    )\n    with pytest.raises(AssertionError) as excinfo:\n        getattr(expect(page.locator(\"div#target\")), f\"not_{method}\")(\n            re.compile(\"apple banana\"),\n            ignore_case=True,\n            timeout=300,\n        )\n    assert f\"not {expected_error_msg}\" in str(excinfo)\n\n\ndef test_assertions_locator_to_have_value(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<input type=text id=foo>\")\n    my_input = page.locator(\"#foo\")\n    expect(my_input).to_have_value(\"\")\n    expect(my_input).not_to_have_value(\"bar\", timeout=100)\n    my_input.fill(\"kektus\")\n    expect(my_input).to_have_value(\"kektus\")\n\n\ndef test_to_have_values_works_with_text(page: Page, server: Server) -> None:\n    page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    locator.select_option([\"R\", \"G\"])\n    expect(locator).to_have_values([\"R\", \"G\"])\n\n\ndef test_to_have_values_follows_labels(page: Page, server: Server) -> None:\n    page.set_content(\n        \"\"\"\n        <label for=\"colors\">Pick a Color</label>\n        <select id=\"colors\" multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"text=Pick a Color\")\n    locator.select_option([\"R\", \"G\"])\n    expect(locator).to_have_values([\"R\", \"G\"])\n\n\ndef test_to_have_values_exact_match_with_text(page: Page, server: Server) -> None:\n    page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"RR\">Red</option>\n            <option value=\"GG\">Green</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    locator.select_option([\"RR\", \"GG\"])\n    with pytest.raises(AssertionError) as excinfo:\n        expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Locator expected to have Values '['R', 'G']'\" in str(excinfo.value)\n    assert \"Actual value: ['RR', 'GG']\" in str(excinfo.value)\n\n\ndef test_to_have_values_works_with_regex(page: Page, server: Server) -> None:\n    page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    locator.select_option([\"R\", \"G\"])\n    expect(locator).to_have_values([re.compile(\"R\"), re.compile(\"G\")])\n\n\ndef test_to_have_values_fails_when_items_not_selected(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n        <select multiple>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    locator.select_option([\"B\"])\n    with pytest.raises(AssertionError) as excinfo:\n        expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Locator expected to have Values '['R', 'G']'\" in str(excinfo.value)\n    assert \"Actual value: ['B']\" in str(excinfo.value)\n\n\ndef test_to_have_values_fails_when_multiple_not_specified(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"R\">Red</option>\n            <option value=\"G\">Green</option>\n            <option value=\"B\">Blue</option>\n        </select>\n    \"\"\"\n    )\n    locator = page.locator(\"select\")\n    locator.select_option([\"B\"])\n    with pytest.raises(AssertionError) as excinfo:\n        expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Error: Not a select element with a multiple attribute\" in str(excinfo.value)\n\n\ndef test_to_have_values_fails_when_not_a_select_element(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n        <input type=\"text\">\n    \"\"\"\n    )\n    locator = page.locator(\"input\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(locator).to_have_values([\"R\", \"G\"], timeout=500)\n    assert \"Error: Not a select element with a multiple attribute\" in str(excinfo.value)\n\n\ndef test_assertions_locator_to_be_checked(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    expect(my_checkbox).not_to_be_checked()\n    with pytest.raises(AssertionError, match=\"Locator expected to be checked\"):\n        expect(my_checkbox).to_be_checked(timeout=100)\n    expect(my_checkbox).to_be_checked(timeout=100, checked=False)\n    with pytest.raises(AssertionError):\n        expect(my_checkbox).to_be_checked(timeout=100, checked=True)\n    my_checkbox.check()\n    expect(my_checkbox).to_be_checked(timeout=100, checked=True)\n    with pytest.raises(AssertionError, match=\"Locator expected to be unchecked\"):\n        expect(my_checkbox).to_be_checked(timeout=100, checked=False)\n    expect(my_checkbox).to_be_checked()\n\n\ndef test_assertions_boolean_checked_with_intermediate_true(page: Page) -> None:\n    page.set_content(\"<input type=checkbox></input>\")\n    page.locator(\"input\").evaluate(\"e => e.indeterminate = true\")\n    expect(page.locator(\"input\")).to_be_checked(indeterminate=True)\n\n\ndef test_assertions_boolean_checked_with_intermediate_true_and_checked(\n    page: Page,\n) -> None:\n    page.set_content(\"<input type=checkbox></input>\")\n    page.locator(\"input\").evaluate(\"e => e.indeterminate = true\")\n    with pytest.raises(\n        AssertionError, match=\"Can't assert indeterminate and checked at the same time\"\n    ):\n        expect(page.locator(\"input\")).to_be_checked(checked=False, indeterminate=True)\n\n\ndef test_assertions_boolean_fail_with_indeterminate_true(page: Page) -> None:\n    page.set_content(\"<input type=checkbox></input>\")\n    with pytest.raises(\n        AssertionError, match='Expect \"to_be_checked\" with timeout 1000ms'\n    ):\n        expect(page.locator(\"input\")).to_be_checked(indeterminate=True, timeout=1000)\n\n\ndef test_assertions_locator_to_be_disabled_enabled(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    expect(my_checkbox).not_to_be_disabled()\n    expect(my_checkbox).to_be_enabled()\n    with pytest.raises(AssertionError):\n        expect(my_checkbox).to_be_disabled(timeout=100)\n    my_checkbox.evaluate(\"e => e.disabled = true\")\n    expect(my_checkbox).to_be_disabled()\n    with pytest.raises(AssertionError, match=\"Locator expected to be enabled\"):\n        expect(my_checkbox).to_be_enabled(timeout=100)\n\n\ndef test_assertions_locator_to_be_enabled_with_true(page: Page) -> None:\n    page.set_content(\"<button>Text</button>\")\n    expect(page.locator(\"button\")).to_be_enabled(enabled=True)\n\n\ndef test_assertions_locator_to_be_enabled_with_false_throws_good_exception(\n    page: Page,\n) -> None:\n    page.set_content(\"<button>Text</button>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be disabled\"):\n        expect(page.locator(\"button\")).to_be_enabled(enabled=False)\n\n\ndef test_assertions_locator_to_be_enabled_with_false(page: Page) -> None:\n    page.set_content(\"<button disabled>Text</button>\")\n    expect(page.locator(\"button\")).to_be_enabled(enabled=False)\n\n\ndef test_assertions_locator_to_be_enabled_with_not_and_false(page: Page) -> None:\n    page.set_content(\"<button>Text</button>\")\n    expect(page.locator(\"button\")).not_to_be_enabled(enabled=False)\n\n\ndef test_assertions_locator_to_be_enabled_eventually(page: Page) -> None:\n    page.set_content(\"<button disabled>Text</button>\")\n    page.eval_on_selector(\n        \"button\",\n        \"\"\"\n        button => setTimeout(() => {\n            button.removeAttribute('disabled');\n        }, 700);\n    \"\"\",\n    )\n    expect(page.locator(\"button\")).to_be_enabled()\n\n\ndef test_assertions_locator_to_be_enabled_eventually_with_not(page: Page) -> None:\n    page.set_content(\"<button>Text</button>\")\n    page.eval_on_selector(\n        \"button\",\n        \"\"\"\n        button => setTimeout(() => {\n            button.setAttribute('disabled', '');\n        }, 700);\n    \"\"\",\n    )\n    expect(page.locator(\"button\")).not_to_be_enabled()\n\n\ndef test_assertions_locator_to_be_editable(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<input></input>\")\n    expect(page.locator(\"input\")).to_be_editable()\n\n\ndef test_assertions_locator_to_be_editable_throws(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<button disabled>Text</button>\")\n    with pytest.raises(\n        AssertionError,\n        match=r\"Element is not an <input>, <textarea>, <select> or \\[contenteditable\\] and does not have a role allowing \\[aria-readonly\\]\",\n    ):\n        expect(page.locator(\"button\")).not_to_be_editable()\n\n\ndef test_assertions_locator_to_be_editable_with_true(page: Page) -> None:\n    page.set_content(\"<input></input>\")\n    expect(page.locator(\"input\")).to_be_editable(editable=True)\n\n\ndef test_assertions_locator_to_be_editable_with_false(page: Page) -> None:\n    page.set_content(\"<input readonly></input>\")\n    expect(page.locator(\"input\")).to_be_editable(editable=False)\n\n\ndef test_assertions_locator_to_be_editable_with_false_and_throw_good_exception(\n    page: Page,\n) -> None:\n    page.set_content(\"<input></input>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be readonly\"):\n        expect(page.locator(\"input\")).to_be_editable(editable=False)\n\n\ndef test_assertions_locator_to_be_editable_with_not_and_false(page: Page) -> None:\n    page.set_content(\"<input></input>\")\n    expect(page.locator(\"input\")).not_to_be_editable(editable=False)\n\n\ndef test_assertions_locator_to_be_empty(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\n        \"<input value=text name=input1></input><input name=input2></input>\"\n    )\n    expect(page.locator(\"input[name=input1]\")).not_to_be_empty()\n    expect(page.locator(\"input[name=input2]\")).to_be_empty()\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"input[name=input1]\")).to_be_empty(timeout=100)\n\n\ndef test_assertions_locator_to_be_focused(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<input type=checkbox>\")\n    my_checkbox = page.locator(\"input\")\n    with pytest.raises(AssertionError):\n        expect(my_checkbox).to_be_focused(timeout=100)\n    my_checkbox.focus()\n    expect(my_checkbox).to_be_focused()\n\n\ndef test_assertions_locator_to_be_hidden_visible(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div style='width: 50px; height: 50px;'>Something</div>\")\n    my_checkbox = page.locator(\"div\")\n    expect(my_checkbox).to_be_visible()\n    with pytest.raises(AssertionError):\n        expect(my_checkbox).to_be_hidden(timeout=100)\n    my_checkbox.evaluate(\"e => e.style.display = 'none'\")\n    expect(my_checkbox).to_be_hidden()\n    with pytest.raises(AssertionError, match=\"Locator expected to be visible\"):\n        expect(my_checkbox).to_be_visible(timeout=100)\n\n\ndef test_assertions_locator_to_be_visible_with_true(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    expect(page.locator(\"button\")).to_be_visible(visible=True)\n\n\ndef test_assertions_locator_to_be_visible_with_false(page: Page) -> None:\n    page.set_content(\"<button hidden>hello</button>\")\n    expect(page.locator(\"button\")).to_be_visible(visible=False)\n\n\ndef test_assertions_locator_to_be_visible_with_false_throws_good_exception(\n    page: Page,\n) -> None:\n    page.set_content(\"<button>hello</button>\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be hidden\"):\n        expect(page.locator(\"button\")).to_be_visible(visible=False)\n\n\ndef test_assertions_locator_to_be_visible_with_not_and_false(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    expect(page.locator(\"button\")).not_to_be_visible(visible=False)\n\n\ndef test_assertions_locator_to_be_visible_eventually(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    page.eval_on_selector(\n        \"div\",\n        \"\"\"\n        div => setTimeout(() => {\n            div.innerHTML = '<span>Hello</span>';\n        }, 700);\n    \"\"\",\n    )\n    expect(page.locator(\"span\")).to_be_visible()\n\n\ndef test_assertions_locator_to_be_visible_eventually_with_not(page: Page) -> None:\n    page.set_content(\"<div><span>Hello</span></div>\")\n    page.eval_on_selector(\n        \"span\",\n        \"\"\"\n        span => setTimeout(() => {\n            span.textContent = '';\n        }, 700);\n    \"\"\",\n    )\n    expect(page.locator(\"span\")).not_to_be_visible()\n\n\ndef test_assertions_should_serialize_regexp_correctly(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<div>iGnOrEcAsE</div>\")\n    expect(page.locator(\"div\")).to_have_text(re.compile(r\"ignorecase\", re.IGNORECASE))\n    page.set_content(\n        \"\"\"<div>start\nsome\nlines\nbetween\nend</div>\"\"\"\n    )\n    expect(page.locator(\"div\")).to_have_text(re.compile(r\"start.*end\", re.DOTALL))\n    page.set_content(\n        \"\"\"<div>line1\nline2\nline3</div>\"\"\"\n    )\n    expect(page.locator(\"div\")).to_have_text(re.compile(r\"^line2$\", re.MULTILINE))\n\n\ndef test_assertions_response_is_ok_pass(page: Page, server: Server) -> None:\n    response = page.request.get(server.EMPTY_PAGE)\n    expect(response).to_be_ok()\n\n\ndef test_assertions_response_is_ok_pass_with_not(page: Page, server: Server) -> None:\n    response = page.request.get(server.PREFIX + \"/unknown\")\n    expect(response).not_to_be_ok()\n\n\ndef test_assertions_response_is_ok_fail(page: Page, server: Server) -> None:\n    response = page.request.get(server.PREFIX + \"/unknown\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/unknown\") in error_message\n    assert \"← 404 Not Found\" in error_message\n\n\ndef test_should_print_response_with_text_content_type_if_to_be_ok_fails(\n    page: Page, server: Server\n) -> None:\n    server.set_route(\n        \"/text-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.setHeader(\"content-type\", \"text/plain\"),\n            r.write(b\"Text error\"),\n            r.finish(),\n        ),\n    )\n    server.set_route(\n        \"/no-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.write(b\"No content type error\"),\n            r.finish(),\n        ),\n    )\n    server.set_route(\n        \"/binary-content-type\",\n        lambda r: (\n            r.setResponseCode(404),\n            r.setHeader(\"content-type\", \"image/bmp\"),\n            r.write(b\"Image content type error\"),\n            r.finish(),\n        ),\n    )\n\n    response = page.request.get(server.PREFIX + \"/text-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/text-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" in error_message\n    assert \"Text error\" in error_message\n\n    response = page.request.get(server.PREFIX + \"/no-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/no-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" not in error_message\n    assert \"No content type error\" not in error_message\n\n    response = page.request.get(server.PREFIX + \"/binary-content-type\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(response).to_be_ok()\n    error_message = str(excinfo.value)\n    assert (\"→ GET \" + server.PREFIX + \"/binary-content-type\") in error_message\n    assert \"← 404 Not Found\" in error_message\n    assert \"Response Text:\" not in error_message\n    assert \"Image content type error\" not in error_message\n\n\ndef test_should_print_users_message_for_page_based_assertion(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<title>new title</title>\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(page, \"Title is not new\").to_have_title(\"old title\", timeout=100)\n    assert \"Title is not new\" in str(excinfo.value)\n    with pytest.raises(AssertionError) as excinfo:\n        expect(page).to_have_title(\"old title\", timeout=100)\n    assert \"Page title expected to be\" in str(excinfo.value)\n\n\ndef test_should_print_expected_value_with_custom_message(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<title>new title</title>\")\n    with pytest.raises(AssertionError) as excinfo:\n        expect(page, \"custom-message\").to_have_title(\"old title\", timeout=100)\n    assert \"custom-message\" in str(excinfo.value)\n    assert \"Expected value: 'old title'\" in str(excinfo.value)\n    with pytest.raises(AssertionError) as excinfo:\n        expect(page.get_by_text(\"hello\"), \"custom-message\").to_be_visible(timeout=100)\n    assert \"custom-message\" in str(excinfo.value)\n    assert \"Expected value\" not in str(excinfo.value)\n\n\ndef test_should_be_attached_default(page: Page) -> None:\n    page.set_content(\"<input></input>\")\n    locator = page.locator(\"input\")\n    expect(locator).to_be_attached()\n\n\ndef test_should_be_attached_with_hidden_element(page: Page) -> None:\n    page.set_content('<button style=\"display:none\">hello</button>')\n    locator = page.locator(\"button\")\n    expect(locator).to_be_attached()\n\n\ndef test_should_be_attached_with_not(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"input\")\n    expect(locator).not_to_be_attached()\n\n\ndef test_should_be_attached_with_attached_true(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    expect(locator).to_be_attached(attached=True)\n\n\ndef test_should_be_attached_with_attached_false(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"input\")\n    expect(locator).to_be_attached(attached=False)\n\n\ndef test_should_be_attached_with_attached_false_and_throw_good_error(\n    page: Page,\n) -> None:\n    page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    with pytest.raises(AssertionError, match=\"Locator expected to be detached\"):\n        expect(locator).to_be_attached(attached=False, timeout=1)\n\n\ndef test_should_be_attached_with_not_and_attached_false(page: Page) -> None:\n    page.set_content(\"<button>hello</button>\")\n    locator = page.locator(\"button\")\n    expect(locator).not_to_be_attached(attached=False)\n\n\ndef test_should_be_attached_eventually(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    locator = page.locator(\"span\")\n    page.locator(\"div\").evaluate(\n        \"(e) => setTimeout(() => e.innerHTML = '<span>hello</span>', 1000)\"\n    )\n    expect(locator).to_be_attached()\n\n\ndef test_should_be_attached_eventually_with_not(page: Page) -> None:\n    page.set_content(\"<div><span>Hello</span></div>\")\n    locator = page.locator(\"span\")\n    page.locator(\"div\").evaluate(\"(e) => setTimeout(() => e.textContent = '', 1000)\")\n    expect(locator).not_to_be_attached()\n\n\ndef test_should_be_attached_fail(page: Page) -> None:\n    page.set_content(\"<button>Hello</button>\")\n    locator = page.locator(\"input\")\n    with pytest.raises(\n        AssertionError, match=\"Locator expected to be attached\"\n    ) as exc_info:\n        expect(locator).to_be_attached(timeout=1000)\n    assert \"locator resolved to\" not in exc_info.value.args[0]\n\n\ndef test_should_be_attached_fail_with_not(page: Page) -> None:\n    page.set_content(\"<input></input>\")\n    locator = page.locator(\"input\")\n    with pytest.raises(AssertionError) as exc_info:\n        expect(locator).not_to_be_attached(timeout=1000)\n    assert \"locator resolved to <input/>\" in exc_info.value.args[0]\n\n\ndef test_should_be_attached_with_impossible_timeout(page: Page) -> None:\n    page.set_content(\"<div id=node>Text content</div>\")\n    expect(page.locator(\"#node\")).to_be_attached(timeout=1)\n\n\ndef test_should_be_attached_with_impossible_timeout_not(page: Page) -> None:\n    page.set_content(\"<div id=node>Text content</div>\")\n    expect(page.locator(\"no-such-thing\")).not_to_be_attached(timeout=1)\n\n\ndef test_should_be_able_to_set_custom_timeout(page: Page) -> None:\n    with pytest.raises(AssertionError) as exc_info:\n        expect(page.locator(\"#a1\")).to_be_visible(timeout=111)\n    assert 'Expect \"to_be_visible\" with timeout 111ms' in str(exc_info.value)\n\n\ndef test_should_be_able_to_set_custom_global_timeout(page: Page) -> None:\n    try:\n        expect.set_options(timeout=111)\n        with pytest.raises(AssertionError) as exc_info:\n            expect(page.locator(\"#a1\")).to_be_visible()\n        assert 'Expect \"to_be_visible\" with timeout 111ms' in str(exc_info.value)\n    finally:\n        expect.set_options(timeout=5_000)\n\n\ndef test_to_have_accessible_name(page: Page) -> None:\n    page.set_content('<div role=\"button\" aria-label=\"Hello\"></div>')\n    locator = page.locator(\"div\")\n    expect(locator).to_have_accessible_name(\"Hello\")\n    expect(locator).not_to_have_accessible_name(\"hello\")\n    expect(locator).to_have_accessible_name(\"hello\", ignore_case=True)\n    expect(locator).to_have_accessible_name(re.compile(r\"ell\\w\"))\n    expect(locator).not_to_have_accessible_name(re.compile(r\"hello\"))\n    expect(locator).to_have_accessible_name(re.compile(r\"hello\"), ignore_case=True)\n\n    page.set_content(\"<button>foo&nbsp;bar\\nbaz</button>\")\n    expect(page.locator(\"button\")).to_have_accessible_name(\"foo bar baz\")\n\n\ndef test_to_have_accessible_error_message(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n      <form>\n        <input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error-message\" />\n        <div id=\"error-message\">Hello</div>\n        <div id=\"irrelevant-error\">This should not be considered.</div>\n      </form>\n    \"\"\"\n    )\n\n    locator = page.locator('input[role=\"textbox\"]')\n    expect(locator).to_have_accessible_error_message(\"Hello\")\n    expect(locator).not_to_have_accessible_error_message(\"hello\")\n    expect(locator).to_have_accessible_error_message(\"hello\", ignore_case=True)\n    expect(locator).to_have_accessible_error_message(re.compile(r\"ell\\w\"))\n    expect(locator).not_to_have_accessible_error_message(re.compile(r\"hello\"))\n    expect(locator).to_have_accessible_error_message(\n        re.compile(r\"hello\"), ignore_case=True\n    )\n    expect(locator).not_to_have_accessible_error_message(\n        \"This should not be considered.\"\n    )\n\n\ndef test_to_have_accessible_error_message_should_handle_multiple_aria_error_message_references(\n    page: Page,\n) -> None:\n    page.set_content(\n        \"\"\"\n      <form>\n        <input role=\"textbox\" aria-invalid=\"true\" aria-errormessage=\"error1 error2\" />\n        <div id=\"error1\">First error message.</div>\n        <div id=\"error2\">Second error message.</div>\n        <div id=\"irrelevant-error\">This should not be considered.</div>\n      </form>\n    \"\"\"\n    )\n\n    locator = page.locator('input[role=\"textbox\"]')\n\n    expect(locator).to_have_accessible_error_message(\n        \"First error message. Second error message.\"\n    )\n    expect(locator).to_have_accessible_error_message(\n        re.compile(r\"first error message.\", re.IGNORECASE)\n    )\n    expect(locator).to_have_accessible_error_message(\n        re.compile(r\"second error message.\", re.IGNORECASE)\n    )\n    expect(locator).not_to_have_accessible_error_message(\n        re.compile(r\"This should not be considered.\", re.IGNORECASE)\n    )\n\n\ndef test_to_have_accessible_description(page: Page) -> None:\n    page.set_content('<div role=\"button\" aria-description=\"Hello\"></div>')\n    locator = page.locator(\"div\")\n    expect(locator).to_have_accessible_description(\"Hello\")\n    expect(locator).not_to_have_accessible_description(\"hello\")\n    expect(locator).to_have_accessible_description(\"hello\", ignore_case=True)\n    expect(locator).to_have_accessible_description(re.compile(r\"ell\\w\"))\n    expect(locator).not_to_have_accessible_description(re.compile(r\"hello\"))\n    expect(locator).to_have_accessible_description(\n        re.compile(r\"hello\"), ignore_case=True\n    )\n\n    page.set_content(\n        \"\"\"\n        <div role=\"button\" aria-describedby=\"desc\"></div>\n        <span id=\"desc\">foo&nbsp;bar\\nbaz</span>\n    \"\"\"\n    )\n    expect(page.locator(\"div\")).to_have_accessible_description(\"foo bar baz\")\n\n\ndef test_to_have_role(page: Page) -> None:\n    page.set_content('<div role=\"button\">Button!</div>')\n    expect(page.locator(\"div\")).to_have_role(\"button\")\n    expect(page.locator(\"div\")).not_to_have_role(\"checkbox\")\n    with pytest.raises(Error) as excinfo:\n        expect(page.locator(\"div\")).to_have_role(re.compile(r\"button|checkbox\"))  # type: ignore\n    assert '\"role\" argument in to_have_role must be a string' in str(excinfo.value)\n"
  },
  {
    "path": "tests/sync/test_browser.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Browser, BrowserType\n\n\ndef test_should_return_browser_type(\n    browser: Browser, browser_type: BrowserType\n) -> None:\n    assert browser.browser_type is browser_type\n"
  },
  {
    "path": "tests/sync/test_browsercontext_client_certificates.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nimport threading\nfrom pathlib import Path\nfrom typing import Dict, Generator, Optional, cast\n\nimport OpenSSL.crypto\nimport OpenSSL.SSL\nimport pytest\nfrom twisted.internet import reactor as _twisted_reactor\nfrom twisted.internet import ssl\nfrom twisted.internet.selectreactor import SelectReactor\nfrom twisted.web import resource, server\nfrom twisted.web.http import Request\n\nfrom playwright.sync_api import Browser, BrowserType, Playwright, expect\n\nreactor = cast(SelectReactor, _twisted_reactor)\n\n\n@pytest.fixture(scope=\"function\", autouse=True)\ndef _skip_webkit_darwin(browser_name: str) -> None:\n    if browser_name == \"webkit\" and sys.platform == \"darwin\":\n        pytest.skip(\"WebKit does not proxy localhost on macOS\")\n\n\nclass HttpsResource(resource.Resource):\n    serverCertificate: ssl.PrivateCertificate\n    isLeaf = True\n\n    def _verify_cert_chain(self, cert: Optional[OpenSSL.crypto.X509]) -> bool:\n        if not cert:\n            return False\n        store = OpenSSL.crypto.X509Store()\n        store.add_cert(self.serverCertificate.original)\n        store_ctx = OpenSSL.crypto.X509StoreContext(store, cert)\n        try:\n            store_ctx.verify_certificate()\n            return True\n        except OpenSSL.crypto.X509StoreContextError:\n            return False\n\n    def render_GET(self, request: Request) -> bytes:\n        tls_socket: OpenSSL.SSL.Connection = request.transport.getHandle()  # type: ignore\n        cert = tls_socket.get_peer_certificate()\n        parts = []\n\n        if self._verify_cert_chain(cert):\n            request.setResponseCode(200)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": f\"Hello {cert.get_subject().CN}, your certificate was issued by {cert.get_issuer().CN}!\",  # type: ignore\n                }\n            )\n        elif cert and cert.get_subject():\n            request.setResponseCode(403)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": f\"Sorry {cert.get_subject().CN}, certificates from {cert.get_issuer().CN} are not welcome here.\",\n                }\n            )\n        else:\n            request.setResponseCode(401)\n            parts.append(\n                {\n                    \"key\": \"message\",\n                    \"value\": \"Sorry, but you need to provide a client certificate to continue.\",\n                }\n            )\n        return b\"\".join(\n            [\n                f'<div data-testid=\"{part[\"key\"]}\">{part[\"value\"]}</div>'.encode()\n                for part in parts\n            ]\n        )\n\n\n@pytest.fixture(scope=\"session\", autouse=True)\ndef _client_certificate_server(assetdir: Path) -> Generator[None, None, None]:\n    certAuthCert = ssl.Certificate.loadPEM(\n        (assetdir / \"client-certificates/server/server_cert.pem\").read_text()\n    )\n    serverCert = ssl.PrivateCertificate.loadPEM(\n        (assetdir / \"client-certificates/server/server_key.pem\").read_text()\n        + (assetdir / \"client-certificates/server/server_cert.pem\").read_text()\n    )\n\n    contextFactory = serverCert.options(certAuthCert)\n    contextFactory.requireCertificate = False\n    resource = HttpsResource()\n    resource.serverCertificate = serverCert\n    site = server.Site(resource)\n\n    def _run() -> None:\n        reactor.listenSSL(8000, site, contextFactory)\n\n    thread = threading.Thread(target=_run)\n    thread.start()\n    yield\n    thread.join()\n\n\ndef test_should_throw_with_untrusted_client_certs(\n    playwright: Playwright, assetdir: Path\n) -> None:\n    serverURL = \"https://localhost:8000/\"\n    request = playwright.request.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": serverURL,\n                \"certPath\": assetdir\n                / \"client-certificates/client/self-signed/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/self-signed/key.pem\",\n            }\n        ],\n    )\n    with pytest.raises(Exception, match=\"alert unknown ca\"):\n        request.get(serverURL)\n    request.dispose()\n\n\ndef test_should_work_with_new_context(browser: Browser, assetdir: Path) -> None:\n    context = browser.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    page = context.new_page()\n    page.goto(\"https://localhost:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    page.goto(\"https://127.0.0.1:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n\n    response = page.context.request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in response.text()\n    )\n    response = page.context.request.get(\"https://127.0.0.1:8000\")\n    assert \"Hello Alice, your certificate was issued by localhost!\" in response.text()\n    context.close()\n\n\ndef test_should_work_with_new_context_passing_as_content(\n    browser: Browser, assetdir: Path\n) -> None:\n    context = browser.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"cert\": (\n                    assetdir / \"client-certificates/client/trusted/cert.pem\"\n                ).read_bytes(),\n                \"key\": (\n                    assetdir / \"client-certificates/client/trusted/key.pem\"\n                ).read_bytes(),\n            }\n        ],\n    )\n    page = context.new_page()\n    page.goto(\"https://localhost:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    page.goto(\"https://127.0.0.1:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n\n    response = page.context.request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in response.text()\n    )\n    response = page.context.request.get(\"https://127.0.0.1:8000\")\n    assert \"Hello Alice, your certificate was issued by localhost!\" in response.text()\n    context.close()\n\n\ndef test_should_work_with_new_persistent_context(\n    browser_type: BrowserType, assetdir: Path, launch_arguments: Dict\n) -> None:\n    context = browser_type.launch_persistent_context(\n        \"\",\n        **launch_arguments,\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    page = context.new_page()\n    page.goto(\"https://localhost:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Sorry, but you need to provide a client certificate to continue.\"\n    )\n    page.goto(\"https://127.0.0.1:8000\")\n    expect(page.get_by_test_id(\"message\")).to_have_text(\n        \"Hello Alice, your certificate was issued by localhost!\"\n    )\n    context.close()\n\n\ndef test_should_work_with_global_api_request_context(\n    playwright: Playwright, assetdir: Path\n) -> None:\n    request = playwright.request.new_context(\n        # TODO: Remove this once we can pass a custom CA.\n        ignore_https_errors=True,\n        client_certificates=[\n            {\n                \"origin\": \"https://127.0.0.1:8000\",\n                \"certPath\": assetdir / \"client-certificates/client/trusted/cert.pem\",\n                \"keyPath\": assetdir / \"client-certificates/client/trusted/key.pem\",\n            }\n        ],\n    )\n    response = request.get(\"https://localhost:8000\")\n    assert (\n        \"Sorry, but you need to provide a client certificate to continue.\"\n        in response.text()\n    )\n    response = request.get(\"https://127.0.0.1:8000\")\n    assert \"Hello Alice, your certificate was issued by localhost!\" in response.text()\n    request.dispose()\n"
  },
  {
    "path": "tests/sync/test_browsercontext_events.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Optional\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, Dialog, Page\n\nfrom ..server import Server, TestServerRequest\n\n\ndef test_console_event_should_work(page: Page) -> None:\n    with page.context.expect_console_message() as console_info:\n        page.evaluate(\"() => console.log('hello')\")\n    message = console_info.value\n    assert message.text == \"hello\"\n    assert message.page == page\n\n\ndef test_console_event_should_work_in_popup(page: Page) -> None:\n    with page.context.expect_console_message() as console_info:\n        with page.expect_popup() as popup_info:\n            page.evaluate(\n                \"\"\"() => {\n                const win = window.open('');\n                win.console.log('hello');\n                }\"\"\"\n            )\n    message = console_info.value\n    popup = popup_info.value\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\ndef test_console_event_should_work_in_popup_2(page: Page, browser_name: str) -> None:\n    with page.context.expect_console_message(\n        lambda msg: msg.type == \"log\"\n    ) as console_info:\n        with page.context.expect_page() as page_info:\n            page.evaluate(\n                \"\"\"async () => {\n                const win = window.open('javascript:console.log(\"hello\")');\n                await new Promise(f => setTimeout(f, 0));\n                win.close();\n            }\"\"\"\n            )\n    message = console_info.value\n    popup = page_info.value\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\ndef test_console_event_should_work_in_immediately_closed_popup(\n    page: Page, browser_name: str\n) -> None:\n    with page.context.expect_console_message(\n        lambda msg: msg.type == \"log\"\n    ) as console_info:\n        with page.context.expect_page() as page_info:\n            page.evaluate(\n                \"\"\"() => {\n                const win = window.open('');\n                win.console.log('hello');\n                win.close();\n            }\"\"\"\n            )\n    message = console_info.value\n    popup = page_info.value\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\ndef test_dialog_event_should_work1(page: Page) -> None:\n    dialog1: Optional[Dialog] = None\n\n    def handle_page_dialog(dialog: Dialog) -> None:\n        nonlocal dialog1\n        dialog1 = dialog\n        dialog.accept(\"hello\")\n\n    page.on(\"dialog\", handle_page_dialog)\n\n    dialog2: Optional[Dialog] = None\n\n    def handle_context_dialog(dialog: Dialog) -> None:\n        nonlocal dialog2\n        dialog2 = dialog\n\n    page.context.on(\"dialog\", handle_context_dialog)\n\n    assert page.evaluate(\"() => prompt('hey?')\") == \"hello\"\n    assert dialog1\n    assert dialog1 == dialog2\n    assert dialog1.message == \"hey?\"\n    assert dialog1.page == page\n\n\ndef test_dialog_event_should_work_in_popup1(page: Page) -> None:\n    dialog: Optional[Dialog] = None\n\n    def handle_dialog(d: Dialog) -> None:\n        nonlocal dialog\n        dialog = d\n        dialog.accept(\"hello\")\n\n    page.context.on(\"dialog\", handle_dialog)\n\n    with page.expect_popup() as popup_info:\n        assert page.evaluate(\"() => window.open('').prompt('hey?')\") == \"hello\"\n    popup = popup_info.value\n    assert dialog\n    assert dialog.message == \"hey?\"\n    assert dialog.page == popup\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\ndef test_dialog_event_should_work_in_popup_2(page: Page, browser_name: str) -> None:\n    def handle_dialog(dialog: Dialog) -> None:\n        assert dialog.message == \"hey?\"\n        assert dialog.page is None\n        dialog.accept(\"hello\")\n\n    page.context.on(\"dialog\", handle_dialog)\n\n    assert page.evaluate(\"() => window.open('javascript:prompt(\\\"hey?\\\")')\")\n\n\n# console message from javascript: url is not reported at all\n@pytest.mark.skip_browser(\"firefox\")\ndef test_dialog_event_should_work_in_immdiately_closed_popup(page: Page) -> None:\n    popup = None\n\n    def handle_popup(p: Page) -> None:\n        nonlocal popup\n        popup = p\n\n    page.on(\"popup\", handle_popup)\n\n    with page.context.expect_console_message() as console_info:\n        page.evaluate(\n            \"\"\"() => {\n                const win = window.open();\n                win.console.log('hello');\n                win.close();\n            }\"\"\"\n        )\n    message = console_info.value\n\n    assert message.text == \"hello\"\n    assert message.page == popup\n\n\ndef test_dialog_event_should_work_with_inline_script_tag(\n    page: Page, server: Server\n) -> None:\n    def handle_route(request: TestServerRequest) -> None:\n        request.setHeader(\"content-type\", \"text/html\")\n        request.write(b\"<script>window.result = prompt('hey?')</script>\")\n        request.finish()\n\n    server.set_route(\"/popup.html\", handle_route)\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<a href='popup.html' target=_blank>Click me</a>\")\n\n    with (\n        page.context.expect_event(\"dialog\") as dialog_info,\n        page.expect_popup() as popup_info,\n    ):\n        page.click(\"a\")\n\n    dialog: Dialog = dialog_info.value\n    popup: Page = popup_info.value\n\n    assert dialog.message == \"hey?\"\n    assert dialog.page == popup\n    dialog.accept(\"hello\")\n    assert popup.evaluate(\"window.result\") == \"hello\"\n\n\ndef test_console_event_should_work_with_context_manager(page: Page) -> None:\n    with page.context.expect_console_message() as cm_info:\n        page.evaluate(\"() => console.log('hello')\")\n    message = cm_info.value\n    assert message.text == \"hello\"\n    assert message.page == page\n\n\ndef test_weberror_event_should_work(context: BrowserContext, page: Page) -> None:\n    with context.expect_event(\"weberror\") as error_info:\n        page.goto('data:text/html,<script>throw new Error(\"Test\")</script>')\n    error = error_info.value\n    assert error.page == page\n    assert error.error.message == \"Test\"\n"
  },
  {
    "path": "tests/sync/test_browsercontext_request_fallback.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Any, Callable, List\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, Error, Page, Request, Route\nfrom tests.server import Server\n\n\ndef _append_with_return_value(values: List, value: Any) -> Any:\n    values.append(value)\n\n\ndef test_should_work(page: Page, context: BrowserContext, server: Server) -> None:\n    context.route(\"**/*\", lambda route: route.fallback())\n    page.goto(server.EMPTY_PAGE)\n\n\ndef test_should_fall_back(page: Page, context: BrowserContext, server: Server) -> None:\n    intercepted: List[int] = []\n    context.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 1),\n            route.fallback(),\n        ),\n    )\n    context.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 2),\n            route.fallback(),\n        ),\n    )\n    context.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 3),\n            route.fallback(),\n        ),\n    )\n\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\ndef test_should_fall_back_async_delayed(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    intercepted = []\n\n    def create_handler(i: int) -> Callable[[Route], None]:\n        def handler(route: Route) -> None:\n            intercepted.append(i)\n            page.wait_for_timeout(500)\n            route.fallback()\n\n        return handler\n\n    context.route(\"**/empty.html\", create_handler(1))\n    context.route(\"**/empty.html\", create_handler(2))\n    context.route(\"**/empty.html\", create_handler(3))\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\ndef test_should_chain_once(page: Page, context: BrowserContext, server: Server) -> None:\n    context.route(\n        \"**/madeup.txt\",\n        lambda route: route.fulfill(status=200, body=\"fulfilled one\"),\n        times=1,\n    )\n    context.route(\"**/madeup.txt\", lambda route: route.fallback(), times=1)\n\n    resp = page.goto(server.PREFIX + \"/madeup.txt\")\n    assert resp\n    body = resp.body()\n    assert body == b\"fulfilled one\"\n\n\ndef test_should_not_chain_fulfill(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    context.route(\"**/empty.html\", handler)\n    context.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(status=200, body=\"fulfilled\"),\n    )\n    context.route(\"**/empty.html\", lambda route: route.fallback())\n\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    body = response.body()\n    assert body == b\"fulfilled\"\n    assert not failed[0]\n\n\ndef test_should_not_chain_abort(\n    page: Page,\n    context: BrowserContext,\n    server: Server,\n    is_webkit: bool,\n    is_firefox: bool,\n) -> None:\n    failed = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    context.route(\"**/empty.html\", handler)\n    context.route(\"**/empty.html\", lambda route: route.abort())\n    context.route(\"**/empty.html\", lambda route: route.fallback())\n\n    with pytest.raises(Error) as excinfo:\n        page.goto(server.EMPTY_PAGE)\n    if is_webkit:\n        assert \"Blocked by Web Inspector\" in excinfo.value.message\n    elif is_firefox:\n        assert \"NS_ERROR_FAILURE\" in excinfo.value.message\n    else:\n        assert \"net::ERR_FAILED\" in excinfo.value.message\n    assert not failed[0]\n\n\ndef test_should_fall_back_after_exception(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    context.route(\"**/empty.html\", lambda route: route.continue_())\n\n    def handler(route: Route) -> None:\n        try:\n            route.fulfill(response=47)  # type: ignore\n        except Exception:\n            route.fallback()\n\n    context.route(\"**/empty.html\", handler)\n\n    page.goto(server.EMPTY_PAGE)\n\n\ndef test_should_amend_http_headers(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    values = []\n\n    def handler(route: Route) -> None:\n        values.append(route.request.headers.get(\"foo\"))\n        values.append(route.request.header_value(\"FOO\"))\n        route.continue_()\n\n    context.route(\"**/sleep.zzz\", handler)\n\n    def handler_with_header_mods(route: Route) -> None:\n        route.fallback(headers={**route.request.headers, \"FOO\": \"bar\"})\n\n    context.route(\"**/*\", handler_with_header_mods)\n\n    page.goto(server.EMPTY_PAGE)\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz')\")\n    values.append(server_request_info.value.getHeader(\"foo\"))\n    assert values == [\"bar\", \"bar\", \"bar\"]\n\n\ndef test_should_delete_header_with_undefined_value(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    server.set_route(\n        \"/something\",\n        lambda r: (\n            r.setHeader(\"Acces-Control-Allow-Origin\", \"*\"),\n            r.write(b\"done\"),\n            r.finish(),\n        ),\n    )\n\n    intercepted_request = []\n\n    def capture_and_continue(route: Route, request: Request) -> None:\n        intercepted_request.append(request)\n        route.continue_()\n\n    context.route(\"**/*\", capture_and_continue)\n\n    def delete_foo_header(route: Route, request: Request) -> None:\n        headers = request.all_headers()\n        del headers[\"foo\"]\n        route.fallback(headers=headers)\n\n    context.route(server.PREFIX + \"/something\", delete_foo_header)\n    with server.expect_request(\"/something\") as server_req_info:\n        text = page.evaluate(\n            \"\"\"\n            async url => {\n                const data = await fetch(url, {\n                    headers: {\n                    foo: 'a',\n                    bar: 'b',\n                    }\n                });\n                return data.text();\n                }\n            \"\"\",\n            server.PREFIX + \"/something\",\n        )\n    server_req = server_req_info.value\n    assert text == \"done\"\n    assert not intercepted_request[0].headers.get(\"foo\")\n    assert intercepted_request[0].headers.get(\"bar\") == \"b\"\n    assert not server_req.getHeader(\"foo\")\n    assert server_req.getHeader(\"bar\") == \"b\"\n\n\ndef test_should_amend_method(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    method: List[str] = []\n    context.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(method, route.request.method),\n            route.continue_(),\n        ),\n    )\n    context.route(\"**/*\", lambda route: route.fallback(method=\"POST\"))\n\n    with server.expect_request(\"/sleep.zzz\") as request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz')\")\n    request = request_info.value\n    assert method == [\"POST\"]\n    assert request.method == b\"POST\"\n\n\ndef test_should_override_request_url(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    url: List[str] = []\n    context.route(\n        \"**/global-var.html\",\n        lambda route: (\n            _append_with_return_value(url, route.request.url),\n            route.continue_(),\n        ),\n    )\n    context.route(\n        \"**/foo\",\n        lambda route: route.fallback(url=server.PREFIX + \"/global-var.html\"),\n    )\n\n    with server.expect_request(\"/global-var.html\") as server_request_info:\n        with page.expect_event(\"response\") as response_info:\n            page.goto(server.PREFIX + \"/foo\")\n    server_request = server_request_info.value\n    response = response_info.value\n    assert url == [server.PREFIX + \"/global-var.html\"]\n    assert response.url == server.PREFIX + \"/global-var.html\"\n    assert response.request.url == server.PREFIX + \"/global-var.html\"\n    assert page.evaluate(\"() => window['globalVar']\") == 123\n    assert server_request.uri == b\"/global-var.html\"\n    assert server_request.method == b\"GET\"\n\n\ndef test_should_amend_post_data(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    post_data: List[str] = []\n    context.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(post_data, route.request.post_data),\n            route.continue_(),\n        ),\n    )\n    context.route(\"**/*\", lambda route: route.fallback(post_data=\"doggo\"))\n\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\")\n    server_request = server_request_info.value\n    assert post_data == [\"doggo\"]\n    assert server_request.post_body == b\"doggo\"\n\n\ndef test_should_amend_binary_post_data(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    post_data_buffer: List[str] = []\n    context.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(post_data_buffer, route.request.post_data),\n            route.continue_(),\n        ),\n    )\n    context.route(\n        \"**/*\", lambda route: route.fallback(post_data=b\"\\x00\\x01\\x02\\x03\\x04\")\n    )\n\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\")\n    server_request = server_request_info.value\n    # FIXME: should this be bytes?\n    assert post_data_buffer == [\"\\x00\\x01\\x02\\x03\\x04\"]\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body == b\"\\x00\\x01\\x02\\x03\\x04\"\n\n\ndef test_should_chain_fallback_into_page(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    intercepted: List[int] = []\n    context.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 1), route.fallback()),\n    )\n    context.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 2), route.fallback()),\n    )\n    context.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 3), route.fallback()),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 4), route.fallback()),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 5), route.fallback()),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (_append_with_return_value(intercepted, 6), route.fallback()),\n    )\n\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [6, 5, 4, 3, 2, 1]\n"
  },
  {
    "path": "tests/sync/test_browsercontext_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\n\nimport pytest\nfrom twisted.web import http\n\nfrom playwright.sync_api import BrowserContext, Page, Route\nfrom tests.server import Server\n\n\ndef test_should_fulfill_intercepted_response(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    def handle(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(\n            response=response,\n            status=201,\n            headers={\"foo\": \"bar\"},\n            content_type=\"text/plain\",\n            body=\"Yo, page!\",\n        )\n\n    context.route(\"**/*\", handle)\n    response = page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 201\n    assert response.headers[\"foo\"] == \"bar\"\n    assert response.headers[\"content-type\"] == \"text/plain\"\n    assert page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\ndef test_should_fulfill_response_with_empty_body(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    def handle(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(\n            response=response, status=201, body=\"\", headers={\"content-length\": \"0\"}\n        )\n\n    context.route(\"**/*\", handle)\n    response = page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 201\n    assert response.text() == \"\"\n\n\ndef test_should_override_with_defaults_when_intercepted_response_not_provided(\n    page: Page, context: BrowserContext, server: Server, browser_name: str\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"my content\".encode())\n        request.finish()\n\n    server.set_route(\"/empty.html\", server_handler)\n\n    def handle(route: Route) -> None:\n        page.request.fetch(route.request)\n        route.fulfill(status=201)\n\n    context.route(\"**/*\", handle)\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.text() == \"\"\n    if browser_name == \"webkit\":\n        assert response.headers == {\"content-type\": \"text/plain\"}\n    else:\n        assert response.headers == {}\n\n\ndef test_should_fulfill_with_any_response(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"Woo-hoo\".encode())\n        request.finish()\n\n    server.set_route(\"/sample\", server_handler)\n    sample_response = page.request.get(server.PREFIX + \"/sample\")\n    context.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            response=sample_response, status=201, content_type=\"text/plain\"\n        ),\n    )\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.text() == \"Woo-hoo\"\n    assert response.headers[\"foo\"] == \"bar\"\n\n\ndef test_should_support_fulfill_after_intercept(\n    page: Page, context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    def handle_route(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(response=response)\n\n    context.route(\"**\", handle_route)\n    with server.expect_request(\"/title.html\") as request_info:\n        response = page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    request = request_info.value\n    assert request.uri.decode() == \"/title.html\"\n    original = (assetdir / \"title.html\").read_text()\n    assert response.text() == original\n\n\ndef test_should_show_exception_after_fulfill(page: Page, server: Server) -> None:\n    def _handle(route: Route) -> None:\n        route.continue_()\n        raise Exception(\"Exception text!?\")\n\n    page.route(\"*/**\", _handle)\n    page.goto(server.EMPTY_PAGE)\n    # Any next API call should throw because handler did throw during previous goto()\n    with pytest.raises(Exception, match=\"Exception text!?\"):\n        page.goto(server.EMPTY_PAGE)\n"
  },
  {
    "path": "tests/sync/test_browsercontext_service_worker_policy.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom playwright.sync_api import Browser\nfrom tests.server import Server\n\n\ndef test_should_allow_service_workers_by_default(\n    browser: Browser, server: Server\n) -> None:\n    context = browser.new_context()\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    page.evaluate(\"() => window.activationPromise\")\n    context.close()\n\n\ndef test_block_blocks_service_worker_registration(\n    browser: Browser, server: Server\n) -> None:\n    context = browser.new_context(service_workers=\"block\")\n    page = context.new_page()\n    with page.expect_console_message(\n        lambda m: \"Service Worker registration blocked by Playwright\" == m.text\n    ):\n        page.goto(server.PREFIX + \"/serviceworkers/fetchdummy/sw.html\")\n    context.close()\n"
  },
  {
    "path": "tests/sync/test_browsercontext_storage_state.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nfrom pathlib import Path\n\nfrom playwright.sync_api import Browser, BrowserContext, Page, StorageState\nfrom tests.server import Server\n\n\ndef test_should_capture_local_storage(context: BrowserContext) -> None:\n    page1 = context.new_page()\n    page1.route(\"**/*\", lambda route: route.fulfill(body=\"<html></html>\"))\n    page1.goto(\"https://www.example.com\")\n    page1.evaluate(\"localStorage['name1'] = 'value1'\")\n    page1.goto(\"https://www.domain.com\")\n    page1.evaluate(\"localStorage['name2'] = 'value2'\")\n\n    state = context.storage_state()\n    origins = state[\"origins\"]\n    assert origins\n    assert len(origins) == 2\n    assert origins[0] == {\n        \"origin\": \"https://www.domain.com\",\n        \"localStorage\": [{\"name\": \"name2\", \"value\": \"value2\"}],\n    }\n    assert origins[1] == {\n        \"origin\": \"https://www.example.com\",\n        \"localStorage\": [{\"name\": \"name1\", \"value\": \"value1\"}],\n    }\n\n\ndef test_should_set_local_storage(browser: Browser) -> None:\n    storage_state: StorageState = {\n        \"origins\": [\n            {\n                \"origin\": \"https://www.example.com\",\n                \"localStorage\": [{\"name\": \"name1\", \"value\": \"value1\"}],\n            }\n        ]\n    }\n    # We intentionally hide the indexed_db part in our API for now\n    storage_state[\"origins\"][0][\"indexedDB\"] = [  # type: ignore\n        {\n            \"name\": \"db\",\n            \"version\": 42,\n            \"stores\": [\n                {\n                    \"name\": \"store\",\n                    \"autoIncrement\": False,\n                    \"records\": [{\"key\": \"bar\", \"value\": \"foo\"}],\n                    \"indexes\": [],\n                }\n            ],\n        }\n    ]\n    context = browser.new_context(storage_state=storage_state)\n\n    page = context.new_page()\n    page.route(\"**/*\", lambda route: route.fulfill(body=\"<html></html>\"))\n    page.goto(\"https://www.example.com\")\n    local_storage = page.evaluate(\"window.localStorage\")\n    assert local_storage == {\"name1\": \"value1\"}\n\n    indexed_db = page.evaluate(\n        \"\"\"async () => {\n        return new Promise((resolve, reject) => {\n            const openRequest = indexedDB.open('db', 42);\n            openRequest.addEventListener('success', () => {\n                const db = openRequest.result;\n                const transaction = db.transaction('store', 'readonly');\n                const getRequest = transaction.objectStore('store').get('bar');\n                getRequest.addEventListener('success', () => resolve(getRequest.result));\n                getRequest.addEventListener('error', () => reject(getRequest.error));\n            });\n            openRequest.addEventListener('error', () => reject(openRequest.error));\n        });\n    }\"\"\"\n    )\n    assert indexed_db == \"foo\"\n    context.close()\n\n\ndef test_should_round_trip_through_the_file(\n    browser: Browser, context: BrowserContext, tmp_path: Path\n) -> None:\n    page1 = context.new_page()\n    page1.route(\n        \"**/*\",\n        lambda route: route.fulfill(body=\"<html></html>\"),\n    )\n    page1.goto(\"https://www.example.com\")\n    page1.evaluate(\n        \"\"\"() => {\n            localStorage[\"name1\"] = \"value1\"\n            document.cookie = \"username=John Doe\"\n            return document.cookie\n        }\"\"\"\n    )\n\n    path = tmp_path / \"storage-state.json\"\n    state = context.storage_state(path=path)\n    with open(path, \"r\") as f:\n        written = json.load(f)\n    assert state == written\n\n    context2 = browser.new_context(storage_state=path)\n    page2 = context2.new_page()\n    page2.route(\n        \"**/*\",\n        lambda route: route.fulfill(body=\"<html></html>\"),\n    )\n    page2.goto(\"https://www.example.com\")\n    local_storage = page2.evaluate(\"window.localStorage\")\n    assert local_storage == {\"name1\": \"value1\"}\n    cookie = page2.evaluate(\"document.cookie\")\n    assert cookie == \"username=John Doe\"\n    context2.close()\n\n\ndef test_should_serialise_indexed_db(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.evaluate(\n        \"\"\"async () => {\n            await new Promise((resolve, reject) => {\n                const openRequest = indexedDB.open('db', 42);\n                openRequest.onupgradeneeded = () => {\n                openRequest.result.createObjectStore('store');\n                };\n                openRequest.onsuccess = () => {\n                const request = openRequest.result.transaction('store', 'readwrite')\n                    .objectStore('store')\n                    .put('foo', 'bar');\n                request.addEventListener('success', resolve);\n                request.addEventListener('error', reject);\n                };\n            });\n        }\"\"\"\n    )\n    assert page.context.storage_state() == {\"cookies\": [], \"origins\": []}\n    assert page.context.storage_state(indexed_db=True) == {\n        \"cookies\": [],\n        \"origins\": [\n            {\n                \"origin\": f\"http://localhost:{server.PORT}\",\n                \"localStorage\": [],\n                \"indexedDB\": [\n                    {\n                        \"name\": \"db\",\n                        \"version\": 42,\n                        \"stores\": [\n                            {\n                                \"name\": \"store\",\n                                \"autoIncrement\": False,\n                                \"records\": [{\"key\": \"bar\", \"value\": \"foo\"}],\n                                \"indexes\": [],\n                            }\n                        ],\n                    }\n                ],\n            }\n        ],\n    }\n"
  },
  {
    "path": "tests/sync/test_browsertype_connect.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport time\nfrom pathlib import Path\nfrom typing import Any, Callable\n\nimport pytest\n\nfrom playwright.sync_api import BrowserType, Error, Playwright, Route\nfrom tests.conftest import RemoteServer\nfrom tests.server import Server\n\n\ndef test_browser_type_connect_slow_mo(\n    server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = browser_type.connect(remote_server.ws_endpoint, slow_mo=100)\n    browser_context = browser.new_context()\n    t1 = time.monotonic()\n    page = browser_context.new_page()\n    assert page.evaluate(\"11 * 11\") == 121\n    assert (time.monotonic() - t1) >= 0.2\n    page.goto(server.EMPTY_PAGE)\n    browser.close()\n\n\ndef test_browser_type_connect_should_be_able_to_reconnect_to_a_browser(\n    server: Server, browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = browser_type.connect(remote_server.ws_endpoint)\n    browser_context = browser.new_context()\n    assert len(browser_context.pages) == 0\n    page = browser_context.new_page()\n    assert len(browser_context.pages) == 1\n    assert page.evaluate(\"11 * 11\") == 121\n    page.goto(server.EMPTY_PAGE)\n    browser.close()\n\n    browser = browser_type.connect(remote_server.ws_endpoint)\n    browser_context = browser.new_context()\n    page = browser_context.new_page()\n    page.goto(server.EMPTY_PAGE)\n    browser.close()\n\n\ndef test_browser_type_connect_should_be_able_to_connect_two_browsers_at_the_same_time(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser1 = browser_type.connect(remote_server.ws_endpoint)\n    assert len(browser1.contexts) == 0\n    browser1.new_context()\n    assert len(browser1.contexts) == 1\n\n    browser2 = browser_type.connect(remote_server.ws_endpoint)\n    assert len(browser2.contexts) == 0\n    browser2.new_context()\n    assert len(browser2.contexts) == 1\n    assert len(browser1.contexts) == 1\n\n    browser1.close()\n    page2 = browser2.new_page()\n    # original browser should still work\n    assert page2.evaluate(\"7 * 6\") == 42\n\n    browser2.close()\n\n\ndef test_browser_type_connect_disconnected_event_should_be_emitted_when_browser_is_closed_or_server_is_closed(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser1 = browser_type.connect(remote.ws_endpoint)\n    browser2 = browser_type.connect(remote.ws_endpoint)\n\n    disconnected1 = []\n    disconnected2 = []\n    browser1.on(\"disconnected\", lambda browser: disconnected1.append(True))\n    browser2.on(\"disconnected\", lambda browser: disconnected2.append(True))\n\n    page2 = browser2.new_page()\n\n    browser1.close()\n    assert len(disconnected1) == 1\n    assert len(disconnected2) == 0\n\n    remote.kill()\n    assert len(disconnected1) == 1\n\n    with pytest.raises(Error):\n        # Tickle connection so that it gets a chance to dispatch disconnect event.\n        page2.title()\n    assert len(disconnected2) == 1\n\n\ndef test_browser_type_disconnected_event_should_have_browser_as_argument(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = browser_type.connect(remote_server.ws_endpoint)\n    event_payloads = []\n    browser.on(\"disconnected\", lambda b: event_payloads.append(b))\n    browser.close()\n    assert event_payloads[0] == browser\n\n\ndef test_browser_type_connect_set_browser_connected_state(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = browser_type.connect(remote_server.ws_endpoint)\n    assert browser.is_connected()\n    browser.close()\n    assert browser.is_connected() is False\n\n\ndef test_browser_type_connect_should_throw_when_used_after_is_connected_returns_false(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    browser = browser_type.connect(remote_server.ws_endpoint)\n    page = browser.new_page()\n\n    remote_server.kill()\n\n    with pytest.raises(Error) as exc_info:\n        page.evaluate(\"1 + 1\")\n    assert \"has been closed\" in exc_info.value.message\n    assert browser.is_connected() is False\n\n\ndef test_browser_type_connect_should_forward_close_events_to_pages(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = browser_type.connect(remote.ws_endpoint)\n    context = browser.new_context()\n    page = context.new_page()\n\n    events = []\n    browser.on(\"disconnected\", lambda browser: events.append(\"browser::disconnected\"))\n    context.on(\"close\", lambda context: events.append(\"context::close\"))\n    page.on(\"close\", lambda page: events.append(\"page::close\"))\n\n    browser.close()\n    assert events == [\"page::close\", \"context::close\", \"browser::disconnected\"]\n    remote.kill()\n    assert events == [\"page::close\", \"context::close\", \"browser::disconnected\"]\n\n\ndef test_browser_type_connect_should_forward_close_events_on_remote_kill(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = browser_type.connect(remote.ws_endpoint)\n    context = browser.new_context()\n    page = context.new_page()\n\n    events = []\n    browser.on(\"disconnected\", lambda context: events.append(\"browser::disconnected\"))\n    context.on(\"close\", lambda context: events.append(\"context::close\"))\n    page.on(\"close\", lambda page: events.append(\"page::close\"))\n    remote.kill()\n    with pytest.raises(Error):\n        page.title()\n    assert events == [\"page::close\", \"context::close\", \"browser::disconnected\"]\n\n\ndef test_connect_to_closed_server_without_hangs(\n    browser_type: BrowserType, launch_server: Callable[[], RemoteServer]\n) -> None:\n    remote_server = launch_server()\n    remote_server.kill()\n    with pytest.raises(Error) as exc:\n        browser_type.connect(remote_server.ws_endpoint)\n    assert \"WebSocket error: \" in exc.value.message\n\n\ndef test_browser_type_connect_should_fulfill_with_global_fetch_result(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    playwright: Playwright,\n    server: Server,\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = browser_type.connect(remote.ws_endpoint)\n    context = browser.new_context()\n    page = context.new_page()\n\n    def handle_request(route: Route) -> None:\n        request = playwright.request.new_context()\n        response = request.get(server.PREFIX + \"/simple.json\")\n        return route.fulfill(response=response)\n\n    page.route(\"**/*\", handle_request)\n\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    assert response.json() == {\"foo\": \"bar\"}\n\n    remote.kill()\n\n\ndef test_set_input_files_should_preserve_last_modified_timestamp(\n    browser_type: BrowserType,\n    launch_server: Callable[[], RemoteServer],\n    assetdir: Path,\n) -> None:\n    # Launch another server to not affect other tests.\n    remote = launch_server()\n\n    browser = browser_type.connect(remote.ws_endpoint)\n    context = browser.new_context()\n    page = context.new_page()\n\n    page.set_content(\"<input type=file multiple=true/>\")\n    input = page.locator(\"input\")\n    files: Any = [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]\n    input.set_input_files([assetdir / file for file in files])\n    assert input.evaluate(\"input => [...input.files].map(f => f.name)\") == files\n    timestamps = input.evaluate(\"input => [...input.files].map(f => f.lastModified)\")\n    expected_timestamps = [os.path.getmtime(assetdir / file) * 1000 for file in files]\n\n    # On Linux browser sometimes reduces the timestamp by 1ms: 1696272058110.0715  -> 1696272058109 or even\n    # rounds it to seconds in WebKit: 1696272058110 -> 1696272058000.\n    for i in range(len(timestamps)):\n        assert abs(timestamps[i] - expected_timestamps[i]) < 1000\n"
  },
  {
    "path": "tests/sync/test_browsertype_connect_cdp.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import BrowserType\nfrom tests.server import find_free_port\n\npytestmark = pytest.mark.only_browser(\"chromium\")\n\n\ndef test_connect_to_an_existing_cdp_session(\n    launch_arguments: Dict, browser_type: BrowserType\n) -> None:\n    port = find_free_port()\n    browser_server = browser_type.launch(\n        **launch_arguments, args=[f\"--remote-debugging-port={port}\"]\n    )\n    cdp_browser = browser_type.connect_over_cdp(f\"http://127.0.0.1:{port}\")\n    assert len(cdp_browser.contexts) == 1\n    cdp_browser.close()\n    browser_server.close()\n"
  },
  {
    "path": "tests/sync/test_cdp_session.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Error, Page\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_work(page: Page) -> None:\n    client = page.context.new_cdp_session(page)\n    events = []\n    client.on(\"Runtime.consoleAPICalled\", lambda params: events.append(params))\n    client.send(\"Runtime.enable\")\n    result = client.send(\n        \"Runtime.evaluate\",\n        {\"expression\": \"window.foo = 'bar'; console.log('log'); 'result'\"},\n    )\n    assert result == {\"result\": {\"type\": \"string\", \"value\": \"result\"}}\n    foo = page.evaluate(\"() => window.foo\")\n    assert foo == \"bar\"\n    assert events[0][\"args\"][0][\"value\"] == \"log\"\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_receive_events(page: Page, server: Server) -> None:\n    client = page.context.new_cdp_session(page)\n    client.send(\"Network.enable\")\n    events = []\n    client.on(\"Network.requestWillBeSent\", lambda event: events.append(event))\n    page.goto(server.EMPTY_PAGE)\n    assert len(events) == 1\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_be_able_to_detach_session(page: Page) -> None:\n    client = page.context.new_cdp_session(page)\n    client.send(\"Runtime.enable\")\n    eval_response = client.send(\n        \"Runtime.evaluate\", {\"expression\": \"1 + 2\", \"returnByValue\": True}\n    )\n    assert eval_response[\"result\"][\"value\"] == 3\n    client.detach()\n    with pytest.raises(Error) as exc_info:\n        client.send(\"Runtime.evaluate\", {\"expression\": \"3 + 1\", \"returnByValue\": True})\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_not_break_page_close(browser: Browser) -> None:\n    context = browser.new_context()\n    page = context.new_page()\n    session = page.context.new_cdp_session(page)\n    session.detach()\n    page.close()\n    context.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_detach_when_page_closes(browser: Browser) -> None:\n    context = browser.new_context()\n    page = context.new_page()\n    session = context.new_cdp_session(page)\n    page.close()\n    with pytest.raises(Error):\n        session.detach()\n    context.close()\n"
  },
  {
    "path": "tests/sync/test_check.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page\n\n\ndef test_check_the_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    page.check(\"input\")\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_not_check_the_checked_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    page.check(\"input\")\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_uncheck_the_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    page.uncheck(\"input\")\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_not_uncheck_the_unchecked_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    page.uncheck(\"input\")\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_check_the_box_by_label(page: Page) -> None:\n    page.set_content(\n        '<label for=\"checkbox\"><input id=\"checkbox\" type=\"checkbox\"></input></label>'\n    )\n    page.check(\"label\")\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_check_the_box_outside_label(page: Page) -> None:\n    page.set_content(\n        '<label for=\"checkbox\">Text</label><div><input id=\"checkbox\" type=\"checkbox\"></input></div>'\n    )\n    page.check(\"label\")\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_check_the_box_inside_label_without_id(page: Page) -> None:\n    page.set_content(\n        '<label>Text<span><input id=\"checkbox\" type=\"checkbox\"></input></span></label>'\n    )\n    page.check(\"label\")\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_check_radio(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n      <input type='radio'>one</input>\n      <input id='two' type='radio'>two</input>\n      <input type='radio'>three</input>\"\"\"\n    )\n    page.check(\"#two\")\n    assert page.evaluate(\"two.checked\")\n\n\ndef test_check_the_box_by_aria_role(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div role='checkbox' id='checkbox'>CHECKBOX</div>\n      <script>\n        checkbox.addEventListener('click', () => checkbox.setAttribute('aria-checked', 'true'))\n      </script>\"\"\"\n    )\n    page.check(\"div\")\n    assert page.evaluate(\"checkbox.getAttribute ('aria-checked')\")\n"
  },
  {
    "path": "tests/sync/test_console.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import List\n\nimport pytest\n\nfrom playwright.sync_api import ConsoleMessage, Page\nfrom tests.server import Server\n\n\ndef test_console_should_work(page: Page, browser_name: str) -> None:\n    messages: List[ConsoleMessage] = []\n    page.once(\"console\", lambda m: messages.append(m))\n    with page.expect_console_message():\n        page.evaluate('() => console.log(\"hello\", 5, {foo: \"bar\"})')\n    assert len(messages) == 1\n    message = messages[0]\n    if browser_name != \"firefox\":\n        assert message.text == \"hello 5 {foo: bar}\"\n        assert str(message) == \"hello 5 {foo: bar}\"\n    else:\n        assert message.text == \"hello 5 JSHandle@object\"\n        assert str(message) == \"hello 5 JSHandle@object\"\n    assert message.type == \"log\"\n    assert message.args[0].json_value() == \"hello\"\n    assert message.args[1].json_value() == 5\n    assert message.args[2].json_value() == {\"foo\": \"bar\"}\n\n\ndef test_console_should_emit_same_log_twice(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m.text))\n    page.evaluate('() => { for (let i = 0; i < 2; ++i ) console.log(\"hello\"); } ')\n    assert messages == [\"hello\", \"hello\"]\n\n\ndef test_console_should_use_text_for__str__(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m))\n    page.evaluate('() => console.log(\"Hello world\")')\n    assert len(messages) == 1\n    assert str(messages[0]) == \"Hello world\"\n\n\ndef test_console_should_work_for_different_console_api_calls(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m))\n    # All console events will be reported before 'page.evaluate' is finished.\n    page.evaluate(\n        \"\"\"() => {\n      // A pair of time/timeEnd generates only one Console API call.\n      console.time('calling console.time');\n      console.timeEnd('calling console.time');\n      console.trace('calling console.trace');\n      console.dir('calling console.dir');\n      console.warn('calling console.warn');\n      console.error('calling console.error');\n      console.log(Promise.resolve('should not wait until resolved!'));\n    }\"\"\"\n    )\n    assert list(map(lambda msg: msg.type, messages)) == [\n        \"timeEnd\",\n        \"trace\",\n        \"dir\",\n        \"warning\",\n        \"error\",\n        \"log\",\n    ]\n\n    assert \"calling console.time\" in messages[0].text\n    assert list(map(lambda msg: msg.text, messages[1:])) == [\n        \"calling console.trace\",\n        \"calling console.dir\",\n        \"calling console.warn\",\n        \"calling console.error\",\n        \"Promise\",\n    ]\n\n\ndef test_console_should_not_fail_for_window_object(\n    page: Page, browser_name: str\n) -> None:\n    messages = []\n    page.once(\"console\", lambda m: messages.append(m))\n    with page.expect_console_message():\n        page.evaluate(\"() => console.error(window)\")\n    assert len(messages) == 1\n    if browser_name != \"firefox\":\n        assert messages[0].text == \"Window\"\n    else:\n        assert messages[0].text == \"JSHandle@object\"\n\n\n# Upstream issue https://bugs.webkit.org/show_bug.cgi?id=229515\n@pytest.mark.skip_browser(\"webkit\")\ndef test_console_should_trigger_correct_Log(page: Page, server: Server) -> None:\n    page.goto(\"about:blank\")\n    with page.expect_console_message() as message:\n        page.evaluate(\"url => fetch(url).catch(e => {})\", server.EMPTY_PAGE)\n    assert (\n        \"Access-Control-Allow-Origin\" in message.value.text\n        or \"CORS\" in message.value.text\n    )\n    assert message.value.type == \"error\"\n\n\ndef test_console_should_have_location_for_console_api_calls(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    with page.expect_console_message() as message_info:\n        page.goto(server.PREFIX + \"/consolelog.html\")\n    message = message_info.value\n    assert message.text == \"yellow\"\n    assert message.type == \"log\"\n    location = message.location\n    # Engines have different column notion.\n    assert location[\"url\"] == server.PREFIX + \"/consolelog.html\"\n    assert location[\"lineNumber\"] == 7\n\n\ndef test_console_should_not_throw_when_there_are_console_messages_in_detached_iframes(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    with page.expect_popup() as popup:\n        page.evaluate(\n            \"\"\"async() => {\n                // 1. Create a popup that Playwright is not connected to.\n                const win = window.open('');\n                window._popup = win;\n                if (window.document.readyState !== 'complete')\n                new Promise(f => window.addEventListener('load', f));\n                // 2. In this popup, create an iframe that console.logs a message.\n                win.document.body.innerHTML = `<iframe src='/consolelog.html'></iframe>`;\n                const frame = win.document.querySelector('iframe');\n                if (!frame.contentDocument || frame.contentDocument.readyState !== 'complete')\n                new Promise(f => frame.addEventListener('load', f));\n                // 3. After that, remove the iframe.\n                frame.remove();\n            }\"\"\"\n        )\n    # 4. Connect to the popup and make sure it doesn't throw.\n    assert popup.value.evaluate(\"1 + 1\") == 2\n"
  },
  {
    "path": "tests/sync/test_context_manager.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, BrowserType\n\n\ndef test_context_managers(browser_type: BrowserType, launch_arguments: Dict) -> None:\n    with browser_type.launch(**launch_arguments) as browser:\n        with browser.new_context() as context:\n            with context.new_page():\n                assert len(context.pages) == 1\n            assert len(context.pages) == 0\n            assert len(browser.contexts) == 1\n        assert len(browser.contexts) == 0\n    assert not browser.is_connected()\n\n\ndef test_context_managers_not_hang(context: BrowserContext) -> None:\n    with pytest.raises(Exception, match=\"Oops!\"):\n        with context.new_page():\n            raise Exception(\"Oops!\")\n"
  },
  {
    "path": "tests/sync/test_element_handle.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Any, Dict\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Error, Page\nfrom tests.server import Server\nfrom tests.sync.utils import Utils\n\n\ndef test_bounding_box(page: Page, server: Server) -> None:\n    page.set_viewport_size({\"width\": 500, \"height\": 500})\n    page.goto(server.PREFIX + \"/grid.html\")\n    element_handle = page.query_selector(\".box:nth-of-type(13)\")\n    assert element_handle\n    box = element_handle.bounding_box()\n    assert box == {\"x\": 100, \"y\": 50, \"width\": 50, \"height\": 50}\n\n\ndef test_bounding_box_handle_nested_frames(page: Page, server: Server) -> None:\n    page.set_viewport_size({\"width\": 500, \"height\": 500})\n    page.goto(server.PREFIX + \"/frames/nested-frames.html\")\n    nested_frame = page.frame(name=\"dos\")\n    assert nested_frame\n    element_handle = nested_frame.query_selector(\"div\")\n    assert element_handle\n    box = element_handle.bounding_box()\n    assert box == {\"x\": 24, \"y\": 224, \"width\": 268, \"height\": 18}\n\n\ndef test_bounding_box_return_null_for_invisible_elements(page: Page) -> None:\n    page.set_content('<div style=\"display:none\">hi</div>')\n    element = page.query_selector(\"div\")\n    assert element\n    assert element.bounding_box() is None\n\n\ndef test_bounding_box_force_a_layout(page: Page) -> None:\n    page.set_viewport_size({\"width\": 500, \"height\": 500})\n    page.set_content('<div style=\"width: 100px; height: 100px\">hello</div>')\n    element_handle = page.query_selector(\"div\")\n    assert element_handle\n    page.evaluate('element => element.style.height = \"200px\"', element_handle)\n    box = element_handle.bounding_box()\n    assert box == {\"x\": 8, \"y\": 8, \"width\": 100, \"height\": 200}\n\n\ndef test_bounding_box_with_SVG_nodes(page: Page) -> None:\n    page.set_content(\n        \"\"\"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"500\" height=\"500\">\n             <rect id=\"theRect\" x=\"30\" y=\"50\" width=\"200\" height=\"300\"></rect>\n           </svg>\"\"\"\n    )\n    element = page.query_selector(\"#therect\")\n    assert element\n    pw_bounding_box = element.bounding_box()\n    web_bounding_box = page.evaluate(\n        \"\"\"e => {\n            rect = e.getBoundingClientRect()\n            return {x: rect.x, y: rect.y, width: rect.width, height: rect.height}\n        }\"\"\",\n        element,\n    )\n    assert pw_bounding_box == web_bounding_box\n\n\n@pytest.mark.skip_browser(\"firefox\")\ndef test_bounding_box_with_page_scale(browser: Browser, server: Server) -> None:\n    context = browser.new_context(\n        viewport={\"width\": 400, \"height\": 400}, is_mobile=True\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    button.evaluate(\n        \"\"\"button => {\n            document.body.style.margin = '0'\n            button.style.borderWidth = '0'\n            button.style.width = '200px'\n            button.style.height = '20px'\n            button.style.marginLeft = '17px'\n            button.style.marginTop = '23px'\n        }\"\"\"\n    )\n\n    box = button.bounding_box()\n    assert box\n    assert round(box[\"x\"] * 100) == 17 * 100\n    assert round(box[\"y\"] * 100) == 23 * 100\n    assert round(box[\"width\"] * 100) == 200 * 100\n    assert round(box[\"height\"] * 100) == 20 * 100\n    context.close()\n\n\ndef test_bounding_box_when_inline_box_child_is_outside_of_viewport(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n            <style>\n            i {\n            position: absolute\n            top: -1000px\n            }\n            body {\n            margin: 0\n            font-size: 12px\n            }\n            </style>\n            <span><i>woof</i><b>doggo</b></span>\n        \"\"\"\n    )\n    handle = page.query_selector(\"span\")\n    assert handle\n    box = handle.bounding_box()\n    assert handle\n    web_bounding_box = handle.evaluate(\n        \"\"\"e => {\n        rect = e.getBoundingClientRect();\n        return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};\n    }\"\"\"\n    )\n\n    def roundbox(b: Any) -> Dict:\n        return {\n            \"x\": round(b[\"x\"] * 100),\n            \"y\": round(b[\"y\"] * 100),\n            \"width\": round(b[\"width\"] * 100),\n            \"height\": round(b[\"height\"] * 100),\n        }\n\n    assert roundbox(box) == roundbox(web_bounding_box)\n\n\ndef test_content_frame(page: Page, server: Server, utils: Utils) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    element_handle = page.query_selector(\"#frame1\")\n    assert element_handle\n    frame = element_handle.content_frame()\n    assert frame == page.frames[1]\n\n\ndef test_content_frame_for_non_iframes(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = frame.evaluate_handle(\"document.body\").as_element()\n    assert element_handle\n    assert element_handle.content_frame() is None\n\n\ndef test_content_frame_for_document_element(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = frame.evaluate_handle(\"document.documentElement\").as_element()\n    assert element_handle\n    assert element_handle.content_frame() is None\n\n\ndef test_owner_frame(page: Page, server: Server, utils: Utils) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = frame.evaluate_handle(\"document.body\").as_element()\n    assert element_handle\n    assert element_handle.owner_frame() == frame\n\n\ndef test_owner_frame_for_cross_process_iframes(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.CROSS_PROCESS_PREFIX + \"/empty.html\")\n    frame = page.frames[1]\n    element_handle = frame.evaluate_handle(\"document.body\").as_element()\n    assert element_handle\n    assert element_handle.owner_frame() == frame\n\n\ndef test_owner_frame_for_document(page: Page, server: Server, utils: Utils) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.frames[1]\n    element_handle = frame.evaluate_handle(\"document\").as_element()\n    assert element_handle\n    assert element_handle.owner_frame() == frame\n\n\ndef test_owner_frame_for_iframe_elements(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.main_frame\n    element_handle = frame.evaluate_handle(\n        'document.querySelector(\"#frame1\")'\n    ).as_element()\n    assert element_handle\n    assert element_handle.owner_frame() == frame\n\n\ndef test_owner_frame_for_cross_frame_evaluations(\n    page: Page, server: Server, utils: Utils\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    utils.attach_frame(page, \"frame1\", server.EMPTY_PAGE)\n    frame = page.main_frame\n    element_handle = frame.evaluate_handle(\n        'document.querySelector(\"#frame1\").contentWindow.document.body'\n    ).as_element()\n    assert element_handle\n    assert element_handle.owner_frame() == frame.child_frames[0]\n\n\ndef test_owner_frame_for_detached_elements(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    div_handle = page.evaluate_handle(\n        \"\"\"() => {\n            div = document.createElement('div');\n            document.body.appendChild(div);\n            return div;\n        }\"\"\"\n    ).as_element()\n    assert div_handle\n\n    assert div_handle.owner_frame() == page.main_frame\n    page.evaluate(\n        \"\"\"() => {\n            div = document.querySelector('div')\n            document.body.removeChild(div)\n        }\"\"\"\n    )\n    assert div_handle.owner_frame() == page.main_frame\n\n\ndef test_click(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    button.click()\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_click_with_node_removed(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    page.evaluate('delete window[\"Node\"]')\n    button = page.query_selector(\"button\")\n    assert button\n    button.click()\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_click_for_shadow_dom_v1(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/shadow.html\")\n    button_handle = page.evaluate_handle(\"button\").as_element()\n    assert button_handle\n    button_handle.click()\n    assert page.evaluate(\"clicked\")\n\n\ndef test_click_for_TextNodes(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    buttonTextNode = page.evaluate_handle(\n        'document.querySelector(\"button\").firstChild'\n    ).as_element()\n    assert buttonTextNode\n    buttonTextNode.click()\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_click_throw_for_detached_nodes(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    page.evaluate(\"button => button.remove()\", button)\n    with pytest.raises(Error) as exc_info:\n        button.click()\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\ndef test_click_throw_for_hidden_nodes_with_force(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    page.evaluate('button => button.style.display = \"none\"', button)\n    with pytest.raises(Error) as exc_info:\n        button.click(force=True)\n    assert \"Element is not visible\" in exc_info.value.message\n\n\ndef test_click_throw_for_recursively_hidden_nodes_with_force(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    page.evaluate('button => button.parentElement.style.display = \"none\"', button)\n    with pytest.raises(Error) as exc_info:\n        button.click(force=True)\n    assert \"Element is not visible\" in exc_info.value.message\n\n\ndef test_click_throw_for__br__elements_with_force(page: Page) -> None:\n    page.set_content(\"hello<br>goodbye\")\n    br = page.query_selector(\"br\")\n    assert br\n    with pytest.raises(Error) as exc_info:\n        br.click(force=True)\n    assert \"Element is outside of the viewport\" in exc_info.value.message\n\n\ndef test_double_click_the_button(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    page.evaluate(\n        \"\"\"() => {\n            window.double = false;\n            button = document.querySelector('button');\n            button.addEventListener('dblclick', event => {\n            window.double = true;\n            });\n        }\"\"\"\n    )\n    button = page.query_selector(\"button\")\n    assert button\n    button.dblclick()\n    assert page.evaluate(\"double\")\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_hover(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/scrollable.html\")\n    button = page.query_selector(\"#button-6\")\n    assert button\n    button.hover()\n    assert page.evaluate('document.querySelector(\"button:hover\").id') == \"button-6\"\n\n\ndef test_hover_when_node_is_removed(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/scrollable.html\")\n    page.evaluate('delete window[\"Node\"]')\n    button = page.query_selector(\"#button-6\")\n    assert button\n    button.hover()\n    assert page.evaluate('document.querySelector(\"button:hover\").id') == \"button-6\"\n\n\ndef test_scroll(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/offscreenbuttons.html\")\n    for i in range(11):\n        button = page.query_selector(f\"#btn{i}\")\n        assert button\n        before = button.evaluate(\n            \"\"\"button => {\n                return button.getBoundingClientRect().right - window.innerWidth\n            }\"\"\"\n        )\n\n        assert before == 10 * i\n        button.scroll_into_view_if_needed()\n        after = button.evaluate(\n            \"\"\"button => {\n                return button.getBoundingClientRect().right - window.innerWidth\n            }\"\"\"\n        )\n\n        assert after <= 0\n        page.evaluate(\"() => window.scrollTo(0, 0)\")\n\n\ndef test_scroll_should_throw_for_detached_element(page: Page) -> None:\n    page.set_content(\"<div>Hello</div>\")\n    div = page.query_selector(\"div\")\n    assert div\n    div.evaluate(\"div => div.remove()\")\n    with pytest.raises(Error) as exc_info:\n        div.scroll_into_view_if_needed()\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\ndef test_should_timeout_waiting_for_visible(page: Page) -> None:\n    page.set_content('<div style=\"display:none\">Hello</div>')\n    div = page.query_selector(\"div\")\n    assert div\n    with pytest.raises(Error) as exc_info:\n        div.scroll_into_view_if_needed(timeout=3000)\n    assert \"element is not visible\" in exc_info.value.message\n    assert \"retrying scroll into view action\" in exc_info.value.message\n\n\ndef test_fill_input(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    handle = page.query_selector(\"input\")\n    assert handle\n    handle.fill(\"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n\n\ndef test_fill_input_when_Node_is_removed(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    page.evaluate('delete window[\"Node\"]')\n    handle = page.query_selector(\"input\")\n    assert handle\n    handle.fill(\"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n\n\ndef test_select_textarea(\n    page: Page, server: Server, is_firefox: bool, is_webkit: bool\n) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = page.query_selector(\"textarea\")\n    assert textarea\n    textarea.evaluate('textarea => textarea.value = \"some value\"')\n    textarea.select_text()\n    if is_firefox or is_webkit:\n        assert textarea.evaluate(\"el => el.selectionStart\") == 0\n        assert textarea.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert page.evaluate(\"() => window.getSelection().toString()\") == \"some value\"\n\n\ndef test_select_input(\n    page: Page, server: Server, is_firefox: bool, is_webkit: bool\n) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    input = page.query_selector(\"input\")\n    assert input\n    input.evaluate('input => input.value = \"some value\"')\n    input.select_text()\n    if is_firefox or is_webkit:\n        assert input.evaluate(\"el => el.selectionStart\") == 0\n        assert input.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert page.evaluate(\"() => window.getSelection().toString()\") == \"some value\"\n\n\ndef test_select_text_select_plain_div(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    div = page.query_selector(\"div.plain\")\n    assert div\n    div.select_text()\n    assert page.evaluate(\"() => window.getSelection().toString()\") == \"Plain div\"\n\n\ndef test_select_text_timeout_waiting_for_invisible_element(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = page.query_selector(\"textarea\")\n    assert textarea\n    textarea.evaluate('e => e.style.display = \"none\"')\n    with pytest.raises(Error) as exc_info:\n        textarea.select_text(timeout=3000)\n    assert \"element is not visible\" in exc_info.value.message\n\n\ndef test_a_nice_preview(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/dom.html\")\n    outer = page.query_selector(\"#outer\")\n    inner = page.query_selector(\"#inner\")\n    assert inner\n    check = page.query_selector(\"#check\")\n    text = inner.evaluate_handle(\"e => e.firstChild\")\n    page.evaluate(\"1\")  # Give them a chance to calculate the preview.\n    assert str(outer) == 'JSHandle@<div id=\"outer\" name=\"value\">…</div>'\n    assert str(inner) == 'JSHandle@<div id=\"inner\">Text,↵more text</div>'\n    assert str(text) == \"JSHandle@#text=Text,↵more text\"\n    assert (\n        str(check) == 'JSHandle@<input checked id=\"check\" foo=\"bar\"\" type=\"checkbox\"/>'\n    )\n\n\ndef test_get_attribute(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = page.query_selector(\"#outer\")\n    assert handle\n    assert handle.get_attribute(\"name\") == \"value\"\n    assert page.get_attribute(\"#outer\", \"name\") == \"value\"\n\n\ndef test_inner_html(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = page.query_selector(\"#outer\")\n    assert handle\n    assert handle.inner_html() == '<div id=\"inner\">Text,\\nmore text</div>'\n    assert page.inner_html(\"#outer\") == '<div id=\"inner\">Text,\\nmore text</div>'\n\n\ndef test_inner_text(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = page.query_selector(\"#inner\")\n    assert handle\n    assert handle.inner_text() == \"Text, more text\"\n    assert page.inner_text(\"#inner\") == \"Text, more text\"\n\n\ndef test_inner_text_should_throw(page: Page) -> None:\n    page.set_content(\"<svg>text</svg>\")\n    with pytest.raises(Error) as exc_info1:\n        page.inner_text(\"svg\")\n    assert \" Node is not an HTMLElement\" in exc_info1.value.message\n\n    handle = page.query_selector(\"svg\")\n    assert handle\n    with pytest.raises(Error) as exc_info2:\n        handle.inner_text()\n    assert \" Node is not an HTMLElement\" in exc_info2.value.message\n\n\ndef test_text_content(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/dom.html\")\n    handle = page.query_selector(\"#inner\")\n    assert handle\n    assert handle.text_content() == \"Text,\\nmore text\"\n    assert page.text_content(\"#inner\") == \"Text,\\nmore text\"\n\n\ndef test_check_the_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\"></input>')\n    input = page.query_selector(\"input\")\n    assert input\n    input.check()\n    assert page.evaluate(\"checkbox.checked\")\n\n\ndef test_uncheck_the_box(page: Page) -> None:\n    page.set_content('<input id=\"checkbox\" type=\"checkbox\" checked></input>')\n    input = page.query_selector(\"input\")\n    assert input\n    input.uncheck()\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_select_single_option(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    select = page.query_selector(\"select\")\n    assert select\n    select.select_option(value=\"blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_focus_a_button(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.query_selector(\"button\")\n    assert button\n    assert button.evaluate(\"button => document.activeElement === button\") is False\n    button.focus()\n    assert button.evaluate(\"button => document.activeElement === button\")\n\n\ndef test_is_visible_and_is_hidden_should_work(page: Page) -> None:\n    page.set_content(\"<div>Hi</div><span></span>\")\n    div = page.query_selector(\"div\")\n    assert div\n    assert div.is_visible()\n    assert div.is_hidden() is False\n    assert page.is_visible(\"div\")\n    assert page.is_hidden(\"div\") is False\n    span = page.query_selector(\"span\")\n    assert span\n    assert span.is_visible() is False\n    assert span.is_hidden()\n    assert page.is_visible(\"span\") is False\n    assert page.is_hidden(\"span\")\n\n\ndef test_is_enabled_and_is_disabled_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <button disabled>button1</button>\n        <button>button2</button>\n        <div>div</div>\n    \"\"\"\n    )\n    div = page.query_selector(\"div\")\n    assert div\n    assert div.is_enabled()\n    assert div.is_disabled() is False\n    assert page.is_enabled(\"div\")\n    assert page.is_disabled(\"div\") is False\n    button1 = page.query_selector(\":text('button1')\")\n    assert button1\n    assert button1.is_enabled() is False\n    assert button1.is_disabled()\n    assert page.is_enabled(\":text('button1')\") is False\n    assert page.is_disabled(\":text('button1')\")\n    button2 = page.query_selector(\":text('button2')\")\n    assert button2\n    assert button2.is_enabled()\n    assert button2.is_disabled() is False\n    assert page.is_enabled(\":text('button2')\")\n    assert page.is_disabled(\":text('button2')\") is False\n\n\ndef test_is_editable_should_work(page: Page) -> None:\n    page.set_content(\"<input id=input1 disabled><textarea></textarea><input id=input2>\")\n    page.eval_on_selector(\"textarea\", \"t => t.readOnly = true\")\n    input1 = page.query_selector(\"#input1\")\n    assert input1\n    assert input1.is_editable() is False\n    assert page.is_editable(\"#input1\") is False\n    input2 = page.query_selector(\"#input2\")\n    assert input2\n    assert input2.is_editable()\n    assert page.is_editable(\"#input2\")\n    textarea = page.query_selector(\"textarea\")\n    assert textarea\n    assert textarea.is_editable() is False\n    assert page.is_editable(\"textarea\") is False\n\n\ndef test_is_checked_should_work(page: Page) -> None:\n    page.set_content('<input type=\"checkbox\" checked><div>Not a checkbox</div>')\n    handle = page.query_selector(\"input\")\n    assert handle\n    assert handle.is_checked()\n    assert page.is_checked(\"input\")\n    handle.evaluate(\"input => input.checked = false\")\n    assert handle.is_checked() is False\n    assert page.is_checked(\"input\") is False\n    with pytest.raises(Error) as exc_info:\n        page.is_checked(\"div\")\n    assert \"Not a checkbox or radio button\" in exc_info.value.message\n\n\ndef test_input_value(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    element = page.query_selector(\"input\")\n    assert element\n    element.fill(\"my-text-content\")\n    assert element.input_value() == \"my-text-content\"\n\n    element.fill(\"\")\n    assert element.input_value() == \"\"\n\n\ndef test_set_checked(page: Page) -> None:\n    page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    input = page.query_selector(\"input\")\n    assert input\n    input.set_checked(True)\n    assert page.evaluate(\"checkbox.checked\")\n    input.set_checked(False)\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_should_allow_disposing_twice(page: Page) -> None:\n    page.set_content(\"<section>39</section>\")\n    element = page.query_selector(\"section\")\n    assert element\n    element.dispose()\n    element.dispose()\n"
  },
  {
    "path": "tests/sync/test_element_handle_wait_for_element_state.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page\n\n\ndef test_should_wait_for_visible(page: Page) -> None:\n    page.set_content('<div id=div style=\"display:none\">content</div>')\n    div = page.query_selector(\"div\")\n    assert div\n    page.evaluate('setTimeout(() => div.style.display = \"block\", 500)')\n    assert div.is_visible() is False\n    div.wait_for_element_state(\"visible\")\n    assert div.is_visible()\n\n\ndef test_should_wait_for_already_visible(page: Page) -> None:\n    page.set_content(\"<div>content</div>\")\n    div = page.query_selector(\"div\")\n    assert div\n    div.wait_for_element_state(\"visible\")\n\n\ndef test_should_timeout_waiting_for_visible(page: Page) -> None:\n    page.set_content('<div style=\"display:none\">content</div>')\n    div = page.query_selector(\"div\")\n    assert div\n    with pytest.raises(Error) as exc_info:\n        div.wait_for_element_state(\"visible\", timeout=1000)\n    assert \"Timeout 1000ms exceeded\" in exc_info.value.message\n\n\ndef test_should_throw_waiting_for_visible_when_detached(page: Page) -> None:\n    page.set_content('<div id=div style=\"display:none\">content</div>')\n    div = page.query_selector(\"div\")\n    assert div\n    page.evaluate(\"setTimeout(() => div.remove(), 500)\")\n    with pytest.raises(Error) as exc_info:\n        div.wait_for_element_state(\"visible\")\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\ndef test_should_wait_for_hidden(page: Page) -> None:\n    page.set_content(\"<div id=div>content</div>\")\n    div = page.query_selector(\"div\")\n    assert div\n    page.evaluate('setTimeout(() => div.style.display = \"none\", 500)')\n    assert div.is_hidden() is False\n    div.wait_for_element_state(\"hidden\")\n    assert div.is_hidden()\n\n\ndef test_should_wait_for_already_hidden(page: Page) -> None:\n    page.set_content(\"<div></div>\")\n    div = page.query_selector(\"div\")\n    assert div\n    div.wait_for_element_state(\"hidden\")\n\n\ndef test_should_wait_for_hidden_when_detached(page: Page) -> None:\n    page.set_content(\"<div id=div>content</div>\")\n    div = page.query_selector(\"div\")\n    assert div\n    page.evaluate(\"setTimeout(() => div.remove(), 500)\")\n    div.wait_for_element_state(\"hidden\")\n    assert div.is_hidden()\n\n\ndef test_should_wait_for_enabled_button(page: Page) -> None:\n    page.set_content(\"<button id=button disabled><span>Target</span></button>\")\n    span = page.query_selector(\"text=Target\")\n    assert span\n    assert span.is_enabled() is False\n    page.evaluate(\"setTimeout(() => button.disabled = false, 500)\")\n    span.wait_for_element_state(\"enabled\")\n    assert span.is_enabled()\n\n\ndef test_should_throw_waiting_for_enabled_when_detached(page: Page) -> None:\n    page.set_content(\"<button id=button disabled>Target</button>\")\n    button = page.query_selector(\"button\")\n    assert button\n    page.evaluate(\"setTimeout(() => button.remove(), 500)\")\n    with pytest.raises(Error) as exc_info:\n        button.wait_for_element_state(\"enabled\")\n    assert \"Element is not attached to the DOM\" in exc_info.value.message\n\n\ndef test_should_wait_for_disabled_button(page: Page) -> None:\n    page.set_content(\"<button id=button><span>Target</span></button>\")\n    span = page.query_selector(\"text=Target\")\n    assert span\n    assert span.is_disabled() is False\n    page.evaluate(\"setTimeout(() => button.disabled = true, 500)\")\n    span.wait_for_element_state(\"disabled\")\n    assert span.is_disabled()\n\n\ndef test_should_wait_for_editable_input(page: Page) -> None:\n    page.set_content(\"<input id=input readonly>\")\n    input = page.query_selector(\"input\")\n    assert input\n    page.evaluate(\"setTimeout(() => input.readOnly = false, 500)\")\n    assert input.is_editable() is False\n    input.wait_for_element_state(\"editable\")\n    assert input.is_editable()\n"
  },
  {
    "path": "tests/sync/test_expect_misc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Page, expect\nfrom tests.server import Server\n\n\ndef test_to_be_in_viewport_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n      <div id=big style=\"height: 10000px;\"></div>\n      <div id=small>foo</div>\n    \"\"\"\n    )\n    expect(page.locator(\"#big\")).to_be_in_viewport()\n    expect(page.locator(\"#small\")).not_to_be_in_viewport()\n    page.locator(\"#small\").scroll_into_view_if_needed()\n    expect(page.locator(\"#small\")).to_be_in_viewport()\n    expect(page.locator(\"#small\")).to_be_in_viewport(ratio=1)\n\n\ndef test_to_be_in_viewport_should_respect_ratio_option(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n      <style>body, div, html { padding: 0; margin: 0; }</style>\n      <div id=big style=\"height: 400vh;\"></div>\n    \"\"\"\n    )\n    expect(page.locator(\"div\")).to_be_in_viewport()\n    expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.1)\n    expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.2)\n\n    expect(page.locator(\"div\")).to_be_in_viewport(ratio=0.25)\n    # In this test, element's ratio is 0.25.\n    expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.26)\n\n    expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.3)\n    expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.7)\n    expect(page.locator(\"div\")).not_to_be_in_viewport(ratio=0.8)\n\n\ndef test_to_be_in_viewport_should_have_good_stack(page: Page, server: Server) -> None:\n    with pytest.raises(AssertionError) as exc_info:\n        expect(page.locator(\"body\")).not_to_be_in_viewport(timeout=1000)\n    assert 'unexpected value \"viewport ratio' in str(exc_info.value)\n\n\ndef test_to_be_in_viewport_should_report_intersection_even_if_fully_covered_by_other_element(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n      <h1>hello</h1>\n      <div style=\"position: relative; height: 10000px; top: -5000px;></div>\n    \"\"\"\n    )\n    expect(page.locator(\"h1\")).to_be_in_viewport()\n"
  },
  {
    "path": "tests/sync/test_extension.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\nfrom typing import Any, Callable, Dict, Generator, List, Optional\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, BrowserType\n\n\n@pytest.fixture()\ndef launch_persistent_context(\n    browser_type: BrowserType,\n    browser_channel: Optional[str],\n    tmp_path: Path,\n    launch_arguments: Dict[str, Any],\n    is_headless_shell: bool,\n) -> Generator[Callable[..., BrowserContext], None, None]:\n    if browser_channel and browser_channel.startswith(\"chrome\"):\n        pytest.skip(\n            \"--load-extension is not supported in Chrome anymore. https://groups.google.com/a/chromium.org/g/chromium-extensions/c/1-g8EFx2BBY/m/S0ET5wPjCAAJ\"\n        )\n    if is_headless_shell:\n        pytest.skip(\"Headless Shell has no support for extensions\")\n\n    contexts: List[BrowserContext] = []\n\n    def launch(extension_path: str, **kwargs: Any) -> BrowserContext:\n        context = browser_type.launch_persistent_context(\n            str(tmp_path),\n            **launch_arguments,\n            **kwargs,\n            args=[\n                f\"--disable-extensions-except={extension_path}\",\n                f\"--load-extension={extension_path}\",\n            ],\n        )\n        contexts.append(context)\n        return context\n\n    yield launch\n\n    for context in contexts:\n        context.close()\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_give_access_to_the_service_worker(\n    launch_persistent_context: Callable[..., BrowserContext],\n    assetdir: Path,\n) -> None:\n    extension_path = str(assetdir / \"extension-mv3-simple\")\n    context = launch_persistent_context(extension_path)\n    service_workers = context.service_workers\n    service_worker = (\n        service_workers[0]\n        if len(service_workers)\n        else context.wait_for_event(\"serviceworker\")\n    )\n    assert service_worker\n    assert service_worker in context.service_workers\n    while not service_worker.evaluate(\"globalThis.MAGIC\") == 42:\n        context.pages[0].wait_for_timeout(100)\n    context.close()\n    assert len(context.background_pages) == 0\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_give_access_to_the_service_worker_when_recording_video(\n    launch_persistent_context: Callable[..., BrowserContext],\n    tmp_path: Path,\n    assetdir: Path,\n) -> None:\n    extension_path = str(assetdir / \"extension-mv3-simple\")\n    context = launch_persistent_context(\n        extension_path, record_video_dir=(tmp_path / \"videos\")\n    )\n    service_workers = context.service_workers\n    service_worker = (\n        service_workers[0]\n        if len(service_workers)\n        else context.wait_for_event(\"serviceworker\")\n    )\n    assert service_worker\n    assert service_worker in context.service_workers\n    while not service_worker.evaluate(\"globalThis.MAGIC\") == 42:\n        context.pages[0].wait_for_timeout(100)\n    context.close()\n    assert len(context.background_pages) == 0\n"
  },
  {
    "path": "tests/sync/test_fetch_browser_context.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nfrom typing import Any, Dict, List\nfrom urllib.parse import parse_qs\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, Error, FilePayload, Page\nfrom tests.server import Server\nfrom tests.utils import must\n\n\ndef test_get_should_work(context: BrowserContext, server: Server) -> None:\n    response = context.request.get(server.PREFIX + \"/simple.json\")\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert response.text() == '{\"foo\": \"bar\"}\\n'\n\n\ndef test_fetch_should_work(context: BrowserContext, server: Server) -> None:\n    response = context.request.fetch(server.PREFIX + \"/simple.json\")\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert response.text() == '{\"foo\": \"bar\"}\\n'\n\n\ndef test_should_throw_on_network_error(context: BrowserContext, server: Server) -> None:\n    server.set_route(\"/test\", lambda request: request.loseConnection())\n    with pytest.raises(Error, match=\"socket hang up\"):\n        context.request.fetch(server.PREFIX + \"/test\")\n\n\ndef test_should_add_session_cookies_to_request(\n    context: BrowserContext, server: Server\n) -> None:\n    context.add_cookies(\n        [\n            {\n                \"name\": \"username\",\n                \"value\": \"John Doe\",\n                \"url\": server.EMPTY_PAGE,\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ]\n    )\n    with server.expect_request(\"/empty.html\") as server_req:\n        context.request.get(server.EMPTY_PAGE)\n    assert server_req.value.getHeader(\"Cookie\") == \"username=John Doe\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_support_query_params(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    expected_params = {\"p1\": \"v1\", \"парам2\": \"знач2\"}\n    with server.expect_request(\"/empty.html\") as server_req:\n        getattr(context.request, method)(\n            server.EMPTY_PAGE + \"?p1=foo\", params=expected_params\n        )\n    assert list(map(lambda x: x.decode(), server_req.value.args[\"p1\".encode()])) == [\n        \"foo\",\n        \"v1\",\n    ]\n    assert server_req.value.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_support_params_passed_as_object(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    params = {\n        \"param1\": \"value1\",\n        \"парам2\": \"знач2\",\n    }\n    with server.expect_request(\"/empty.html\") as server_req:\n        getattr(context.request, method)(server.EMPTY_PAGE, params=params)\n    assert server_req.value.args[\"param1\".encode()][0].decode() == \"value1\"\n    assert len(server_req.value.args[\"param1\".encode()]) == 1\n    assert server_req.value.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_support_params_passed_as_strings(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    params = \"?param1=value1&param1=value2&парам2=знач2\"\n    with server.expect_request(\"/empty.html\") as server_req:\n        getattr(context.request, method)(server.EMPTY_PAGE, params=params)\n    assert list(\n        map(lambda x: x.decode(), server_req.value.args[\"param1\".encode()])\n    ) == [\"value1\", \"value2\"]\n    assert len(server_req.value.args[\"param1\".encode()]) == 2\n    assert server_req.value.args[\"парам2\".encode()][0].decode() == \"знач2\"\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_support_fail_on_status_code(\n    context: BrowserContext, server: Server, method: str\n) -> None:\n    with pytest.raises(Error, match=\"404 Not Found\"):\n        getattr(context.request, method)(\n            server.PREFIX + \"/this-does-clearly-not-exist.html\",\n            fail_on_status_code=True,\n        )\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_support_ignore_https_errors_option(\n    context: BrowserContext, https_server: Server, method: str\n) -> None:\n    response = getattr(context.request, method)(\n        https_server.EMPTY_PAGE, ignore_https_errors=True\n    )\n    assert response.ok\n    assert response.status == 200\n\n\ndef test_should_not_add_context_cookie_if_cookie_header_passed_as_parameter(\n    context: BrowserContext, server: Server\n) -> None:\n    context.add_cookies(\n        [\n            {\n                \"name\": \"username\",\n                \"value\": \"John Doe\",\n                \"url\": server.EMPTY_PAGE,\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ]\n    )\n    with server.expect_request(\"/empty.html\") as server_req:\n        context.request.get(server.EMPTY_PAGE, headers={\"Cookie\": \"foo=bar\"})\n    assert server_req.value.getHeader(\"Cookie\") == \"foo=bar\"\n\n\n@pytest.mark.parametrize(\"method\", [\"delete\", \"patch\", \"post\", \"put\"])\ndef test_should_support_post_data(\n    context: BrowserContext, method: str, server: Server\n) -> None:\n    def support_post_data(fetch_data: Any, request_post_data: Any) -> None:\n        with server.expect_request(\"/simple.json\") as request:\n            response = getattr(context.request, method)(\n                server.PREFIX + \"/simple.json\", data=fetch_data\n            )\n        assert request.value.method.decode() == method.upper()\n        assert request.value.post_body == request_post_data\n        assert response.status == 200\n        assert response.url == server.PREFIX + \"/simple.json\"\n        assert request.value.getHeader(\"Content-Length\") == str(\n            len(must(request.value.post_body))\n        )\n\n    support_post_data(\"My request\", \"My request\".encode())\n    support_post_data(b\"My request\", \"My request\".encode())\n    support_post_data([\"my\", \"request\"], json.dumps([\"my\", \"request\"]).encode())\n    support_post_data({\"my\": \"request\"}, json.dumps({\"my\": \"request\"}).encode())\n    with pytest.raises(Error, match=\"Unsupported 'data' type: <class 'function'>\"):\n        support_post_data(lambda: None, None)\n\n\ndef test_should_support_application_x_www_form_urlencoded(\n    context: BrowserContext, server: Server\n) -> None:\n    with server.expect_request(\"/empty.html\") as server_req:\n        context.request.post(\n            server.PREFIX + \"/empty.html\",\n            form={\n                \"firstName\": \"John\",\n                \"lastName\": \"Doe\",\n                \"file\": \"f.js\",\n            },\n        )\n    assert server_req.value.method == b\"POST\"\n    assert (\n        server_req.value.getHeader(\"Content-Type\")\n        == \"application/x-www-form-urlencoded\"\n    )\n    body = must(server_req.value.post_body).decode()\n    assert server_req.value.getHeader(\"Content-Length\") == str(len(body))\n    params: Dict[bytes, List[bytes]] = parse_qs(server_req.value.post_body)\n    assert params[b\"firstName\"] == [b\"John\"]\n    assert params[b\"lastName\"] == [b\"Doe\"]\n    assert params[b\"file\"] == [b\"f.js\"]\n\n\ndef test_should_support_multipart_form_data(\n    context: BrowserContext, server: Server\n) -> None:\n    file: FilePayload = {\n        \"name\": \"f.js\",\n        \"mimeType\": \"text/javascript\",\n        \"buffer\": b\"var x = 10;\\r\\n;console.log(x);\",\n    }\n    with server.expect_request(\"/empty.html\") as server_req:\n        context.request.post(\n            server.PREFIX + \"/empty.html\",\n            multipart={\n                \"firstName\": \"John\",\n                \"lastName\": \"Doe\",\n                \"file\": file,\n            },\n        )\n    assert server_req.value.method == b\"POST\"\n    content_type = server_req.value.getHeader(\"Content-Type\")\n    assert content_type\n    assert content_type.startswith(\"multipart/form-data; \")\n    assert server_req.value.getHeader(\"Content-Length\") == str(\n        len(must(server_req.value.post_body))\n    )\n    assert server_req.value.args[b\"firstName\"] == [b\"John\"]\n    assert server_req.value.args[b\"lastName\"] == [b\"Doe\"]\n    assert server_req.value.args[b\"file\"][0] == file[\"buffer\"]\n\n\ndef test_should_add_default_headers(\n    context: BrowserContext, page: Page, server: Server\n) -> None:\n    with server.expect_request(\"/empty.html\") as server_req:\n        context.request.get(server.EMPTY_PAGE)\n    assert server_req.value.getHeader(\"Accept\") == \"*/*\"\n    assert server_req.value.getHeader(\"Accept-Encoding\") == \"gzip,deflate,br\"\n    assert server_req.value.getHeader(\"User-Agent\") == page.evaluate(\n        \"() => navigator.userAgent\"\n    )\n"
  },
  {
    "path": "tests/sync/test_fetch_global.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nfrom pathlib import Path\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom playwright.sync_api import APIResponse, Error, Playwright, StorageState\nfrom tests.server import Server, TestServerRequest\n\n\n@pytest.mark.parametrize(\n    \"method\", [\"fetch\", \"delete\", \"get\", \"head\", \"patch\", \"post\", \"put\"]\n)\ndef test_should_work(playwright: Playwright, method: str, server: Server) -> None:\n    request = playwright.request.new_context()\n    response: APIResponse = getattr(request, method)(server.PREFIX + \"/simple.json\")\n    assert response.status == 200\n    assert response.status_text == \"OK\"\n    assert response.ok is True\n    assert response.url == server.PREFIX + \"/simple.json\"\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert {\n        \"name\": \"Content-Type\",\n        \"value\": \"application/json\",\n    } in response.headers_array\n    assert response.text() == (\"\" if method == \"head\" else '{\"foo\": \"bar\"}\\n')\n\n\ndef test_should_dispose_global_request(playwright: Playwright, server: Server) -> None:\n    request = playwright.request.new_context()\n    response = request.get(server.PREFIX + \"/simple.json\")\n    assert response.json() == {\"foo\": \"bar\"}\n    response.dispose()\n    with pytest.raises(Error, match=\"Response has been disposed\"):\n        response.body()\n\n\ndef test_should_support_global_user_agent_option(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context(user_agent=\"My Agent\")\n    response = request.get(server.PREFIX + \"/empty.html\")\n    with server.expect_request(\"/empty.html\") as server_req:\n        request.get(server.EMPTY_PAGE)\n    assert response.ok is True\n    assert response.url == server.EMPTY_PAGE\n\n    assert server_req.value.getHeader(\"user-agent\") == \"My Agent\"\n\n\ndef test_should_support_global_timeout_option(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context(timeout=100)\n    server.set_route(\"/empty.html\", lambda req: None)\n    with pytest.raises(Error, match=\"Timeout 100ms exceeded\"):\n        request.get(server.EMPTY_PAGE)\n\n\ndef test_should_support_timeout_option_in_get_method(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context()\n    server.set_route(\"/empty.html\", lambda req: None)\n    with pytest.raises(Error, match=\"APIRequestContext.get: Timeout 123ms exceeded.\"):\n        request.get(server.EMPTY_PAGE, timeout=123)\n\n\ndef test_should_propagate_extra_http_headers_with_redirects(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/simple.json\")\n    request = playwright.request.new_context(extra_http_headers={\"My-Secret\": \"Value\"})\n    with server.expect_request(\"/a/redirect1\") as server_req1:\n        with server.expect_request(\"/b/c/redirect2\") as server_req2:\n            with server.expect_request(\"/simple.json\") as server_req3:\n                request.get(f\"{server.PREFIX}/a/redirect1\")\n    assert server_req1.value.getHeader(\"my-secret\") == \"Value\"\n    assert server_req2.value.getHeader(\"my-secret\") == \"Value\"\n    assert server_req3.value.getHeader(\"my-secret\") == \"Value\"\n\n\ndef test_should_support_global_http_credentials_option(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request1 = playwright.request.new_context()\n    response1 = request1.get(server.EMPTY_PAGE)\n    assert response1.status == 401\n    response1.dispose()\n\n    request2 = playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\"}\n    )\n    response2 = request2.get(server.EMPTY_PAGE)\n    assert response2.status == 200\n    assert response2.ok is True\n    response2.dispose()\n\n\ndef test_should_return_error_with_wrong_credentials(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"wrong\"}\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    assert response.ok is False\n\n\ndef test_should_work_with_correct_credentials_and_matching_origin(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX,\n        }\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 200\n    response.dispose()\n\n\ndef test_should_work_with_correct_credentials_and_matching_origin_case_insensitive(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.upper(),\n        }\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 200\n    response.dispose()\n\n\ndef test_should_return_error_with_correct_credentials_and_mismatching_scheme(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    request = playwright.request.new_context(\n        http_credentials={\n            \"username\": \"user\",\n            \"password\": \"pass\",\n            \"origin\": server.PREFIX.replace(\"http://\", \"https://\"),\n        }\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    response.dispose()\n\n\ndef test_should_return_error_with_correct_credentials_and_mismatching_hostname(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    hostname = urlparse(server.PREFIX).hostname\n    assert hostname\n    origin = server.PREFIX.replace(hostname, \"mismatching-hostname\")\n    request = playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    response.dispose()\n\n\ndef test_should_return_error_with_correct_credentials_and_mismatching_port(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_auth(\"/empty.html\", \"user\", \"pass\")\n    origin = server.PREFIX.replace(str(server.PORT), str(server.PORT + 1))\n    request = playwright.request.new_context(\n        http_credentials={\"username\": \"user\", \"password\": \"pass\", \"origin\": origin}\n    )\n    response = request.get(server.EMPTY_PAGE)\n    assert response.status == 401\n    response.dispose()\n\n\ndef test_should_support_global_ignore_https_errors_option(\n    playwright: Playwright, https_server: Server\n) -> None:\n    request = playwright.request.new_context(ignore_https_errors=True)\n    response = request.get(https_server.EMPTY_PAGE)\n    assert response.status == 200\n    assert response.ok is True\n    assert response.url == https_server.EMPTY_PAGE\n    response.dispose()\n\n\ndef test_should_resolve_url_relative_to_global_base_url_option(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context(base_url=server.PREFIX)\n    response = request.get(\"/empty.html\")\n    assert response.status == 200\n    assert response.ok is True\n    assert response.url == server.EMPTY_PAGE\n    response.dispose()\n\n\ndef test_should_use_playwright_as_a_user_agent(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context()\n    with server.expect_request(\"/empty.html\") as server_req:\n        request.get(server.EMPTY_PAGE)\n    assert str(server_req.value.getHeader(\"User-Agent\")).startswith(\"Playwright/\")\n    request.dispose()\n\n\ndef test_should_return_empty_body(playwright: Playwright, server: Server) -> None:\n    request = playwright.request.new_context()\n    response = request.get(server.EMPTY_PAGE)\n    body = response.body()\n    assert len(body) == 0\n    assert response.text() == \"\"\n    request.dispose()\n    with pytest.raises(Error, match=\"Response has been disposed\"):\n        response.body()\n\n\ndef test_storage_state_should_round_trip_through_file(\n    playwright: Playwright, tmp_path: Path\n) -> None:\n    expected: StorageState = {\n        \"cookies\": [\n            {\n                \"name\": \"a\",\n                \"value\": \"b\",\n                \"domain\": \"a.b.one.com\",\n                \"path\": \"/\",\n                \"expires\": -1,\n                \"httpOnly\": False,\n                \"secure\": False,\n                \"sameSite\": \"Lax\",\n            }\n        ],\n        \"origins\": [],\n    }\n    request = playwright.request.new_context(storage_state=expected)\n    path = tmp_path / \"storage-state.json\"\n    actual = request.storage_state(path=path)\n    assert actual == expected\n\n    written = path.read_text(\"utf8\")\n    assert json.loads(written) == expected\n\n    request2 = playwright.request.new_context(storage_state=path)\n    state2 = request2.storage_state()\n    assert state2 == expected\n\n\ndef test_should_throw_an_error_when_max_redirects_is_exceeded(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/b/c/redirect3\")\n    server.set_redirect(\"/b/c/redirect3\", \"/b/c/redirect4\")\n    server.set_redirect(\"/b/c/redirect4\", \"/simple.json\")\n\n    request = playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        for max_redirects in [1, 2, 3]:\n            with pytest.raises(Error) as exc_info:\n                request.fetch(\n                    server.PREFIX + \"/a/redirect1\",\n                    method=method,\n                    max_redirects=max_redirects,\n                )\n            assert \"Max redirect count exceeded\" in str(exc_info)\n\n\ndef test_should_not_follow_redirects_when_max_redirects_is_set_to_0(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_redirect(\"/a/redirect1\", \"/b/c/redirect2\")\n    server.set_redirect(\"/b/c/redirect2\", \"/simple.json\")\n\n    request = playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        response = request.fetch(\n            server.PREFIX + \"/a/redirect1\", method=method, max_redirects=0\n        )\n        assert response.headers[\"location\"] == \"/b/c/redirect2\"\n        assert response.status == 302\n\n\ndef test_should_throw_an_error_when_max_redirects_is_less_than_0(\n    playwright: Playwright,\n    server: Server,\n) -> None:\n    request = playwright.request.new_context()\n    for method in [\"GET\", \"PUT\", \"POST\", \"OPTIONS\", \"HEAD\", \"PATCH\"]:\n        with pytest.raises(AssertionError) as exc_info:\n            request.fetch(\n                server.PREFIX + \"/a/redirect1\", method=method, max_redirects=-1\n            )\n        assert \"'max_redirects' must be greater than or equal to '0'\" in str(exc_info)\n\n\ndef test_should_serialize_null_values_in_json(\n    playwright: Playwright, server: Server\n) -> None:\n    request = playwright.request.new_context()\n    server.set_route(\"/echo\", lambda req: (req.write(req.post_body), req.finish()))\n    response = request.post(server.PREFIX + \"/echo\", data={\"foo\": None})\n    assert response.status == 200\n    assert response.text() == '{\"foo\": null}'\n    request.dispose()\n\n\ndef test_should_throw_when_fail_on_status_code_is_true(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_route(\n        \"/empty.html\",\n        lambda req: (\n            req.setResponseCode(404),\n            req.setHeader(\"Content-Length\", \"10\"),\n            req.setHeader(\"Content-Type\", \"text/plain\"),\n            req.write(b\"Not found.\"),\n            req.finish(),\n        ),\n    )\n    request = playwright.request.new_context(fail_on_status_code=True)\n    with pytest.raises(Error, match=\"404 Not Found\"):\n        request.fetch(server.EMPTY_PAGE)\n    request.dispose()\n\n\ndef test_should_not_throw_when_fail_on_status_code_is_false(\n    playwright: Playwright, server: Server\n) -> None:\n    server.set_route(\n        \"/empty.html\",\n        lambda req: (\n            req.setResponseCode(404),\n            req.setHeader(\"Content-Length\", \"10\"),\n            req.setHeader(\"Content-Type\", \"text/plain\"),\n            req.write(b\"Not found.\"),\n            req.finish(),\n        ),\n    )\n    request = playwright.request.new_context(fail_on_status_code=False)\n    response = request.fetch(server.EMPTY_PAGE)\n    assert response.status == 404\n    request.dispose()\n\n\ndef test_should_follow_max_redirects(playwright: Playwright, server: Server) -> None:\n    redirect_count = 0\n\n    def _handle_request(req: TestServerRequest) -> None:\n        nonlocal redirect_count\n        redirect_count += 1\n        req.setResponseCode(301)\n        req.setHeader(\"Location\", server.EMPTY_PAGE)\n        req.finish()\n\n    server.set_route(\"/empty.html\", _handle_request)\n    request = playwright.request.new_context(max_redirects=1)\n    with pytest.raises(Error, match=\"Max redirect count exceeded\"):\n        request.fetch(server.EMPTY_PAGE)\n    assert redirect_count == 2\n    request.dispose()\n"
  },
  {
    "path": "tests/sync/test_fill.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page\nfrom tests.server import Server\n\n\ndef test_fill_textarea(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    page.fill(\"textarea\", \"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n\n\ndef test_fill_input(page: Page, server: Server) -> None:\n    page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    page.fill(\"input\", \"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n"
  },
  {
    "path": "tests/sync/test_har.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport json\nimport os\nimport re\nimport zipfile\nfrom pathlib import Path\nfrom typing import Any, cast\n\nimport pytest\n\nfrom playwright.sync_api import Browser, BrowserContext, Error, Page, Route, expect\nfrom tests.server import Server\n\n\ndef test_should_work(browser: Browser, server: Server, tmp_path: Path) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(record_har_path=path)\n    page = context.new_page()\n    page.goto(server.EMPTY_PAGE)\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n\n\ndef test_should_omit_content(browser: Browser, server: Server, tmp_path: Path) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(record_har_path=path, record_har_content=\"omit\")\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert \"text\" not in content1\n        assert \"encoding\" not in content1\n\n\ndef test_should_omit_content_legacy(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(record_har_path=path, record_har_omit_content=True)\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert \"text\" not in content1\n        assert \"encoding\" not in content1\n\n\ndef test_should_attach_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har.zip\")\n    context = browser.new_context(\n        record_har_path=path,\n        record_har_content=\"attach\",\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    page.evaluate(\"() => fetch('/pptr.png').then(r => r.arrayBuffer())\")\n    context.close()\n    with zipfile.ZipFile(path) as z:\n        with z.open(\"har.har\") as har:\n            entries = json.load(har)[\"log\"][\"entries\"]\n\n            assert \"encoding\" not in entries[0][\"response\"][\"content\"]\n            assert (\n                entries[0][\"response\"][\"content\"][\"mimeType\"]\n                == \"text/html; charset=utf-8\"\n            )\n            assert (\n                \"75841480e2606c03389077304342fac2c58ccb1b\"\n                in entries[0][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[0][\"response\"][\"content\"][\"size\"] >= 96\n            assert entries[0][\"response\"][\"content\"][\"compression\"] == 0\n\n            assert \"encoding\" not in entries[1][\"response\"][\"content\"]\n            assert (\n                entries[1][\"response\"][\"content\"][\"mimeType\"]\n                == \"text/css; charset=utf-8\"\n            )\n            assert (\n                \"79f739d7bc88e80f55b9891a22bf13a2b4e18adb\"\n                in entries[1][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[1][\"response\"][\"content\"][\"size\"] >= 37\n            assert entries[1][\"response\"][\"content\"][\"compression\"] == 0\n\n            assert \"encoding\" not in entries[2][\"response\"][\"content\"]\n            assert entries[2][\"response\"][\"content\"][\"mimeType\"] == \"image/png\"\n            assert (\n                \"a4c3a18f0bb83f5d9fe7ce561e065c36205762fa\"\n                in entries[2][\"response\"][\"content\"][\"_file\"]\n            )\n            assert entries[2][\"response\"][\"content\"][\"size\"] >= 6000\n            assert entries[2][\"response\"][\"content\"][\"compression\"] == 0\n\n            with z.open(\"75841480e2606c03389077304342fac2c58ccb1b.html\") as f:\n                assert b\"HAR Page\" in f.read()\n\n            with z.open(\"79f739d7bc88e80f55b9891a22bf13a2b4e18adb.css\") as f:\n                assert b\"pink\" in f.read()\n\n            with z.open(\"a4c3a18f0bb83f5d9fe7ce561e065c36205762fa.png\") as f:\n                assert len(f.read()) == entries[2][\"response\"][\"content\"][\"size\"]\n\n\ndef test_should_include_content(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(record_har_path=path)\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n\n        content1 = log[\"entries\"][0][\"response\"][\"content\"]\n        assert content1[\"mimeType\"] == \"text/html; charset=utf-8\"\n        assert \"HAR Page\" in content1[\"text\"]\n\n\ndef test_should_default_to_full_mode(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(\n        record_har_path=path,\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert log[\"entries\"][0][\"request\"][\"bodySize\"] >= 0\n\n\ndef test_should_support_minimal_mode(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(\n        record_har_path=path,\n        record_har_mode=\"minimal\",\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert log[\"entries\"][0][\"request\"][\"bodySize\"] == -1\n\n\ndef test_should_filter_by_glob(browser: Browser, server: Server, tmp_path: str) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(\n        base_url=server.PREFIX,\n        record_har_path=path,\n        record_har_url_filter=\"/*.css\",\n        ignore_https_errors=True,\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert len(log[\"entries\"]) == 1\n        assert log[\"entries\"][0][\"request\"][\"url\"].endswith(\"one-style.css\")\n\n\ndef test_should_filter_by_regexp(\n    browser: Browser, server: Server, tmp_path: str\n) -> None:\n    path = os.path.join(tmp_path, \"log.har\")\n    context = browser.new_context(\n        base_url=server.PREFIX,\n        record_har_path=path,\n        record_har_url_filter=re.compile(\"HAR.X?HTML\", re.I),\n        ignore_https_errors=True,\n    )\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/har.html\")\n    context.close()\n    with open(path) as f:\n        data = json.load(f)\n        assert \"log\" in data\n        log = data[\"log\"]\n        assert len(log[\"entries\"]) == 1\n        assert log[\"entries\"][0][\"request\"][\"url\"].endswith(\"har.html\")\n\n\ndef test_should_context_route_from_har_matching_the_method_and_following_redirects(\n    context: BrowserContext, assetdir: Path\n) -> None:\n    context.route_from_har(har=assetdir / \"har-fulfill.har\")\n    page = context.new_page()\n    page.goto(\"http://no.playwright/\")\n    # HAR contains a redirect for the script that should be followed automatically.\n    assert page.evaluate(\"window.value\") == \"foo\"\n    # HAR contains a POST for the css file that should not be used.\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\ndef test_should_page_route_from_har_matching_the_method_and_following_redirects(\n    page: Page, assetdir: Path\n) -> None:\n    page.route_from_har(har=assetdir / \"har-fulfill.har\")\n    page.goto(\"http://no.playwright/\")\n    # HAR contains a redirect for the script that should be followed automatically.\n    assert page.evaluate(\"window.value\") == \"foo\"\n    # HAR contains a POST for the css file that should not be used.\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\ndef test_fallback_continue_should_continue_when_not_found_in_har(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(har=assetdir / \"har-fulfill.har\", not_found=\"fallback\")\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/one-style.html\")\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n\n\ndef test_by_default_should_abort_requests_not_found_in_har(\n    context: BrowserContext,\n    server: Server,\n    assetdir: Path,\n    is_chromium: bool,\n    is_webkit: bool,\n) -> None:\n    context.route_from_har(har=assetdir / \"har-fulfill.har\")\n    page = context.new_page()\n\n    with pytest.raises(Error) as exc_info:\n        page.goto(server.EMPTY_PAGE)\n    assert exc_info.value\n    if is_chromium:\n        assert \"net::ERR_FAILED\" in exc_info.value.message\n    elif is_webkit:\n        assert \"Blocked by Web Inspector\" in exc_info.value.message\n    else:\n        assert \"NS_ERROR_FAILURE\" in exc_info.value.message\n\n\ndef test_fallback_continue_should_continue_requests_on_bad_har(\n    context: BrowserContext, server: Server, tmp_path: Path\n) -> None:\n    path_to_invalid_har = tmp_path / \"invalid.har\"\n    with path_to_invalid_har.open(\"w\") as f:\n        json.dump({\"log\": {}}, f)\n    context.route_from_har(har=path_to_invalid_har, not_found=\"fallback\")\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/one-style.html\")\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n\n\ndef test_should_only_handle_requests_matching_url_filter(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(\n        har=assetdir / \"har-fulfill.har\", not_found=\"fallback\", url=\"**/*.js\"\n    )\n    page = context.new_page()\n\n    def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    context.route(\"http://no.playwright/\", handler)\n    page.goto(\"http://no.playwright/\")\n    assert page.evaluate(\"window.value\") == \"foo\"\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgba(0, 0, 0, 0)\")\n\n\ndef test_should_only_handle_requests_matching_url_filter_no_fallback(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(har=assetdir / \"har-fulfill.har\", url=\"**/*.js\")\n    page = context.new_page()\n\n    def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    context.route(\"http://no.playwright/\", handler)\n    page.goto(\"http://no.playwright/\")\n    assert page.evaluate(\"window.value\") == \"foo\"\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgba(0, 0, 0, 0)\")\n\n\ndef test_should_only_handle_requests_matching_url_filter_no_fallback_page(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    page.route_from_har(har=assetdir / \"har-fulfill.har\", url=\"**/*.js\")\n\n    def handler(route: Route) -> None:\n        assert route.request.url == \"http://no.playwright/\"\n        route.fulfill(\n            status=200,\n            content_type=\"text/html\",\n            body='<script src=\"./script.js\"></script><div>hello</div>',\n        )\n\n    page.route(\"http://no.playwright/\", handler)\n    page.goto(\"http://no.playwright/\")\n    assert page.evaluate(\"window.value\") == \"foo\"\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgba(0, 0, 0, 0)\")\n\n\ndef test_should_support_regex_filter(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(\n        har=assetdir / \"har-fulfill.har\",\n        url=re.compile(r\".*(\\.js|.*\\.css|no.playwright\\/)\"),\n    )\n    page = context.new_page()\n    page.goto(\"http://no.playwright/\")\n    assert page.evaluate(\"window.value\") == \"foo\"\n    expect(page.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 0, 0)\")\n\n\ndef test_should_go_back_to_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = context.new_page()\n    page.goto(\"https://theverge.com/\")\n    page.goto(server.EMPTY_PAGE)\n    expect(page).to_have_url(server.EMPTY_PAGE)\n\n    response = page.go_back()\n    assert response\n    expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\n@pytest.mark.skip_browser(\n    \"firefox\"\n)  # skipped upstream (https://github.com/microsoft/playwright/blob/6a8d835145e2f4002ee00b67a80a1f70af956703/tests/library/browsercontext-har.spec.ts#L214)\ndef test_should_go_forward_to_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = context.new_page()\n    page.goto(\"https://theverge.com/\")\n    page.goto(server.EMPTY_PAGE)\n    expect(page).to_have_url(server.EMPTY_PAGE)\n    page.goto(\"https://theverge.com/\")\n    expect(page).to_have_url(\"https://www.theverge.com/\")\n    page.go_back()\n    expect(page).to_have_url(server.EMPTY_PAGE)\n    response = page.go_forward()\n    assert response\n    expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\ndef test_should_reload_redirected_navigation(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(\n        har=assetdir / \"har-redirect.har\", url=re.compile(r\"/.*theverge.*/\")\n    )\n    page = context.new_page()\n    page.goto(\"https://theverge.com/\")\n    expect(page).to_have_url(\"https://www.theverge.com/\")\n    response = page.reload()\n    assert response\n    expect(page).to_have_url(\"https://www.theverge.com/\")\n    assert response.request.url == \"https://www.theverge.com/\"\n    assert page.evaluate(\"window.location.href\") == \"https://www.theverge.com/\"\n\n\ndef test_should_fulfill_from_har_with_content_in_a_file(\n    context: BrowserContext, server: Server, assetdir: Path\n) -> None:\n    context.route_from_har(har=assetdir / \"har-sha1.har\")\n    page = context.new_page()\n    page.goto(\"http://no.playwright/\")\n    assert page.content() == \"<html><head></head><body>Hello, world</body></html>\"\n\n\ndef test_should_round_trip_har_zip(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context_1 = browser.new_context(record_har_mode=\"minimal\", record_har_path=har_path)\n    page_1 = context_1.new_page()\n    page_1.goto(server.PREFIX + \"/one-style.html\")\n    context_1.close()\n\n    context_2 = browser.new_context()\n    context_2.route_from_har(har=har_path, not_found=\"abort\")\n    page_2 = context_2.new_page()\n    page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page_2.content()\n    expect(page_2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context_2.close()\n\n\ndef test_should_round_trip_har_with_post_data(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    server.set_route(\n        \"/echo\", lambda req: (req.write(cast(Any, req).post_body), req.finish())\n    )\n    fetch_function = \"\"\"\n        async (body) => {\n            const response = await fetch('/echo', { method: 'POST', body });\n            return response.text();\n        };\n    \"\"\"\n    har_path = tmp_path / \"har.zip\"\n    context_1 = browser.new_context(record_har_mode=\"minimal\", record_har_path=har_path)\n    page_1 = context_1.new_page()\n    page_1.goto(server.EMPTY_PAGE)\n\n    assert page_1.evaluate(fetch_function, \"1\") == \"1\"\n    assert page_1.evaluate(fetch_function, \"2\") == \"2\"\n    assert page_1.evaluate(fetch_function, \"3\") == \"3\"\n    context_1.close()\n\n    context_2 = browser.new_context()\n    context_2.route_from_har(har=har_path, not_found=\"abort\")\n    page_2 = context_2.new_page()\n    page_2.goto(server.EMPTY_PAGE)\n    assert page_2.evaluate(fetch_function, \"1\") == \"1\"\n    assert page_2.evaluate(fetch_function, \"2\") == \"2\"\n    assert page_2.evaluate(fetch_function, \"3\") == \"3\"\n    with pytest.raises(Exception):\n        page_2.evaluate(fetch_function, \"4\")\n    context_2.close()\n\n\ndef test_should_disambiguate_by_header(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    server.set_route(\n        \"/echo\",\n        lambda req: (req.write(cast(str, req.getHeader(\"baz\")).encode()), req.finish()),\n    )\n    fetch_function = \"\"\"\n        async (bazValue) => {\n            const response = await fetch('/echo', {\n            method: 'POST',\n            body: '',\n            headers: {\n                foo: 'foo-value',\n                bar: 'bar-value',\n                baz: bazValue,\n            }\n            });\n            return response.text();\n        };\n    \"\"\"\n    har_path = tmp_path / \"har.zip\"\n    context_1 = browser.new_context(record_har_mode=\"minimal\", record_har_path=har_path)\n    page_1 = context_1.new_page()\n    page_1.goto(server.EMPTY_PAGE)\n\n    assert page_1.evaluate(fetch_function, \"baz1\") == \"baz1\"\n    assert page_1.evaluate(fetch_function, \"baz2\") == \"baz2\"\n    assert page_1.evaluate(fetch_function, \"baz3\") == \"baz3\"\n    context_1.close()\n\n    context_2 = browser.new_context()\n    context_2.route_from_har(har=har_path)\n    page_2 = context_2.new_page()\n    page_2.goto(server.EMPTY_PAGE)\n    assert page_2.evaluate(fetch_function, \"baz1\") == \"baz1\"\n    assert page_2.evaluate(fetch_function, \"baz2\") == \"baz2\"\n    assert page_2.evaluate(fetch_function, \"baz3\") == \"baz3\"\n    assert page_2.evaluate(fetch_function, \"baz4\") == \"baz1\"\n    context_2.close()\n\n\ndef test_should_produce_extracted_zip(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.har\"\n    context = browser.new_context(\n        record_har_mode=\"minimal\", record_har_path=har_path, record_har_content=\"attach\"\n    )\n    page_1 = context.new_page()\n    page_1.goto(server.PREFIX + \"/one-style.html\")\n    context.close()\n\n    assert har_path.exists()\n    with har_path.open() as r:\n        content = r.read()\n        assert \"log\" in content\n        assert \"background-color\" not in r.read()\n\n    context_2 = browser.new_context()\n    context_2.route_from_har(har_path, not_found=\"abort\")\n    page_2 = context_2.new_page()\n    page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page_2.content()\n    expect(page_2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context_2.close()\n\n\ndef test_should_update_har_zip_for_context(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context = browser.new_context()\n    context.route_from_har(har_path, update=True)\n    page_1 = context.new_page()\n    page_1.goto(server.PREFIX + \"/one-style.html\")\n    context.close()\n\n    assert har_path.exists()\n\n    context_2 = browser.new_context()\n    context_2.route_from_har(har_path, not_found=\"abort\")\n    page_2 = context_2.new_page()\n    page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page_2.content()\n    expect(page_2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context_2.close()\n\n\ndef test_should_update_har_zip_for_page(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context = browser.new_context()\n    page_1 = context.new_page()\n    page_1.route_from_har(har_path, update=True)\n    page_1.goto(server.PREFIX + \"/one-style.html\")\n    context.close()\n\n    assert har_path.exists()\n\n    context_2 = browser.new_context()\n    page_2 = context_2.new_page()\n    page_2.route_from_har(har_path, not_found=\"abort\")\n    page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page_2.content()\n    expect(page_2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context_2.close()\n\n\ndef test_should_update_har_zip_for_page_with_different_options(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.zip\"\n    context1 = browser.new_context()\n    page1 = context1.new_page()\n    page1.route_from_har(\n        har_path, update=True, update_content=\"embed\", update_mode=\"full\"\n    )\n    page1.goto(server.PREFIX + \"/one-style.html\")\n    context1.close()\n\n    context2 = browser.new_context()\n    page2 = context2.new_page()\n    page2.route_from_har(har_path, not_found=\"abort\")\n    page2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page2.content()\n    expect(page2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context2.close()\n\n\ndef test_should_update_extracted_har_zip_for_page(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    har_path = tmp_path / \"har.har\"\n    context = browser.new_context()\n    page_1 = context.new_page()\n    page_1.route_from_har(har_path, update=True)\n    page_1.goto(server.PREFIX + \"/one-style.html\")\n    context.close()\n\n    assert har_path.exists()\n    with har_path.open() as r:\n        content = r.read()\n        assert \"log\" in content\n        assert \"background-color\" not in r.read()\n\n    context_2 = browser.new_context()\n    page_2 = context_2.new_page()\n    page_2.route_from_har(har_path, not_found=\"abort\")\n    page_2.goto(server.PREFIX + \"/one-style.html\")\n    assert \"hello, world!\" in page_2.content()\n    expect(page_2.locator(\"body\")).to_have_css(\"background-color\", \"rgb(255, 192, 203)\")\n    context_2.close()\n"
  },
  {
    "path": "tests/sync/test_input.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\nfrom typing import Any\n\nfrom playwright.sync_api import Page\n\n\ndef test_expect_file_chooser(page: Page) -> None:\n    page.set_content(\"<input type=file></input>\")\n    with page.expect_file_chooser() as fc_info:\n        page.click('input[type=\"file\"]')\n    fc = fc_info.value\n    fc.set_files(\n        {\"name\": \"test.txt\", \"mimeType\": \"text/plain\", \"buffer\": b\"Hello World\"}\n    )\n\n\ndef test_set_input_files_should_preserve_last_modified_timestamp(\n    page: Page,\n    assetdir: Path,\n) -> None:\n    page.set_content(\"<input type=file multiple=true/>\")\n    input = page.locator(\"input\")\n    files: Any = [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]\n    input.set_input_files([assetdir / file for file in files])\n    assert input.evaluate(\"input => [...input.files].map(f => f.name)\") == files\n    timestamps = input.evaluate(\"input => [...input.files].map(f => f.lastModified)\")\n    expected_timestamps = [os.path.getmtime(assetdir / file) * 1000 for file in files]\n\n    # On Linux browser sometimes reduces the timestamp by 1ms: 1696272058110.0715  -> 1696272058109 or even\n    # rounds it to seconds in WebKit: 1696272058110 -> 1696272058000.\n    for i in range(len(timestamps)):\n        assert abs(timestamps[i] - expected_timestamps[i]) < 1000\n"
  },
  {
    "path": "tests/sync/test_launcher.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import BrowserType, Error\n\n\n@pytest.mark.skip_browser(\"firefox\")\ndef test_browser_type_launch_should_throw_if_page_argument_is_passed(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    with pytest.raises(Error) as exc:\n        browser_type.launch(**launch_arguments, args=[\"http://example.com\"])\n    assert \"can not specify page\" in exc.value.message\n\n\ndef test_browser_type_launch_should_reject_if_launched_browser_fails_immediately(\n    browser_type: BrowserType, launch_arguments: Dict, assetdir: Path\n) -> None:\n    with pytest.raises(Error):\n        browser_type.launch(\n            **launch_arguments,\n            executable_path=assetdir / \"dummy_bad_browser_executable.js\",\n        )\n\n\ndef test_browser_type_launch_should_reject_if_executable_path_is_invalid(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    with pytest.raises(Error) as exc:\n        browser_type.launch(**launch_arguments, executable_path=\"random-invalid-path\")\n    assert \"executable doesn't exist\" in exc.value.message\n\n\ndef test_browser_type_executable_path_should_work(\n    browser_type: BrowserType, browser_channel: str\n) -> None:\n    if browser_channel:\n        return\n    executable_path = browser_type.executable_path\n    assert os.path.exists(executable_path)\n    assert os.path.realpath(executable_path) == os.path.realpath(executable_path)\n\n\ndef test_browser_type_name_should_work(\n    browser_type: BrowserType, is_webkit: bool, is_firefox: bool, is_chromium: bool\n) -> None:\n    if is_webkit:\n        assert browser_type.name == \"webkit\"\n    elif is_firefox:\n        assert browser_type.name == \"firefox\"\n    elif is_chromium:\n        assert browser_type.name == \"chromium\"\n    else:\n        raise ValueError(\"Unknown browser\")\n\n\ndef test_browser_close_should_fire_close_event_for_all_contexts(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = browser_type.launch(**launch_arguments)\n    context = browser.new_context()\n    closed = []\n    context.on(\"close\", lambda _: closed.append(True))\n    browser.close()\n    assert closed == [True]\n\n\ndef test_browser_close_should_be_callable_twice(\n    browser_type: BrowserType, launch_arguments: Dict\n) -> None:\n    browser = browser_type.launch(**launch_arguments)\n    browser.close()\n    browser.close()\n"
  },
  {
    "path": "tests/sync/test_listeners.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom playwright.sync_api import Page, Response\nfrom tests.server import Server\n\n\ndef test_listeners(page: Page, server: Server) -> None:\n    log = []\n\n    def print_response(response: Response) -> None:\n        log.append(response)\n\n    page.on(\"response\", print_response)\n    page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    assert len(log) > 0\n    page.remove_listener(\"response\", print_response)\n\n    log = []\n    page.goto(f\"{server.PREFIX}/input/textarea.html\")\n    assert len(log) == 0\n"
  },
  {
    "path": "tests/sync/test_locator_get_by.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nfrom playwright.sync_api import Page, expect\n\n\ndef test_get_by_test_id(page: Page) -> None:\n    page.set_content(\"<div><div data-testid='Hello'>Hello world</div></div>\")\n    expect(page.get_by_test_id(\"Hello\")).to_have_text(\"Hello world\")\n    expect(page.main_frame.get_by_test_id(\"Hello\")).to_have_text(\"Hello world\")\n    expect(page.locator(\"div\").get_by_test_id(\"Hello\")).to_have_text(\"Hello world\")\n\n\ndef test_get_by_test_id_escape_id(page: Page) -> None:\n    page.set_content(\"<div><div data-testid='He\\\"llo'>Hello world</div></div>\")\n    expect(page.get_by_test_id('He\"llo')).to_have_text(\"Hello world\")\n\n\ndef test_get_by_text(page: Page) -> None:\n    page.set_content(\"<div><div>yo</div><div>ya</div><div>\\nye  </div></div>\")\n\n    expect(page.get_by_text(\"yo\")).to_have_count(1)\n    expect(page.main_frame.get_by_text(\"yo\")).to_have_count(1)\n    expect(page.locator(\"div\").get_by_text(\"yo\")).to_have_count(1)\n\n    assert \">\\nye  </div>\" in page.get_by_text(\"ye\").evaluate(\"e => e.outerHTML\")\n    assert \">\\nye  </div>\" in page.get_by_text(r\"ye\").evaluate(\"e => e.outerHTML\")\n\n    page.set_content(\"<div> ye </div><div>ye</div>\")\n    assert \"> ye </div>\" in page.get_by_text(\"ye\", exact=True).first.evaluate(\n        \"e => e.outerHTML\"\n    )\n\n    page.set_content(\"<div>Hello world</div><div>Hello</div>\")\n    assert (\n        page.get_by_text(\"Hello\", exact=True).evaluate(\"e => e.outerHTML\")\n        == \"<div>Hello</div>\"\n    )\n\n\ndef test_get_by_label(page: Page) -> None:\n    page.set_content(\n        \"<div><label for=target>Name</label><input id=target type=text></div>\"\n    )\n\n    expect(page.get_by_label(\"Name\")).to_have_count(1)\n    expect(page.main_frame.get_by_label(\"Name\")).to_have_count(1)\n    expect(page.locator(\"div\").get_by_label(\"Name\")).to_have_count(1)\n\n    assert page.get_by_text(\"Name\").evaluate(\"e => e.nodeName\") == \"LABEL\"\n    assert page.get_by_label(\"Name\").evaluate(\"e => e.nodeName\") == \"INPUT\"\n    assert page.main_frame.get_by_label(\"Name\").evaluate(\"e => e.nodeName\") == \"INPUT\"\n    assert (\n        page.locator(\"div\").get_by_label(\"Name\").evaluate(\"e => e.nodeName\") == \"INPUT\"\n    )\n\n\ndef test_get_by_label_with_nested_elements(page: Page) -> None:\n    page.set_content(\n        \"<label for=target>Last <span>Name</span></label><input id=target type=text>\"\n    )\n\n    expect(page.get_by_label(\"last name\")).to_have_attribute(\"id\", \"target\")\n    expect(page.get_by_label(\"st na\")).to_have_attribute(\"id\", \"target\")\n    expect(page.get_by_label(\"Name\")).to_have_attribute(\"id\", \"target\")\n    expect(page.get_by_label(\"Last Name\", exact=True)).to_have_attribute(\"id\", \"target\")\n    expect(\n        page.get_by_label(re.compile(r\"Last\\s+name\", re.IGNORECASE))\n    ).to_have_attribute(\"id\", \"target\")\n\n    expect(page.get_by_label(\"Last\", exact=True)).to_have_count(0)\n    expect(page.get_by_label(\"last name\", exact=True)).to_have_count(0)\n    expect(page.get_by_label(\"Name\", exact=True)).to_have_count(0)\n    expect(page.get_by_label(\"what?\")).to_have_count(0)\n    expect(page.get_by_label(re.compile(r\"last name\"))).to_have_count(0)\n\n\ndef test_get_by_placeholder(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div>\n    <input placeholder=\"Hello\">\n    <input placeholder=\"Hello World\">\n  </div>\"\"\"\n    )\n\n    expect(page.get_by_placeholder(\"hello\")).to_have_count(2)\n    expect(page.main_frame.get_by_placeholder(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_placeholder(\"hello\")).to_have_count(2)\n\n    expect(page.get_by_placeholder(\"hello\")).to_have_count(2)\n    expect(page.get_by_placeholder(\"Hello\", exact=True)).to_have_count(1)\n    expect(page.get_by_placeholder(re.compile(r\"wor\", re.IGNORECASE))).to_have_count(1)\n\n    # Coverage\n    expect(page.main_frame.get_by_placeholder(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_placeholder(\"hello\")).to_have_count(2)\n\n\ndef test_get_by_alt_text(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div>\n    <input alt=\"Hello\">\n    <input alt=\"Hello World\">\n  </div>\"\"\"\n    )\n\n    expect(page.get_by_alt_text(\"hello\")).to_have_count(2)\n    expect(page.main_frame.get_by_alt_text(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_alt_text(\"hello\")).to_have_count(2)\n\n    expect(page.get_by_alt_text(\"hello\")).to_have_count(2)\n    expect(page.get_by_alt_text(\"Hello\", exact=True)).to_have_count(1)\n    expect(page.get_by_alt_text(re.compile(r\"wor\", re.IGNORECASE))).to_have_count(1)\n\n    # Coverage\n    expect(page.main_frame.get_by_alt_text(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_alt_text(\"hello\")).to_have_count(2)\n\n\ndef test_get_by_title(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div>\n    <input title=\"Hello\">\n    <input title=\"Hello World\">\n  </div>\"\"\"\n    )\n\n    expect(page.get_by_title(\"hello\")).to_have_count(2)\n    expect(page.main_frame.get_by_title(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_title(\"hello\")).to_have_count(2)\n\n    expect(page.get_by_title(\"hello\")).to_have_count(2)\n    expect(page.get_by_title(\"Hello\", exact=True)).to_have_count(1)\n    expect(page.get_by_title(re.compile(r\"wor\", re.IGNORECASE))).to_have_count(1)\n\n    # Coverage\n    expect(page.main_frame.get_by_title(\"hello\")).to_have_count(2)\n    expect(page.locator(\"div\").get_by_title(\"hello\")).to_have_count(2)\n\n\ndef test_get_by_escaping(page: Page) -> None:\n    page.set_content(\n        \"\"\"<label id=label for=control>Hello my\nwo\"rld</label><input id=control />\"\"\"\n    )\n    page.locator(\"input\").evaluate(\n        \"\"\"input => {\n    input.setAttribute('placeholder', 'hello my\\\\nwo\"rld');\n    input.setAttribute('title', 'hello my\\\\nwo\"rld');\n    input.setAttribute('alt', 'hello my\\\\nwo\"rld');\n  }\"\"\"\n    )\n    expect(page.get_by_text('hello my\\nwo\"rld')).to_have_attribute(\"id\", \"label\")\n    expect(page.get_by_text('hello     my         wo\"rld')).to_have_attribute(\n        \"id\", \"label\"\n    )\n    expect(page.get_by_label('hello my\\nwo\"rld')).to_have_attribute(\"id\", \"control\")\n    expect(page.get_by_placeholder('hello my\\nwo\"rld')).to_have_attribute(\n        \"id\", \"control\"\n    )\n    expect(page.get_by_alt_text('hello my\\nwo\"rld')).to_have_attribute(\"id\", \"control\")\n    expect(page.get_by_title('hello my\\nwo\"rld')).to_have_attribute(\"id\", \"control\")\n\n    page.set_content(\n        \"\"\"<label id=label for=control>Hello my\nworld</label><input id=control />\"\"\"\n    )\n    page.locator(\"input\").evaluate(\n        \"\"\"input => {\n    input.setAttribute('placeholder', 'hello my\\\\nworld');\n    input.setAttribute('title', 'hello my\\\\nworld');\n    input.setAttribute('alt', 'hello my\\\\nworld');\n  }\"\"\"\n    )\n    expect(page.get_by_text(\"hello my\\nworld\")).to_have_attribute(\"id\", \"label\")\n    expect(page.get_by_text(\"hello      my       world\")).to_have_attribute(\n        \"id\", \"label\"\n    )\n    expect(page.get_by_label(\"hello my\\nworld\")).to_have_attribute(\"id\", \"control\")\n    expect(page.get_by_placeholder(\"hello my\\nworld\")).to_have_attribute(\n        \"id\", \"control\"\n    )\n    expect(page.get_by_alt_text(\"hello my\\nworld\")).to_have_attribute(\"id\", \"control\")\n    expect(page.get_by_title(\"hello my\\nworld\")).to_have_attribute(\"id\", \"control\")\n\n\ndef test_get_by_role(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n    <button>Hello</button>\n    <button>Hel\"lo</button>\n    <div role=\"dialog\">I am a dialog</div>\n  \"\"\"\n    )\n    expect(page.get_by_role(\"button\", name=\"hello\")).to_have_count(1)\n    expect(page.get_by_role(\"button\", name='Hel\"lo')).to_have_count(1)\n    expect(\n        page.get_by_role(\"button\", name=re.compile(r\"he\", re.IGNORECASE))\n    ).to_have_count(2)\n    expect(page.get_by_role(\"dialog\")).to_have_count(1)\n"
  },
  {
    "path": "tests/sync/test_locators.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport re\nimport traceback\nfrom typing import Callable\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom playwright._impl._path_utils import get_file_dirname\nfrom playwright.sync_api import Error, Page, expect\nfrom tests.server import Server\n\n_dirname = get_file_dirname()\nFILE_TO_UPLOAD = _dirname / \"..\" / \"assets/file-to-upload.txt\"\n\n\ndef test_locators_click_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    button.click()\n    assert page.evaluate(\"window['result']\") == \"Clicked\"\n\n\ndef test_locators_click_should_work_with_node_removed(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    page.evaluate(\"delete window['Node']\")\n    button = page.locator(\"button\")\n    button.click()\n    assert page.evaluate(\"window['result']\") == \"Clicked\"\n\n\ndef test_locators_click_should_work_for_text_nodes(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    page.evaluate(\n        \"\"\"() => {\n        window['double'] = false;\n        const button = document.querySelector('button');\n        button.addEventListener('dblclick', event => {\n        window['double'] = true;\n        });\n    }\"\"\"\n    )\n    button = page.locator(\"button\")\n    button.dblclick()\n    assert page.evaluate(\"double\") is True\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_locators_should_have_repr(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    button.click()\n    assert (\n        str(button)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/input/button.html'> selector='button'>\"\n    )\n\n\ndef test_locators_get_attribute_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    button = page.locator(\"#outer\")\n    assert button.get_attribute(\"name\") == \"value\"\n    assert button.get_attribute(\"foo\") is None\n\n\ndef test_locators_input_value_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    page.fill(\"#textarea\", \"input value\")\n    text_area = page.locator(\"#textarea\")\n    assert text_area.input_value() == \"input value\"\n\n\ndef test_locators_inner_html_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#outer\")\n    assert locator.inner_html() == '<div id=\"inner\">Text,\\nmore text</div>'\n\n\ndef test_locators_inner_text_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#inner\")\n    assert locator.inner_text() == \"Text, more text\"\n\n\ndef test_locators_text_content_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    locator = page.locator(\"#inner\")\n    assert locator.text_content() == \"Text,\\nmore text\"\n\n\ndef test_locators_is_hidden_and_is_visible_should_work(page: Page) -> None:\n    page.set_content(\"<div>Hi</div><span></span>\")\n\n    div = page.locator(\"div\")\n    assert div.is_visible() is True\n    assert div.is_hidden() is False\n\n    span = page.locator(\"span\")\n    assert span.is_visible() is False\n    assert span.is_hidden() is True\n\n\ndef test_locators_is_enabled_and_is_disabled_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <button disabled>button1</button>\n        <button>button2</button>\n        <div>div</div>\n    \"\"\"\n    )\n\n    div = page.locator(\"div\")\n    assert div.is_enabled()\n    assert div.is_disabled() is False\n\n    button1 = page.locator(':text(\"button1\")')\n    assert button1.is_enabled() is False\n    assert button1.is_disabled() is True\n\n    button1 = page.locator(':text(\"button2\")')\n    assert button1.is_enabled()\n    assert button1.is_disabled() is False\n\n\ndef test_locators_is_editable_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <input id=input1 disabled><textarea></textarea><input id=input2>\n    \"\"\"\n    )\n\n    input1 = page.locator(\"#input1\")\n    assert input1.is_editable() is False\n\n    input2 = page.locator(\"#input2\")\n    assert input2.is_editable() is True\n\n\ndef test_locators_is_checked_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <input type='checkbox' checked><div>Not a checkbox</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"input\")\n    assert element.is_checked() is True\n    element.evaluate(\"e => e.checked = false\")\n    assert element.is_checked() is False\n\n\ndef test_locators_all_text_contents_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div><div>C</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"div\")\n    assert element.all_text_contents() == [\"A\", \"B\", \"C\"]\n\n\ndef test_locators_all_inner_texts(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div><div>C</div>\n    \"\"\"\n    )\n\n    element = page.locator(\"div\")\n    assert element.all_inner_texts() == [\"A\", \"B\", \"C\"]\n\n\ndef test_locators_should_query_existing_element(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/playground.html\")\n    page.set_content(\n        \"\"\"<html><body><div class=\"second\"><div class=\"inner\">A</div></div></body></html>\"\"\"\n    )\n    html = page.locator(\"html\")\n    second = html.locator(\".second\")\n    inner = second.locator(\".inner\")\n    assert page.evaluate(\"e => e.textContent\", inner.element_handle()) == \"A\"\n\n\ndef test_locators_evaluate_handle_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/dom.html\")\n    outer = page.locator(\"#outer\")\n    inner = outer.locator(\"#inner\")\n    check = inner.locator(\"#check\")\n    text = inner.evaluate_handle(\"e => e.firstChild\")\n    page.evaluate(\"1 + 1\")\n    assert (\n        str(outer)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer'>\"\n    )\n    assert (\n        str(inner)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer >> #inner'>\"\n    )\n    assert str(text) == \"JSHandle@#text=Text,↵more text\"\n    assert (\n        str(check)\n        == f\"<Locator frame=<Frame name= url='{server.PREFIX}/dom.html'> selector='#outer >> #inner >> #check'>\"\n    )\n\n\ndef test_locators_should_query_existing_elements(page: Page) -> None:\n    page.set_content(\"\"\"<html><body><div>A</div><br/><div>B</div></body></html>\"\"\")\n    html = page.locator(\"html\")\n    elements = html.locator(\"div\").element_handles()\n    assert len(elements) == 2\n    result = []\n    for element in elements:\n        result.append(page.evaluate(\"e => e.textContent\", element))\n    assert result == [\"A\", \"B\"]\n\n\ndef test_locators_return_empty_array_for_non_existing_elements(page: Page) -> None:\n    page.set_content(\"\"\"<html><body><div>A</div><br/><div>B</div></body></html>\"\"\")\n    html = page.locator(\"html\")\n    elements = html.locator(\"abc\").element_handles()\n    assert len(elements) == 0\n    assert elements == []\n\n\ndef test_locators_evaluate_all_should_work(page: Page) -> None:\n    page.set_content(\n        \"\"\"<html><body><div class=\"tweet\"><div class=\"like\">100</div><div class=\"like\">10</div></div></body></html>\"\"\"\n    )\n    tweet = page.locator(\".tweet .like\")\n    content = tweet.evaluate_all(\"nodes => nodes.map(n => n.innerText)\")\n    assert content == [\"100\", \"10\"]\n\n\ndef test_locators_evaluate_all_should_work_with_missing_selector(page: Page) -> None:\n    page.set_content(\"\"\"<div class=\"a\">not-a-child-div</div><div id=\"myId\"></div\"\"\")\n    tweet = page.locator(\"#myId .a\")\n    nodes_length = tweet.evaluate_all(\"nodes => nodes.length\")\n    assert nodes_length == 0\n\n\ndef test_locators_hover_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/scrollable.html\")\n    button = page.locator(\"#button-6\")\n    button.hover()\n    assert page.evaluate(\"document.querySelector('button:hover').id\") == \"button-6\"\n\n\ndef test_locators_fill_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    button = page.locator(\"input\")\n    button.fill(\"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n\n\ndef test_locators_clear_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    button = page.locator(\"input\")\n    button.fill(\"some value\")\n    assert page.evaluate(\"result\") == \"some value\"\n    button.clear()\n    assert page.evaluate(\"result\") == \"\"\n\n\ndef test_locators_check_should_work(page: Page) -> None:\n    page.set_content(\"<input id='checkbox' type='checkbox'></input>\")\n    button = page.locator(\"input\")\n    button.check()\n    assert page.evaluate(\"checkbox.checked\") is True\n\n\ndef test_locators_uncheck_should_work(page: Page) -> None:\n    page.set_content(\"<input id='checkbox' type='checkbox' checked></input>\")\n    button = page.locator(\"input\")\n    button.uncheck()\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_locators_select_option_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    select = page.locator(\"select\")\n    select.select_option(\"blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_locators_focus_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    assert button.evaluate(\"button => document.activeElement === button\") is False\n    button.focus()\n    assert button.evaluate(\"button => document.activeElement === button\") is True\n\n\ndef test_locators_dispatch_event_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    button.dispatch_event(\"click\")\n    assert page.evaluate(\"result\") == \"Clicked\"\n\n\ndef test_locators_should_upload_a_file(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/fileupload.html\")\n    input = page.locator(\"input[type=file]\")\n\n    file_path = os.path.relpath(FILE_TO_UPLOAD, os.getcwd())\n    input.set_input_files(file_path)\n    assert (\n        page.evaluate(\"e => e.files[0].name\", input.element_handle())\n        == \"file-to-upload.txt\"\n    )\n\n\ndef test_locators_upload_nonexistant_file(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/fileupload.html\")\n    with pytest.raises(FileNotFoundError):\n        page.locator(\"input[type=file]\").set_input_files(\"nonexistant.html\")\n\n\ndef test_locators_should_press(page: Page) -> None:\n    page.set_content(\"<input type='text' />\")\n    page.locator(\"input\").press(\"h\")\n    assert page.eval_on_selector(\"input\", \"input => input.value\") == \"h\"\n\n\ndef test_locators_should_scroll_into_view(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/offscreenbuttons.html\")\n    for i in range(11):\n        button = page.locator(f\"#btn{i}\")\n        before = button.evaluate(\n            \"button => button.getBoundingClientRect().right - window.innerWidth\"\n        )\n        assert before == 10 * i\n        button.scroll_into_view_if_needed()\n        after = button.evaluate(\n            \"button => button.getBoundingClientRect().right - window.innerWidth\"\n        )\n        assert after <= 0\n        page.evaluate(\"window.scrollTo(0, 0)\")\n\n\ndef test_locators_should_select_textarea(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n    textarea = page.locator(\"textarea\")\n    textarea.evaluate(\"textarea => textarea.value = 'some value'\")\n    textarea.select_text()\n    textarea.select_text(timeout=25_000)\n    if browser_name == \"firefox\" or browser_name == \"webkit\":\n        assert textarea.evaluate(\"el => el.selectionStart\") == 0\n        assert textarea.evaluate(\"el => el.selectionEnd\") == 10\n    else:\n        assert page.evaluate(\"window.getSelection().toString()\") == \"some value\"\n\n\ndef test_locators_should_type(page: Page) -> None:\n    page.set_content(\"<input type='text' />\")\n    page.locator(\"input\").type(\"hello\")\n    assert page.eval_on_selector(\"input\", \"input => input.value\") == \"hello\"\n\n\ndef test_locators_should_screenshot(\n    page: Page, server: Server, assert_to_be_golden: Callable[[bytes, str], None]\n) -> None:\n    page.set_viewport_size(\n        {\n            \"width\": 500,\n            \"height\": 500,\n        }\n    )\n    page.goto(server.PREFIX + \"/grid.html\")\n    page.evaluate(\"window.scrollBy(50, 100)\")\n    element = page.locator(\".box:nth-of-type(3)\")\n    assert_to_be_golden(element.screenshot(), \"screenshot-element-bounding-box.png\")\n    assert_to_be_golden(\n        element.screenshot(timeout=1_000), \"screenshot-element-bounding-box.png\"\n    )\n\n\ndef test_locators_should_return_bounding_box(page: Page, server: Server) -> None:\n    page.set_viewport_size(\n        {\n            \"width\": 500,\n            \"height\": 500,\n        }\n    )\n    page.goto(server.PREFIX + \"/grid.html\")\n    element = page.locator(\".box:nth-of-type(13)\")\n    box = element.bounding_box()\n    assert box == {\n        \"x\": 100,\n        \"y\": 50,\n        \"width\": 50,\n        \"height\": 50,\n    }\n\n\ndef test_locators_should_respect_first_and_last(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <section>\n            <div><p>A</p></div>\n            <div><p>A</p><p>A</p></div>\n            <div><p>A</p><p>A</p><p>A</p></div>\n        </section>\"\"\"\n    )\n    assert page.locator(\"div >> p\").count() == 6\n    assert page.locator(\"div\").locator(\"p\").count() == 6\n    assert page.locator(\"div\").first.locator(\"p\").count() == 1\n    assert page.locator(\"div\").last.locator(\"p\").count() == 3\n\n\ndef test_locators_should_respect_nth(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n    <section>\n        <div><p>A</p></div>\n        <div><p>A</p><p>A</p></div>\n        <div><p>A</p><p>A</p><p>A</p></div>\n    </section>\"\"\"\n    )\n    assert page.locator(\"div >> p\").nth(0).count() == 1\n    assert page.locator(\"div\").nth(1).locator(\"p\").count() == 2\n    assert page.locator(\"div\").nth(2).locator(\"p\").count() == 3\n\n\ndef test_locators_should_throw_on_capture_without_nth(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <section><div><p>A</p></div></section>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"Can't query n-th element\"):\n        page.locator(\"*css=div >> p\").nth(1).click()\n\n\ndef test_locators_should_throw_due_to_strictness(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <div>A</div><div>B</div>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"strict mode violation\"):\n        page.locator(\"div\").is_visible()\n\n\ndef test_locators_should_throw_due_to_strictness_2(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <select><option>One</option><option>Two</option></select>\n    \"\"\"\n    )\n    with pytest.raises(Error, match=\"strict mode violation\"):\n        page.locator(\"option\").evaluate(\"e => {}\")\n\n\ndef test_locators_set_checked(page: Page) -> None:\n    page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    locator = page.locator(\"input\")\n    locator.set_checked(True)\n    assert page.evaluate(\"checkbox.checked\")\n    locator.set_checked(False)\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_should_combine_visible_with_other_selectors(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div>\n        <div class=\"item\" style=\"display: none\">Hidden data0</div>\n        <div class=\"item\">visible data1</div>\n        <div class=\"item\" style=\"display: none\">Hidden data1</div>\n        <div class=\"item\">visible data2</div>\n        <div class=\"item\" style=\"display: none\">Hidden data1</div>\n        <div class=\"item\">visible data3</div>\n        </div>\n    \"\"\"\n    )\n    locator = page.locator(\".item >> visible=true\").nth(1)\n    expect(locator).to_have_text(\"visible data2\")\n    expect(page.locator(\".item >> visible=true >> text=data3\")).to_have_text(\n        \"visible data3\"\n    )\n\n\ndef test_should_support_filter_visible(page: Page) -> None:\n    page.set_content(\n        \"\"\"<div>\n    <div class=\"item\" style=\"display: none\">Hidden data0</div>\n    <div class=\"item\">visible data1</div>\n    <div class=\"item\" style=\"display: none\">Hidden data1</div>\n    <div class=\"item\">visible data2</div>\n    <div class=\"item\" style=\"display: none\">Hidden data2</div>\n    <div class=\"item\">visible data3</div>\n    </div>\n    \"\"\"\n    )\n    locator = page.locator(\".item\").filter(visible=True).nth(1)\n    expect(locator).to_have_text(\"visible data2\")\n    expect(\n        page.locator(\".item\").filter(visible=True).get_by_text(\"data3\")\n    ).to_have_text(\"visible data3\")\n    expect(\n        page.locator(\".item\").filter(visible=False).get_by_text(\"data1\")\n    ).to_have_text(\"Hidden data1\")\n\n\ndef test_locator_count_should_work_with_deleted_map_in_main_world(page: Page) -> None:\n    page.evaluate(\"Map = 1\")\n    page.locator(\"#searchResultTableDiv .x-grid3-row\").count()\n    expect(page.locator(\"#searchResultTableDiv .x-grid3-row\")).to_have_count(0)\n\n\ndef test_locator_locator_and_framelocator_locator_should_accept_locator(\n    page: Page,\n) -> None:\n    page.set_content(\n        \"\"\"\n        <div><input value=outer></div>\n        <iframe srcdoc=\"<div><input value=inner></div>\"></iframe>\n    \"\"\"\n    )\n\n    input_locator = page.locator(\"input\")\n    assert input_locator.input_value() == \"outer\"\n    assert page.locator(\"div\").locator(input_locator).input_value() == \"outer\"\n    assert page.frame_locator(\"iframe\").locator(input_locator).input_value() == \"inner\"\n    assert (\n        page.frame_locator(\"iframe\").locator(\"div\").locator(input_locator).input_value()\n        == \"inner\"\n    )\n\n    div_locator = page.locator(\"div\")\n    assert div_locator.locator(\"input\").input_value() == \"outer\"\n    assert (\n        page.frame_locator(\"iframe\").locator(div_locator).locator(\"input\").input_value()\n        == \"inner\"\n    )\n\n\ndef route_iframe(page: Page) -> None:\n    page.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(\n            body='<iframe src=\"iframe.html\" name=\"frame1\"></iframe>',\n            content_type=\"text/html\",\n        ),\n    )\n    page.route(\n        \"**/iframe.html\",\n        lambda route: route.fulfill(\n            body=\"\"\"<html>\n          <div>\n            <button>Hello iframe</button>\n            <iframe src=\"iframe-2.html\"></iframe>\n          </div>\n          <span>1</span>\n          <span>2</span>\n        </html>\"\"\",\n            content_type=\"text/html\",\n        ),\n    )\n    page.route(\n        \"**/iframe-2.html\",\n        lambda route: route.fulfill(\n            body=\"<html><button>Hello nested iframe</button></html>\",\n            content_type=\"text/html\",\n        ),\n    )\n\n\ndef test_locators_frame_should_work_with_iframe(page: Page, server: Server) -> None:\n    route_iframe(page)\n    page.goto(server.EMPTY_PAGE)\n    button = page.frame_locator(\"iframe\").locator(\"button\")\n    button.wait_for()\n    assert button.inner_text() == \"Hello iframe\"\n    button.click()\n    assert (\n        repr(page.frame_locator(\"iframe\"))\n        == f\"<FrameLocator frame=<Frame name= url='{server.PREFIX}/empty.html'> selector='iframe'>\"\n    )\n\n\ndef test_locators_frame_should_work_for_nested_iframe(\n    page: Page, server: Server\n) -> None:\n    route_iframe(page)\n    page.goto(server.EMPTY_PAGE)\n    button = page.frame_locator(\"iframe\").frame_locator(\"iframe\").locator(\"button\")\n    button.wait_for()\n    assert button.inner_text() == \"Hello nested iframe\"\n    button.click()\n\n\ndef test_locators_frame_should_work_with_locator_frame_locator(\n    page: Page, server: Server\n) -> None:\n    route_iframe(page)\n    page.goto(server.EMPTY_PAGE)\n    button = page.locator(\"body\").frame_locator(\"iframe\").locator(\"button\")\n    button.wait_for()\n    assert button.inner_text() == \"Hello iframe\"\n    button.click()\n\n\ndef test_locator_content_frame_should_work(page: Page, server: Server) -> None:\n    route_iframe(page)\n    page.goto(server.EMPTY_PAGE)\n    locator = page.locator(\"iframe\")\n    frame_locator = locator.content_frame\n    button = frame_locator.locator(\"button\")\n    assert button.inner_text() == \"Hello iframe\"\n    expect(button).to_have_text(\"Hello iframe\")\n    button.click()\n\n\ndef test_frame_locator_owner_should_work(page: Page, server: Server) -> None:\n    route_iframe(page)\n    page.goto(server.EMPTY_PAGE)\n    frame_locator = page.frame_locator(\"iframe\")\n    locator = frame_locator.owner\n    expect(locator).to_be_visible()\n    assert locator.get_attribute(\"name\") == \"frame1\"\n\n\ndef route_ambiguous(page: Page) -> None:\n    page.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(\n            body=\"\"\"\n        <iframe src=\"iframe-1.html\"></iframe>\n        <iframe src=\"iframe-2.html\"></iframe>\n        <iframe src=\"iframe-3.html\"></iframe>\n    \"\"\",\n            content_type=\"text/html\",\n        ),\n    )\n    page.route(\n        \"**/iframe-*\",\n        lambda route: route.fulfill(\n            body=f\"<html><button>Hello from {urlparse(route.request.url).path[1:]}</button></html>\",\n            content_type=\"text/html\",\n        ),\n    )\n\n\ndef test_locator_frame_locator_should_throw_on_ambiguity(\n    page: Page, server: Server\n) -> None:\n    route_ambiguous(page)\n    page.goto(server.EMPTY_PAGE)\n    button = page.locator(\"body\").frame_locator(\"iframe\").locator(\"button\")\n    with pytest.raises(\n        Error,\n        match=r'.*strict mode violation: locator\\(\"body\"\\)\\.locator\\(\"iframe\"\\) resolved to 3 elements.*',\n    ):\n        button.wait_for()\n\n\ndef test_locator_frame_locator_should_not_throw_on_first_last_nth(\n    page: Page, server: Server\n) -> None:\n    route_ambiguous(page)\n    page.goto(server.EMPTY_PAGE)\n    button1 = page.locator(\"body\").frame_locator(\"iframe\").first.locator(\"button\")\n    assert button1.text_content() == \"Hello from iframe-1.html\"\n    button2 = page.locator(\"body\").frame_locator(\"iframe\").nth(1).locator(\"button\")\n    assert button2.text_content() == \"Hello from iframe-2.html\"\n    button3 = page.locator(\"body\").frame_locator(\"iframe\").last.locator(\"button\")\n    assert button3.text_content() == \"Hello from iframe-3.html\"\n\n\ndef test_drag_to(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    page.locator(\"#source\").drag_to(page.locator(\"#target\"))\n    assert (\n        page.eval_on_selector(\n            \"#target\", \"target => target.contains(document.querySelector('#source'))\"\n        )\n        is True\n    )\n\n\ndef test_locator_query_should_filter_by_text(page: Page, server: Server) -> None:\n    page.set_content(\"<div>Foobar</div><div>Bar</div>\")\n    expect(page.locator(\"div\", has_text=\"Foo\")).to_have_text(\"Foobar\")\n\n\ndef test_locator_query_should_filter_by_text_2(page: Page, server: Server) -> None:\n    page.set_content(\"<div>foo <span>hello world</span> bar</div>\")\n    expect(page.locator(\"div\", has_text=\"hello world\")).to_have_text(\n        \"foo hello world bar\"\n    )\n\n\ndef test_locator_query_should_filter_by_regex(page: Page, server: Server) -> None:\n    page.set_content(\"<div>Foobar</div><div>Bar</div>\")\n    expect(page.locator(\"div\", has_text=re.compile(r\"Foo.*\"))).to_have_text(\"Foobar\")\n\n\ndef test_locator_query_should_filter_by_text_with_quotes(\n    page: Page, server: Server\n) -> None:\n    page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    expect(page.locator(\"div\", has_text='Hello \"world\"')).to_have_text('Hello \"world\"')\n\n\ndef test_locator_query_should_filter_by_regex_with_quotes(\n    page: Page, server: Server\n) -> None:\n    page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    expect(page.locator(\"div\", has_text=re.compile('Hello \"world\"'))).to_have_text(\n        'Hello \"world\"'\n    )\n\n\ndef test_locator_query_should_filter_by_regex_and_regexp_flags(\n    page: Page, server: Server\n) -> None:\n    page.set_content('<div>Hello \"world\"</div><div>Hello world</div>')\n    expect(\n        page.locator(\"div\", has_text=re.compile('hElLo \"world', re.IGNORECASE))\n    ).to_have_text('Hello \"world\"')\n\n\ndef test_locator_should_return_page(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/frames/two-frames.html\")\n    outer = page.locator(\"#outer\")\n    assert outer.page == page\n\n    inner = outer.locator(\"#inner\")\n    assert inner.page == page\n\n    in_frame = page.frames[1].locator(\"div\")\n    assert in_frame.page == page\n\n\ndef test_locator_should_support_has_locator(page: Page, server: Server) -> None:\n    page.set_content(\"<div><span>hello</span></div><div><span>world</span></div>\")\n    expect(page.locator(\"div\", has=page.locator(\"text=world\"))).to_have_count(1)\n    assert (\n        page.locator(\"div\", has=page.locator(\"text=world\")).evaluate(\"e => e.outerHTML\")\n        == \"<div><span>world</span></div>\"\n    )\n    expect(page.locator(\"div\", has=page.locator('text=\"hello\"'))).to_have_count(1)\n    assert (\n        page.locator(\"div\", has=page.locator('text=\"hello\"')).evaluate(\n            \"e => e.outerHTML\"\n        )\n        == \"<div><span>hello</span></div>\"\n    )\n    expect(page.locator(\"div\", has=page.locator(\"xpath=./span\"))).to_have_count(2)\n    expect(page.locator(\"div\", has=page.locator(\"span\"))).to_have_count(2)\n    expect(page.locator(\"div\", has=page.locator(\"span\", has_text=\"wor\"))).to_have_count(\n        1\n    )\n    assert (\n        page.locator(\"div\", has=page.locator(\"span\", has_text=\"wor\")).evaluate(\n            \"e => e.outerHTML\"\n        )\n        == \"<div><span>world</span></div>\"\n    )\n    expect(\n        page.locator(\n            \"div\",\n            has=page.locator(\"span\"),\n            has_text=\"wor\",\n        )\n    ).to_have_count(1)\n\n\ndef test_locator_should_enforce_same_frame_for_has_locator(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.PREFIX + \"/frames/two-frames.html\")\n    child = page.frames[1]\n    with pytest.raises(Error) as exc_info:\n        page.locator(\"div\", has=child.locator(\"span\"))\n    assert (\n        'Inner \"has\" locator must belong to the same frame.' in exc_info.value.message\n    )\n\n\ndef test_locator_should_support_locator_or(page: Page, server: Server) -> None:\n    page.set_content(\"<div>hello</div><span>world</span>\")\n    expect(page.locator(\"div\").or_(page.locator(\"span\"))).to_have_count(2)\n    expect(page.locator(\"div\").or_(page.locator(\"span\"))).to_have_text(\n        [\"hello\", \"world\"]\n    )\n    expect(\n        page.locator(\"span\").or_(page.locator(\"article\")).or_(page.locator(\"div\"))\n    ).to_have_text([\"hello\", \"world\"])\n    expect(page.locator(\"article\").or_(page.locator(\"someting\"))).to_have_count(0)\n    expect(page.locator(\"article\").or_(page.locator(\"div\"))).to_have_text(\"hello\")\n    expect(page.locator(\"article\").or_(page.locator(\"span\"))).to_have_text(\"world\")\n    expect(page.locator(\"div\").or_(page.locator(\"article\"))).to_have_text(\"hello\")\n    expect(page.locator(\"span\").or_(page.locator(\"article\"))).to_have_text(\"world\")\n\n\ndef test_locator_highlight_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/grid.html\")\n    page.locator(\".box\").nth(3).highlight()\n    assert page.locator(\"x-pw-glass\").is_visible()\n\n\ndef test_should_support_locator_that(page: Page) -> None:\n    page.set_content(\n        \"<section><div><span>hello</span></div><div><span>world</span></div></section>\"\n    )\n\n    expect(page.locator(\"div\").filter(has_text=\"hello\")).to_have_count(1)\n    expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"hello\")\n    ).to_have_count(1)\n    expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(0)\n    expect(\n        page.locator(\"section\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(1)\n    expect(page.locator(\"div\").filter(has_text=\"hello\").locator(\"span\")).to_have_count(\n        1\n    )\n    expect(\n        page.locator(\"div\").filter(has=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    expect(page.locator(\"div\").filter(has=page.locator(\"span\"))).to_have_count(2)\n    expect(\n        page.locator(\"div\").filter(\n            has=page.locator(\"span\"),\n            has_text=\"world\",\n        )\n    ).to_have_count(1)\n\n\ndef test_should_filter_by_case_insensitive_regex_in_a_child(page: Page) -> None:\n    page.set_content('<div class=\"test\"><h5>Title Text</h5></div>')\n    expect(\n        page.locator(\"div\", has_text=re.compile(r\"^title text$\", re.I))\n    ).to_have_text(\"Title Text\")\n\n\ndef test_should_filter_by_case_insensitive_regex_in_multiple_children(\n    page: Page,\n) -> None:\n    page.set_content('<div class=\"test\"><h5>Title</h5> <h2><i>Text</i></h2></div>')\n    expect(\n        page.locator(\"div\", has_text=re.compile(r\"^title text$\", re.I))\n    ).to_have_class(\"test\")\n\n\ndef test_should_filter_by_regex_with_special_symbols(page: Page) -> None:\n    page.set_content(\n        '<div class=\"test\"><h5>First/\"and\"</h5><h2><i>Second\\\\</i></h2></div>'\n    )\n    expect(\n        page.locator(\"div\", has_text=re.compile(r'^first\\/\".*\"second\\\\$', re.S | re.I))\n    ).to_have_class(\"test\")\n\n\ndef test_should_support_locator_filter(page: Page) -> None:\n    page.set_content(\n        \"<section><div><span>hello</span></div><div><span>world</span></div></section>\"\n    )\n\n    expect(page.locator(\"div\").filter(has_text=\"hello\")).to_have_count(1)\n    expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"hello\")\n    ).to_have_count(1)\n    expect(\n        page.locator(\"div\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(0)\n    expect(\n        page.locator(\"section\", has_text=\"hello\").filter(has_text=\"world\")\n    ).to_have_count(1)\n    expect(page.locator(\"div\").filter(has_text=\"hello\").locator(\"span\")).to_have_count(\n        1\n    )\n    expect(\n        page.locator(\"div\").filter(has=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    expect(page.locator(\"div\").filter(has=page.locator(\"span\"))).to_have_count(2)\n    expect(\n        page.locator(\"div\").filter(\n            has=page.locator(\"span\"),\n            has_text=\"world\",\n        )\n    ).to_have_count(1)\n    expect(\n        page.locator(\"div\").filter(has_not=page.locator(\"span\", has_text=\"world\"))\n    ).to_have_count(1)\n    expect(page.locator(\"div\").filter(has_not=page.locator(\"section\"))).to_have_count(2)\n    expect(page.locator(\"div\").filter(has_not=page.locator(\"span\"))).to_have_count(0)\n\n    expect(page.locator(\"div\").filter(has_not_text=\"hello\")).to_have_count(1)\n    expect(page.locator(\"div\").filter(has_not_text=\"foo\")).to_have_count(2)\n\n\ndef test_locators_should_support_locator_and(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <div data-testid=foo>hello</div><div data-testid=bar>world</div>\n        <span data-testid=foo>hello2</span><span data-testid=bar>world2</span>\n    \"\"\"\n    )\n    expect(page.locator(\"div\").and_(page.locator(\"div\"))).to_have_count(2)\n    expect(page.locator(\"div\").and_(page.get_by_test_id(\"foo\"))).to_have_text([\"hello\"])\n    expect(page.locator(\"div\").and_(page.get_by_test_id(\"bar\"))).to_have_text([\"world\"])\n    expect(page.get_by_test_id(\"foo\").and_(page.locator(\"div\"))).to_have_text([\"hello\"])\n    expect(page.get_by_test_id(\"bar\").and_(page.locator(\"span\"))).to_have_text(\n        [\"world2\"]\n    )\n    expect(\n        page.locator(\"span\").and_(page.get_by_test_id(re.compile(\"bar|foo\")))\n    ).to_have_count(2)\n\n\ndef test_locators_has_does_not_encode_unicode(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    locators = [\n        page.locator(\"button\", has_text=\"Драматург\"),\n        page.locator(\"button\", has_text=re.compile(\"Драматург\")),\n        page.locator(\"button\", has=page.locator(\"text=Драматург\")),\n    ]\n    for locator in locators:\n        with pytest.raises(Error) as exc_info:\n            locator.click(timeout=1_000)\n        assert \"Драматург\" in exc_info.value.message\n\n\ndef test_locators_should_focus_and_blur_a_button(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/button.html\")\n    button = page.locator(\"button\")\n    assert not button.evaluate(\"button => document.activeElement === button\")\n\n    focused = False\n    blurred = False\n\n    def focus_event() -> None:\n        nonlocal focused\n        focused = True\n\n    def blur_event() -> None:\n        nonlocal blurred\n        blurred = True\n\n    page.expose_function(\"focusEvent\", focus_event)\n    page.expose_function(\"blurEvent\", blur_event)\n    button.evaluate(\n        \"\"\"button => {\n        button.addEventListener('focus', window['focusEvent']);\n        button.addEventListener('blur', window['blurEvent']);\n    }\"\"\"\n    )\n\n    button.focus()\n    assert focused\n    assert not blurred\n    assert button.evaluate(\"button => document.activeElement === button\")\n\n    button.blur()\n    assert focused\n    assert blurred\n    assert not button.evaluate(\"button => document.activeElement === button\")\n\n\ndef test_locator_all_should_work(page: Page) -> None:\n    page.set_content(\"<div><p>A</p><p>B</p><p>C</p></div>\")\n    texts = []\n    for p in page.locator(\"p\").all():\n        texts.append(p.text_content())\n    assert texts == [\"A\", \"B\", \"C\"]\n\n\ndef test_locator_click_timeout_error_should_contain_call_log(page: Page) -> None:\n    with pytest.raises(Error) as exc_info:\n        page.get_by_role(\"button\", name=\"Hello Python\").click(timeout=42)\n    formatted_exception = \"\".join(\n        traceback.format_exception(type(exc_info.value), value=exc_info.value, tb=None)\n    )\n    assert \"Locator.click: Timeout 42ms exceeded.\" in formatted_exception\n    assert (\n        'waiting for get_by_role(\"button\", name=\"Hello Python\")' in formatted_exception\n    )\n    assert (\n        \"During handling of the above exception, another exception occurred\"\n        not in formatted_exception\n    )\n\n\ndef test_locator_should_ignore_deprecated_is_hidden_and_visible_timeout(\n    page: Page,\n) -> None:\n    page.set_content(\"<div>foo</div>\")\n    div = page.locator(\"div\")\n    assert div.is_hidden(timeout=10) is False\n    assert div.is_visible(timeout=10) is True\n"
  },
  {
    "path": "tests/sync/test_network.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Page, Playwright, Route\nfrom tests.server import Server\n\n\ndef test_response_server_addr(page: Page, server: Server) -> None:\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    server_addr = response.server_addr()\n    assert server_addr\n    assert server_addr[\"port\"] == server.PORT\n    assert server_addr[\"ipAddress\"] in [\"127.0.0.1\", \"[::1]\"]\n\n\ndef test_response_security_details(\n    browser: Browser,\n    https_server: Server,\n    browser_name: str,\n    is_win: bool,\n    is_linux: bool,\n) -> None:\n    if (browser_name == \"webkit\" and is_linux) or (browser_name == \"webkit\" and is_win):\n        pytest.skip(\"https://github.com/microsoft/playwright/issues/6759\")\n    page = browser.new_page(ignore_https_errors=True)\n    response = page.goto(https_server.EMPTY_PAGE)\n    assert response\n    response.finished()\n    security_details = response.security_details()\n    assert security_details\n    if browser_name == \"webkit\" and is_win:\n        assert security_details == {\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": -1,\n        }\n    elif browser_name == \"webkit\":\n        assert security_details == {\n            \"protocol\": \"TLS 1.3\",\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": 33086084863,\n        }\n    else:\n        assert security_details == {\n            \"issuer\": \"puppeteer-tests\",\n            \"protocol\": \"TLS 1.3\",\n            \"subjectName\": \"puppeteer-tests\",\n            \"validFrom\": 1550084863,\n            \"validTo\": 33086084863,\n        }\n    page.close()\n\n\ndef test_response_security_details_none_without_https(\n    page: Page, server: Server\n) -> None:\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    security_details = response.security_details()\n    assert security_details is None\n\n\ndef test_should_fulfill_with_global_fetch_result(\n    page: Page, playwright: Playwright, server: Server\n) -> None:\n    def handle_request(route: Route) -> None:\n        request = playwright.request.new_context()\n        response = request.get(server.PREFIX + \"/simple.json\")\n        route.fulfill(response=response)\n        request.dispose()\n\n    page.route(\"**/*\", handle_request)\n\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 200\n    assert response.json() == {\"foo\": \"bar\"}\n\n\ndef test_should_report_if_response_was_from_service_worker(\n    page: Page, server: Server\n) -> None:\n    response = page.goto(server.PREFIX + \"/serviceworkers/fetch/sw.html\")\n    assert response\n    assert not response.from_service_worker\n    page.evaluate(\"() => window.activationPromise\")\n    with page.expect_response(\"**/example.txt\") as response_info:\n        page.evaluate(\"() => fetch('/example.txt')\")\n    assert response_info.value.from_service_worker\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_report_service_worker_request(page: Page, server: Server) -> None:\n    with page.context.expect_event(\"serviceworker\") as worker_info:\n        page.goto(server.PREFIX + \"/serviceworkers/fetch/sw.html\")\n        page.evaluate(\"() => window.activationPromise\")\n    with page.context.expect_event(\n        \"request\", lambda r: r.service_worker is not None\n    ) as request_info:\n        page.evaluate(\"() => fetch('/example.txt')\")\n    assert request_info.value.service_worker == worker_info.value\n"
  },
  {
    "path": "tests/sync/test_page.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page\nfrom tests.server import Server\n\n\ndef test_input_value(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/textarea.html\")\n\n    page.fill(\"input\", \"my-text-content\")\n    assert page.input_value(\"input\") == \"my-text-content\"\n\n    page.fill(\"input\", \"\")\n    assert page.input_value(\"input\") == \"\"\n\n\ndef test_drag_and_drop_helper_method(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/drag-n-drop.html\")\n    page.drag_and_drop(\"#source\", \"#target\")\n    assert (\n        page.eval_on_selector(\n            \"#target\", \"target => target.contains(document.querySelector('#source'))\"\n        )\n        is True\n    )\n\n\ndef test_should_check_box_using_set_checked(page: Page) -> None:\n    page.set_content(\"`<input id='checkbox' type='checkbox'></input>`\")\n    page.set_checked(\"input\", True)\n    assert page.evaluate(\"checkbox.checked\") is True\n    page.set_checked(\"input\", False)\n    assert page.evaluate(\"checkbox.checked\") is False\n\n\ndef test_should_set_bodysize_and_headersize(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    with page.expect_request(\"*/**\") as request_info:\n        page.evaluate(\n            \"() => fetch('./get', { method: 'POST', body: '12345'}).then(r => r.text())\"\n        )\n    request = request_info.value\n    sizes = request.sizes()\n    assert sizes[\"requestBodySize\"] == 5\n    assert sizes[\"requestHeadersSize\"] >= 300\n\n\ndef test_should_set_bodysize_to_0(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    with page.expect_request(\"*/**\") as request_info:\n        page.evaluate(\"() => fetch('./get').then(r => r.text())\")\n\n    request = request_info.value\n    sizes = request.sizes()\n    assert sizes[\"requestBodySize\"] == 0\n    assert sizes[\"requestHeadersSize\"] >= 200\n\n\ndef test_sync_stacks_should_work(page: Page, server: Server) -> None:\n    page.route(\"**/empty.html\", lambda route: route.abort())\n    with pytest.raises(Error) as exc_info:\n        page.goto(server.EMPTY_PAGE)\n    assert exc_info.value.stack\n    assert __file__ in exc_info.value.stack\n\n\ndef test_emitted_for_domcontentloaded_and_load(page: Page, server: Server) -> None:\n    with page.expect_event(\"domcontentloaded\") as dom_info:\n        with page.expect_event(\"load\") as load_info:\n            page.goto(server.EMPTY_PAGE)\n    assert isinstance(dom_info.value, Page)\n    assert isinstance(load_info.value, Page)\n\n\ndef test_page_pause_should_reset_default_timeouts(\n    page: Page, headless: bool, server: Server\n) -> None:\n    if not headless:\n        pytest.skip()\n\n    page.goto(server.EMPTY_PAGE)\n    page.pause()\n    with pytest.raises(Error, match=\"Timeout 30000ms exceeded.\"):\n        page.get_by_text(\"foo\").click()\n\n\ndef test_page_pause_should_reset_custom_timeouts(\n    page: Page, headless: bool, server: Server\n) -> None:\n    if not headless:\n        pytest.skip()\n\n    page.set_default_timeout(123)\n    page.set_default_navigation_timeout(456)\n    page.goto(server.EMPTY_PAGE)\n    page.pause()\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded.\"):\n        page.get_by_text(\"foo\").click()\n\n    server.set_route(\"/empty.html\", lambda route: None)\n    with pytest.raises(Error, match=\"Timeout 456ms exceeded.\"):\n        page.goto(server.EMPTY_PAGE)\n\n\ndef test_page_should_ignore_deprecated_is_hidden_and_visible_timeout(\n    page: Page,\n) -> None:\n    page.set_content(\"<div>foo</div>\")\n    assert page.is_hidden(\"div\", timeout=10) is False\n    assert page.is_visible(\"div\", timeout=10) is True\n"
  },
  {
    "path": "tests/sync/test_page_add_locator_handler.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport pytest\n\nfrom playwright.sync_api import Error, Locator, Page, expect\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\ndef test_should_work(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    before_count = 0\n    after_count = 0\n\n    original_locator = page.get_by_text(\"This interstitial covers the button\")\n\n    def handler(locator: Locator) -> None:\n        assert locator == original_locator\n        nonlocal before_count\n        nonlocal after_count\n        before_count += 1\n        page.locator(\"#close\").click()\n        after_count += 1\n\n    page.add_locator_handler(original_locator, handler)\n\n    for args in [\n        [\"mouseover\", 1],\n        [\"mouseover\", 1, \"capture\"],\n        [\"mouseover\", 2],\n        [\"mouseover\", 2, \"capture\"],\n        [\"pointerover\", 1],\n        [\"pointerover\", 1, \"capture\"],\n        [\"none\", 1],\n        [\"remove\", 1],\n        [\"hide\", 1],\n    ]:\n        page.locator(\"#aside\").hover()\n        before_count = 0\n        after_count = 0\n        page.evaluate(\n            \"(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }\",\n            args,\n        )\n        assert before_count == 0\n        assert after_count == 0\n        page.locator(\"#target\").click()\n        assert before_count == args[1]\n        assert after_count == args[1]\n        assert page.evaluate(\"window.clicked\") == 1\n        expect(page.locator(\"#interstitial\")).not_to_be_visible()\n\n\ndef test_should_work_with_a_custom_check(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    def handler() -> None:\n        if page.get_by_text(\"This interstitial covers the button\").is_visible():\n            page.locator(\"#close\").click()\n\n    page.add_locator_handler(page.locator(\"body\"), handler, no_wait_after=True)\n\n    for args in [\n        [\"mouseover\", 2],\n        [\"none\", 1],\n        [\"remove\", 1],\n        [\"hide\", 1],\n    ]:\n        page.locator(\"#aside\").hover()\n        page.evaluate(\n            \"(args) => { window.clicked = 0; window.setupAnnoyingInterstitial(...args); }\",\n            args,\n        )\n        page.locator(\"#target\").click()\n        assert page.evaluate(\"window.clicked\") == 1\n        expect(page.locator(\"#interstitial\")).not_to_be_visible()\n\n\ndef test_should_work_with_locator_hover(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        '() => { window.setupAnnoyingInterstitial(\"pointerover\", 1, \"capture\"); }'\n    )\n    page.locator(\"#target\").hover()\n    expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert (\n        page.eval_on_selector(\n            \"#target\", \"e => window.getComputedStyle(e).backgroundColor\"\n        )\n        == \"rgb(255, 255, 0)\"\n    )\n\n\ndef test_should_not_work_with_force_true(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n\n    page.locator(\"#aside\").hover()\n    page.evaluate('() => { window.setupAnnoyingInterstitial(\"none\", 1); }')\n    page.locator(\"#target\").click(force=True, timeout=2000)\n    assert page.locator(\"#interstitial\").is_visible()\n    assert page.evaluate(\"window.clicked\") is None\n\n\ndef test_should_throw_when_page_closes(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), lambda: page.close()\n    )\n\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"mouseover\", 1); }'\n    )\n    with pytest.raises(Error) as exc:\n        page.locator(\"#target\").click()\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc.value.message\n\n\ndef test_should_throw_when_handler_times_out(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    called = 0\n\n    def handler() -> None:\n        nonlocal called\n        called += 1\n        # Deliberately timeout.\n        try:\n            page.wait_for_timeout(9999999)\n        except Exception:\n            pass\n\n    page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), handler\n    )\n\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"mouseover\", 1); }'\n    )\n    with pytest.raises(Error) as exc:\n        page.locator(\"#target\").click(timeout=3000)\n    assert \"Timeout 3000ms exceeded\" in exc.value.message\n\n    with pytest.raises(Error) as exc:\n        page.locator(\"#target\").click(timeout=3000)\n    assert \"Timeout 3000ms exceeded\" in exc.value.message\n\n    # Should not enter the same handler while it is still running.\n    assert called == 1\n\n\ndef test_should_work_with_to_be_visible(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n\n    called = 0\n\n    def handler() -> None:\n        nonlocal called\n        called += 1\n        page.locator(\"#close\").click()\n\n    page.add_locator_handler(\n        page.get_by_text(\"This interstitial covers the button\"), handler\n    )\n\n    page.evaluate(\n        '() => { window.clicked = 0; window.setupAnnoyingInterstitial(\"remove\", 1); }'\n    )\n    expect(page.locator(\"#target\")).to_be_visible()\n    expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 1\n\n\ndef test_should_work_when_owner_frame_detaches(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.evaluate(\n        \"\"\"\n    () => {\n        const iframe = document.createElement('iframe');\n        iframe.src = 'data:text/html,<body>hello from iframe</body>';\n        document.body.append(iframe);\n\n        const target = document.createElement('button');\n        target.textContent = 'Click me';\n        target.id = 'target';\n        target.addEventListener('click', () => window._clicked = true);\n        document.body.appendChild(target);\n\n        const closeButton = document.createElement('button');\n        closeButton.textContent = 'close';\n        closeButton.id = 'close';\n        closeButton.addEventListener('click', () => iframe.remove());\n        document.body.appendChild(closeButton);\n    }\n    \"\"\"\n    )\n    page.add_locator_handler(\n        page.frame_locator(\"iframe\").locator(\"body\"),\n        lambda: page.locator(\"#close\").click(),\n    )\n    page.locator(\"#target\").click()\n    assert page.query_selector(\"iframe\") is None\n    assert page.evaluate(\"window._clicked\") is True\n\n\ndef test_should_work_with_times_option(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler() -> None:\n        nonlocal called\n        called += 1\n\n    page.add_locator_handler(\n        page.locator(\"body\"), _handler, no_wait_after=True, times=2\n    )\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('mouseover', 4);\n    }\n    \"\"\"\n    )\n    with pytest.raises(Error) as exc_info:\n        page.locator(\"#target\").click(timeout=3000)\n    assert called == 2\n    assert page.evaluate(\"window.clicked\") == 0\n    expect(page.locator(\"#interstitial\")).to_be_visible()\n    assert \"Timeout 3000ms exceeded\" in exc_info.value.message\n    assert (\n        '<div>This interstitial covers the button</div> from <div class=\"visible\" id=\"interstitial\">…</div> subtree intercepts pointer events'\n        in exc_info.value.message\n    )\n\n\ndef test_should_wait_for_hidden_by_default(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler(button: Locator) -> None:\n        nonlocal called\n        called += 1\n        button.click()\n\n    page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('timeout', 1);\n    }\n    \"\"\"\n    )\n    page.locator(\"#target\").click()\n    assert page.evaluate(\"window.clicked\") == 1\n    expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 1\n\n\ndef test_should_wait_for_hidden_by_default_2(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler() -> None:\n        nonlocal called\n        called += 1\n\n    page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    with pytest.raises(Error) as exc_info:\n        page.locator(\"#target\").click(timeout=3000)\n    assert page.evaluate(\"window.clicked\") == 0\n    assert page.locator(\"#interstitial\").is_visible()\n    assert called == 1\n    assert (\n        'locator handler has finished, waiting for get_by_role(\"button\", name=\"close\") to be hidden'\n        in exc_info.value.message\n    )\n\n\ndef test_should_work_with_noWaitAfter(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler(button: Locator) -> None:\n        nonlocal called\n        called += 1\n        if called == 1:\n            button.click()\n        else:\n            page.locator(\"#interstitial\").wait_for(state=\"hidden\")\n\n    page.add_locator_handler(\n        page.get_by_role(\"button\", name=\"close\"), _handler, no_wait_after=True\n    )\n    page.locator(\"#aside\").hover()\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('timeout', 1);\n    }\n    \"\"\"\n    )\n    page.locator(\"#target\").click()\n    assert page.evaluate(\"window.clicked\") == 1\n    expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    assert called == 2\n\n\ndef test_should_removeLocatorHandler(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/input/handle-locator.html\")\n    called = 0\n\n    def _handler(locator: Locator) -> None:\n        nonlocal called\n        called += 1\n        locator.click()\n\n    page.add_locator_handler(page.get_by_role(\"button\", name=\"close\"), _handler)\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    page.locator(\"#target\").click()\n    assert called == 1\n    assert page.evaluate(\"window.clicked\") == 1\n    expect(page.locator(\"#interstitial\")).not_to_be_visible()\n    page.evaluate(\n        \"\"\"\n    () => {\n        window.clicked = 0;\n        window.setupAnnoyingInterstitial('hide', 1);\n    }\n    \"\"\"\n    )\n    page.remove_locator_handler(page.get_by_role(\"button\", name=\"close\"))\n    with pytest.raises(Error) as error:\n        page.locator(\"#target\").click(timeout=3000)\n    assert called == 1\n    assert page.evaluate(\"window.clicked\") == 0\n    expect(page.locator(\"#interstitial\")).to_be_visible()\n    assert \"Timeout 3000ms exceeded\" in error.value.message\n"
  },
  {
    "path": "tests/sync/test_page_aria_snapshot.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\n\nimport pytest\n\nfrom playwright.sync_api import Locator, Page, expect\n\n\ndef _unshift(snapshot: str) -> str:\n    lines = snapshot.split(\"\\n\")\n    whitespace_prefix_length = 100\n    for line in lines:\n        if not line.strip():\n            continue\n        match = re.match(r\"^(\\s*)\", line)\n        if match and len(match[1]) < whitespace_prefix_length:\n            whitespace_prefix_length = len(match[1])\n    return \"\\n\".join(\n        [line[whitespace_prefix_length:] for line in lines if line.strip()]\n    )\n\n\ndef check_and_match_snapshot(locator: Locator, snapshot: str) -> None:\n    assert locator.aria_snapshot() == _unshift(snapshot)\n    expect(locator).to_match_aria_snapshot(snapshot)\n\n\ndef test_should_snapshot(page: Page) -> None:\n    page.set_content(\"<h1>title</h1>\")\n    check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - heading \"title\" [level=1]\n    \"\"\",\n    )\n\n\ndef test_should_snapshot_list(page: Page) -> None:\n    page.set_content(\"<h1>title</h1><h1>title 2</h1>\")\n    check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - heading \"title\" [level=1]\n      - heading \"title 2\" [level=1]\n    \"\"\",\n    )\n\n\ndef test_should_snapshot_list_with_list(page: Page) -> None:\n    page.set_content(\"<ul><li>one</li><li>two</li></ul>\")\n    check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list:\n        - listitem: one\n        - listitem: two\n    \"\"\",\n    )\n\n\ndef test_should_snapshot_list_with_accessible_name(page: Page) -> None:\n    page.set_content('<ul aria-label=\"my list\"><li>one</li><li>two</li></ul>')\n    check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list \"my list\":\n        - listitem: one\n        - listitem: two\n    \"\"\",\n    )\n\n\ndef test_should_snapshot_complex(page: Page) -> None:\n    page.set_content('<ul><li><a href=\"about:blank\">link</a></li></ul>')\n    check_and_match_snapshot(\n        page.locator(\"body\"),\n        \"\"\"\n      - list:\n        - listitem:\n          - link \"link\":\n            - /url: about:blank\n    \"\"\",\n    )\n\n\ndef test_should_snapshot_with_unexpected_children_equal(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n      <ul>\n        <li>One</li>\n        <li>Two</li>\n        <li>Three</li>\n      </ul>\n    \"\"\"\n    )\n    expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n      - list:\n        - listitem: One\n        - listitem: Three\n    \"\"\",\n    )\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n        - list:\n          - /children: equal\n          - listitem: One\n          - listitem: Three\n      \"\"\",\n            timeout=1000,\n        )\n\n\ndef test_should_snapshot_with_unexpected_children_deep_equal(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n      <ul>\n        <li>\n          <ul>\n            <li>1.1</li>\n            <li>1.2</li>\n          </ul>\n        </li>\n      </ul>\n    \"\"\"\n    )\n    expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n      - list:\n        - listitem:\n          - list:\n            - listitem: 1.1\n    \"\"\",\n    )\n    expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - list:\n          - /children: equal\n          - listitem:\n            - list:\n              - listitem: 1.1\n      \"\"\",\n    )\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n          - list:\n            - /children: deep-equal\n            - listitem:\n              - list:\n                - listitem: 1.1\n        \"\"\",\n            timeout=1000,\n        )\n\n\ndef test_should_snapshot_with_restored_contain_mode_inside_deep_equal(\n    page: Page,\n) -> None:\n    page.set_content(\n        \"\"\"\n      <ul>\n        <li>\n          <ul>\n            <li>1.1</li>\n            <li>1.2</li>\n          </ul>\n        </li>\n      </ul>\n    \"\"\"\n    )\n    with pytest.raises(AssertionError):\n        expect(page.locator(\"body\")).to_match_aria_snapshot(\n            \"\"\"\n        - list:\n          - /children: deep-equal\n          - listitem:\n            - list:\n              - listitem: 1.1\n      \"\"\",\n            timeout=1000,\n        )\n    expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - list:\n          - /children: deep-equal\n          - listitem:\n            - list:\n              - /children: contain\n              - listitem: 1.1\n      \"\"\",\n    )\n\n\ndef test_match_values_both_against_regex_and_string(page: Page) -> None:\n    page.set_content('<a href=\"/auth?r=/\">Log in</a>')\n    expect(page.locator(\"body\")).to_match_aria_snapshot(\n        \"\"\"\n        - link \"Log in\":\n          - /url: /auth?r=/\n      \"\"\",\n    )\n"
  },
  {
    "path": "tests/sync/test_page_clock.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nfrom typing import Any, Generator, List\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page\nfrom tests.server import Server\n\n\n@pytest.fixture(autouse=True)\ndef calls(page: Page) -> List[Any]:\n    calls: List[Any] = []\n    page.expose_function(\"stub\", lambda *args: calls.append(list(args)))\n    return calls\n\n\nclass TestRunFor:\n    @pytest.fixture(autouse=True)\n    def before_each(self, page: Page) -> Generator[None, None, None]:\n        page.clock.install(time=0)\n        page.clock.pause_at(1000)\n        yield\n\n    def test_run_for_triggers_immediately_without_specified_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(window.stub)\")\n        page.clock.run_for(0)\n        assert len(calls) == 1\n\n    def test_run_for_does_not_trigger_without_sufficient_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(window.stub, 100)\")\n        page.clock.run_for(10)\n        assert len(calls) == 0\n\n    def test_run_for_triggers_after_sufficient_delay(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(window.stub, 100)\")\n        page.clock.run_for(100)\n        assert len(calls) == 1\n\n    def test_run_for_triggers_simultaneous_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(window.stub, 100); setTimeout(window.stub, 100)\")\n        page.clock.run_for(100)\n        assert len(calls) == 2\n\n    def test_run_for_triggers_multiple_simultaneous_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\n            \"setTimeout(window.stub, 100); setTimeout(window.stub, 100); setTimeout(window.stub, 99); setTimeout(window.stub, 100)\"\n        )\n        page.clock.run_for(100)\n        assert len(calls) == 4\n\n    def test_run_for_waits_after_setTimeout_was_called(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(window.stub, 150)\")\n        page.clock.run_for(50)\n        assert len(calls) == 0\n        page.clock.run_for(100)\n        assert len(calls) == 1\n\n    def test_run_for_triggers_event_when_some_throw(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\n            \"setTimeout(() => { throw new Error(); }, 100); setTimeout(window.stub, 120)\"\n        )\n        with pytest.raises(Error):\n            page.clock.run_for(120)\n        assert len(calls) == 1\n\n    def test_run_for_creates_updated_Date_while_ticking(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.clock.set_system_time(0)\n        page.evaluate(\"setInterval(() => { window.stub(new Date().getTime()); }, 10)\")\n        page.clock.run_for(100)\n        assert calls == [\n            [10],\n            [20],\n            [30],\n            [40],\n            [50],\n            [60],\n            [70],\n            [80],\n            [90],\n            [100],\n        ]\n\n    def test_run_for_passes_8_seconds(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\"setInterval(window.stub, 4000)\")\n        page.clock.run_for(\"08\")\n        assert len(calls) == 2\n\n    def test_run_for_passes_1_minute(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\"setInterval(window.stub, 6000)\")\n        page.clock.run_for(\"01:00\")\n        assert len(calls) == 10\n\n    def test_run_for_passes_2_hours_34_minutes_and_10_seconds(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setInterval(window.stub, 10000)\")\n        page.clock.run_for(\"02:34:10\")\n        assert len(calls) == 925\n\n    def test_run_for_throws_for_invalid_format(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setInterval(window.stub, 10000)\")\n        with pytest.raises(Error):\n            page.clock.run_for(\"12:02:34:10\")\n        assert len(calls) == 0\n\n    def test_run_for_returns_the_current_now_value(self, page: Page) -> None:\n        page.clock.set_system_time(0)\n        value = 200\n        page.clock.run_for(value)\n        assert page.evaluate(\"Date.now()\") == value\n\n\nclass TestFastForward:\n    @pytest.fixture(autouse=True)\n    def before_each(self, page: Page) -> Generator[None, None, None]:\n        page.clock.install(time=0)\n        page.clock.pause_at(1)\n        yield\n\n    def test_ignores_timers_which_wouldnt_be_run(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\n            \"setTimeout(() => { window.stub('should not be logged'); }, 1000)\"\n        )\n        page.clock.fast_forward(500)\n        assert len(calls) == 0\n\n    def test_pushes_back_execution_time_for_skipped_timers(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.evaluate(\"setTimeout(() => { window.stub(Date.now()); }, 1000)\")\n        page.clock.fast_forward(2000)\n        assert calls == [[1000 + 2000]]\n\n    def test_supports_string_time_arguments(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\n            \"setTimeout(() => { window.stub(Date.now()); }, 100000)\"\n        )  # 100000 = 1:40\n        page.clock.fast_forward(\"01:50\")\n        assert calls == [[1000 + 110000]]\n\n\nclass TestStubTimers:\n    @pytest.fixture(autouse=True)\n    def before_each(self, page: Page) -> Generator[None, None, None]:\n        page.clock.install(time=0)\n        page.clock.pause_at(1)\n        yield\n\n    def test_sets_initial_timestamp(self, page: Page) -> None:\n        page.clock.set_system_time(1.4)\n        assert page.evaluate(\"Date.now()\") == 1400\n\n    def test_replaces_global_setTimeout(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\"setTimeout(window.stub, 1000)\")\n        page.clock.run_for(1000)\n        assert len(calls) == 1\n\n    def test_global_fake_setTimeout_should_return_id(self, page: Page) -> None:\n        to = page.evaluate(\"setTimeout(window.stub, 1000)\")\n        assert isinstance(to, int)\n\n    def test_replaces_global_clearTimeout(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\n            \"\"\"\n            const to = setTimeout(window.stub, 1000);\n            clearTimeout(to);\n        \"\"\"\n        )\n        page.clock.run_for(1000)\n        assert len(calls) == 0\n\n    def test_replaces_global_setInterval(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\"setInterval(window.stub, 500)\")\n        page.clock.run_for(1000)\n        assert len(calls) == 2\n\n    def test_replaces_global_clearInterval(self, page: Page, calls: List[Any]) -> None:\n        page.evaluate(\n            \"\"\"\n            const to = setInterval(window.stub, 500);\n            clearInterval(to);\n        \"\"\"\n        )\n        page.clock.run_for(1000)\n        assert len(calls) == 0\n\n    def test_replaces_global_performance_now(self, page: Page) -> None:\n        page.evaluate(\n            \"\"\"() => {\n            window.waitForPromise = new Promise(async resolve => {\n                const prev = performance.now();\n                await new Promise(f => setTimeout(f, 1000));\n                const next = performance.now();\n                resolve({ prev, next });\n            });\n        }\"\"\"\n        )\n        page.clock.run_for(1000)\n        assert page.evaluate(\"window.waitForPromise\") == {\"prev\": 1000, \"next\": 2000}\n\n    def test_fakes_Date_constructor(self, page: Page) -> None:\n        now = page.evaluate(\"new Date().getTime()\")\n        assert now == 1000\n\n\nclass TestStubTimersPerformance:\n    def test_replaces_global_performance_time_origin(self, page: Page) -> None:\n        page.clock.install(time=1)\n        page.clock.pause_at(2)\n        page.evaluate(\n            \"\"\"() => {\n            window.waitForPromise = new Promise(async resolve => {\n                const prev = performance.now();\n                await new Promise(f => setTimeout(f, 1000));\n                const next = performance.now();\n                resolve({ prev, next });\n            });\n        }\"\"\"\n        )\n        page.clock.run_for(1000)\n        assert page.evaluate(\"performance.timeOrigin\") == 1000\n        assert page.evaluate(\"window.waitForPromise\") == {\"prev\": 1000, \"next\": 2000}\n\n\nclass TestPopup:\n    def test_should_tick_after_popup(self, page: Page) -> None:\n        page.clock.install(time=0)\n        now = datetime.datetime.fromisoformat(\"2015-09-25\")\n        page.clock.pause_at(now)\n        with page.expect_popup() as popup_info:\n            page.evaluate(\"window.open('about:blank')\")\n        popup = popup_info.value\n        popup_time = popup.evaluate(\"Date.now()\")\n        assert popup_time == now.timestamp() * 1000\n        page.clock.run_for(1000)\n        popup_time_after = popup.evaluate(\"Date.now()\")\n        assert popup_time_after == now.timestamp() * 1000 + 1000\n\n    def test_should_tick_before_popup(self, page: Page) -> None:\n        page.clock.install(time=0)\n        now = datetime.datetime.fromisoformat(\"2015-09-25\")\n        page.clock.pause_at(now)\n        page.clock.run_for(1000)\n        with page.expect_popup() as popup_info:\n            page.evaluate(\"window.open('about:blank')\")\n        popup = popup_info.value\n        popup_time = popup.evaluate(\"Date.now()\")\n        assert popup_time == int(now.timestamp() * 1_000 + 1000)\n        assert datetime.datetime.fromtimestamp(popup_time / 1_000).year == 2015\n\n    def test_should_run_time_before_popup(self, page: Page, server: Server) -> None:\n        server.set_route(\n            \"/popup.html\",\n            lambda res: (\n                res.setHeader(\"Content-Type\", \"text/html\"),\n                res.write(b\"<script>window.time = Date.now()</script>\"),\n                res.finish(),\n            ),\n        )\n        page.goto(server.EMPTY_PAGE)\n        # Wait for 2 second in real life to check that it is past in popup.\n        page.wait_for_timeout(2000)\n        with page.expect_popup() as popup_info:\n            page.evaluate(\"window.open('{}')\".format(server.PREFIX + \"/popup.html\"))\n        popup = popup_info.value\n        popup_time = popup.evaluate(\"window.time\")\n        assert popup_time >= 2000\n\n    def test_should_not_run_time_before_popup_on_pause(\n        self, page: Page, server: Server\n    ) -> None:\n        server.set_route(\n            \"/popup.html\",\n            lambda res: (\n                res.setHeader(\"Content-Type\", \"text/html\"),\n                res.write(b\"<script>window.time = Date.now()</script>\"),\n                res.finish(),\n            ),\n        )\n        page.clock.install(time=0)\n        page.clock.pause_at(1)\n        page.goto(server.EMPTY_PAGE)\n        # Wait for 2 second in real life to check that it is past in popup.\n        page.wait_for_timeout(2000)\n        with page.expect_popup() as popup_info:\n            page.evaluate(\"window.open('{}')\".format(server.PREFIX + \"/popup.html\"))\n        popup = popup_info.value\n        popup_time = popup.evaluate(\"window.time\")\n        assert popup_time == 1000\n\n\nclass TestSetFixedTime:\n    def test_allows_passing_as_int(self, page: Page) -> None:\n        page.clock.set_fixed_time(1)\n        assert page.evaluate(\"Date.now()\") == 1000\n        page.clock.set_fixed_time(int(2))\n        assert page.evaluate(\"Date.now()\") == 2000\n\n    def test_does_not_fake_methods(self, page: Page) -> None:\n        page.clock.set_fixed_time(0)\n        # Should not stall.\n        page.evaluate(\"new Promise(f => setTimeout(f, 1))\")\n\n    def test_allows_setting_time_multiple_times(self, page: Page) -> None:\n        page.clock.set_fixed_time(0.1)\n        assert page.evaluate(\"Date.now()\") == 100\n        page.clock.set_fixed_time(0.2)\n        assert page.evaluate(\"Date.now()\") == 200\n\n    def test_fixed_time_is_not_affected_by_clock_manipulation(self, page: Page) -> None:\n        page.clock.set_fixed_time(0.1)\n        assert page.evaluate(\"Date.now()\") == 100\n        page.clock.fast_forward(20)\n        assert page.evaluate(\"Date.now()\") == 100\n\n    def test_allows_installing_fake_timers_after_setting_time(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.clock.set_fixed_time(0.1)\n        assert page.evaluate(\"Date.now()\") == 100\n        page.clock.set_fixed_time(0.2)\n        page.evaluate(\"setTimeout(() => window.stub(Date.now()))\")\n        page.clock.run_for(0)\n        assert calls == [[200]]\n\n\nclass TestWhileRunning:\n    def test_should_progress_time(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.wait_for_timeout(1000)\n        now = page.evaluate(\"Date.now()\")\n        assert 1000 <= now <= 2000\n\n    def test_should_run_for(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.run_for(10000)\n        now = page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    def test_should_fast_forward(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.fast_forward(10000)\n        now = page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    def test_should_fast_forward_to(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.fast_forward(10000)\n        now = page.evaluate(\"Date.now()\")\n        assert 10000 <= now <= 11000\n\n    def test_should_pause(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1)\n        page.wait_for_timeout(1000)\n        now = page.evaluate(\"Date.now()\")\n        assert 0 <= now <= 1000\n\n    def test_should_pause_and_fast_forward(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1)\n        page.clock.fast_forward(1000)\n        now = page.evaluate(\"Date.now()\")\n        assert now == 2000\n\n    def test_should_set_system_time_on_pause(self, page: Page) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1)\n        now = page.evaluate(\"Date.now()\")\n        assert now == 1000\n\n\nclass TestWhileOnPause:\n    def test_fast_forward_should_not_run_nested_immediate(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1000)\n        page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                setTimeout(() => window.stub('inner'), 0);\n            }, 1000);\n        \"\"\"\n        )\n        page.clock.fast_forward(1000)\n        assert calls == [[\"outer\"]]\n        page.clock.fast_forward(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n\n    def test_run_for_should_not_run_nested_immediate(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1000)\n        page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                setTimeout(() => window.stub('inner'), 0);\n            }, 1000);\n        \"\"\"\n        )\n        page.clock.run_for(1000)\n        assert calls == [[\"outer\"]]\n        page.clock.run_for(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n\n    def test_run_for_should_not_run_nested_immediate_from_microtask(\n        self, page: Page, calls: List[Any]\n    ) -> None:\n        page.clock.install(time=0)\n        page.goto(\"data:text/html,\")\n        page.clock.pause_at(1000)\n        page.evaluate(\n            \"\"\"\n            setTimeout(() => {\n                window.stub('outer');\n                void Promise.resolve().then(() => setTimeout(() => window.stub('inner'), 0));\n            }, 1000);\n        \"\"\"\n        )\n        page.clock.run_for(1000)\n        assert calls == [[\"outer\"]]\n        page.clock.run_for(1)\n        assert calls == [[\"outer\"], [\"inner\"]]\n"
  },
  {
    "path": "tests/sync/test_page_event_console.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom playwright.sync_api import Page\n\n\ndef test_console_messages_should_work(page: Page) -> None:\n    page.evaluate(\n        \"\"\"() => {\n            for (let i = 0; i < 301; i++)\n                console.log('message' + i);\n        }\"\"\"\n    )\n\n    messages = page.console_messages()\n    objects = [{\"text\": m.text, \"type\": m.type, \"page\": m.page} for m in messages]\n\n    expected = []\n    for i in range(201, 301):\n        expected.append({\"text\": f\"message{i}\", \"type\": \"log\", \"page\": page})\n\n    assert len(objects) >= 100, \"should be at least 100 messages\"\n    message_count = len(messages) - len(expected)\n    assert objects[message_count:] == expected, \"should return last messages\"\n"
  },
  {
    "path": "tests/sync/test_page_event_pageerror.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page\n\n\ndef test_page_errors_should_work(page: Page) -> None:\n    page.evaluate(\n        \"\"\"async () => {\n            for (let i = 0; i < 301; i++)\n                window.setTimeout(() => { throw new Error('error' + i); }, 0);\n            await new Promise(f => window.setTimeout(f, 100));\n        }\"\"\"\n    )\n\n    errors = page.page_errors()\n    messages = [e.message for e in errors]\n\n    expected = []\n    for i in range(201, 301):\n        expected.append(f\"error{i}\")\n\n    assert len(messages) >= 100, \"should be at least 100 errors\"\n    message_count = len(messages) - len(expected)\n    assert messages[message_count:] == expected, \"should return last errors\"\n"
  },
  {
    "path": "tests/sync/test_page_event_request.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page, Request, Route\nfrom tests.server import Server\n\n\ndef test_should_return_last_requests(page: Page, server: Server) -> None:\n    page.goto(server.PREFIX + \"/title.html\")\n    for i in range(200):\n\n        def _handle_route(route: Route) -> None:\n            route.fulfill(\n                status=200,\n                body=f\"url:{route.request.url}\",\n            )\n\n        page.route(f\"**/fetch?{i}\", _handle_route)\n\n    # #0 is the navigation request, so start with #1.\n    for i in range(1, 100):\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + f\"/fetch?{i}\")\n    first_100_requests_with_goto = page.requests()\n    first_100_requests = first_100_requests_with_goto[1:]\n\n    for i in range(100, 200):\n        page.evaluate(\"url => fetch(url)\", server.PREFIX + f\"/fetch?{i}\")\n    last_100_requests = page.requests()\n\n    all_requests = first_100_requests + last_100_requests\n\n    def gather_response(request: Request) -> dict:\n        response = request.response()\n        assert response\n        return {\"text\": response.text(), \"url\": request.url}\n\n    # All 199 requests are fully functional.\n    received = [gather_response(request) for request in all_requests]\n    expected = []\n    for i in range(1, 200):\n        url = server.PREFIX + f\"/fetch?{i}\"\n        expected.append({\"url\": url, \"text\": f\"url:{url}\"})\n    assert received == expected\n"
  },
  {
    "path": "tests/sync/test_page_network_response.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\nfrom twisted.web import http\n\nfrom playwright.sync_api import Error, Page\nfrom tests.server import Server\n\n\ndef test_should_reject_response_finished_if_page_closes(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n\n    def handle_get(request: http.Request) -> None:\n        # In Firefox, |fetch| will be hanging until it receives |Content-Type| header\n        # from server.\n        request.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\")\n        request.write(b\"hello \")\n\n    server.set_route(\"/get\", handle_get)\n    # send request and wait for server response\n    with page.expect_response(\"**/*\") as response_info:\n        page.evaluate(\"() => fetch('./get', { method: 'GET' })\")\n    page_response = response_info.value\n    page.close()\n    with pytest.raises(Error) as exc_info:\n        page_response.finished()\n    error = exc_info.value\n    assert \"closed\" in error.message\n\n\ndef test_should_reject_response_finished_if_context_closes(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n\n    def handle_get(request: http.Request) -> None:\n        # In Firefox, |fetch| will be hanging until it receives |Content-Type| header\n        # from server.\n        request.setHeader(\"Content-Type\", \"text/plain; charset=utf-8\")\n        request.write(b\"hello \")\n\n    server.set_route(\"/get\", handle_get)\n    # send request and wait for server response\n    with page.expect_response(\"**/*\") as response_info:\n        page.evaluate(\"() => fetch('./get', { method: 'GET' })\")\n    page_response = response_info.value\n\n    page.context.close()\n    with pytest.raises(Error) as exc_info:\n        page_response.finished()\n    error = exc_info.value\n    assert \"closed\" in error.message\n"
  },
  {
    "path": "tests/sync/test_page_request_fallback.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Any, Callable, List\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page, Request, Route\nfrom tests.server import Server\n\n\ndef _append_with_return_value(values: List, value: Any) -> Any:\n    values.append(value)\n\n\ndef test_should_work(page: Page, server: Server) -> None:\n    page.route(\"**/*\", lambda route: route.fallback())\n    page.goto(server.EMPTY_PAGE)\n\n\ndef test_should_fall_back(page: Page, server: Server) -> None:\n    intercepted: List[str] = []\n    page.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 1),\n            route.fallback(),\n        ),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 2),\n            route.fallback(),\n        ),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 3),\n            route.fallback(),\n        ),\n    )\n\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\ndef test_should_fall_back_async_delayed(page: Page, server: Server) -> None:\n    intercepted: List[str] = []\n\n    def create_handler(i: int) -> Callable[[Route], None]:\n        def handler(route: Route) -> None:\n            _append_with_return_value(intercepted, i)\n            page.wait_for_timeout(500)\n            route.fallback()\n\n        return handler\n\n    page.route(\"**/empty.html\", create_handler(1))\n    page.route(\"**/empty.html\", create_handler(2))\n    page.route(\"**/empty.html\", create_handler(3))\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\ndef test_should_chain_once(page: Page, server: Server) -> None:\n    page.route(\n        \"**/madeup.txt\",\n        lambda route: route.fulfill(status=200, body=\"fulfilled one\"),\n        times=1,\n    )\n    page.route(\"**/madeup.txt\", lambda route: route.fallback(), times=1)\n\n    resp = page.goto(server.PREFIX + \"/madeup.txt\")\n    assert resp\n    body = resp.body()\n    assert body == b\"fulfilled one\"\n\n\ndef test_should_not_chain_fulfill(page: Page, server: Server) -> None:\n    failed: List[bool] = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    page.route(\"**/empty.html\", handler)\n    page.route(\n        \"**/empty.html\",\n        lambda route: route.fulfill(status=200, body=\"fulfilled\"),\n    )\n    page.route(\"**/empty.html\", lambda route: route.fallback())\n\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    body = response.body()\n    assert body == b\"fulfilled\"\n    assert not failed[0]\n\n\ndef test_should_not_chain_abort(\n    page: Page, server: Server, is_webkit: bool, is_firefox: bool\n) -> None:\n    failed: List[bool] = [False]\n\n    def handler(route: Route) -> None:\n        failed[0] = True\n\n    page.route(\"**/empty.html\", handler)\n    page.route(\"**/empty.html\", lambda route: route.abort())\n    page.route(\"**/empty.html\", lambda route: route.fallback())\n\n    with pytest.raises(Error) as excinfo:\n        page.goto(server.EMPTY_PAGE)\n    if is_webkit:\n        assert \"Blocked by Web Inspector\" in excinfo.value.message\n    elif is_firefox:\n        assert \"NS_ERROR_FAILURE\" in excinfo.value.message\n    else:\n        assert \"net::ERR_FAILED\" in excinfo.value.message\n    assert not failed[0]\n\n\ndef test_should_fall_back_after_exception(page: Page, server: Server) -> None:\n    page.route(\"**/empty.html\", lambda route: route.continue_())\n\n    def handler(route: Route) -> None:\n        try:\n            route.fulfill(response=47)  # type: ignore\n        except Exception:\n            route.fallback()\n\n    page.route(\"**/empty.html\", handler)\n\n    page.goto(server.EMPTY_PAGE)\n\n\ndef test_should_amend_http_headers(page: Page, server: Server) -> None:\n    values: List[str] = []\n\n    def handler(route: Route) -> None:\n        _append_with_return_value(values, route.request.headers.get(\"foo\"))\n        _append_with_return_value(values, route.request.header_value(\"FOO\"))\n        route.continue_()\n\n    page.route(\"**/sleep.zzz\", handler)\n\n    def handler_with_header_mods(route: Route) -> None:\n        route.fallback(headers={**route.request.headers, \"FOO\": \"bar\"})\n\n    page.route(\"**/*\", handler_with_header_mods)\n\n    page.goto(server.EMPTY_PAGE)\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz')\")\n    _append_with_return_value(values, server_request_info.value.getHeader(\"foo\"))\n    assert values == [\"bar\", \"bar\", \"bar\"]\n\n\ndef test_should_delete_header_with_undefined_value(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    server.set_route(\n        \"/something\",\n        lambda r: (\n            r.setHeader(\"Acces-Control-Allow-Origin\", \"*\"),\n            r.write(b\"done\"),\n            r.finish(),\n        ),\n    )\n\n    intercepted_request = []\n\n    def capture_and_continue(route: Route, request: Request) -> None:\n        intercepted_request.append(request)\n        route.continue_()\n\n    page.route(\"**/*\", capture_and_continue)\n\n    def delete_foo_header(route: Route, request: Request) -> None:\n        headers = request.all_headers()\n        route.fallback(headers={**headers, \"foo\": None})  # type: ignore\n\n    page.route(server.PREFIX + \"/something\", delete_foo_header)\n    with server.expect_request(\"/something\") as server_req_info:\n        text = page.evaluate(\n            \"\"\"\n            async url => {\n                const data = await fetch(url, {\n                    headers: {\n                    foo: 'a',\n                    bar: 'b',\n                    }\n                });\n                return data.text();\n                }\n            \"\"\",\n            server.PREFIX + \"/something\",\n        )\n    server_req = server_req_info.value\n    assert text == \"done\"\n    assert not intercepted_request[0].headers.get(\"foo\")\n    assert intercepted_request[0].headers.get(\"bar\") == \"b\"\n    assert not server_req.getHeader(\"foo\")\n    assert server_req.getHeader(\"bar\") == \"b\"\n\n\ndef test_should_amend_method(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    method: List[str] = []\n    page.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(method, route.request.method),\n            route.continue_(),\n        ),\n    )\n    page.route(\"**/*\", lambda route: route.fallback(method=\"POST\"))\n\n    with server.expect_request(\"/sleep.zzz\") as request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz')\")\n    request = request_info.value\n    assert method == [\"POST\"]\n    assert request.method == b\"POST\"\n\n\ndef test_should_override_request_url(page: Page, server: Server) -> None:\n    url: List[str] = []\n    page.route(\n        \"**/global-var.html\",\n        lambda route: (\n            _append_with_return_value(url, route.request.url),\n            route.continue_(),\n        ),\n    )\n    page.route(\n        \"**/foo\",\n        lambda route: route.fallback(url=server.PREFIX + \"/global-var.html\"),\n    )\n\n    with server.expect_request(\"/global-var.html\") as server_request_info:\n        with page.expect_event(\"response\") as response_info:\n            page.goto(server.PREFIX + \"/foo\")\n    server_request = server_request_info.value\n    response = response_info.value\n    assert url == [server.PREFIX + \"/global-var.html\"]\n    assert response.url == server.PREFIX + \"/global-var.html\"\n    assert response.request.url == server.PREFIX + \"/global-var.html\"\n    assert page.evaluate(\"() => window['globalVar']\") == 123\n    assert server_request.uri == b\"/global-var.html\"\n    assert server_request.method == b\"GET\"\n\n\ndef test_should_amend_post_data(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    post_data: List[str] = []\n    page.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(post_data, route.request.post_data),\n            route.continue_(),\n        ),\n    )\n    page.route(\"**/*\", lambda route: route.fallback(post_data=\"doggo\"))\n\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\")\n    server_request = server_request_info.value\n    assert post_data == [\"doggo\"]\n    assert server_request.post_body == b\"doggo\"\n\n\ndef test_should_amend_binary_post_data(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    post_data_buffer: List[str] = []\n    page.route(\n        \"**/*\",\n        lambda route: (\n            _append_with_return_value(post_data_buffer, route.request.post_data),\n            route.continue_(),\n        ),\n    )\n    page.route(\"**/*\", lambda route: route.fallback(post_data=b\"\\x00\\x01\\x02\\x03\\x04\"))\n\n    with server.expect_request(\"/sleep.zzz\") as server_request_info:\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\")\n    server_request = server_request_info.value\n    # FIXME: should this be bytes?\n    assert post_data_buffer == [\"\\x00\\x01\\x02\\x03\\x04\"]\n    assert server_request.method == b\"POST\"\n    assert server_request.post_body == b\"\\x00\\x01\\x02\\x03\\x04\"\n\n\ndef test_should_chain_fallback_with_dynamic_url(server: Server, page: Page) -> None:\n    intercepted: List[int] = []\n    page.route(\n        \"**/bar\",\n        lambda route: (\n            _append_with_return_value(intercepted, 1),\n            route.fallback(url=server.EMPTY_PAGE),\n        ),\n    )\n    page.route(\n        \"**/foo\",\n        lambda route: (\n            _append_with_return_value(intercepted, 2),\n            route.fallback(url=\"http://localhost/bar\"),\n        ),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: (\n            _append_with_return_value(intercepted, 3),\n            route.fallback(url=\"http://localhost/foo\"),\n        ),\n    )\n\n    page.goto(server.EMPTY_PAGE)\n    assert intercepted == [3, 2, 1]\n\n\ndef test_should_amend_json_post_data(server: Server, page: Page) -> None:\n    page.goto(server.EMPTY_PAGE)\n    post_data = []\n\n    def handler(route: Route) -> None:\n        post_data.append(route.request.post_data)\n        route.continue_()\n\n    page.route(\"**/*\", handler)\n    page.route(\n        \"**/*\",\n        lambda route: route.fallback(post_data={\"foo\": \"bar\"}),\n    )\n\n    with server.expect_request(\"/sleep.zzz\") as server_request:\n        page.evaluate(\"() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })\")\n    assert post_data == ['{\"foo\": \"bar\"}']\n    assert server_request.value.post_body == b'{\"foo\": \"bar\"}'\n"
  },
  {
    "path": "tests/sync/test_page_request_gc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page\nfrom tests.server import Server\n\n\ndef test_should_work(page: Page, server: Server) -> None:\n    page.evaluate(\n        \"\"\"() => {\n        globalThis.objectToDestroy = { hello: 'world' };\n        globalThis.weakRef = new WeakRef(globalThis.objectToDestroy);\n    }\"\"\"\n    )\n    page.request_gc()\n    assert page.evaluate(\"() => globalThis.weakRef.deref()\") == {\"hello\": \"world\"}\n\n    page.request_gc()\n    assert page.evaluate(\"() => globalThis.weakRef.deref()\") == {\"hello\": \"world\"}\n\n    page.evaluate(\"() => globalThis.objectToDestroy = null\")\n    page.request_gc()\n    assert page.evaluate(\"() => globalThis.weakRef.deref()\") is None\n"
  },
  {
    "path": "tests/sync/test_page_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page, Route\nfrom tests.server import Server, TestServerRequest\n\n\ndef test_should_support_timeout_option_in_route_fetch(\n    server: Server, page: Page\n) -> None:\n    def _handle(request: TestServerRequest) -> None:\n        request.responseHeaders.addRawHeader(\"Content-Length\", \"4096\")\n        request.responseHeaders.addRawHeader(\"Content-Type\", \"text/html\")\n        request.write(b\"\")\n\n    server.set_route(\n        \"/slow\",\n        _handle,\n    )\n\n    def handle(route: Route) -> None:\n        with pytest.raises(Error) as error:\n            route.fetch(timeout=1000)\n        assert \"Route.fetch: Timeout 1000ms exceeded.\" in error.value.message\n\n    page.route(\"**/*\", lambda route: handle(route))\n    with pytest.raises(Error) as error:\n        page.goto(server.PREFIX + \"/slow\", timeout=2000)\n    assert \"Timeout 2000ms exceeded\" in error.value.message\n\n\ndef test_should_intercept_with_url_override(server: Server, page: Page) -> None:\n    def handle(route: Route) -> None:\n        response = route.fetch(url=server.PREFIX + \"/one-style.html\")\n        route.fulfill(response=response)\n\n    page.route(\"**/*.html\", lambda route: handle(route))\n    response = page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 200\n    assert \"one-style.css\" in response.body().decode(\"utf-8\")\n"
  },
  {
    "path": "tests/sync/test_page_request_timeout.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import BrowserContext, Error, Page\nfrom tests.server import Server\n\n\ndef test_context_request_should_support_timeout_option(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    # https://github.com/microsoft/playwright/issues/39220\n\n    server.set_route(\"/\", lambda req: None)\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded\"):\n        page.request.get(server.PREFIX, timeout=123)\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded\"):\n        context.request.get(server.PREFIX, timeout=123)\n\n    context.set_default_timeout(123)\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded\"):\n        page.request.get(server.PREFIX)\n    with pytest.raises(Error, match=\"Timeout 123ms exceeded\"):\n        context.request.get(server.PREFIX)\n"
  },
  {
    "path": "tests/sync/test_page_select_option.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pytest\n\nfrom playwright.sync_api import Error, Page\nfrom tests.server import Server\n\n\ndef test_select_option_should_select_single_option(server: Server, page: Page) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", \"blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_select_option_should_select_single_option_by_value(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", \"blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_select_option_should_select_single_option_by_label(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", label=\"Indigo\")\n    assert page.evaluate(\"result.onInput\") == [\"indigo\"]\n    assert page.evaluate(\"result.onChange\") == [\"indigo\"]\n\n\ndef test_select_option_should_select_single_option_by_empty_label(\n    page: Page, server: Server\n) -> None:\n    page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"indigo\">Indigo</option>\n            <option value=\"violet\"></option>\n        </select>\n    \"\"\"\n    )\n    assert page.locator(\"select\").input_value() == \"indigo\"\n    page.select_option(\"select\", label=\"\")\n    assert page.locator(\"select\").input_value() == \"violet\"\n\n\ndef test_select_option_should_select_single_option_by_handle(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", element=page.query_selector(\"[id=whiteOption]\"))\n    assert page.evaluate(\"result.onInput\") == [\"white\"]\n    assert page.evaluate(\"result.onChange\") == [\"white\"]\n\n\ndef test_select_option_should_select_single_option_by_index(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", index=2)\n    assert page.evaluate(\"result.onInput\") == [\"brown\"]\n    assert page.evaluate(\"result.onChange\") == [\"brown\"]\n\n\ndef test_select_option_should_select_single_option_by_index_0(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", index=0)\n    assert page.evaluate(\"result.onInput\") == [\"black\"]\n\n\ndef test_select_option_should_select_only_first_option(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", [\"blue\", \"green\", \"red\"])\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_select_option_should_not_throw_when_select_causes_navigation(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.eval_on_selector(\n        \"select\",\n        \"select => select.addEventListener('input', () => window.location = '/empty.html')\",\n    )\n    with page.expect_navigation():\n        page.select_option(\"select\", \"blue\")\n    assert \"empty.html\" in page.url\n\n\ndef test_select_option_should_select_multiple_options(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"makeMultiple()\")\n    page.select_option(\"select\", [\"blue\", \"green\", \"red\"])\n    assert page.evaluate(\"result.onInput\") == [\"blue\", \"green\", \"red\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\", \"green\", \"red\"]\n\n\ndef test_select_option_should_select_multiple_options_with_attributes(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"makeMultiple()\")\n    page.select_option(\n        \"select\",\n        value=\"blue\",\n        label=\"Green\",\n        index=4,\n    )\n    assert page.evaluate(\"result.onInput\") == [\"blue\", \"gray\", \"green\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\", \"gray\", \"green\"]\n\n\ndef test_select_option_should_select_option_with_empty_value(\n    page: Page, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\n        \"\"\"\n        <select>\n            <option value=\"first\">First</option>\n            <option value=\"\">Second</option>\n        </select>\n    \"\"\"\n    )\n    assert page.locator(\"select\").input_value() == \"first\"\n    page.select_option(\"select\", value=\"\")\n    assert page.locator(\"select\").input_value() == \"\"\n\n\ndef test_select_option_should_respect_event_bubbling(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", \"blue\")\n    assert page.evaluate(\"result.onBubblingInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onBubblingChange\") == [\"blue\"]\n\n\ndef test_select_option_should_throw_when_element_is_not_a__select_(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    with pytest.raises(Error) as exc_info:\n        page.select_option(\"body\", \"\")\n    assert \"Element is not a <select> element\" in exc_info.value.message\n\n\ndef test_select_option_should_return_on_no_matched_values(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    with pytest.raises(Error) as exc_info:\n        page.select_option(\"select\", [\"42\", \"abc\"], timeout=1000)\n    assert \"Timeout 1000\" in exc_info.value.message\n\n\ndef test_select_option_should_return_an_array_of_matched_values(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"makeMultiple()\")\n    result = page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    assert result == [\"black\", \"blue\", \"magenta\"]\n\n\ndef test_select_option_should_return_an_array_of_one_element_when_multiple_is_not_set(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    result = page.select_option(\"select\", [\"42\", \"blue\", \"black\", \"magenta\"])\n    assert len(result) == 1\n\n\ndef test_select_option_should_return_on_no_values(server: Server, page: Page) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    result = page.select_option(\"select\", [])\n    assert result == []\n\n\ndef test_select_option_should_unselect_with_null(server: Server, page: Page) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"makeMultiple()\")\n    result = page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    assert result == [\"black\", \"blue\", \"magenta\"]\n    page.select_option(\"select\", None)\n    assert page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\ndef test_select_option_should_deselect_all_options_when_passed_no_values_for_a_multiple_select(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"makeMultiple()\")\n    page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    page.select_option(\"select\", [])\n    assert page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\ndef test_select_option_should_deselect_all_options_when_passed_no_values_for_a_select_without_multiple(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", [\"blue\", \"black\", \"magenta\"])\n    page.select_option(\"select\", [])\n    assert page.eval_on_selector(\n        \"select\",\n        \"select => Array.from(select.options).every(option => !option.selected)\",\n    )\n\n\ndef test_select_option_should_work_when_re_defining_top_level_event_class(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.evaluate(\"window.Event = null\")\n    page.select_option(\"select\", \"blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n\n\ndef test_select_options_should_fall_back_to_selecting_by_label(\n    server: Server, page: Page\n) -> None:\n    page.goto(server.PREFIX + \"/input/select.html\")\n    page.select_option(\"select\", \"Blue\")\n    assert page.evaluate(\"result.onInput\") == [\"blue\"]\n    assert page.evaluate(\"result.onChange\") == [\"blue\"]\n"
  },
  {
    "path": "tests/sync/test_pdf.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.sync_api import Page\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_be_able_to_save_pdf_file(page: Page, tmp_path: Path) -> None:\n    output_file = tmp_path / \"foo.png\"\n    page.pdf(path=str(output_file))\n    assert os.path.getsize(output_file) > 0\n\n\n@pytest.mark.only_browser(\"chromium\")\ndef test_should_be_able_capture_pdf_without_path(page: Page) -> None:\n    buffer = page.pdf()\n    assert buffer\n"
  },
  {
    "path": "tests/sync/test_queryselector.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom pathlib import Path\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Error, Page, Selectors\n\nfrom .utils import Utils\n\n\ndef test_selectors_register_should_work(\n    selectors: Selectors, browser: Browser, browser_name: str\n) -> None:\n    tag_selector = \"\"\"\n        {\n            create(root, target) {\n                return target.nodeName;\n            },\n            query(root, selector) {\n                return root.querySelector(selector);\n            },\n            queryAll(root, selector) {\n                return Array.from(root.querySelectorAll(selector));\n            }\n        }\"\"\"\n\n    selector_name = f\"tag_{browser_name}\"\n    selector2_name = f\"tag2_{browser_name}\"\n\n    # Register one engine before creating context.\n    selectors.register(selector_name, tag_selector)\n\n    context = browser.new_context()\n    # Register another engine after creating context.\n    selectors.register(selector2_name, tag_selector)\n\n    page = context.new_page()\n    page.set_content(\"<div><span></span></div><div></div>\")\n\n    assert page.eval_on_selector(f\"{selector_name}=DIV\", \"e => e.nodeName\") == \"DIV\"\n    assert page.eval_on_selector(f\"{selector_name}=SPAN\", \"e => e.nodeName\") == \"SPAN\"\n    assert page.eval_on_selector_all(f\"{selector_name}=DIV\", \"es => es.length\") == 2\n\n    assert page.eval_on_selector(f\"{selector2_name}=DIV\", \"e => e.nodeName\") == \"DIV\"\n    assert page.eval_on_selector(f\"{selector2_name}=SPAN\", \"e => e.nodeName\") == \"SPAN\"\n    assert page.eval_on_selector_all(f\"{selector2_name}=DIV\", \"es => es.length\") == 2\n\n    # Selector names are case-sensitive.\n    with pytest.raises(Error) as exc:\n        page.query_selector(\"tAG=DIV\")\n    assert 'Unknown engine \"tAG\" while parsing selector tAG=DIV' in exc.value.message\n\n    context.close()\n\n\ndef test_selectors_register_should_work_with_path(\n    selectors: Selectors, page: Page, utils: Utils, assetdir: Path\n) -> None:\n    utils.register_selector_engine(\n        selectors, \"foo\", path=assetdir / \"sectionselectorengine.js\"\n    )\n    page.set_content(\"<section></section>\")\n    assert page.eval_on_selector(\"foo=whatever\", \"e => e.nodeName\") == \"SECTION\"\n\n\ndef test_selectors_register_should_work_in_main_and_isolated_world(\n    selectors: Selectors, page: Page, utils: Utils\n) -> None:\n    dummy_selector_script = \"\"\"{\n      create(root, target) { },\n      query(root, selector) {\n        return window.__answer;\n      },\n      queryAll(root, selector) {\n        return window['__answer'] ? [window['__answer'], document.body, document.documentElement] : [];\n      }\n    }\"\"\"\n\n    utils.register_selector_engine(selectors, \"main\", dummy_selector_script)\n    utils.register_selector_engine(\n        selectors, \"isolated\", dummy_selector_script, content_script=True\n    )\n    page.set_content(\"<div><span><section></section></span></div>\")\n    page.evaluate('() => window.__answer = document.querySelector(\"span\")')\n    # Works in main if asked.\n    assert page.eval_on_selector(\"main=ignored\", \"e => e.nodeName\") == \"SPAN\"\n    assert page.eval_on_selector(\"css=div >> main=ignored\", \"e => e.nodeName\") == \"SPAN\"\n    assert page.eval_on_selector_all(\n        \"main=ignored\", \"es => window.__answer !== undefined\"\n    )\n    assert (\n        page.eval_on_selector_all(\"main=ignored\", \"es => es.filter(e => e).length\") == 3\n    )\n    # Works in isolated by default.\n    assert page.query_selector(\"isolated=ignored\") is None\n    assert page.query_selector(\"css=div >> isolated=ignored\") is None\n    # $$eval always works in main, to avoid adopting nodes one by one.\n    assert page.eval_on_selector_all(\n        \"isolated=ignored\", \"es => window.__answer !== undefined\"\n    )\n    assert (\n        page.eval_on_selector_all(\"isolated=ignored\", \"es => es.filter(e => e).length\")\n        == 3\n    )\n    # At least one engine in main forces all to be in main.\n    assert (\n        page.eval_on_selector(\"main=ignored >> isolated=ignored\", \"e => e.nodeName\")\n        == \"SPAN\"\n    )\n    assert (\n        page.eval_on_selector(\"isolated=ignored >> main=ignored\", \"e => e.nodeName\")\n        == \"SPAN\"\n    )\n    # Can be chained to css.\n    assert (\n        page.eval_on_selector(\"main=ignored >> css=section\", \"e => e.nodeName\")\n        == \"SECTION\"\n    )\n\n\ndef test_selectors_register_should_handle_errors(\n    selectors: Selectors, page: Page, utils: Utils\n) -> None:\n    with pytest.raises(Error) as exc:\n        page.query_selector(\"neverregister=ignored\")\n    assert (\n        'Unknown engine \"neverregister\" while parsing selector neverregister=ignored'\n        in exc.value.message\n    )\n\n    dummy_selector_engine_script = \"\"\"{\n      create(root, target) {\n        return target.nodeName;\n      },\n      query(root, selector) {\n        return root.querySelector('dummy');\n      },\n      queryAll(root, selector) {\n        return Array.from(root.query_selector_all('dummy'));\n      }\n    }\"\"\"\n\n    with pytest.raises(Error) as exc:\n        selectors.register(\"$\", dummy_selector_engine_script)\n    assert (\n        exc.value.message\n        == \"Selectors.register: Selector engine name may only contain [a-zA-Z0-9_] characters\"\n    )\n\n    # Selector names are case-sensitive.\n    utils.register_selector_engine(selectors, \"dummy\", dummy_selector_engine_script)\n    utils.register_selector_engine(selectors, \"duMMy\", dummy_selector_engine_script)\n\n    with pytest.raises(Error) as exc:\n        selectors.register(\"dummy\", dummy_selector_engine_script)\n    assert (\n        exc.value.message\n        == 'Selectors.register: \"dummy\" selector engine has been already registered'\n    )\n\n    with pytest.raises(Error) as exc:\n        selectors.register(\"css\", dummy_selector_engine_script)\n    assert (\n        exc.value.message == 'Selectors.register: \"css\" is a predefined selector engine'\n    )\n\n\ndef test_should_work_with_layout_selectors(page: Page) -> None:\n    #        +--+  +--+\n    #        | 1|  | 2|\n    #        +--+  ++-++\n    #        | 3|   | 4|\n    #   +-------+  ++-++\n    #   |   0   |  | 5|\n    #   | +--+  +--+--+\n    #   | | 6|  | 7|\n    #   | +--+  +--+\n    #   |       |\n    #   O-------+\n    #           +--+\n    #           | 8|\n    #           +--++--+\n    #               | 9|\n    #               +--+\n\n    boxes = [\n        # x, y, width, height\n        [0, 0, 150, 150],\n        [100, 200, 50, 50],\n        [200, 200, 50, 50],\n        [100, 150, 50, 50],\n        [201, 150, 50, 50],\n        [200, 100, 50, 50],\n        [50, 50, 50, 50],\n        [150, 50, 50, 50],\n        [150, -51, 50, 50],\n        [201, -101, 50, 50],\n    ]\n    page.set_content(\n        '<container style=\"width: 500px; height: 500px; position: relative;\"></container>'\n    )\n    page.eval_on_selector(\n        \"container\",\n        \"\"\"(container, boxes) => {\n    for (let i = 0; i < boxes.length; i++) {\n      const div = document.createElement('div');\n      div.style.position = 'absolute';\n      div.style.overflow = 'hidden';\n      div.style.boxSizing = 'border-box';\n      div.style.border = '1px solid black';\n      div.id = 'id' + i;\n      div.textContent = 'id' + i;\n      const box = boxes[i];\n      div.style.left = box[0] + 'px';\n      // Note that top is a flipped y coordinate.\n      div.style.top = (250 - box[1] - box[3]) + 'px';\n      div.style.width = box[2] + 'px';\n      div.style.height = box[3] + 'px';\n      container.appendChild(div);\n      const span = document.createElement('span');\n      span.textContent = '' + i;\n      div.appendChild(span);\n    }\n  }\"\"\",\n        boxes,\n    )\n\n    assert page.eval_on_selector(\"div:right-of(#id6)\", \"e => e.id\") == \"id7\"\n    assert page.eval_on_selector(\"div:right-of(#id1)\", \"e => e.id\") == \"id2\"\n    assert page.eval_on_selector(\"div:right-of(#id3)\", \"e => e.id\") == \"id4\"\n    assert page.query_selector(\"div:right-of(#id4)\") is None\n    assert page.eval_on_selector(\"div:right-of(#id0)\", \"e => e.id\") == \"id7\"\n    assert page.eval_on_selector(\"div:right-of(#id8)\", \"e => e.id\") == \"id9\"\n    assert (\n        page.eval_on_selector_all(\n            \"div:right-of(#id3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id2,id5,id7,id8,id9\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:right-of(#id3, 50)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id2,id5,id7,id8\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:right-of(#id3, 49)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id8\"\n    )\n\n    assert page.eval_on_selector(\"div:left-of(#id2)\", \"e => e.id\") == \"id1\"\n    assert page.query_selector(\"div:left-of(#id0)\") is None\n    assert page.eval_on_selector(\"div:left-of(#id5)\", \"e => e.id\") == \"id0\"\n    assert page.eval_on_selector(\"div:left-of(#id9)\", \"e => e.id\") == \"id8\"\n    assert page.eval_on_selector(\"div:left-of(#id4)\", \"e => e.id\") == \"id3\"\n    assert (\n        page.eval_on_selector_all(\n            \"div:left-of(#id5)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id7,id3,id1,id6,id8\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:left-of(#id5, 3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id8\"\n    )\n\n    assert page.eval_on_selector(\"div:above(#id0)\", \"e => e.id\") == \"id3\"\n    assert page.eval_on_selector(\"div:above(#id5)\", \"e => e.id\") == \"id4\"\n    assert page.eval_on_selector(\"div:above(#id7)\", \"e => e.id\") == \"id5\"\n    assert page.eval_on_selector(\"div:above(#id8)\", \"e => e.id\") == \"id0\"\n    assert page.eval_on_selector(\"div:above(#id9)\", \"e => e.id\") == \"id8\"\n    assert page.query_selector(\"div:above(#id2)\") is None\n    assert (\n        page.eval_on_selector_all(\n            \"div:above(#id5)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id2,id3,id1\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:above(#id5, 20)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id4,id3\"\n    )\n\n    assert page.eval_on_selector(\"div:below(#id4)\", \"e => e.id\") == \"id5\"\n    assert page.eval_on_selector(\"div:below(#id3)\", \"e => e.id\") == \"id0\"\n    assert page.eval_on_selector(\"div:below(#id2)\", \"e => e.id\") == \"id4\"\n    assert page.eval_on_selector(\"div:below(#id6)\", \"e => e.id\") == \"id8\"\n    assert page.eval_on_selector(\"div:below(#id7)\", \"e => e.id\") == \"id8\"\n    assert page.eval_on_selector(\"div:below(#id8)\", \"e => e.id\") == \"id9\"\n    assert page.query_selector(\"div:below(#id9)\") is None\n    assert (\n        page.eval_on_selector_all(\n            \"div:below(#id3)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id6,id7,id8,id9\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:below(#id3, 105)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id6,id7\"\n    )\n\n    assert page.eval_on_selector(\"div:near(#id0)\", \"e => e.id\") == \"id3\"\n    assert (\n        page.eval_on_selector_all(\n            \"div:near(#id7)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id5,id3,id6\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:near(#id0)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id3,id6,id7,id8,id1,id5\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:near(#id6)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0,id3,id7\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:near(#id6, 10)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id0\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            \"div:near(#id0, 100)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id3,id6,id7,id8,id1,id5,id4,id2\"\n    )\n\n    assert (\n        page.eval_on_selector_all(\n            \"div:below(#id5):above(#id8)\", \"els => els.map(e => e.id).join(',')\"\n        )\n        == \"id7,id6\"\n    )\n    assert page.eval_on_selector(\"div:below(#id5):above(#id8)\", \"e => e.id\") == \"id7\"\n\n    assert (\n        page.eval_on_selector_all(\n            \"div:right-of(#id0) + div:above(#id8)\",\n            \"els => els.map(e => e.id).join(',')\",\n        )\n        == \"id5,id6,id3\"\n    )\n\n    with pytest.raises(Error) as exc_info:\n        page.query_selector(\":near(50)\")\n    assert (\n        '\"near\" engine expects a selector list and optional maximum distance in pixels'\n        in exc_info.value.message\n    )\n    with pytest.raises(Error) as exc_info:\n        page.query_selector('left-of=\"div\"')\n    assert '\"left-of\" selector cannot be first' in exc_info.value.message\n"
  },
  {
    "path": "tests/sync/test_request_fulfill.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page, Route\nfrom tests.server import Server\n\n\ndef test_should_fetch_original_request_and_fulfill(page: Page, server: Server) -> None:\n    def handle(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(response=response)\n\n    page.route(\"**/*\", handle)\n    response = page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 200\n    assert page.title() == \"Woof-Woof\"\n\n\ndef test_should_fulfill_json(page: Page, server: Server) -> None:\n    def handle(route: Route) -> None:\n        route.fulfill(status=201, headers={\"foo\": \"bar\"}, json={\"bar\": \"baz\"})\n\n    page.route(\"**/*\", handle)\n\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert response.json() == {\"bar\": \"baz\"}\n\n\ndef test_should_fulfill_json_overriding_existing_response(\n    page: Page, server: Server\n) -> None:\n    server.set_route(\n        \"/tags\",\n        lambda request: (\n            request.setHeader(\"foo\", \"bar\"),\n            request.write('{\"tags\": [\"a\", \"b\"]}'.encode()),\n            request.finish(),\n        ),\n    )\n\n    original = {}\n\n    def handle(route: Route) -> None:\n        response = route.fetch()\n        json = response.json()\n        original[\"tags\"] = json[\"tags\"]\n        json[\"tags\"] = [\"c\"]\n        route.fulfill(response=response, json=json)\n\n    page.route(\"**/*\", handle)\n\n    response = page.goto(server.PREFIX + \"/tags\")\n    assert response\n    assert response.status == 200\n    assert response.headers[\"content-type\"] == \"application/json\"\n    assert response.headers[\"foo\"] == \"bar\"\n    assert original[\"tags\"] == [\"a\", \"b\"]\n    assert response.json() == {\"tags\": [\"c\"]}\n"
  },
  {
    "path": "tests/sync/test_request_intercept.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom pathlib import Path\n\nfrom twisted.web import http\n\nfrom playwright.sync_api import Page, Route\nfrom tests.server import Server\n\n\ndef test_should_not_follow_redirects_when_max_redirects_is_set_to_0_in_route_fetch(\n    server: Server, page: Page\n) -> None:\n    server.set_redirect(\"/foo\", \"/empty.html\")\n\n    def handle(route: Route) -> None:\n        response = route.fetch(max_redirects=0)\n        assert response.headers[\"location\"] == \"/empty.html\"\n        assert response.status == 302\n        route.fulfill(body=\"hello\")\n\n    page.route(\"**/*\", lambda route: handle(route))\n    page.goto(server.PREFIX + \"/foo\")\n    assert \"hello\" in page.content()\n\n\ndef test_should_fulfill_intercepted_response(page: Page, server: Server) -> None:\n    def handle(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(\n            response=response,\n            status=201,\n            headers={\"foo\": \"bar\"},\n            content_type=\"text/plain\",\n            body=\"Yo, page!\",\n        )\n\n    page.route(\"**/*\", handle)\n    response = page.goto(server.PREFIX + \"/empty.html\")\n    assert response\n    assert response.status == 201\n    assert response.headers[\"foo\"] == \"bar\"\n    assert response.headers[\"content-type\"] == \"text/plain\"\n    assert page.evaluate(\"() => document.body.textContent\") == \"Yo, page!\"\n\n\ndef test_should_fulfill_response_with_empty_body(page: Page, server: Server) -> None:\n    def handle(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(\n            response=response, status=201, body=\"\", headers={\"content-length\": \"0\"}\n        )\n\n    page.route(\"**/*\", handle)\n    response = page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    assert response.status == 201\n    assert response.text() == \"\"\n\n\ndef test_should_override_with_defaults_when_intercepted_response_not_provided(\n    page: Page, server: Server, browser_name: str\n) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"my content\".encode())\n        request.finish()\n\n    server.set_route(\"/empty.html\", server_handler)\n\n    def handle(route: Route) -> None:\n        page.request.fetch(route.request)\n        route.fulfill(status=201)\n\n    page.route(\"**/*\", handle)\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.text() == \"\"\n    if browser_name == \"webkit\":\n        assert response.headers == {\"content-type\": \"text/plain\"}\n    else:\n        assert response.headers == {}\n\n\ndef test_should_fulfill_with_any_response(page: Page, server: Server) -> None:\n    def server_handler(request: http.Request) -> None:\n        request.setHeader(\"foo\", \"bar\")\n        request.write(\"Woo-hoo\".encode())\n        request.finish()\n\n    server.set_route(\"/sample\", server_handler)\n    sample_response = page.request.get(server.PREFIX + \"/sample\")\n    page.route(\n        \"**/*\",\n        lambda route: route.fulfill(\n            response=sample_response, status=201, content_type=\"text/plain\"\n        ),\n    )\n    response = page.goto(server.EMPTY_PAGE)\n    assert response\n    assert response.status == 201\n    assert response.text() == \"Woo-hoo\"\n    assert response.headers[\"foo\"] == \"bar\"\n\n\ndef test_should_support_fulfill_after_intercept(\n    page: Page, server: Server, assetdir: Path\n) -> None:\n    def handle_route(route: Route) -> None:\n        response = page.request.fetch(route.request)\n        route.fulfill(response=response)\n\n    page.route(\"**\", handle_route)\n    with server.expect_request(\"/title.html\") as request_info:\n        response = page.goto(server.PREFIX + \"/title.html\")\n    assert response\n    request = request_info.value\n    assert request.uri.decode() == \"/title.html\"\n    original = (assetdir / \"title.html\").read_text()\n    assert response.text() == original\n\n\ndef test_should_intercept_by_glob(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    page.route(\n        \"http://localhos**?*oo\",\n        lambda route: route.fulfill(body=\"intercepted\", status=200),\n    )\n\n    result = page.evaluate(\n        \"url => fetch(url).then(r => r.text())\", server.PREFIX + \"/?foo\"\n    )\n\n    assert result == \"intercepted\"\n"
  },
  {
    "path": "tests/sync/test_resource_timing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import Browser, Page\nfrom tests.server import Server\n\n\ndef test_should_work(page: Page, server: Server) -> None:\n    with page.expect_event(\"requestfinished\") as request_info:\n        page.goto(server.EMPTY_PAGE)\n    request = request_info.value\n    timing = request.timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= timing[\"connectEnd\"]\n    assert timing[\"responseStart\"] >= timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n\n\ndef test_should_work_for_subresource(\n    page: Page, server: Server, is_win: bool, is_mac: bool, is_webkit: bool\n) -> None:\n    if is_webkit and (is_mac or is_win):\n        pytest.skip()\n    requests = []\n    page.on(\"requestfinished\", lambda request: requests.append(request))\n    page.goto(server.PREFIX + \"/one-style.html\")\n    assert len(requests) >= 2\n    timing = requests[1].timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= 0\n    assert timing[\"responseStart\"] > timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n\n\ndef test_should_work_for_ssl(\n    browser: Browser, https_server: Server, is_mac: bool, is_webkit: bool\n) -> None:\n    if is_webkit and is_mac:\n        pytest.skip()\n    page = browser.new_page(ignore_https_errors=True)\n    with page.expect_event(\"requestfinished\") as request_info:\n        page.goto(https_server.EMPTY_PAGE)\n    request = request_info.value\n    timing = request.timing\n    verify_connections_timing_consistency(timing)\n    assert timing[\"requestStart\"] >= timing[\"connectEnd\"]\n    assert timing[\"responseStart\"] >= timing[\"requestStart\"]\n    assert timing[\"responseEnd\"] >= timing[\"responseStart\"]\n    assert timing[\"responseEnd\"] < 10000\n    page.close()\n\n\n@pytest.mark.skip_browser(\"webkit\")  # In WebKit, redirects don\"t carry the timing info\ndef test_should_work_for_redirect(page: Page, server: Server) -> None:\n    server.set_redirect(\"/foo.html\", \"/empty.html\")\n    responses = []\n    page.on(\"response\", lambda response: responses.append(response))\n    page.goto(server.PREFIX + \"/foo.html\")\n    for r in responses:\n        r.finished()\n\n    assert len(responses) == 2\n    assert responses[0].url == server.PREFIX + \"/foo.html\"\n    assert responses[1].url == server.PREFIX + \"/empty.html\"\n\n    timing1 = responses[0].request.timing\n    verify_connections_timing_consistency(timing1)\n    assert timing1[\"requestStart\"] >= timing1[\"connectEnd\"]\n    assert timing1[\"responseStart\"] > timing1[\"requestStart\"]\n    assert timing1[\"responseEnd\"] >= timing1[\"responseStart\"]\n    assert timing1[\"responseEnd\"] < 10000\n\n    timing2 = responses[1].request.timing\n    verify_connections_timing_consistency(timing2)\n    assert timing2[\"requestStart\"] >= 0\n    assert timing2[\"responseStart\"] > timing2[\"requestStart\"]\n    assert timing2[\"responseEnd\"] >= timing2[\"responseStart\"]\n    assert timing2[\"responseEnd\"] < 10000\n\n\ndef verify_timing_value(value: float, previous: float) -> None:\n    assert value == -1 or value > 0 and value >= previous\n\n\ndef verify_connections_timing_consistency(timing: Dict) -> None:\n    verify_timing_value(timing[\"domainLookupStart\"], -1)\n    verify_timing_value(timing[\"domainLookupEnd\"], timing[\"domainLookupStart\"])\n    verify_timing_value(timing[\"connectStart\"], timing[\"domainLookupEnd\"])\n    verify_timing_value(timing[\"secureConnectionStart\"], timing[\"connectStart\"])\n    verify_timing_value(timing[\"connectEnd\"], timing[\"secureConnectionStart\"])\n"
  },
  {
    "path": "tests/sync/test_route_web_socket.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nimport time\nfrom typing import Any, Awaitable, Callable, Literal, Optional, Union\n\nfrom playwright.sync_api import Browser, Frame, Page, WebSocketRoute\nfrom tests.server import Server, WebSocketProtocol\n\n\ndef assert_equal(\n    actual_cb: Callable[[], Union[Any, Awaitable[Any]]], expected: Any\n) -> None:\n    __tracebackhide__ = True\n    start_time = time.time()\n    attempts = 0\n    while True:\n        actual = actual_cb()\n        if actual == expected:\n            return\n        attempts += 1\n        if time.time() - start_time > 10:\n            raise TimeoutError(f\"Timed out after 10 seconds. Last actual was: {actual}\")\n        time.sleep(0.1)\n\n\ndef setup_ws(\n    target: Union[Page, Frame],\n    port: int,\n    protocol: Union[Literal[\"blob\"], Literal[\"arraybuffer\"]],\n) -> None:\n    target.goto(\"about:blank\")\n    target.evaluate(\n        \"\"\"({ port, binaryType }) => {\n    window.log = [];\n    window.ws = new WebSocket('ws://localhost:' + port + '/ws');\n    window.ws.binaryType = binaryType;\n    window.ws.addEventListener('open', () => window.log.push('open'));\n    window.ws.addEventListener('close', event => window.log.push(`close code=${event.code} reason=${event.reason} wasClean=${event.wasClean}`));\n    window.ws.addEventListener('error', event => window.log.push(`error`));\n    window.ws.addEventListener('message', async event => {\n      let data;\n      if (typeof event.data === 'string')\n        data = event.data;\n      else if (event.data instanceof Blob)\n        data = 'blob:' + event.data.text();\n      else\n        data = 'arraybuffer:' + (new Blob([event.data])).text();\n      window.log.push(`message: data=${data} origin=${event.origin} lastEventId=${event.lastEventId}`);\n    });\n    window.wsOpened = new Promise(f => window.ws.addEventListener('open', () => f()));\n  }\"\"\",\n        {\"port\": port, \"binaryType\": protocol},\n    )\n\n\ndef test_should_work_with_ws_close(page: Page, server: Server) -> None:\n    route: Optional[\"WebSocketRoute\"] = None\n\n    def _handle_ws(ws: WebSocketRoute) -> None:\n        ws.connect_to_server()\n        nonlocal route\n        route = ws\n\n    page.route_web_socket(re.compile(\".*\"), _handle_ws)\n\n    with server.expect_websocket() as ws_task:\n        setup_ws(page, server.PORT, \"blob\")\n    page.evaluate(\"window.wsOpened\")\n    ws = ws_task.value\n    assert route\n    route.send(\"hello\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    closed_event = []\n    ws.events.once(\"close\", lambda code, reason: closed_event.append((code, reason)))\n    route.close(code=3009, reason=\"oops\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=hello origin=ws://localhost:{server.PORT} lastEventId=\",\n            \"close code=3009 reason=oops wasClean=true\",\n        ],\n    )\n    assert_equal(lambda: closed_event, [(3009, \"oops\")])\n\n\ndef test_should_pattern_match(page: Page, server: Server) -> None:\n    page.route_web_socket(re.compile(r\".*/ws$\"), lambda ws: ws.connect_to_server())\n    page.route_web_socket(\n        \"**/mock-ws\", lambda ws: ws.on_message(lambda message: ws.send(\"mock-response\"))\n    )\n\n    page.goto(\"about:blank\")\n    with server.expect_websocket() as ws_info:\n        page.evaluate(\n            \"\"\"async ({ port }) => {\n                window.log = [];\n                window.ws1 = new WebSocket('ws://localhost:' + port + '/ws');\n                window.ws1.addEventListener('message', event => window.log.push(`ws1:${event.data}`));\n                window.ws2 = new WebSocket('ws://localhost:' + port + '/something/something/mock-ws');\n                window.ws2.addEventListener('message', event => window.log.push(`ws2:${event.data}`));\n                await Promise.all([\n                    new Promise(f => window.ws1.addEventListener('open', f)),\n                    new Promise(f => window.ws2.addEventListener('open', f)),\n                ]);\n            }\"\"\",\n            {\"port\": server.PORT},\n        )\n    ws = ws_info.value\n    ws.events.on(\"message\", lambda payload, isBinary: ws.sendMessage(b\"response\"))\n\n    page.evaluate(\"window.ws1.send('request')\")\n    assert_equal(lambda: page.evaluate(\"window.log\"), [\"ws1:response\"])\n\n    page.evaluate(\"window.ws2.send('request')\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"), [\"ws1:response\", \"ws2:mock-response\"]\n    )\n\n\ndef test_should_work_with_server(page: Page, server: Server) -> None:\n    route = None\n\n    def _handle_ws(ws: WebSocketRoute) -> None:\n        server = ws.connect_to_server()\n\n        def _ws_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-respond\":\n                ws.send(\"response\")\n                return\n            if message == \"to-block\":\n                return\n            if message == \"to-modify\":\n                server.send(\"modified\")\n                return\n            server.send(message)\n\n        ws.on_message(_ws_on_message)\n\n        def _server_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-block\":\n                return\n            if message == \"to-modify\":\n                ws.send(\"modified\")\n                return\n            ws.send(message)\n\n        server.on_message(_server_on_message)\n        server.send(\"fake\")\n        nonlocal route\n        route = ws\n\n    page.route_web_socket(re.compile(\".*\"), _handle_ws)\n    log = []\n\n    def _once_web_socket_connection(ws: WebSocketProtocol) -> None:\n        ws.events.on(\n            \"message\", lambda data, is_binary: log.append(f\"message: {data.decode()}\")\n        )\n        ws.events.on(\n            \"close\",\n            lambda code, reason: log.append(f\"close: code={code} reason={reason}\"),\n        )\n\n    server.once_web_socket_connection(_once_web_socket_connection)\n\n    with server.expect_websocket() as ws_info:\n        setup_ws(page, server.PORT, \"blob\")\n    page.evaluate(\"window.wsOpened\")\n    ws = ws_info.value\n    assert_equal(lambda: log, [\"message: fake\"])\n\n    ws.sendMessage(b\"to-modify\")\n    ws.sendMessage(b\"to-block\")\n    ws.sendMessage(b\"pass-server\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    page.evaluate(\n        \"\"\"() => {\n        window.ws.send('to-respond');\n        window.ws.send('to-modify');\n        window.ws.send('to-block');\n        window.ws.send('pass-client');\n    }\"\"\"\n    )\n    assert_equal(\n        lambda: log, [\"message: fake\", \"message: modified\", \"message: pass-client\"]\n    )\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n    assert route\n    route.send(\"another\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=modified origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=pass-server origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=another origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n\n    page.evaluate(\n        \"\"\"() => {\n        window.ws.send('pass-client-2');\n    }\"\"\"\n    )\n    assert_equal(\n        lambda: log,\n        [\n            \"message: fake\",\n            \"message: modified\",\n            \"message: pass-client\",\n            \"message: pass-client-2\",\n        ],\n    )\n\n    page.evaluate(\n        \"\"\"() => {\n        window.ws.close(3009, 'problem');\n    }\"\"\"\n    )\n    assert_equal(\n        lambda: log,\n        [\n            \"message: fake\",\n            \"message: modified\",\n            \"message: pass-client\",\n            \"message: pass-client-2\",\n            \"close: code=3009 reason=problem\",\n        ],\n    )\n\n\ndef test_should_work_without_server(page: Page, server: Server) -> None:\n    route = None\n\n    def _handle_ws(ws: WebSocketRoute) -> None:\n        def _ws_on_message(message: Union[str, bytes]) -> None:\n            if message == \"to-respond\":\n                ws.send(\"response\")\n\n        ws.on_message(_ws_on_message)\n        nonlocal route\n        route = ws\n\n    page.route_web_socket(re.compile(\".*\"), _handle_ws)\n    setup_ws(page, server.PORT, \"blob\")\n\n    page.evaluate(\n        \"\"\"async () => {\n        await window.wsOpened;\n        window.ws.send('to-respond');\n        window.ws.send('to-block');\n        window.ws.send('to-respond');\n    }\"\"\"\n    )\n\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n    assert route\n    route.send(\"another\")\n    # wait for the message to be processed\n    page.wait_for_timeout(100)\n    route.close(code=3008, reason=\"oops\")\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=response origin=ws://localhost:{server.PORT} lastEventId=\",\n            f\"message: data=another origin=ws://localhost:{server.PORT} lastEventId=\",\n            \"close code=3008 reason=oops wasClean=true\",\n        ],\n    )\n\n\ndef test_should_work_with_base_url(browser: Browser, server: Server) -> None:\n    context = browser.new_context(base_url=f\"http://localhost:{server.PORT}\")\n    page = context.new_page()\n\n    def _handle_ws(ws: WebSocketRoute) -> None:\n        ws.on_message(lambda message: ws.send(message))\n\n    page.route_web_socket(\"/ws\", _handle_ws)\n    setup_ws(page, server.PORT, \"blob\")\n\n    page.evaluate(\n        \"\"\"async () => {\n        await window.wsOpened;\n        window.ws.send('echo');\n    }\"\"\"\n    )\n\n    assert_equal(\n        lambda: page.evaluate(\"window.log\"),\n        [\n            \"open\",\n            f\"message: data=echo origin=ws://localhost:{server.PORT} lastEventId=\",\n        ],\n    )\n    context.close()\n\n\ndef test_should_work_with_no_trailing_slash(page: Page, server: Server) -> None:\n    log: list[str] = []\n\n    async def handle_ws(ws: WebSocketRoute) -> None:\n        def on_message(message: Union[str, bytes]) -> None:\n            assert isinstance(message, str)\n            log.append(message)\n            ws.send(\"response\")\n\n        ws.on_message(on_message)\n\n    # No trailing slash in the route pattern\n    page.route_web_socket(f\"ws://localhost:{server.PORT}\", handle_ws)\n\n    page.goto(\"about:blank\")\n    page.evaluate(\n        \"\"\"({ port }) => {\n            window.log = [];\n            // No trailing slash in WebSocket URL\n            window.ws = new WebSocket('ws://localhost:' + port);\n            window.ws.addEventListener('message', event => window.log.push(event.data));\n        }\"\"\",\n        {\"port\": server.PORT},\n    )\n\n    assert_equal(lambda: page.evaluate(\"window.ws.readyState\"), 1)  # WebSocket.OPEN\n    page.evaluate(\"window.ws.send('query')\")\n    assert_equal(lambda: log, [\"query\"])\n    assert_equal(lambda: page.evaluate(\"window.log\"), [\"response\"])\n"
  },
  {
    "path": "tests/sync/test_selectors_misc.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import Page\n\n\ndef test_should_work_with_internal_and(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n        <div class=foo>hello</div><div class=bar>world</div>\n        <span class=foo>hello2</span><span class=bar>world2</span>\n    \"\"\"\n    )\n    assert (\n        page.eval_on_selector_all(\n            'div >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == []\n    assert (\n        page.eval_on_selector_all(\n            'div >> internal:and=\".foo\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello\"]\n    assert (\n        page.eval_on_selector_all(\n            'div >> internal:and=\".bar\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"world\"]\n    assert (\n        page.eval_on_selector_all(\n            'span >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello2\", \"world2\"]\n    assert (\n        page.eval_on_selector_all(\n            '.foo >> internal:and=\"div\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"hello\"]\n    assert (\n        page.eval_on_selector_all(\n            '.bar >> internal:and=\"span\"', \"els => els.map(e => e.textContent)\"\n        )\n    ) == [\"world2\"]\n"
  },
  {
    "path": "tests/sync/test_sync.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport multiprocessing\nimport os\nfrom typing import Any, Callable, Dict\n\nimport pytest\n\nfrom playwright.sync_api import (\n    Browser,\n    BrowserContext,\n    Dialog,\n    Error,\n    Page,\n    TimeoutError,\n    sync_playwright,\n)\nfrom tests.server import Server\nfrom tests.utils import TARGET_CLOSED_ERROR_MESSAGE\n\n\ndef test_sync_query_selector(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n    <h1 id=\"foo\">Bar</h1>\n    \"\"\"\n    )\n    e1 = page.query_selector(\"#foo\")\n    assert e1\n    e2 = page.query_selector(\"h1\")\n    assert e2\n    assert e1.inner_text() == e2.inner_text()\n\n\ndef test_page_repr(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    assert repr(page) == f\"<Page url={page.url!r}>\"\n\n\ndef test_frame_repr(page: Page, server: Server) -> None:\n    page.goto(server.EMPTY_PAGE)\n    assert (\n        repr(page.main_frame)\n        == f\"<Frame name={page.main_frame.name} url={page.main_frame.url!r}>\"\n    )\n\n\ndef test_browser_context_repr(context: BrowserContext) -> None:\n    assert repr(context) == f\"<BrowserContext browser={context.browser}>\"\n\n\ndef test_browser_repr(browser: Browser) -> None:\n    assert (\n        repr(browser)\n        == f\"<Browser type={browser._impl_obj._browser_type} version={browser.version}>\"\n    )\n\n\ndef test_browser_type_repr(browser: Browser) -> None:\n    browser_type = browser._impl_obj._browser_type\n    assert (\n        repr(browser_type)\n        == f\"<BrowserType name={browser_type.name} executable_path={browser_type.executable_path}>\"\n    )\n\n\ndef test_dialog_repr(page: Page) -> None:\n    def on_dialog(dialog: Dialog) -> None:\n        dialog.accept()\n        assert (\n            repr(dialog)\n            == f\"<Dialog type={dialog.type} message={dialog.message} default_value={dialog.default_value}>\"\n        )\n\n    page.on(\"dialog\", on_dialog)\n    page.evaluate(\"alert('yo')\")\n\n\ndef test_console_repr(page: Page) -> None:\n    messages = []\n    page.on(\"console\", lambda m: messages.append(m))\n    page.evaluate('() => console.log(\"Hello world\")')\n    message = messages[0]\n    assert repr(message) == f\"<ConsoleMessage type={message.type} text={message.text}>\"\n\n\ndef test_sync_click(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n    <button onclick=\"window.clicked=true\">Bar</button>\n    \"\"\"\n    )\n    page.click(\"text=Bar\")\n    assert page.evaluate(\"()=>window.clicked\")\n\n\ndef test_sync_nested_query_selector(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n    <div id=\"one\">\n        <span class=\"two\">\n            <label>\n                MyValue\n            </label>\n        </span>\n    </div>\n    \"\"\"\n    )\n    e1 = page.query_selector(\"#one\")\n    assert e1\n    e2 = e1.query_selector(\".two\")\n    assert e2\n    e3 = e2.query_selector(\"label\")\n    assert e3\n    assert e3.inner_text() == \"MyValue\"\n\n\ndef test_sync_handle_multiple_pages(context: BrowserContext) -> None:\n    page1 = context.new_page()\n    page2 = context.new_page()\n    assert len(context.pages) == 2\n    page1.set_content(\"one\")\n    page2.set_content(\"two\")\n    assert \"one\" in page1.content()\n    assert \"two\" in page2.content()\n    page1.close()\n    assert len(context.pages) == 1\n    page2.close()\n    assert len(context.pages) == 0\n    for page in [page1, page2]:\n        with pytest.raises(Error):\n            page.content()\n\n\ndef test_sync_wait_for_event(page: Page, server: Server) -> None:\n    with page.expect_event(\"popup\", timeout=10000) as popup:\n        page.evaluate(\"(url) => window.open(url)\", server.EMPTY_PAGE)\n    assert popup.value\n\n\ndef test_sync_wait_for_event_raise(page: Page) -> None:\n    with pytest.raises(AssertionError):\n        with page.expect_event(\"popup\", timeout=500):\n            assert False\n\n    with pytest.raises(Error) as exc_info:\n        with page.expect_event(\"popup\", timeout=500):\n            page.wait_for_timeout(1_000)\n    assert \"Timeout 500ms exceeded\" in exc_info.value.message\n\n\ndef test_sync_make_existing_page_sync(page: Page) -> None:\n    page = page\n    assert page.evaluate(\"() => ({'playwright': true})\") == {\"playwright\": True}\n    page.set_content(\"<h1>myElement</h1>\")\n    page.wait_for_selector(\"text=myElement\")\n\n\ndef test_sync_network_events(page: Page, server: Server) -> None:\n    server.set_route(\n        \"/hello-world\",\n        lambda request: (\n            request.setHeader(\"Content-Type\", \"text/plain\"),\n            request.write(b\"Hello world\"),\n            request.finish(),\n        ),\n    )\n    page.goto(server.EMPTY_PAGE)\n    messages = []\n    page.on(\n        \"request\", lambda request: messages.append(f\">>{request.method}{request.url}\")\n    )\n    page.on(\n        \"response\",\n        lambda response: messages.append(f\"<<{response.status}{response.url}\"),\n    )\n    response = page.evaluate(\"\"\"async ()=> (await fetch(\"/hello-world\")).text()\"\"\")\n    assert response == \"Hello world\"\n    assert messages == [\n        f\">>GET{server.PREFIX}/hello-world\",\n        f\"<<200{server.PREFIX}/hello-world\",\n    ]\n\n\ndef test_console_should_work(page: Page, browser_name: str) -> None:\n    messages = []\n    page.once(\"console\", lambda m: messages.append(m))\n    page.evaluate('() => console.log(\"hello\", 5, {foo: \"bar\"})')\n    assert len(messages) == 1\n    message = messages[0]\n    if browser_name != \"firefox\":\n        assert message.text == \"hello 5 {foo: bar}\"\n        assert str(message) == \"hello 5 {foo: bar}\"\n    else:\n        assert message.text == \"hello 5 JSHandle@object\"\n        assert str(message) == \"hello 5 JSHandle@object\"\n    assert message.type == \"log\"\n    assert message.args[0].json_value() == \"hello\"\n    assert message.args[1].json_value() == 5\n    assert message.args[2].json_value() == {\"foo\": \"bar\"}\n\n\ndef test_sync_download(browser: Browser, server: Server) -> None:\n    server.set_route(\n        \"/downloadWithFilename\",\n        lambda request: (\n            request.setHeader(\"Content-Type\", \"application/octet-stream\"),\n            request.setHeader(\"Content-Disposition\", \"attachment; filename=file.txt\"),\n            request.write(b\"Hello world\"),\n            request.finish(),\n        ),\n    )\n    page = browser.new_page(accept_downloads=True)\n    page.set_content(f'<a href=\"{server.PREFIX}/downloadWithFilename\">download</a>')\n\n    with page.expect_event(\"download\") as download:\n        page.click(\"a\")\n    assert download.value\n    assert download.value.suggested_filename == \"file.txt\"\n    path = download.value.path()\n    assert os.path.isfile(path)\n    with open(path, \"r\") as fd:\n        assert fd.read() == \"Hello world\"\n    page.close()\n\n\ndef test_sync_workers_page_workers(page: Page, server: Server) -> None:\n    with page.expect_event(\"worker\") as event_worker:\n        page.goto(server.PREFIX + \"/worker/worker.html\")\n    assert event_worker.value\n    worker = page.workers[0]\n    assert \"worker.js\" in worker.url\n\n    assert worker.evaluate('() => self[\"workerFunction\"]()') == \"worker function result\"\n\n    page.goto(server.EMPTY_PAGE)\n    assert len(page.workers) == 0\n\n\ndef test_sync_playwright_multiple_times() -> None:\n    with pytest.raises(Error) as exc:\n        with sync_playwright() as pw:\n            assert pw.chromium\n    assert (\n        \"It looks like you are using Playwright Sync API inside the asyncio loop.\"\n        in exc.value.message\n    )\n\n\ndef test_sync_set_default_timeout(page: Page) -> None:\n    page.set_default_timeout(1)\n    with pytest.raises(TimeoutError) as exc:\n        page.wait_for_function(\"false\")\n    assert \"Timeout 1ms exceeded.\" in exc.value.message\n\n\ndef test_close_should_reject_all_promises(\n    context: BrowserContext, sync_gather: Callable\n) -> None:\n    new_page = context.new_page()\n    with pytest.raises(Error) as exc_info:\n        sync_gather(\n            lambda: new_page.evaluate(\"() => new Promise(r => {})\"),\n            lambda: new_page.close(),\n        )\n    assert TARGET_CLOSED_ERROR_MESSAGE in exc_info.value.message\n\n\ndef test_expect_response_should_work(page: Page, server: Server) -> None:\n    with page.expect_response(\"**/*\") as resp:\n        page.goto(server.EMPTY_PAGE)\n    assert resp.value\n    assert resp.value.url == server.EMPTY_PAGE\n    assert resp.value.status == 200\n    assert resp.value.ok\n    assert resp.value.request\n\n\ndef test_expect_response_should_not_hang_when_predicate_throws(page: Page) -> None:\n    with pytest.raises(Exception, match=\"Oops!\"):\n        with page.expect_response(\"**/*\"):\n            raise Exception(\"Oops!\")\n\n\ndef test_expect_response_should_use_context_timeout(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    page.goto(server.EMPTY_PAGE)\n\n    context.set_default_timeout(1_000)\n    with pytest.raises(Error) as exc_info:\n        with page.expect_response(\"https://playwright.dev\"):\n            pass\n    assert exc_info.type is TimeoutError\n    assert \"Timeout 1000ms exceeded\" in exc_info.value.message\n\n\ndef _test_sync_playwright_stop_multiple_times() -> None:\n    playwright = sync_playwright().start()\n    playwright.stop()\n    playwright.stop()\n\n\ndef test_sync_playwright_stop_multiple_times() -> None:\n    p = multiprocessing.Process(target=_test_sync_playwright_stop_multiple_times)\n    p.start()\n    p.join()\n    assert p.exitcode == 0\n\n\ndef _test_call_sync_method_after_playwright_close_with_own_loop(\n    browser_name: str,\n    launch_arguments: Dict[str, Any],\n    empty_page: str,\n) -> None:\n    playwright = sync_playwright().start()\n    browser = playwright[browser_name].launch(**launch_arguments)\n    context = browser.new_context()\n    page = context.new_page()\n    page.goto(empty_page)\n    playwright.stop()\n    with pytest.raises(Error) as exc:\n        page.evaluate(\"1+1\")\n    assert \"Event loop is closed! Is Playwright already stopped?\" in str(exc.value)\n\n\ndef test_call_sync_method_after_playwright_close_with_own_loop(\n    server: Server, browser_name: str, launch_arguments: Dict[str, Any]\n) -> None:\n    p = multiprocessing.Process(\n        target=_test_call_sync_method_after_playwright_close_with_own_loop,\n        args=[browser_name, launch_arguments, server.EMPTY_PAGE],\n    )\n    p.start()\n    p.join()\n    assert p.exitcode == 0\n\n\ndef test_should_return_proper_api_name_on_error(page: Page) -> None:\n    try:\n        page.evaluate(\"does_not_exist\")\n\n        assert (\n            False\n        ), \"Accessing undefined JavaScript variable should have thrown exception\"\n    except Exception as error:\n        # Each browser returns slightly different error messages, but they should all start with \"Page.evaluate:\", because that was the Playwright method where the error originated\n        assert str(error).startswith(\"Page.evaluate:\")\n"
  },
  {
    "path": "tests/sync/test_tap.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Generator, Optional\n\nimport pytest\n\nfrom playwright.sync_api import Browser, BrowserContext, ElementHandle, JSHandle, Page\n\n\n@pytest.fixture\ndef context(browser: Browser) -> Generator[BrowserContext, None, None]:\n    context = browser.new_context(has_touch=True)\n    yield context\n    context.close()\n\n\ndef test_should_send_all_of_the_correct_events(page: Page) -> None:\n    page.set_content(\n        \"\"\"\n            <div id=\"a\" style=\"background: lightblue; width: 50px; height: 50px\">a</div>\n            <div id=\"b\" style=\"background: pink; width: 50px; height: 50px\">b</div>\n        \"\"\"\n    )\n    page.tap(\"#a\")\n    element_handle = track_events(page.query_selector(\"#b\"))\n    page.tap(\"#b\")\n    assert element_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n        \"mouseover\",\n        \"mouseenter\",\n        \"mousemove\",\n        \"mousedown\",\n        \"mouseup\",\n        \"click\",\n    ]\n\n\ndef test_should_not_send_mouse_events_touchstart_is_canceled(page: Page) -> None:\n    page.set_content(\"hello world\")\n    page.evaluate(\n        \"\"\"() => {\n            // touchstart is not cancelable unless passive is false\n            document.addEventListener('touchstart', t => t.preventDefault(), {passive: false});\n        }\"\"\"\n    )\n    events_handle = track_events(page.query_selector(\"body\"))\n    page.tap(\"body\")\n    assert events_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n    ]\n\n\ndef test_should_not_send_mouse_events_touchend_is_canceled(page: Page) -> None:\n    page.set_content(\"hello world\")\n    page.evaluate(\n        \"\"\"() => {\n            // touchstart is not cancelable unless passive is false\n            document.addEventListener('touchend', t => t.preventDefault());\n        }\"\"\"\n    )\n    events_handle = track_events(page.query_selector(\"body\"))\n    page.tap(\"body\")\n    assert events_handle.json_value() == [\n        \"pointerover\",\n        \"pointerenter\",\n        \"pointerdown\",\n        \"touchstart\",\n        \"pointerup\",\n        \"pointerout\",\n        \"pointerleave\",\n        \"touchend\",\n    ]\n\n\ndef track_events(target: Optional[ElementHandle]) -> JSHandle:\n    assert target\n    return target.evaluate_handle(\n        \"\"\"target => {\n            const events = [];\n            for (const event of [\n                'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click',\n                'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerup',\n                'touchstart', 'touchend', 'touchmove', 'touchcancel',])\n                target.addEventListener(event, () => events.push(event), false);\n            return events;\n        }\"\"\"\n    )\n"
  },
  {
    "path": "tests/sync/test_tracing.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nimport threading\nfrom pathlib import Path\nfrom typing import Callable, ContextManager\n\nfrom playwright.sync_api import (\n    Browser,\n    BrowserContext,\n    BrowserType,\n    Page,\n    Response,\n    expect,\n)\nfrom tests.server import Server\n\nfrom .conftest import TraceViewerPage\n\n\ndef test_browser_context_output_trace(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    context = browser.new_context()\n    context.tracing.start(screenshots=True, snapshots=True)\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/grid.html\")\n    context.tracing.stop(path=tmp_path / \"trace.zip\")\n    assert Path(tmp_path / \"trace.zip\").exists()\n    context.close()\n\n\ndef test_start_stop(browser: Browser) -> None:\n    context = browser.new_context()\n    context.tracing.start()\n    context.tracing.stop()\n    context.close()\n\n\ndef test_browser_context_should_not_throw_when_stopping_without_start_but_not_exporting(\n    context: BrowserContext,\n) -> None:\n    context.tracing.stop()\n\n\ndef test_browser_context_output_trace_chunk(\n    browser: Browser, server: Server, tmp_path: Path\n) -> None:\n    context = browser.new_context()\n    context.tracing.start(screenshots=True, snapshots=True)\n    page = context.new_page()\n    page.goto(server.PREFIX + \"/grid.html\")\n    button = page.locator(\".box\").first\n\n    context.tracing.start_chunk(title=\"foo\")\n    button.click()\n    context.tracing.stop_chunk(path=tmp_path / \"trace1.zip\")\n    assert Path(tmp_path / \"trace1.zip\").exists()\n\n    context.tracing.start_chunk(title=\"foo\")\n    button.click()\n    context.tracing.stop_chunk(path=tmp_path / \"trace2.zip\")\n    assert Path(tmp_path / \"trace2.zip\").exists()\n    context.close()\n\n\ndef test_should_collect_sources(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start(sources=True)\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<button>Click</button>\")\n\n    def my_method_outer() -> None:\n        def my_method_inner() -> None:\n            page.get_by_text(\"Click\").click()\n\n        my_method_inner()\n\n    my_method_outer()\n    path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=path)\n\n    with show_trace_viewer(path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n            ]\n        )\n        trace_viewer.show_source_tab()\n        # Check that stack frames are shown (they might be anonymous in Python)\n        expect(trace_viewer.stack_frames).to_contain_text(\n            [\n                re.compile(r\"my_method_inner\"),\n                re.compile(r\"my_method_outer\"),\n                re.compile(r\"test_should_collect_sources\"),\n            ]\n        )\n\n        trace_viewer.select_action(\"Set content\")\n        # Check that the source file is shown\n        expect(trace_viewer.page.locator(\".source-tab-file-name\")).to_have_attribute(\n            \"title\", re.compile(r\".*test_.*\\.py\")\n        )\n        expect(trace_viewer.page.locator(\".source-line-running\")).to_contain_text(\n            'page.set_content(\"<button>Click</button>\")'\n        )\n\n\ndef test_should_collect_trace_with_resources_but_no_js(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start(screenshots=True, snapshots=True)\n    page.goto(server.PREFIX + \"/frames/frame.html\")\n    page.set_content(\"<button>Click</button>\")\n    page.click('\"Click\"')\n    page.mouse.move(20, 20)\n    page.mouse.dblclick(30, 30)\n    page.request.get(server.EMPTY_PAGE)\n    page.keyboard.insert_text(\"abc\")\n    page.wait_for_timeout(2000)  # Give it some time to produce screenshots.\n    page.route(\"**/empty.html\", lambda route: route.continue_())\n    page.goto(server.EMPTY_PAGE)\n    page.goto(server.PREFIX + \"/one-style.html\")\n    page.close()\n    trace_file_path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=trace_file_path)\n\n    with show_trace_viewer(trace_file_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/frames/frame\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n                re.compile(r\"Mouse move\"),\n                re.compile(r\"Double click\"),\n                re.compile(r\"GET \\\"/empty\\.html\\\"\"),\n                re.compile(r'Insert \"abc\"'),\n                re.compile(r\"Wait for timeout\"),\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r'Navigate to \"/one-style\\.html\"'),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n        trace_viewer.select_action(\"Set content\")\n        expect(trace_viewer.page.locator(\".browser-frame-address-bar\")).to_have_text(\n            server.PREFIX + \"/frames/frame.html\"\n        )\n        frame = trace_viewer.snapshot_frame(\"Set content\", 0, False)\n        expect(frame.locator(\"button\")).to_have_text(\"Click\")\n\n\ndef test_should_correctly_determine_sync_apiname(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable,\n) -> None:\n    context.tracing.start(screenshots=True, snapshots=True)\n\n    received_response = threading.Event()\n\n    def _handle_response(response: Response) -> None:\n        response.request.all_headers()\n        response.text()\n        received_response.set()\n\n    page.once(\"response\", _handle_response)\n    page.goto(server.PREFIX + \"/grid.html\")\n    received_response.wait()\n\n    page.close()\n    trace_file_path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=trace_file_path)\n\n    with show_trace_viewer(trace_file_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/grid\\.html\"'),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n\ndef test_should_collect_two_traces(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start(screenshots=True, snapshots=True)\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<button>Click</button>\")\n    page.click('\"Click\"')\n    tracing1_path = tmp_path / \"trace1.zip\"\n    context.tracing.stop(path=tracing1_path)\n\n    context.tracing.start(screenshots=True, snapshots=True)\n    page.dblclick('\"Click\"')\n    page.close()\n    tracing2_path = tmp_path / \"trace2.zip\"\n    context.tracing.stop(path=tracing2_path)\n\n    with show_trace_viewer(tracing1_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r\"Click\"),\n            ]\n        )\n\n    with show_trace_viewer(tracing2_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r\"Double click\"),\n                re.compile(r\"Close\"),\n            ]\n        )\n\n\ndef test_should_work_with_playwright_context_managers(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start(screenshots=True, snapshots=True)\n    page.goto(server.EMPTY_PAGE)\n    page.set_content(\"<button>Click</button>\")\n    with page.expect_console_message() as message_info:\n        page.evaluate('() => console.log(\"hello\")')\n        page.click('\"Click\"')\n    assert (message_info.value).text == \"hello\"\n\n    with page.expect_popup():\n        page.evaluate(\"window._popup = window.open(document.location.href)\")\n    trace_file_path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=trace_file_path)\n\n    with show_trace_viewer(trace_file_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r\"Set content\"),\n                re.compile(r'Wait for event \"page\\.expect_event\\(console\\)\"'),\n                re.compile(r\"Evaluate\"),\n                re.compile(r\"Click\"),\n                re.compile(r'Wait for event \"page\\.expect_event\\(popup\\)\"'),\n                re.compile(r\"Evaluate\"),\n            ]\n        )\n\n\ndef test_should_display_wait_for_load_state_even_if_did_not_wait_for_it(\n    context: BrowserContext,\n    page: Page,\n    server: Server,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start(screenshots=True, snapshots=True)\n\n    page.goto(server.EMPTY_PAGE)\n    page.wait_for_load_state(\"load\")\n    page.wait_for_load_state(\"load\")\n\n    trace_file_path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=trace_file_path)\n\n    with show_trace_viewer(trace_file_path) as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/empty\\.html\"'),\n                re.compile(r'Wait for event \"frame\\.wait_for_load_state\"'),\n                re.compile(r'Wait for event \"frame\\.wait_for_load_state\"'),\n            ]\n        )\n\n\ndef test_should_respect_traces_dir_and_name(\n    browser_type: BrowserType,\n    server: Server,\n    tmp_path: Path,\n    launch_arguments: dict,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    traces_dir = tmp_path / \"traces\"\n    browser = browser_type.launch(traces_dir=traces_dir, **launch_arguments)\n    context = browser.new_context()\n    page = context.new_page()\n\n    context.tracing.start(name=\"name1\", snapshots=True)\n    page.goto(server.PREFIX + \"/one-style.html\")\n    context.tracing.stop_chunk(path=tmp_path / \"trace1.zip\")\n    assert (traces_dir / \"name1.trace\").exists()\n    assert (traces_dir / \"name1.network\").exists()\n\n    context.tracing.start_chunk(name=\"name2\")\n    page.goto(server.PREFIX + \"/har.html\")\n    context.tracing.stop(path=tmp_path / \"trace2.zip\")\n    assert (traces_dir / \"name2.trace\").exists()\n    assert (traces_dir / \"name2.network\").exists()\n\n    browser.close()\n\n    with show_trace_viewer(tmp_path / \"trace1.zip\") as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/one-style\\.html\"'),\n            ]\n        )\n        frame = trace_viewer.snapshot_frame('Navigate to \"/one-style.html\"', 0, False)\n        expect(frame.locator(\"body\")).to_have_css(\n            \"background-color\", \"rgb(255, 192, 203)\"\n        )\n        expect(frame.locator(\"body\")).to_have_text(\"hello, world!\")\n\n    with show_trace_viewer(tmp_path / \"trace2.zip\") as trace_viewer:\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r'Navigate to \"/har\\.html\"'),\n            ]\n        )\n        frame = trace_viewer.snapshot_frame('Navigate to \"/har.html\"', 0, False)\n        expect(frame.locator(\"body\")).to_have_css(\n            \"background-color\", \"rgb(255, 192, 203)\"\n        )\n        expect(frame.locator(\"body\")).to_have_text(\"hello, world!\")\n\n\ndef test_should_show_tracing_group_in_action_list(\n    context: BrowserContext,\n    tmp_path: Path,\n    show_trace_viewer: Callable[[Path], ContextManager[TraceViewerPage]],\n) -> None:\n    context.tracing.start()\n    page = context.new_page()\n\n    context.tracing.group(\"outer group\")\n    page.goto(\"data:text/html,<!DOCTYPE html><body><div>Hello world</div></body>\")\n    context.tracing.group(\"inner group 1\")\n    page.locator(\"body\").click()\n    context.tracing.group_end()\n    context.tracing.group(\"inner group 2\")\n    page.get_by_text(\"Hello\").is_visible()\n    context.tracing.group_end()\n    context.tracing.group_end()\n\n    trace_path = tmp_path / \"trace.zip\"\n    context.tracing.stop(path=trace_path)\n\n    with show_trace_viewer(trace_path) as trace_viewer:\n        trace_viewer.expand_action(\"inner group 1\")\n        expect(trace_viewer.action_titles).to_have_text(\n            [\n                re.compile(r\"Create page\"),\n                re.compile(r\"outer group\"),\n                re.compile(r\"Navigate to \\\"data:\\\"\"),\n                re.compile(r\"inner group 1\"),\n                re.compile(r\"Click\"),\n                re.compile(r\"inner group 2\"),\n            ]\n        )\n"
  },
  {
    "path": "tests/sync/test_unroute_behavior.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom playwright.sync_api import BrowserContext, Page\nfrom tests.server import Server\nfrom tests.utils import must\n\n\ndef test_context_unroute_all_removes_all_handlers(\n    page: Page, context: BrowserContext, server: Server\n) -> None:\n    context.route(\n        \"**/*\",\n        lambda route: route.abort(),\n    )\n    context.route(\n        \"**/empty.html\",\n        lambda route: route.abort(),\n    )\n    context.unroute_all()\n    page.goto(server.EMPTY_PAGE)\n\n\ndef test_page_unroute_all_removes_all_routes(page: Page, server: Server) -> None:\n    page.route(\n        \"**/*\",\n        lambda route: route.abort(),\n    )\n    page.route(\n        \"**/empty.html\",\n        lambda route: route.abort(),\n    )\n    page.unroute_all()\n    response = must(page.goto(server.EMPTY_PAGE))\n    assert response.ok\n"
  },
  {
    "path": "tests/sync/test_video.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nfrom pathlib import Path\nfrom typing import Dict\n\nimport pytest\n\nfrom playwright.sync_api import Browser, BrowserType, Error\nfrom tests.server import Server\n\n\ndef test_should_expose_video_path(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = browser.new_page(\n        record_video_dir=tmp_path, record_video_size={\"width\": 100, \"height\": 200}\n    )\n    page.goto(server.PREFIX + \"/grid.html\")\n    video = page.video\n    assert video\n    path = video.path()\n    assert repr(page.video) == f\"<Video page={page}>\"\n    assert str(tmp_path) in str(path)\n    page.wait_for_timeout(1000)\n    page.context.close()\n\n\ndef test_video_should_exist(browser: Browser, tmp_path: Path, server: Server) -> None:\n    page = browser.new_page(record_video_dir=tmp_path)\n    page.goto(server.PREFIX + \"/grid.html\")\n    video = page.video\n    assert video\n    path = video.path()\n    assert str(tmp_path) in str(path)\n    page.wait_for_timeout(1000)\n    page.context.close()\n    assert os.path.exists(path)\n\n\ndef test_record_video_to_path(browser: Browser, tmp_path: Path, server: Server) -> None:\n    page = browser.new_page(record_video_dir=tmp_path)\n    page.goto(server.PREFIX + \"/grid.html\")\n    video = page.video\n    assert video\n    path = video.path()\n    assert str(tmp_path) in str(path)\n    page.wait_for_timeout(1000)\n    page.context.close()\n    assert os.path.exists(path)\n\n\ndef test_record_video_to_path_persistent(\n    browser_type: BrowserType, tmp_path: Path, server: Server, launch_arguments: Dict\n) -> None:\n    context = browser_type.launch_persistent_context(\n        tmp_path, **launch_arguments, record_video_dir=tmp_path\n    )\n    page = context.pages[0]\n    page.goto(server.PREFIX + \"/grid.html\")\n    video = page.video\n    assert video\n    path = video.path()\n    assert str(tmp_path) in str(path)\n    page.wait_for_timeout(1000)\n    context.close()\n    assert os.path.exists(path)\n\n\ndef test_record_video_can_get_video_path_immediately(\n    browser_type: BrowserType, tmp_path: Path, launch_arguments: Dict\n) -> None:\n    context = browser_type.launch_persistent_context(\n        tmp_path, **launch_arguments, record_video_dir=tmp_path\n    )\n    page = context.pages[0]\n    video = page.video\n    assert video\n    path = video.path()\n    assert str(tmp_path) in str(path)\n    page.wait_for_timeout(1000)\n    context.close()\n    assert os.path.exists(path)\n\n\ndef test_should_error_if_page_not_closed_before_save_as(\n    browser: Browser, tmp_path: Path, server: Server\n) -> None:\n    page = browser.new_page(record_video_dir=tmp_path)\n    page.goto(server.PREFIX + \"/grid.html\")\n    page.wait_for_timeout(1000)  # Give it some time to record.\n    out_path = tmp_path / \"some-video.webm\"\n    with pytest.raises(Error) as err:\n        video = page.video\n        assert video\n        video.save_as(out_path)\n    assert video\n    assert \"Page is not yet closed. Close the page prior to calling save_as\" in str(err)\n    assert not os.path.exists(out_path)\n    page.close()\n\n    video.save_as(out_path)\n    assert os.path.exists(out_path)\n"
  },
  {
    "path": "tests/sync/utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom typing import Any, List, cast\n\nfrom playwright.sync_api import Error, Frame, Page, Selectors, ViewportSize\n\n\nclass Utils:\n    def attach_frame(self, page: Page, frame_id: str, url: str) -> Frame:\n        handle = page.evaluate_handle(\n            \"\"\"async ({ frame_id, url }) => {\n                const frame = document.createElement('iframe');\n                frame.src = url;\n                frame.id = frame_id;\n                document.body.appendChild(frame);\n                await new Promise(x => frame.onload = x);\n                return frame;\n            }\"\"\",\n            {\"frame_id\": frame_id, \"url\": url},\n        )\n        element_handle = handle.as_element()\n        assert element_handle\n        frame = element_handle.content_frame()\n        assert frame\n        return frame\n\n    def detach_frame(self, page: Page, frame_id: str) -> None:\n        page.evaluate(\n            \"frame_id => document.getElementById(frame_id).remove()\", frame_id\n        )\n\n    def dump_frames(self, frame: Frame, indentation: str = \"\") -> List[str]:\n        indentation = indentation or \"\"\n        description = re.sub(r\":\\d+/\", \":<PORT>/\", frame.url)\n        if frame.name:\n            description += \" (\" + frame.name + \")\"\n        result = [indentation + description]\n        sorted_frames = sorted(\n            frame.child_frames, key=lambda frame: frame.url + frame.name\n        )\n        for child in sorted_frames:\n            result = result + utils.dump_frames(child, \"    \" + indentation)\n        return result\n\n    def verify_viewport(self, page: Page, width: int, height: int) -> None:\n        assert cast(ViewportSize, page.viewport_size)[\"width\"] == width\n        assert cast(ViewportSize, page.viewport_size)[\"height\"] == height\n        assert page.evaluate(\"window.innerWidth\") == width\n        assert page.evaluate(\"window.innerHeight\") == height\n\n    def register_selector_engine(\n        self, selectors: Selectors, *args: Any, **kwargs: Any\n    ) -> None:\n        try:\n            selectors.register(*args, **kwargs)\n        except Error as exc:\n            if \"has been already registered\" not in exc.message:\n                raise exc\n\n\nutils = Utils()\n"
  },
  {
    "path": "tests/test_installation.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\nfrom venv import EnvBuilder\n\n\ndef test_install(tmp_path: Path, browser_name: str) -> None:\n    env_dir = tmp_path / \"env\"\n    env = EnvBuilder(with_pip=True)\n    env.create(env_dir=env_dir)\n    context = env.ensure_directories(env_dir)\n    root = Path(__file__).parent.parent.resolve()\n    if sys.platform == \"win32\":\n        wheelpath = list((root / \"dist\").glob(\"playwright*win_amd64*.whl\"))[0]\n    elif sys.platform == \"linux\":\n        wheelpath = list((root / \"dist\").glob(\"playwright*manylinux1*.whl\"))[0]\n    elif sys.platform == \"darwin\":\n        wheelpath = list((root / \"dist\").glob(\"playwright*macosx_*.whl\"))[0]\n    subprocess.check_output(\n        [\n            context.env_exe,\n            \"-m\",\n            \"pip\",\n            \"install\",\n            str(wheelpath),\n        ]\n    )\n    environ = os.environ.copy()\n    environ[\"PLAYWRIGHT_BROWSERS_PATH\"] = str(tmp_path)\n    subprocess.check_output(\n        [context.env_exe, \"-m\", \"playwright\", \"install\", browser_name], env=environ\n    )\n    shutil.copyfile(root / \"tests\" / \"assets\" / \"client.py\", tmp_path / \"main.py\")\n    subprocess.check_output(\n        [context.env_exe, str(tmp_path / \"main.py\"), browser_name], env=environ\n    )\n    assert (tmp_path / f\"{browser_name}.png\").exists()\n"
  },
  {
    "path": "tests/test_reference_count_async.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport gc\nimport os\nimport tempfile\nfrom collections import defaultdict\nfrom typing import Any\n\nimport objgraph\nimport pytest\n\nfrom playwright.async_api import async_playwright\nfrom tests.server import Server\n\n\n@pytest.mark.asyncio\nasync def test_memory_objects(server: Server, browser_name: str) -> None:\n    async with async_playwright() as p:\n        browser = await p[browser_name].launch()\n        page = await browser.new_page()\n        await page.goto(server.EMPTY_PAGE)\n\n        page.on(\"dialog\", lambda dialog: dialog.dismiss())\n        for _ in range(100):\n            await page.evaluate(\"\"\"async () => alert()\"\"\")\n\n        await page.route(\"**/*\", lambda route, _: route.fulfill(body=\"OK\"))\n\n        def handle_network_response_received(event: Any) -> None:\n            event[\"__pw__is_last_network_response_received_event\"] = True\n\n        if browser_name == \"chromium\":\n            # https://github.com/microsoft/playwright-python/issues/1602\n            client = await page.context.new_cdp_session(page)\n            await client.send(\"Network.enable\")\n\n            client.on(\n                \"Network.responseReceived\",\n                handle_network_response_received,\n            )\n\n        for _ in range(100):\n            response = await page.evaluate(\"\"\"async () => (await fetch(\"/\")).text()\"\"\")\n            assert response == \"OK\"\n\n        await browser.close()\n\n    gc.collect()\n\n    pw_objects: defaultdict = defaultdict(int)\n    for o in objgraph.by_type(\"dict\"):\n        assert isinstance(o, dict)\n        name = o.get(\"_type\")\n        # https://github.com/microsoft/playwright-python/issues/1602\n        if o.get(\"__pw__is_last_network_response_received_event\", False):\n            assert False\n        if not name:\n            continue\n        pw_objects[name] += 1\n\n    assert \"Dialog\" not in pw_objects\n    assert \"Request\" not in pw_objects\n    assert \"Route\" not in pw_objects\n\n\n@pytest.mark.asyncio\nasync def test_tracing_should_not_leak_protocol_callbacks(browser_name: str) -> None:\n    \"\"\"\n    Regression test for https://github.com/microsoft/playwright-python/issues/2977\n\n    This test ensures that ProtocolCallback objects don't accumulate when tracing is enabled.\n    The memory leak occurred because no_reply callbacks were created with circular references\n    but never cleaned up.\n    \"\"\"\n\n    def count_protocol_callbacks() -> int:\n        \"\"\"Count ProtocolCallback objects in memory.\"\"\"\n        gc.collect()\n        count = 0\n        for obj in gc.get_objects():\n            if (\n                hasattr(obj, \"__class__\")\n                and obj.__class__.__name__ == \"ProtocolCallback\"\n            ):\n                count += 1\n        return count\n\n    with tempfile.TemporaryDirectory() as temp_dir:\n        trace_file = os.path.join(temp_dir, \"test_trace.zip\")\n\n        async with async_playwright() as p:\n            browser = await p[browser_name].launch()\n            context = await browser.new_context()\n\n            # Start tracing to trigger the creation of no_reply callbacks\n            await context.tracing.start(screenshots=True, snapshots=True)\n\n            initial_count = count_protocol_callbacks()\n\n            # Perform operations that would create tracing callbacks\n            for _ in range(3):\n                page = await context.new_page()\n                await page.goto(\"data:text/html,<h1>Test Page</h1>\")\n                await page.wait_for_load_state(\"networkidle\")\n                await page.evaluate(\n                    \"document.querySelector('h1').textContent = 'Modified'\"\n                )\n                await page.close()\n\n            # Stop tracing\n            await context.tracing.stop(path=trace_file)\n            await browser.close()\n\n    # Force garbage collection and check callback count\n    gc.collect()\n    final_count = count_protocol_callbacks()\n\n    # The key assertion: callback count should not have increased significantly\n    # Allow for a small number of legitimate callbacks but ensure no major leak\n    assert (\n        final_count - initial_count <= 5\n    ), f\"ProtocolCallback leak detected: initial={initial_count}, final={final_count}, leaked={final_count - initial_count}\"\n"
  },
  {
    "path": "tests/testserver/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEsjCCApoCCQCIPLvQDgoZojANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9w\ndXBwZXRlZXItdGVzdHMwIBcNMTkwMjEzMTkwNzQzWhgPMzAxODA2MTYxOTA3NDNa\nMBoxGDAWBgNVBAMMD3B1cHBldGVlci10ZXN0czCCAiIwDQYJKoZIhvcNAQEBBQAD\nggIPADCCAgoCggIBAJue1yqA4qn0SJR3rgTd6sCYVHMKqUouD0No09H7qf+5ZaIb\n3yGpC5J9Bsf/ZbvD5xpgqbGEYkHj7Qh6Z/cPCSHA+ZpsUzDXVrLFXrdwwiK1FrIS\nrDI2RYsiP+e52XPC/acWC/7f+E54C62oMjYojaVaDn8gu06gyS1rXK2JITQ6CrKn\nb+PVSkjtPB4ku245u1qCKoblkNEZSkEmw8Csl+gw6ydGqOSQAoo8rsDte5zCMnPX\n7XzL6EhRqpiVx7PCuQWnXhL7j9N214Pit7s7F8TeAA6yZR9oswW+h0dWO+XwocJ1\nrwkODXOngbCqO+GUxyuavIl2m0d2MP8n6Wa9RVqYetmPQzafKkR5hjiV4mgCFqNQ\nbHMTjI6udcR+h5pYoWKxN9/gJaWwyAAzck0AiMeGVrvKR3JKACqlTMzy/Y30obRF\ndddURoFf2wjKJvuTK9hHI7pwM5tlPEwu9bTCWNA6XXs2Bq1f6N2OAKhpKOcihNem\naeGUPmygLPb66z9JO75yZXM+1yk1ScXaNHWZLmluVpEPk7maWULpSpxPAlaN3PmK\n8lEihgfBBovampxZo8SvPEt+g5jGyPq9weNg8ic8476PuRVQdg7D8spVxl6whDlJ\nbcFojzgrX70t13jqZOtla4WK1vRnZAGplfoH0i5WvAVw+i5S/OVzsmNDtGFbAgMB\nAAEwDQYJKoZIhvcNAQELBQADggIBADUAjA/dH+b5UxDC5SL98w1hphw9PvD1cuGS\nsVnKPM236JoTiO3KVfm3NMBfSoBi1hPNkXzqr/R4xbyje4Kc4oYcdjGtpll3T5da\nwkx1+qumx6O2mEaOshxh76dfZfZne6SQphQKHw8PD10CfDb/NMnmdEbiOSENSqS4\njGELuGviUl361oCBU45UEN7lfs7ANAhwSZyEO7deroyGdvsxfQUaqQrEQsG30jn3\nt0cCamYU6eK3bNR/yNXJrZFv3dzoquRY9H52YtVElRqdAIsNlnbxbqz0cm5xFKFt\nYTIrMSO1EvDTbB0PPwC5FJvONHhjwiWzgVXSnZrcs/05TsWWnSHH92S+wGCIBC+0\n6fcSKnjdBn9ks5TrDX0TRY6N890KyDQWxPRhHYrMVpn833WY8y/SguxqiMgLFgMD\nWLy6yZzJloW7NgpLGAfMA0nMG1O92hfKmQw82Pyf3SVXGTDiXiEOXn0vN6bsPaV/\n3Ws2LJQECnVfHj3TsuxdtwcO+VGcFCarMOqlhE6IlQzfK8ykYdP6wCkVgXEtiVCR\nT1OWUWCFowoFpwBFLf1lA065qsAymddnkrUEOMiScZ/3OZhmd+FvgQ+O0iYuqpeI\nxauiQ68+Jb4KjVWnu5QBVq8n1vUJ5+gAzowNMN9G+1+A282Ox23T48dce22BTS6B\n3Taaccm+\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testserver/key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCbntcqgOKp9EiU\nd64E3erAmFRzCqlKLg9DaNPR+6n/uWWiG98hqQuSfQbH/2W7w+caYKmxhGJB4+0I\nemf3DwkhwPmabFMw11ayxV63cMIitRayEqwyNkWLIj/nudlzwv2nFgv+3/hOeAut\nqDI2KI2lWg5/ILtOoMkta1ytiSE0Ogqyp2/j1UpI7TweJLtuObtagiqG5ZDRGUpB\nJsPArJfoMOsnRqjkkAKKPK7A7XucwjJz1+18y+hIUaqYlcezwrkFp14S+4/TdteD\n4re7OxfE3gAOsmUfaLMFvodHVjvl8KHCda8JDg1zp4GwqjvhlMcrmryJdptHdjD/\nJ+lmvUVamHrZj0M2nypEeYY4leJoAhajUGxzE4yOrnXEfoeaWKFisTff4CWlsMgA\nM3JNAIjHhla7ykdySgAqpUzM8v2N9KG0RXXXVEaBX9sIyib7kyvYRyO6cDObZTxM\nLvW0wljQOl17NgatX+jdjgCoaSjnIoTXpmnhlD5soCz2+us/STu+cmVzPtcpNUnF\n2jR1mS5pblaRD5O5mllC6UqcTwJWjdz5ivJRIoYHwQaL2pqcWaPErzxLfoOYxsj6\nvcHjYPInPOO+j7kVUHYOw/LKVcZesIQ5SW3BaI84K1+9Ldd46mTrZWuFitb0Z2QB\nqZX6B9IuVrwFcPouUvzlc7JjQ7RhWwIDAQABAoICAFUvM5SejHR/taMfh/A+EZxv\nRfrbISPr5or9vMU6vymuMIX2P8PLJvx+19Fuah/H8p8rvnffgXGT9FIpvvMsFdGW\nMotnNHqNxXWCOICthnc9LTk4o22w64xnqReNUgzd9b8agGJ58w/xAmOCqEmhFTgn\n/bt1DVLTDIyCMm8Dm1tdUjHNGaBbRph40+mkLbz+eSHoEqNY0lbDQzQ6pfi4AUcm\nT/Jl6VmDwwAsi3QsCvgaDUgAMI2ZiILdwUZY5sHtmx4PKZ22elpEuWAGIJCqni4z\nX1CsMlJpG2XPj3lrKMqLV+B8Tt3kBVUDoig0ZybqK8QgpYeRlxodBmEFVevZOzar\nr/qDRh+vrQQxjpFYfrMkPiueRmz0+K1a7KiKSmrjHIb9CTi3BpgEhawbsOB7M+9z\nG5Q7YtGbVyJPmEAAva89ZZqYvyAwxZk3V4pwpoUYzjgPiHm6Oq0vzKPuCgQxsYzx\nUrCVRo7pSE4tTin4SRThY2/yHiMJl8QY//MkahgY8KEHtXE36km6pMRH/ssdSm+C\nSNCOtzUDY8wpaDQ++aB29NWqgnSgwoBrRUXr5NHq+wNpWtmD7L0wDSKUCPfiCduR\nDoSHBIno5U2jgPrH5Wk7X7loG2XxiDR0qtNOiH24SCI+C1nsLRGBS2Tmo0Qby/Ll\noYYCZ0U3S7wk9UY5HcuRAoIBAQDIl8HTaIuzyOrrpsRdUv8jAxnkdhVjYhWGp7mU\n5concRazcEO5/vDJlsIuQI/w7U+xS7PCBPRq7NxUtaUntlQ00s224Ws0sPHIWUD0\nNBsodTX8hik2PdmZ5ZbBHVaeVbMV/5zV8eOPGGCsAn//7l4YIp2I2Abs79leqSDI\n7tBpF4IsUq7xqcVZ1QhWBZmTqE4gYDVqFEVe9O6OmAdkM1qxVSur2E5Ib+islnu8\nyKlu0QlXg596zLVAjxajKYf20NXxh7O+xt5QDEy3dmJEhz5viS6eI7QECM7Lid2T\nc22mABSKzYfbwQroM9yBiI3p0zjwRhha0hRKocLkSiNUlOWHAoIBAQDGmwRM6Xmu\nj7/lV3KvrOmvcNIbUwMbYY/ATY3Wuph6GFjwdliRiju9F3ZUHMV+yNlVDqH3DeRC\nQwGKIcFiVk+4fq1AbWVCWk2MOf49akeJwqFzgF8nkxVXF9PS73VdEvreSvy7g89t\nABqJN6pmGHWVkE1mf/3LJyS2Y7WCZqSaWG3TBZ2SRb2t/t/DnX1L6tzNMkuNAizA\nsDB3J1hH1eGcWn/24NB9sc7i2Bk+Cpi0S/xDn9FfoBo5U0p/lpopgfFoSeQZXq1A\nKIQdtUPLp3KR9EG/ItimfW875zqFe8bekB9/gakyLsbIyINz1iQQS1L1FFmOO8zN\nRtRmm3MrG9qNAoIBAQC1v2rLFgqeVwkjgvKgbDbnjkPDkIpIhfJjE00+8AV+PyUG\naE21FJ0uyf4e0jiZXyu5xJGW1c5vozTvO7XsiXM6eVYSwaPVFg28LcKAgUWqHqlP\nqG9myhuDKVaymtaEl7mv0O5VmtlIKhpNP+aiCWQQEi0SdEmyHI+jCTK/XEJRNg+o\nATKpm91IS5FF/8Tq2LAQ/ZroBn3kT6BmarEnxLADxNvQ1Cf50gvLdH2gy19ZHOWN\n+aBiL2B6oissotCifQ2bzgy6ao27kalhAU6AMNoNTQqEFm1gymo0WTH+C7PpmGEE\ncr0KC5rKUVMVuph6p/sLGTev8nCYPoDLP7FLTa25AoIBAGmP559B0c141pR+AJVj\noOoBW4vueY5KMvARyLxDfdwXqN5W6QiiotIE8H4QtOCIvQu6tVftaE/X8a+L9Y/h\nNIppuoiuHM5B1UodYQcfwFp2uv37U5hjU0pxfcN2R7lq5zDURrUcgFn9Xh1lGwsd\nIRKYGqvKiAk9CwRuxwFCsWbgba9mIrSmoQUknacJxJlfgnEGtKWEbGkWvQv4O7Ii\n+sHyUGXWZLsKkV59Yh1Z4ISkhrci8VSUcpvZq5VZZSN+z+OQss7RReD+KArqV9id\nbgYp//AqA2Gq9j6uzqo4eiG+FR/euSHVPw9llIkzXwPSJYvifx9cpaTOawMGyRY2\nvdkCggEBAJECl41qbQE7OIPsSmhcz0nK6L4aIkdOxZ6hs6xO7fPwh7EntojPIB6J\nbMuvfujqW4SZ+ZpwZkCc8p8j2VuQvlXLI+s6923IdYAOK5ND9q3Xj7AqgJjUKbhH\nlpYUtfDmIjqVADoiIYXmZBPAo7QvzkX7A2qclV7VL/Dc94bBS6M+v/JGh7QyTCsO\noPK6IOlGL1yg+CdZIzdSiJKVeESPMOBhNtPhm+vOXvRV08ECEILD1j52rUKcPs+I\nuINxopeXePgekedm7nyAW3IMHFKa4EiuEU3LQOaWeKEnaxNdOh12Pyyd6w2iAmrr\nrj/p/2CWVN8OTi7CY5cOTCadHZRyjYA=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "tests/utils.py",
    "content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom typing import Optional, TypeVar\n\nTARGET_CLOSED_ERROR_MESSAGE = \"Target page, context or browser has been closed\"\n\nMustType = TypeVar(\"MustType\")\n\n\ndef must(value: Optional[MustType]) -> MustType:\n    assert value\n    return value\n\n\ndef chromium_version_less_than(a: str, b: str) -> bool:\n    left = list(map(int, a.split(\".\")))\n    right = list(map(int, b.split(\".\")))\n    for i in range(4):\n        if left[i] > right[i]:\n            return False\n        if left[i] < right[i]:\n            return True\n    return False\n"
  },
  {
    "path": "utils/docker/.gitignore",
    "content": "oras/\n"
  },
  {
    "path": "utils/docker/Dockerfile.jammy",
    "content": "FROM ubuntu:jammy\n\nARG DEBIAN_FRONTEND=noninteractive\nARG TZ=America/Los_Angeles\nARG DOCKER_IMAGE_NAME_TEMPLATE=\"mcr.microsoft.com/playwright/python:v%version%-jammy\"\n\nENV LANG=C.UTF-8\nENV LC_ALL=C.UTF-8\n\n# === INSTALL Python ===\n\nRUN apt-get update && \\\n    # Install Python\n    apt-get install -y python3 python3-distutils curl && \\\n    update-alternatives --install /usr/bin/python python /usr/bin/python3 1 && \\\n    curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \\\n    python get-pip.py && \\\n    rm get-pip.py && \\\n    # Feature-parity with node.js base images.\n    apt-get install -y --no-install-recommends git openssh-client gpg && \\\n    # clean apt cache\n    rm -rf /var/lib/apt/lists/* && \\\n    # Create the pwuser\n    adduser pwuser\n\n# === BAKE BROWSERS INTO IMAGE ===\n\nENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright\n\n# 1. Add tip-of-tree Playwright package to install its browsers.\n#    The package should be built beforehand from tip-of-tree Playwright.\nCOPY ./dist/*-manylinux*.whl /tmp/\n\n# 2. Bake in browsers & deps.\n#    Browsers will be downloaded in `/ms-playwright`.\n#    Note: make sure to set 777 to the registry so that any user can access\n#    registry.\nRUN mkdir /ms-playwright && \\\n    mkdir /ms-playwright-agent && \\\n    cd /ms-playwright-agent && \\\n    pip install virtualenv && \\\n    virtualenv venv && \\\n    . venv/bin/activate && \\\n    # if its amd64 then install the manylinux1_x86_64 pip package\n    if [ \"$(uname -m)\" = \"x86_64\" ]; then pip install /tmp/*manylinux1_x86_64*.whl; fi && \\\n    # if its arm64 then install the manylinux1_aarch64 pip package\n    if [ \"$(uname -m)\" = \"aarch64\" ]; then pip install /tmp/*manylinux_2_17_aarch64*.whl; fi && \\\n    playwright mark-docker-image \"${DOCKER_IMAGE_NAME_TEMPLATE}\" && \\\n    playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \\\n    # Workaround for https://github.com/microsoft/playwright/issues/27313\n    # While the gstreamer plugin load process can be in-process, it ended up throwing\n    # an error that it can't have libsoup2 and libsoup3 in the same process because\n    # libgstwebrtc is linked against libsoup2. So we just remove the plugin.\n    if [ \"$(uname -m)\" = \"aarch64\" ]; then \\\n        rm /usr/lib/aarch64-linux-gnu/gstreamer-1.0/libgstwebrtc.so; \\\n    else \\\n        rm /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstwebrtc.so; \\\n    fi && \\\n    rm /tmp/*.whl && \\\n    rm -rf /ms-playwright-agent && \\\n    chmod -R 777 /ms-playwright\n"
  },
  {
    "path": "utils/docker/Dockerfile.noble",
    "content": "FROM ubuntu:noble\n\nARG DEBIAN_FRONTEND=noninteractive\nARG TZ=America/Los_Angeles\nARG DOCKER_IMAGE_NAME_TEMPLATE=\"mcr.microsoft.com/playwright/python:v%version%-noble\"\n\nENV LANG=C.UTF-8\nENV LC_ALL=C.UTF-8\n\n# === INSTALL Python ===\n\nRUN apt-get update && \\\n    # Install Python\n    apt-get install -y python3 curl && \\\n    # Align with upstream Python image and don't be externally managed:\n    # https://github.com/docker-library/python/issues/948\n    rm /usr/lib/python3.12/EXTERNALLY-MANAGED && \\\n    update-alternatives --install /usr/bin/python python /usr/bin/python3 1 && \\\n    curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \\\n    python get-pip.py && \\\n    rm get-pip.py && \\\n    # Feature-parity with node.js base images.\n    apt-get install -y --no-install-recommends git openssh-client gpg && \\\n    # clean apt cache\n    rm -rf /var/lib/apt/lists/* && \\\n    # Create the pwuser\n    adduser pwuser\n\n# === BAKE BROWSERS INTO IMAGE ===\n\nENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright\n\n# 1. Add tip-of-tree Playwright package to install its browsers.\n#    The package should be built beforehand from tip-of-tree Playwright.\nCOPY ./dist/*-manylinux*.whl /tmp/\n\n# 2. Bake in browsers & deps.\n#    Browsers will be downloaded in `/ms-playwright`.\n#    Note: make sure to set 777 to the registry so that any user can access\n#    registry.\nRUN mkdir /ms-playwright && \\\n    mkdir /ms-playwright-agent && \\\n    cd /ms-playwright-agent && \\\n    pip install virtualenv && \\\n    virtualenv venv && \\\n    . venv/bin/activate && \\\n    # if its amd64 then install the manylinux1_x86_64 pip package\n    if [ \"$(uname -m)\" = \"x86_64\" ]; then pip install /tmp/*manylinux1_x86_64*.whl; fi && \\\n    # if its arm64 then install the manylinux1_aarch64 pip package\n    if [ \"$(uname -m)\" = \"aarch64\" ]; then pip install /tmp/*manylinux_2_17_aarch64*.whl; fi && \\\n    playwright mark-docker-image \"${DOCKER_IMAGE_NAME_TEMPLATE}\" && \\\n    playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \\\n    rm /tmp/*.whl && \\\n    rm -rf /ms-playwright-agent && \\\n    chmod -R 777 /ms-playwright\n"
  },
  {
    "path": "utils/docker/build.sh",
    "content": "#!/bin/bash\nset -e\nset +x\n\nif [[ ($1 == '--help') || ($1 == '-h') || ($1 == '') || ($2 == '') ]]; then\n  echo \"usage: $(basename $0) {--arm64,--amd64} {jammy,noble} playwright:localbuild-noble\"\n  echo\n  echo \"Build Playwright docker image and tag it as 'playwright:localbuild-noble'.\"\n  echo \"Once image is built, you can run it with\"\n  echo \"\"\n  echo \"  docker run --rm -it playwright:localbuild-noble /bin/bash\"\n  echo \"\"\n  echo \"NOTE: this requires on Playwright PIP dependencies to be installed\"\n  echo \"\"\n  exit 0\nfi\n\nfunction cleanup() {\n  rm -rf \"dist/\"\n}\n\ntrap \"cleanup; cd $(pwd -P)\" EXIT\ncd \"$(dirname \"$0\")\"\n\npushd ../../\nfor wheel in $(python setup.py --list-wheels); do\n  PLAYWRIGHT_TARGET_WHEEL=$wheel python -m build --wheel\ndone\npopd\nmkdir dist/\ncp ../../dist/*-manylinux*.whl dist/\n\nPLATFORM=\"\"\nif [[ \"$1\" == \"--arm64\" ]]; then\n  PLATFORM=\"linux/arm64\";\nelif [[ \"$1\" == \"--amd64\" ]]; then\n  PLATFORM=\"linux/amd64\"\nelse\n  echo \"ERROR: unknown platform specifier - $1. Only --arm64 or --amd64 is supported\"\n  exit 1\nfi\n\ndocker build --platform \"${PLATFORM}\" -t \"$3\" -f \"Dockerfile.$2\" .\n"
  },
  {
    "path": "utils/docker/publish_docker.sh",
    "content": "#!/bin/bash\n\nset -e\nset +x\n\ntrap \"cd $(pwd -P)\" EXIT\ncd \"$(dirname \"$0\")\"\n\nMCR_IMAGE_NAME=\"playwright/python\"\nPW_VERSION=$(python -c \"from playwright._repo_version import version;print(version)\")\n\nRELEASE_CHANNEL=\"$1\"\nif [[ \"${RELEASE_CHANNEL}\" == \"stable\" ]]; then\n  if [[ \"${PW_VERSION}\" == *post* ]]; then\n    echo \"ERROR: cannot publish stable docker with Playwright version '${PW_VERSION}'\"\n    exit 1\n  fi\nelse\n  echo \"ERROR: unknown release channel - ${RELEASE_CHANNEL}\"\n  echo \"Must be either 'stable' or 'canary'\"\n  exit 1\nfi\n\n# Ubuntu 22.04\nJAMMY_TAGS=(\n  \"v${PW_VERSION}-jammy\"\n)\n\n# Ubuntu 24.04\nNOBLE_TAGS=(\n  \"v${PW_VERSION}\"\n  \"v${PW_VERSION}-noble\"\n)\n\ntag_and_push() {\n  local source=\"$1\"\n  local target=\"$2\"\n  echo \"-- tagging: $target\"\n  docker tag $source $target\n  docker push $target\n  attach_eol_manifest $target\n}\n\nattach_eol_manifest() {\n  local image=\"$1\"\n  local today=$(date -u +'%Y-%m-%d')\n  install_oras_if_needed\n  # oras is re-using Docker credentials, so we don't need to login.\n  # Following the advice in https://portal.microsofticm.com/imp/v3/incidents/incident/476783820/summary\n  ./oras/oras attach --artifact-type application/vnd.microsoft.artifact.lifecycle --annotation \"vnd.microsoft.artifact.lifecycle.end-of-life.date=$today\" $image\n}\n\ninstall_oras_if_needed() {\n  if [[ -x oras/oras ]]; then\n    return\n  fi\n  local version=\"1.1.0\"\n  curl -sLO \"https://github.com/oras-project/oras/releases/download/v${version}/oras_${version}_linux_amd64.tar.gz\"\n  mkdir -p oras\n  tar -zxf oras_${version}_linux_amd64.tar.gz -C oras\n  rm oras_${version}_linux_amd64.tar.gz\n}\n\npublish_docker_images_with_arch_suffix() {\n  local FLAVOR=\"$1\"\n  local TAGS=()\n  if [[ \"$FLAVOR\" == \"jammy\" ]]; then\n    TAGS=(\"${JAMMY_TAGS[@]}\")\n  elif [[ \"$FLAVOR\" == \"noble\" ]]; then\n    TAGS=(\"${NOBLE_TAGS[@]}\")\n  else\n    echo \"ERROR: unknown flavor - $FLAVOR. Must be either 'jammy', or 'noble'\"\n    exit 1\n  fi\n  local ARCH=\"$2\"\n  if [[ \"$ARCH\" != \"amd64\" && \"$ARCH\" != \"arm64\" ]]; then\n    echo \"ERROR: unknown arch - $ARCH. Must be either 'amd64' or 'arm64'\"\n    exit 1\n  fi\n  # Prune docker images to avoid platform conflicts\n  docker system prune -fa\n  ./build.sh \"--${ARCH}\" \"${FLAVOR}\" \"${MCR_IMAGE_NAME}:localbuild\"\n\n  for ((i = 0; i < ${#TAGS[@]}; i++)) do\n    local TAG=\"${TAGS[$i]}\"\n    tag_and_push \"${MCR_IMAGE_NAME}:localbuild\" \"playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}-${ARCH}\"\n  done\n}\n\npublish_docker_manifest () {\n  local FLAVOR=\"$1\"\n  local TAGS=()\n  if [[ \"$FLAVOR\" == \"jammy\" ]]; then\n    TAGS=(\"${JAMMY_TAGS[@]}\")\n  elif [[ \"$FLAVOR\" == \"noble\" ]]; then\n    TAGS=(\"${NOBLE_TAGS[@]}\")\n  else\n    echo \"ERROR: unknown flavor - $FLAVOR. Must be either 'jammy', or 'noble'\"\n    exit 1\n  fi\n\n  for ((i = 0; i < ${#TAGS[@]}; i++)) do\n    local TAG=\"${TAGS[$i]}\"\n    local BASE_IMAGE_TAG=\"playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAG}\"\n    local IMAGE_NAMES=\"\"\n    if [[ \"$2\" == \"arm64\" || \"$2\" == \"amd64\" ]]; then\n        IMAGE_NAMES=\"${IMAGE_NAMES} ${BASE_IMAGE_TAG}-$2\"\n    fi\n    if [[ \"$3\" == \"arm64\" || \"$3\" == \"amd64\" ]]; then\n        IMAGE_NAMES=\"${IMAGE_NAMES} ${BASE_IMAGE_TAG}-$3\"\n    fi\n    docker manifest create \"${BASE_IMAGE_TAG}\" $IMAGE_NAMES\n    docker manifest push \"${BASE_IMAGE_TAG}\"\n  done\n}\n\n# Jammy\npublish_docker_images_with_arch_suffix jammy amd64\npublish_docker_images_with_arch_suffix jammy arm64\npublish_docker_manifest jammy amd64 arm64\n\npublish_docker_images_with_arch_suffix noble amd64\npublish_docker_images_with_arch_suffix noble arm64\npublish_docker_manifest noble amd64 arm64\n"
  },
  {
    "path": "utils/linting/check_file_header.py",
    "content": "#!/usr/bin/env python\n# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport sys\nfrom pathlib import Path\nfrom typing import List\n\nLICENSE_HEADER = \"\"\"\n# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\".strip()\n\n\ndef file_has_license(file_path: str) -> None:\n    path = Path(file_path)\n    if not path.name.endswith(\".py\"):\n        return\n    if not path.exists():\n        raise Exception(f\"File {file_path} does not exist\")\n    content = path.read_text(encoding=\"utf-8\")\n    if content.strip() == \"\":\n        return\n    if content.startswith(\"#!\"):\n        # flake8: noqa: E203\n        content = content[content.find(\"\\n\") + 1 :]\n    if not content.startswith(LICENSE_HEADER):\n        raise Exception(f\"File {file_path} does not have license header\")\n\n\ndef main() -> None:\n    errors: List[str] = []\n\n    for file_path in sys.argv[1:]:\n        try:\n            file_has_license(file_path)\n        except Exception as e:\n            errors.append(str(e))\n\n    if errors:\n        print(\"Missing license header in files:\")\n        for error in errors:\n            print(\"  -\", error)\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  }
]