diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..d6f49421364e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/calibreapp-image-actions.yml b/.github/workflows/calibreapp-image-actions.yml index 4a5864a18a4e..38e7c5a47140 100644 --- a/.github/workflows/calibreapp-image-actions.yml +++ b/.github/workflows/calibreapp-image-actions.yml @@ -36,7 +36,7 @@ jobs: github.event.pull_request.head.repo.full_name == github.repository) steps: - name: Checkout Branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compress Images id: calibre uses: calibreapp/image-actions@main @@ -49,7 +49,7 @@ jobs: if: | github.event_name != 'pull_request' && steps.calibre.outputs.markdown != '' - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v7 with: title: Auto Compress Images branch-suffix: timestamp diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 379be069d393..adeee615cce6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,27 +4,28 @@ on: push: branches: - trunk + workflow_dispatch: jobs: deploy: - if: contains(toJson(github.event.commits), '[deploy site]') == true - runs-on: ubuntu-20.04 + if: contains(toJson(github.event.commits), '[deploy site]') == true || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-24.04 steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Hugo - uses: peaceiris/actions-hugo@v2 + uses: peaceiris/actions-hugo@v3 with: - hugo-version: '0.110.0' + hugo-version: ' 0.125.4' extended: true - name: Build run: chmod +x build-site.sh && ./build-site.sh env: SELENIUM_CI_TOKEN: ${{secrets.SELENIUM_CI_TOKEN}} - name: Deploy - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: personal_token: ${{ secrets.SELENIUM_CI_TOKEN }} publish_dir: ./website_and_docs/public diff --git a/.github/workflows/dotnet-examples.yml b/.github/workflows/dotnet-examples.yml index f9970b3d2ebe..ad0fad7f95b8 100644 --- a/.github/workflows/dotnet-examples.yml +++ b/.github/workflows/dotnet-examples.yml @@ -21,39 +21,71 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + release: [ stable, nightly ] + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: Xvfb :99 & - - name: Set up .Net - uses: actions/setup-dotnet@v3 + - name: Set up .Net Stable + if: matrix.release == 'stable' + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 8.x + - name: Set up .Net Nightly + if: matrix.release == 'nightly' + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + source-url: https://nuget.pkg.github.com/seleniumhq/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Update Nightly version non-Windows + if: matrix.release == 'nightly' && matrix.os != 'windows' + run: + | + pip install -r ./scripts/requirements.txt + latest_nightly=$(python ./scripts/latest-nightly-version.py nuget Selenium.WebDriver) + echo $latest_nightly + dotnet add examples/dotnet/SeleniumDocs/SeleniumDocs.csproj package Selenium.WebDriver --version $latest_nightly + dotnet add examples/dotnet/SeleniumDocs/SeleniumDocs.csproj package Selenium.Support --version $latest_nightly + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Update Nightly version Windows + if: matrix.release == 'nightly' && matrix.os == 'windows' + shell: pwsh + run: + | + pip install -r ./scripts/requirements.txt + $latest_nightly = python ./scripts/latest-nightly-version.py nuget Selenium.WebDriver + dotnet add examples/dotnet/SeleniumDocs/SeleniumDocs.csproj package Selenium.WebDriver --version $latest_nightly + dotnet add examples/dotnet/SeleniumDocs/SeleniumDocs.csproj package Selenium.Support --version $latest_nightly + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 11 - name: Run tests - uses: nick-invision/retry@v2.8.3 + uses: nick-fields/retry@v3.0.2 with: timeout_minutes: 20 max_attempts: 3 diff --git a/.github/workflows/java-examples.yml b/.github/workflows/java-examples.yml index 0c68bb1a7371..d78c625faae3 100644 --- a/.github/workflows/java-examples.yml +++ b/.github/workflows/java-examples.yml @@ -21,38 +21,74 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + release: [ stable, nightly ] + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: Xvfb :99 & - name: Set up Java - uses: actions/setup-java@v3 + id: java + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 11 - - name: Run Tests - uses: nick-invision/retry@v2.8.3 + java-version: 17 + - name: Import test cert non-Windows + if: matrix.os != 'windows' + run: sudo keytool -import -noprompt -trustcacerts -alias SeleniumHQ -file examples/java/src/test/resources/tls.crt -keystore ${{ steps.java.outputs.path }}/lib/security/cacerts -storepass changeit + - name: Import test cert Windows + if: matrix.os == 'windows' + run: keytool -import -noprompt -trustcacerts -alias SeleniumHQ -file examples/java/src/test/resources/tls.crt -keystore ${{ steps.java.outputs.path }}/lib/security/cacerts -storepass changeit + - name: Run Tests Stable + if: matrix.release == 'stable' + uses: nick-invision/retry@v3.0.2 with: - timeout_minutes: 20 + timeout_minutes: 40 max_attempts: 3 command: | cd examples/java - mvn -B test + mvn -B test -D"jdk.internal.httpclient.disableHostnameVerification=true" + - name: Run Tests Nightly Linux/macOS + if: matrix.release == 'nightly' && matrix.os != 'windows' + uses: nick-invision/retry@v3.0.2 + with: + timeout_minutes: 40 + max_attempts: 3 + command: | + pip install yq + xml_content=$(curl -sf https://oss.sonatype.org/service/local/repositories/snapshots/content/org/seleniumhq/selenium/selenium-java/) + latest_snapshot=$(echo "$xml_content" | xq '.content.data."content-item"' | jq -r 'sort_by(.lastModified) | last | .text') + echo "Latest Selenium Snapshot: $latest_snapshot" + cd examples/java + mvn -B -U test -D"jdk.internal.httpclient.disableHostnameVerification=true" + + - name: Run Tests Nightly Windows + if: matrix.release == 'nightly' && matrix.os == 'windows' + uses: nick-invision/retry@v3.0.2 + with: + timeout_minutes: 40 + max_attempts: 3 + command: | + pip install yq + $xml_content = Invoke-WebRequest -Uri "https://oss.sonatype.org/service/local/repositories/snapshots/content/org/seleniumhq/selenium/selenium-java/" + $latest_snapshot = $xml_content.Content | xq '.content.data.\"content-item\"' | jq -r 'sort_by(.lastModified) | last | .text' + Write-Output "Latest Selenium Snapshot: $latest_snapshot" + cd examples/java + mvn -B -U test -D"jdk.internal.httpclient.disableHostnameVerification=true" diff --git a/.github/workflows/js-examples.yml b/.github/workflows/js-examples.yml index 6f655ce8620d..2003ee2a4ab6 100644 --- a/.github/workflows/js-examples.yml +++ b/.github/workflows/js-examples.yml @@ -21,15 +21,16 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + release: [ stable, nightly ] + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Chrome for set binary test uses: browser-actions/setup-chrome@v1 with: @@ -41,47 +42,81 @@ jobs: edge-version: stable id: setup-edge - name: Install Firefox for set binary test - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' uses: browser-actions/setup-firefox@v1 with: firefox-version: latest id: setup-firefox - name: Set ENV Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> $env:GITHUB_ENV echo "EDGE_BIN=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" >> $env:GITHUB_ENV echo "FF_BIN=C:\Program Files (x86)\Mozilla Firefox\firefox-browser.exe" >> $env:GITHUB_ENV - name: Set ENV Mac - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> "$GITHUB_ENV" echo "EDGE_BIN=/Users/runner/hostedtoolcache/msedge/stable/x64/Contents/MacOS/Microsoft Edge" >> "$GITHUB_ENV" echo "FF_BIN=/Users/runner/hostedtoolcache/firefox/latest/x64/Contents/MacOS/firefox" >> "$GITHUB_ENV" - name: Set ENV Linux - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> "$GITHUB_ENV" echo "EDGE_BIN=/opt/hostedtoolcache/msedge/stable/x64/msedge" >> "$GITHUB_ENV" echo "FF_BIN=/opt/hostedtoolcache/firefox/latest/x64/firefox" >> "$GITHUB_ENV" - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: Xvfb :99 & + - name: Setup Node Stable + if: matrix.release == 'stable' + uses: actions/setup-node@v4 + with: + node-version: '22.x' + - name: Setup Node Nightly + if: matrix.release == 'nightly' + uses: actions/setup-node@v4 + with: + node-version: '22.x' + registry-url: 'https://npm.pkg.github.com/' + - name: Use Nightly package.json in Ubuntu/macOS + if: matrix.release == 'nightly' && matrix.os != 'windows' + run: + | + pip install -r ./scripts/requirements.txt + latest_nightly=$(python ./scripts/latest-nightly-version.py npm selenium-webdriver) + echo $latest_nightly + npm install --prefix ./examples/javascript --save selenium-webdriver@npm:@seleniumhq/selenium-webdriver@$latest_nightly + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Use Nightly package.json in Windows + if: matrix.release == 'nightly' && matrix.os == 'windows' + run: + | + pip install -r ./scripts/requirements.txt + $latest_nightly = python ./scripts/latest-nightly-version.py npm selenium-webdriver + npm install --prefix ./examples/javascript --save selenium-webdriver@npm:@seleniumhq/selenium-webdriver@$latest_nightly + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Install Requirements working-directory: ./examples/javascript run: npm install + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run tests - uses: nick-invision/retry@v2.8.3 + uses: nick-invision/retry@v3.0.2 with: timeout_minutes: 20 max_attempts: 3 diff --git a/.github/workflows/kotlin-examples.yml b/.github/workflows/kotlin-examples.yml index fb595ee6ceca..ce53e7052e6e 100644 --- a/.github/workflows/kotlin-examples.yml +++ b/.github/workflows/kotlin-examples.yml @@ -21,15 +21,15 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Chrome for set binary test uses: browser-actions/setup-chrome@v1 with: @@ -41,49 +41,49 @@ jobs: edge-version: stable id: setup-edge - name: Install Firefox for set binary test - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' uses: browser-actions/setup-firefox@v1 with: firefox-version: latest id: setup-firefox - name: Set ENV Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> $env:GITHUB_ENV echo "EDGE_BIN=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" >> $env:GITHUB_ENV echo "FF_BIN=C:\Program Files (x86)\Mozilla Firefox\firefox-browser.exe" >> $env:GITHUB_ENV - name: Set ENV Mac - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> "$GITHUB_ENV" echo "EDGE_BIN=/Users/runner/hostedtoolcache/msedge/stable/x64/Contents/MacOS/Microsoft Edge" >> "$GITHUB_ENV" echo "FF_BIN=/Users/runner/hostedtoolcache/firefox/latest/x64/Contents/MacOS/firefox" >> "$GITHUB_ENV" - name: Set ENV Linux - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: | echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> "$GITHUB_ENV" echo "EDGE_BIN=/opt/hostedtoolcache/msedge/stable/x64/msedge" >> "$GITHUB_ENV" echo "FF_BIN=/opt/hostedtoolcache/firefox/latest/x64/firefox" >> "$GITHUB_ENV" - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: Xvfb :99 & - name: Set up Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 8 + java-version: 11 - name: Run tests - uses: nick-invision/retry@v2.8.3 + uses: nick-invision/retry@v3.0.2 with: timeout_minutes: 20 max_attempts: 3 diff --git a/.github/workflows/label-commenter.yml b/.github/workflows/label-commenter.yml index 576e6f9e5889..df36c74c6fab 100644 --- a/.github/workflows/label-commenter.yml +++ b/.github/workflows/label-commenter.yml @@ -11,8 +11,8 @@ permissions: jobs: comment: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Label Commenter uses: peaceiris/actions-label-commenter@v1 diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index b40bf0d3c807..243c75765a84 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -16,18 +16,18 @@ jobs: group: ${{ github.workflow }}-${{ github.ref }} steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Hugo - uses: peaceiris/actions-hugo@v2 + uses: peaceiris/actions-hugo@v3 with: - hugo-version: "0.110.0" + hugo-version: ' 0.125.4' extended: true - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '18.14.2' + node-version: '22.15.0' cache: 'npm' # The action defaults to search for the dependency file (package-lock.json, # npm-shrinkwrap.json or yarn.lock) in the repository root, and uses its @@ -42,13 +42,13 @@ jobs: - name: Link check continue-on-error: true # <- If set to false, run fails with broken links - uses: untitaker/hyperlink@0.1.27 + uses: untitaker/hyperlink@0.1.44 with: args: website_and_docs/public/ --check-anchors - name: Archive hyperlink results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: hyperlink-report path: website_and_docs/tmp/.hyperlink/hyperlink.log - retention-days: 7 # default is 90 days \ No newline at end of file + retention-days: 7 # default is 90 days diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 8d7bd8cb2d4b..c5180609e8ed 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -14,7 +14,7 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: process-only: 'issues' issue-lock-inactive-days: '30' diff --git a/.github/workflows/python-examples.yml b/.github/workflows/python-examples.yml index 6ee0f573ca8b..c910df4b11cf 100644 --- a/.github/workflows/python-examples.yml +++ b/.github/workflows/python-examples.yml @@ -21,47 +21,84 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + include: + - os: ubuntu + release: stable + python: '3.9' + - os: ubuntu + release: nightly + python: '3.11' + - os: windows + release: stable + python: '3.9' + - os: windows + release: nightly + python: '3.12' + - os: macos + release: stable + python: '3.10' + - os: macos + release: nightly + python: '3.13' + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu' run: Xvfb :99 & - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: 3.8 - - name: Install dependencies + python-version: ${{ matrix.python }} + - name: Install dependencies nightly non-Windows + if: matrix.release == 'nightly' && matrix.os != 'windows' + run: | + pip install -r ./scripts/requirements.txt + latest_nightly_python=$(python ./scripts/latest-python-nightly-version.py) + cd examples/python + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install --index-url https://test.pypi.org/simple/ selenium==$latest_nightly_python --extra-index-url https://pypi.org/simple/ --upgrade --force-reinstall --break-system-packages + - name: Install dependencies nightly Windows + if: matrix.release == 'nightly' && matrix.os == 'windows' + run: | + pip install -r ./scripts/requirements.txt + $latest_nightly_python = python ./scripts/latest-python-nightly-version.py + cd examples/python + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install --index-url https://test.pypi.org/simple/ selenium==$latest_nightly_python --extra-index-url https://pypi.org/simple/ --upgrade --force-reinstall --break-system-packages + - name: Install dependencies stable + if: matrix.release == 'stable' working-directory: ./examples/python run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 11 - name: Run tests - uses: nick-invision/retry@v2.8.3 + uses: nick-invision/retry@v3.0.2 with: - timeout_minutes: 20 + timeout_minutes: 60 max_attempts: 3 command: | cd examples/python - pytest + pytest --reruns 3 diff --git a/.github/workflows/ruby-examples.yml b/.github/workflows/ruby-examples.yml index a4de4331c645..b0717816e088 100644 --- a/.github/workflows/ruby-examples.yml +++ b/.github/workflows/ruby-examples.yml @@ -21,46 +21,109 @@ env: GH_TOKEN: ${{ secrets.SELENIUM_CI_TOKEN }} jobs: - test_examples: + tests: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + release: [ stable, nightly ] + runs-on: ${{ format('{0}-latest', matrix.os) }} steps: - name: Checkout GitHub repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove driver directories Windows - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' run: | rm "$env:ChromeWebDriver" -r -v rm "$env:EdgeWebDriver" -r -v rm "$env:GeckoWebDriver" -r -v - name: Remove driver directories Non-Windows - if: matrix.os != 'windows-latest' + if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - - name: Start Xvfb - if: matrix.os == 'ubuntu-latest' - run: Xvfb :99 & + - name: Setup Fluxbox and Xvfb + if: matrix.os == 'ubuntu' + run: | + sudo apt-get -y install fluxbox libxss1 libappindicator3-1 libindicator7 + Xvfb :99 & + fluxbox -display :99 & + echo "DISPLAY=:99" >> "$GITHUB_ENV" - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: 3.0 + ruby-version: 3.1 bundler-cache: true - - name: Install Gems + - name: Install Gems Nightly non-Windows + if: matrix.release == 'nightly' && matrix.os != 'windows' + run: + | + pip install -r ./scripts/requirements.txt + latest_nightly_webdriver=$(python ./scripts/latest-nightly-version.py rubygems selenium-webdriver) + echo $latest_nightly_webdriver + cd examples/ruby + bundle install + bundle remove selenium-webdriver + bundle add selenium-webdriver --version $latest_nightly_webdriver --source "https://token:$%7B%7Bsecrets.GITHUB_TOKEN%7D%7D@rubygems.pkg.github.com/seleniumhq" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install Gems Nightly Windows + if: matrix.release == 'nightly' && matrix.os == 'windows' + run: + | + pip install -r ./scripts/requirements.txt + $latest_nightly_webdriver = python ./scripts/latest-nightly-version.py rubygems selenium-webdriver + cd examples/ruby + bundle install + bundle remove selenium-webdriver + bundle add selenium-webdriver --version $latest_nightly_webdriver --source "https://token:$%7B%7Bsecrets.GITHUB_TOKEN%7D%7D@rubygems.pkg.github.com/seleniumhq" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install Gems Stable + if: matrix.release == 'stable' working-directory: ./examples/ruby run: bundle install - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 11 - - name: Run tests - uses: nick-invision/retry@v2.8.3 + - name: Run tests on Windows + if: matrix.os == 'windows' + uses: nick-invision/retry@v3.0.2 with: timeout_minutes: 20 - max_attempts: 3 + max_attempts: 2 command: | cd examples/ruby bundle exec rspec + new_command_on_retry: | + cd examples/ruby; $env:DEBUG="true"; bundle exec rspec --only-failures --backtrace + - name: Run tests on ${{ matrix.os }} + if: matrix.os != 'windows' + uses: nick-invision/retry@v3.0.2 + with: + timeout_minutes: 20 + max_attempts: 2 + command: | + cd examples/ruby + bundle exec rspec + new_command_on_retry: | + cd examples/ruby + DEBUG=true bundle exec rspec --only-failures --backtrace + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout GitHub repo + uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1 + bundler-cache: true + - name: Install dependencies + working-directory: ./examples/ruby + run: bundle install + - name: Run RuboCop + working-directory: ./examples/ruby + run: bundle exec rubocop diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18f98f7c3a2b..e92e293f41ee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,14 +10,14 @@ on: jobs: test_build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Hugo - uses: peaceiris/actions-hugo@v2 + uses: peaceiris/actions-hugo@v3 with: - hugo-version: '0.110.0' + hugo-version: '0.125.4' extended: true - name: Build run: chmod +x build-site.sh && ./build-site.sh diff --git a/.gitignore b/.gitignore index 43ede48a62fd..c828c5469d5d 100755 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ website_and_docs/resources .settings .gitignore .pydevproject +**/*.iml +**/.gradle diff --git a/.gitpod.yml b/.gitpod.yml index 0938b32433b6..c5bf57fb4d95 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,12 +1,12 @@ tasks: - name: Install Hugo, init submodules and start website init: | - curl -LO https://github.com/gohugoio/hugo/releases/download/v0.110.0/hugo_extended_0.110.0_linux-amd64.deb && \ - sudo dpkg -i hugo_extended_0.110.0_linux-amd64.deb && \ - sudo rm hugo_extended_0.110.0_linux-amd64.deb + curl -LO https://github.com/gohugoio/hugo/releases/download/v0.125.4/hugo_extended_0.125.4_linux-amd64.deb && \ + sudo dpkg -i hugo_extended_0.125.4_linux-amd64.deb && \ + sudo rm hugo_extended_0.125.4_linux-amd64.deb command: | cd website_and_docs - hugo server --baseUrl $(gp url 1313) --appendPort=false + hugo server --baseURL $(gp url 1313) --appendPort=false # List the ports you want to expose and what to do when they are served. See https://www.gitpod.io/docs/config-ports/ ports: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37b19be3a79b..f9fd866c46e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,3 @@ # Contributing to the Selenium site and docs -Please follow this [link](https://selenium.dev/documentation/about/contributing/) -for all the contribution details. +Please follow this [link](https://selenium.dev/documentation/about/contributing/) for all the contribution details. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index f93f63a77a3b..da798cca98c0 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,3 +1,3 @@ # Governance -Content moved to https://www.selenium.dev/governance/ +Content moved to diff --git a/LICENSE b/LICENSE index 6ff5cc6fe3f8..75b9810f863a 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Software Freedom Conservancy (SFC) + Copyright 2025 Software Freedom Conservancy (SFC) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 9cf6125e7dac..b58633891dfa 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ [![GitHub Actions](https://github.com/seleniumhq/seleniumhq.github.io/workflows/Publish%20Selenium%20Site/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions?query=workflow%3A%22Publish+Selenium+Site%22) +[![Run Java examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/java-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/java-examples.yml) +[![Run Kotlin examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/kotlin-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/kotlin-examples.yml) +[![Run Python examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/python-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/python-examples.yml) +[![Run JavaScript examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/js-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/js-examples.yml) +[![Run Ruby examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/ruby-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/ruby-examples.yml) +[![Run DotNet examples](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/dotnet-examples.yml/badge.svg)](https://github.com/SeleniumHQ/seleniumhq.github.io/actions/workflows/dotnet-examples.yml) Selenium @@ -11,11 +17,11 @@ This is the repository used to build and publish the official Selenium [website] We use [Hugo](https://gohugo.io/) and the [Docsy theme](https://www.docsy.dev/) to build and render the site. You will need the **extended** Sass/SCSS version of the Hugo binary to work on this site. We recommend -to use Hugo 0.110.0. +to use **[Hugo 0.125.4](https://github.com/gohugoio/hugo/releases/tag/v0.125.4)** Steps needed to have this working locally and work on it: -- Follow the [Install Hugo](https://www.docsy.dev/docs/get-started/other-options/#install-hugo) instructions from Docsy +- [Install Hugo](https://gohugo.io/installation/) and follow the [Get Started](https://www.docsy.dev/docs/get-started/) instructions from Docsy - [Install go](https://go.dev/doc/install) - Clone this repository - Run `cd website_and_docs` @@ -25,31 +31,26 @@ A full contribution guideline can be seen at [contributing](https://selenium.dev ## How to get involved? -Please check all the information available at https://selenium.dev/getinvolved/ +Please check all the information available at -### Do not want to clone the repository to contribute? Use GitPod. +### Do not want to clone the repository to contribute? Use GitPod [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/SeleniumHQ/seleniumhq.github.io) - ## For Selenium Site and Documentation maintainers ### How does the site and docs get build? GitHub actions runs for every commit on each PR and protected branch. The regular CI execution will build the site with Hugo to verify that the commit works. The description of these steps can be seen -at the actions configuration file, [one for testing a PR](./.github/workflows/test.yml), and -[one for deploying the site](./.github/workflows/deploy.yml) +at the actions configuration file, [one for testing a PR](./.github/workflows/test.yml), and [one for deploying the site](./.github/workflows/deploy.yml) ### How are the site and docs deployed? -After each CI execution that happens in the `trunk` branch, the script [build-site.sh](./build-site.sh) -is executed for deployment. This script checks for the string `[deploy site]` in the commit message. +After each CI execution that happens in the `trunk` branch, the script [build-site.sh](./build-site.sh) is executed for deployment. This script checks for the string `[deploy site]` in the commit message. -If the commit message contains that string, and the commit is in `trunk`, a -[GitHub action](./.github/workflows/deploy.yml) is triggered to build and deploy the site. -The site and docs will be built, and the changes will be committed to the branch `publish` -by the user [Selenium-CI](https://github.com/selenium-ci/). +If the commit message contains that string, and the commit is in `trunk`, a [GitHub action](./.github/workflows/deploy.yml) is triggered to build and deploy the site. +The site and docs will be built, and the changes will be committed to the branch `publish` by the user [Selenium-CI](https://github.com/selenium-ci/). *What is important to take into account is that the source files for the site are in the `trunk` branch, and the files that get deployed are pushed to the `publish` branch.* @@ -59,11 +60,9 @@ repo [settings](https://github.com/SeleniumHQ/seleniumhq.github.io/settings) (if you should be able to access the link). The selenium. -domain is managed at https://www.gandi.net/en, if you need access to it, reach out to -any of the [PLC](https://www.selenium.dev/project/structure/#plc) or [TLC](https://www.selenium.dev/project/structure/#tlc) +domain is managed at , if you need access to it, reach out to any of the [PLC](https://www.selenium.dev/project/structure/#plc) or [TLC](https://www.selenium.dev/project/structure/#tlc) members, who can help you with that. If for any reason, you need to setup the domain redirection again, we followed this [guide](http://spector.io/how-to-set-up-github-pages-with-a-custom-domain-on-gandi/), -but any tutorial/guide showing how to redirect a domain to GitHub pages should do. - +but any tutorial/guide showing how to redirect a domain to GitHub pages should do. diff --git a/examples/dotnet/SeleniumDocs/BaseChromeTest.cs b/examples/dotnet/SeleniumDocs/BaseChromeTest.cs index 6b5695d8c47f..415525c07158 100644 --- a/examples/dotnet/SeleniumDocs/BaseChromeTest.cs +++ b/examples/dotnet/SeleniumDocs/BaseChromeTest.cs @@ -6,9 +6,9 @@ namespace SeleniumDocs public class BaseChromeTest : BaseTest { [TestInitialize] - public void CreateDriver() + public void AutoStartDriver() { - driver = new ChromeDriver(); + StartDriver(); } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/BaseTest.cs b/examples/dotnet/SeleniumDocs/BaseTest.cs index be17872f1e65..e8d439c9d804 100644 --- a/examples/dotnet/SeleniumDocs/BaseTest.cs +++ b/examples/dotnet/SeleniumDocs/BaseTest.cs @@ -17,7 +17,7 @@ public class BaseTest protected IWebDriver driver; protected Uri GridUrl; private Process _webserverProcess; - private const string ServerJarName = "selenium-server-4.15.0.jar"; + private const string ServerJarName = "selenium-server-4.32.0.jar"; private static readonly string BaseDirectory = AppContext.BaseDirectory; private const string RelativePathToGrid = "../../../../../"; private readonly string _examplesDirectory = Path.GetFullPath(Path.Combine(BaseDirectory, RelativePathToGrid)); @@ -33,12 +33,7 @@ public void Cleanup() } } - protected void StartDriver() - { - driver = new ChromeDriver(); - } - - protected void StartDriver(string browserVersion) + protected void StartDriver(string browserVersion = "stable") { ChromeOptions options = new ChromeOptions { diff --git a/examples/dotnet/SeleniumDocs/BiDi/CDP/CDPTest.cs b/examples/dotnet/SeleniumDocs/BiDi/CDP/CDPTest.cs new file mode 100644 index 000000000000..3188d183d748 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/BiDi/CDP/CDPTest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; + +namespace SeleniumDocs.BiDi.CDP +{ + [TestClass] + public class CDPTest : BaseChromeTest + { + [TestMethod] + public void SetCookie() + { + var cookie = new Dictionary + { + { "name", "cheese" }, + { "value", "gouda" }, + { "domain", "www.selenium.dev" }, + { "secure", true } + }; + ((ChromeDriver)driver).ExecuteCdpCommand("Network.setCookie", cookie); + + driver.Url = "https://www.selenium.dev/"; + Cookie cheese = driver.Manage().Cookies.GetCookieNamed("cheese"); + Assert.AreEqual("gouda", cheese.Value); + + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/BiDi/CDP/LoggingTest.cs b/examples/dotnet/SeleniumDocs/BiDi/CDP/LoggingTest.cs new file mode 100644 index 000000000000..c7f912be43da --- /dev/null +++ b/examples/dotnet/SeleniumDocs/BiDi/CDP/LoggingTest.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; + +namespace SeleniumDocs.BiDi.CDP +{ + [TestClass] + public class LoggingTest : BaseChromeTest + { + [TestMethod] + public async Task ConsoleLogs() + { + driver.Url = "https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"; + + using IJavaScriptEngine monitor = new JavaScriptEngine(driver); + var messages = new List(); + monitor.JavaScriptConsoleApiCalled += (_, e) => + { + messages.Add(e.MessageContent); + }; + await monitor.StartEventMonitoring(); + + driver.FindElement(By.Id("consoleLog")).Click(); + driver.FindElement(By.Id("consoleError")).Click(); + new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => messages.Count > 1); + monitor.StopEventMonitoring(); + + Assert.IsTrue(messages.Contains("Hello, world!")); + Assert.IsTrue(messages.Contains("I am console error")); + } + + [TestMethod] + public async Task JsErrors() + { + driver.Url = "https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"; + + using IJavaScriptEngine monitor = new JavaScriptEngine(driver); + var messages = new List(); + monitor.JavaScriptExceptionThrown += (_, e) => + { + messages.Add(e.Message); + }; + await monitor.StartEventMonitoring(); + + driver.FindElement(By.Id("jsException")).Click(); + new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !messages.IsNullOrEmpty()); + monitor.StopEventMonitoring(); + + Assert.IsTrue(messages.Contains("Uncaught")); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/BidiApiTest.cs b/examples/dotnet/SeleniumDocs/BiDi/CDP/NetworkTest.cs similarity index 52% rename from examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/BidiApiTest.cs rename to examples/dotnet/SeleniumDocs/BiDi/CDP/NetworkTest.cs index f088fb915cab..b51b60d7d1b0 100644 --- a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/BidiApiTest.cs +++ b/examples/dotnet/SeleniumDocs/BiDi/CDP/NetworkTest.cs @@ -1,17 +1,24 @@ -using System; -using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.IdentityModel.Tokens; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; -using OpenQA.Selenium.Support.UI; +using OpenQA.Selenium.DevTools; +using System.Linq; +using OpenQA.Selenium.DevTools.V132.Network; +using OpenQA.Selenium.DevTools.V132.Performance; -namespace SeleniumDocs.Bidirectional.ChromeDevTools + +namespace SeleniumDocs.BiDi.CDP { [TestClass] - public class BidiApiTest : BaseChromeTest + public class NetworkTest : BaseTest { + [TestInitialize] + public void Startup() + { + StartDriver("132"); + } + [TestMethod] public async Task BasicAuthentication() { @@ -20,11 +27,10 @@ public async Task BasicAuthentication() UriMatcher = uri => uri.AbsoluteUri.Contains("herokuapp"), Credentials = new PasswordCredentials("admin", "admin") }; - var networkInterceptor = driver.Manage().Network; networkInterceptor.AddAuthenticationHandler(handler); - await networkInterceptor.StartMonitoring(); + driver.Navigate().GoToUrl("https://the-internet.herokuapp.com/basic_auth"); await networkInterceptor.StopMonitoring(); @@ -32,93 +38,6 @@ public async Task BasicAuthentication() driver.FindElement(By.TagName("p")).Text); } - [TestMethod] - public async Task PinScript() - { - driver.Url = "https://www.selenium.dev/selenium/web/xhtmlTest.html"; - var element = driver.FindElement(By.Id("id1")); - - var key = await new JavaScriptEngine(driver).PinScript("return arguments;"); - - var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element); - - var expected = new List - { - 1L, - true, - element - }; - CollectionAssert.AreEqual(expected, (ICollection)arguments); - } - - [TestMethod] - public async Task MutatedElements() - { - driver.Url = "https://www.selenium.dev/selenium/web/dynamic.html"; - - var mutations = new List(); - using IJavaScriptEngine monitor = new JavaScriptEngine(driver); - monitor.DomMutated += (_, e) => - { - var locator = By.CssSelector($"*[data-__webdriver_id='{e.AttributeData.TargetId}']"); - mutations.Add(driver.FindElement(locator)); - }; - - await monitor.StartEventMonitoring(); - await monitor.EnableDomMutationMonitoring(); - - driver.FindElement(By.Id("reveal")).Click(); - - new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !mutations.IsNullOrEmpty()); - await monitor.DisableDomMutationMonitoring(); - monitor.StopEventMonitoring(); - - var revealed = driver.FindElement(By.Id("revealed")); - Assert.AreEqual(revealed, mutations[0]); - } - - [TestMethod] - public async Task ConsoleLogs() - { - driver.Url = "https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"; - - using IJavaScriptEngine monitor = new JavaScriptEngine(driver); - var messages = new List(); - monitor.JavaScriptConsoleApiCalled += (_, e) => - { - messages.Add(e.MessageContent); - }; - - await monitor.StartEventMonitoring(); - driver.FindElement(By.Id("consoleLog")).Click(); - driver.FindElement(By.Id("consoleError")).Click(); - new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => messages.Count > 1); - monitor.StopEventMonitoring(); - - Assert.IsTrue(messages.Contains("Hello, world!")); - Assert.IsTrue(messages.Contains("I am console error")); - } - - [TestMethod] - public async Task JsErrors() - { - driver.Url = "https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"; - - using IJavaScriptEngine monitor = new JavaScriptEngine(driver); - var messages = new List(); - monitor.JavaScriptExceptionThrown += (_, e) => - { - messages.Add(e.Message); - }; - - await monitor.StartEventMonitoring(); - driver.FindElement(By.Id("jsException")).Click(); - new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !messages.IsNullOrEmpty()); - monitor.StopEventMonitoring(); - - Assert.IsTrue(messages.Contains("Uncaught")); - } - [TestMethod] public async Task RecordNetworkResponse() { @@ -129,8 +48,8 @@ public async Task RecordNetworkResponse() { contentType.Add(e.ResponseHeaders["content-type"]); }; - await networkInterceptor.StartMonitoring(); + driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/blank.html"); await networkInterceptor.StopMonitoring(); @@ -149,11 +68,10 @@ public async Task TransformNetworkResponse() Body = "Creamy, delicious cheese!" } }; - INetwork networkInterceptor = driver.Manage().Network; networkInterceptor.AddResponseHandler(handler); - await networkInterceptor.StartMonitoring(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); await networkInterceptor.StopMonitoring(); @@ -174,16 +92,60 @@ public async Task TransformNetworkRequest() return request; } }; - INetwork networkInterceptor = driver.Manage().Network; networkInterceptor.AddRequestHandler(handler); - await networkInterceptor.StartMonitoring(); + driver.Url = "https://www.selenium.dev/selenium/web/devToolsRequestInterceptionTest.html"; driver.FindElement(By.TagName("button")).Click(); await networkInterceptor.StopMonitoring(); Assert.AreEqual("two", driver.FindElement(By.Id("result")).Text); } + + [TestMethod] + public async Task PerformanceMetrics() + { + driver.Url = "https://www.selenium.dev/selenium/web/frameset.html"; + + var session = ((IDevTools)driver).GetDevToolsSession(); + var domains = session.GetVersionSpecificDomains(); + + await domains.Performance.Enable(new OpenQA.Selenium.DevTools.V132.Performance.EnableCommandSettings()); + var metricsResponse = + await session.SendCommand( + new GetMetricsCommandSettings() + ); + + var metrics = metricsResponse.Metrics.ToDictionary( + dict => dict.Name, + dict => dict.Value + ); + + Assert.IsTrue(metrics["DevToolsCommandDuration"] > 0); + Assert.AreEqual(12, metrics["Frames"]); + } + + [TestMethod] + public async Task SetCookie() + { + var session = ((IDevTools)driver).GetDevToolsSession(); + var domains = session.GetVersionSpecificDomains(); + await domains.Network.Enable(new OpenQA.Selenium.DevTools.V132.Network.EnableCommandSettings()); + + var cookieCommandSettings = new SetCookieCommandSettings + { + Name = "cheese", + Value = "gouda", + Domain = "www.selenium.dev", + Secure = true + }; + await domains.Network.SetCookie(cookieCommandSettings); + + driver.Url = "https://www.selenium.dev/"; + OpenQA.Selenium.Cookie cheese = driver.Manage().Cookies.GetCookieNamed("cheese"); + Assert.AreEqual("gouda", cheese.Value); + } + } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/BiDi/CDP/ScriptTest.cs b/examples/dotnet/SeleniumDocs/BiDi/CDP/ScriptTest.cs new file mode 100644 index 000000000000..b3b92ee2cd35 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/BiDi/CDP/ScriptTest.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Tokens; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; + +namespace SeleniumDocs.BiDi.CDP +{ + [TestClass] + public class ScriptTest : BaseChromeTest + { + [TestMethod] + public async Task PinScript() + { + driver.Url = "https://www.selenium.dev/selenium/web/xhtmlTest.html"; + var element = driver.FindElement(By.Id("id1")); + + var key = await new JavaScriptEngine(driver).PinScript("return arguments;"); + var arguments = ((WebDriver)driver).ExecuteScript(key, 1, true, element); + + var expected = new List + { + 1L, + true, + element + }; + CollectionAssert.AreEqual(expected, (ICollection)arguments); + } + + [TestMethod] + public async Task MutatedElements() + { + driver.Url = "https://www.selenium.dev/selenium/web/dynamic.html"; + var mutations = new List(); + + using IJavaScriptEngine monitor = new JavaScriptEngine(driver); + monitor.DomMutated += (_, e) => + { + var locator = By.CssSelector($"*[data-__webdriver_id='{e.AttributeData.TargetId}']"); + mutations.Add(driver.FindElement(locator)); + }; + await monitor.StartEventMonitoring(); + await monitor.EnableDomMutationMonitoring(); + + driver.FindElement(By.Id("reveal")).Click(); + + new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(_ => !mutations.IsNullOrEmpty()); + await monitor.DisableDomMutationMonitoring(); + monitor.StopEventMonitoring(); + + var revealed = driver.FindElement(By.Id("revealed")); + Assert.AreEqual(revealed, mutations[0]); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpApiTest.cs b/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpApiTest.cs deleted file mode 100644 index f02a25c4ad19..000000000000 --- a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpApiTest.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using OpenQA.Selenium; -using OpenQA.Selenium.DevTools; -using OpenQA.Selenium.DevTools.V118.Network; -using OpenQA.Selenium.DevTools.V118.Performance; - -namespace SeleniumDocs.Bidirectional.ChromeDevtools -{ - [TestClass] - public class CdpApiTest : BaseTest - { - [TestInitialize] - public void Startup() - { - StartDriver("118"); - } - - [TestMethod] - public async Task SetCookie() - { - var session = ((IDevTools)driver).GetDevToolsSession(); - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new OpenQA.Selenium.DevTools.V118.Network.EnableCommandSettings()); - - var cookieCommandSettings = new SetCookieCommandSettings - { - Name = "cheese", - Value = "gouda", - Domain = "www.selenium.dev", - Secure = true - }; - - await domains.Network.SetCookie(cookieCommandSettings); - - driver.Url = "https://www.selenium.dev/"; - OpenQA.Selenium.Cookie cheese = driver.Manage().Cookies.GetCookieNamed("cheese"); - Assert.AreEqual("gouda", cheese.Value); - } - - [TestMethod] - public async Task PerformanceMetrics() - { - driver.Url = "https://www.selenium.dev/selenium/web/frameset.html"; - - var session = ((IDevTools)driver).GetDevToolsSession(); - var domains = session.GetVersionSpecificDomains(); - await domains.Performance.Enable(new OpenQA.Selenium.DevTools.V118.Performance.EnableCommandSettings()); - - var metricsResponse = - await session.SendCommand( - new GetMetricsCommandSettings() - ); - - var metrics = metricsResponse.Metrics.ToDictionary( - dict => dict.Name, - dict => dict.Value - ); - - Assert.IsTrue(metrics["DevToolsCommandDuration"] > 0); - Assert.AreEqual(12, metrics["Frames"]); - } - - [TestMethod] - public async Task BasicAuth() - { - var session = ((IDevTools)driver).GetDevToolsSession(); - var domains = session.GetVersionSpecificDomains(); - await domains.Network.Enable(new OpenQA.Selenium.DevTools.V118.Network.EnableCommandSettings()); - - var encodedAuth = Convert.ToBase64String(Encoding.Default.GetBytes("admin:admin")); - var headerSettings = new SetExtraHTTPHeadersCommandSettings - { - Headers = new Headers() - { - { "authorization", "Basic " + encodedAuth } - } - }; - - await domains.Network.SetExtraHTTPHeaders(headerSettings); - - driver.Url = "https://the-internet.herokuapp.com/basic_auth"; - - var element = driver.FindElement(By.TagName("p")); - Assert.AreEqual("Congratulations! You must have the proper credentials.", element.Text); - } - } -} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpEndpointTest.cs b/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpEndpointTest.cs deleted file mode 100644 index ff4c4d089c3c..000000000000 --- a/examples/dotnet/SeleniumDocs/Bidirectional/ChromeDevtools/CdpEndpointTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; - -namespace SeleniumDocs.Bidirectional.ChromeDevtools -{ - [TestClass] - public class CdpEndpointTest : BaseTest - { - private readonly Dictionary emptyDictionary = new(); - - [TestInitialize] - public void Startup() - { - StartDriver(); - } - - [TestMethod] - public void SetCookie() - { - var cookie = new Dictionary - { - { "name", "cheese" }, - { "value", "gouda" }, - { "domain", "www.selenium.dev" }, - { "secure", true } - }; - - ((ChromeDriver)driver).ExecuteCdpCommand("Network.setCookie", cookie); - - driver.Url = "https://www.selenium.dev/"; - Cookie cheese = driver.Manage().Cookies.GetCookieNamed("cheese"); - Assert.AreEqual("gouda", cheese.Value); - - } - - [TestMethod] - public void PerformanceMetrics() - { - driver.Url = "https://www.selenium.dev/selenium/web/frameset.html"; - - ((ChromeDriver)driver).ExecuteCdpCommand("Performance.enable", emptyDictionary); - - Dictionary response = (Dictionary)((ChromeDriver)driver) - .ExecuteCdpCommand("Performance.getMetrics", emptyDictionary); - - Object[] metricList = (object[])response["metrics"]; - var metrics = metricList.OfType>() - .ToDictionary( - dict => (string)dict["name"], - dict => dict["value"] - ); - - Assert.IsTrue((double)metrics["DevToolsCommandDuration"] > 0); - Assert.AreEqual((long)12, metrics["Frames"]); - } - - [TestMethod] - public void BasicAuth() - { - ((ChromeDriver)driver).ExecuteCdpCommand("Network.enable", emptyDictionary); - - string encodedAuth = Convert.ToBase64String(Encoding.Default.GetBytes("admin:admin")); - var headers = new Dictionary - { - { "headers", new Dictionary { { "authorization", "Basic " + encodedAuth } } } - }; - - ((ChromeDriver)driver).ExecuteCdpCommand("Network.setExtraHTTPHeaders", headers); - - driver.Url = "https://the-internet.herokuapp.com/basic_auth"; - - var element = driver.FindElement(By.TagName("p")); - Assert.AreEqual("Congratulations! You must have the proper credentials.", element.Text); - } - } -} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/BrowsingContextTest.cs b/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/BrowsingContextTest.cs deleted file mode 100644 index 7de31af093f6..000000000000 --- a/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/BrowsingContextTest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace SeleniumDocs.Bidirectional.WebDriverBiDi -{ - [TestClass] - public class BrowsingContextTest : BaseTest - { - - } -} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/LogTest.cs b/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/LogTest.cs deleted file mode 100644 index 54e3f92884b2..000000000000 --- a/examples/dotnet/SeleniumDocs/Bidirectional/WebDriverBiDi/LogTest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace SeleniumDocs.Bidirectional.WebDriverBiDi -{ - [TestClass] - public class LogTest : BaseTest - { - - } -} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Browsers/ChromeTest.cs b/examples/dotnet/SeleniumDocs/Browsers/ChromeTest.cs index d25b7eb43533..15b1cf9b6177 100644 --- a/examples/dotnet/SeleniumDocs/Browsers/ChromeTest.cs +++ b/examples/dotnet/SeleniumDocs/Browsers/ChromeTest.cs @@ -177,8 +177,7 @@ private static string GetChromeLocation() { BrowserVersion = "stable" }; - DriverFinder.FullPath(options); - return options.BinaryLocation; + return new DriverFinder(options).GetBrowserPath(); } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Browsers/EdgeTest.cs b/examples/dotnet/SeleniumDocs/Browsers/EdgeTest.cs index 7e1c1a1bd223..f1b118f2497d 100644 --- a/examples/dotnet/SeleniumDocs/Browsers/EdgeTest.cs +++ b/examples/dotnet/SeleniumDocs/Browsers/EdgeTest.cs @@ -177,8 +177,7 @@ private static string GetEdgeLocation() { BrowserVersion = "stable" }; - DriverFinder.FullPath(options); - return options.BinaryLocation; + return new DriverFinder(options).GetBrowserPath(); } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Browsers/FirefoxTest.cs b/examples/dotnet/SeleniumDocs/Browsers/FirefoxTest.cs index bb23656a3fb7..fc792c3eb8f4 100644 --- a/examples/dotnet/SeleniumDocs/Browsers/FirefoxTest.cs +++ b/examples/dotnet/SeleniumDocs/Browsers/FirefoxTest.cs @@ -201,8 +201,7 @@ private static string GetFirefoxLocation() { BrowserVersion = "stable" }; - DriverFinder.FullPath(options); - return options.BinaryLocation; + return new DriverFinder(options).GetBrowserPath(); } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Browsers/InternetExplorerTest.cs b/examples/dotnet/SeleniumDocs/Browsers/InternetExplorerTest.cs index 7a336f07fb48..cbcffe0e2cf3 100644 --- a/examples/dotnet/SeleniumDocs/Browsers/InternetExplorerTest.cs +++ b/examples/dotnet/SeleniumDocs/Browsers/InternetExplorerTest.cs @@ -11,7 +11,7 @@ namespace SeleniumDocs.Browsers [EnabledOnOs("WINDOWS")] public class InternetExplorerTest { - private InternetExplorerDriver driver; + private InternetExplorerDriver _driver; private string _logLocation; private string _tempPath; @@ -26,7 +26,7 @@ public void Cleanup() { File.Delete(_tempPath); } - driver.Quit(); + _driver.Quit(); } [TestMethod] @@ -35,14 +35,14 @@ public void BasicOptionsWin10() var options = new InternetExplorerOptions(); options.AttachToEdgeChrome = true; options.EdgeExecutablePath = GetEdgeLocation(); - driver = new InternetExplorerDriver(options); + _driver = new InternetExplorerDriver(options); } [TestMethod] public void BasicOptionsWin11() { var options = new InternetExplorerOptions(); - driver = new InternetExplorerDriver(options); + _driver = new InternetExplorerDriver(options); } [TestMethod] @@ -52,10 +52,11 @@ public void LogsToFile() var service = InternetExplorerDriverService.CreateDefaultService(); service.LogFile = GetLogLocation(); - driver = new InternetExplorerDriver(service); - driver.Quit(); // Close the Service log file before reading + _driver = new InternetExplorerDriver(service); + _driver.Quit(); // Close the Service log file before reading var lines = File.ReadLines(GetLogLocation()); - Assert.IsNotNull(lines.FirstOrDefault(line => line.Contains("geckodriver INFO Listening on"))); + Console.WriteLine("Lines: {0}", lines); + Assert.IsTrue(lines.Contains("Started InternetExplorerDriver server")); } [TestMethod] @@ -70,7 +71,7 @@ public void LogsToConsole() //service.LogToConsole = true; - driver = new InternetExplorerDriver(service); + _driver = new InternetExplorerDriver(service); Assert.IsTrue(stringWriter.ToString().Contains("geckodriver INFO Listening on")); Console.SetOut(originalOutput); stringWriter.Dispose(); @@ -84,8 +85,8 @@ public void LogsLevel() service.LoggingLevel = InternetExplorerDriverLogLevel.Warn; - driver = new InternetExplorerDriver(service); - driver.Quit(); // Close the Service log file before reading + _driver = new InternetExplorerDriver(service); + _driver.Quit(); // Close the Service log file before reading var lines = File.ReadLines(GetLogLocation()); Assert.IsNotNull(lines.FirstOrDefault(line => line.Contains("Invalid capability setting: timeouts is type null"))); } @@ -97,7 +98,7 @@ public void SupportingFilesLocation() service.LibraryExtractionPath = GetTempDirectory(); - driver = new InternetExplorerDriver(service); + _driver = new InternetExplorerDriver(service); Assert.IsTrue(File.Exists(GetTempDirectory() + "/IEDriver.tmp")); } diff --git a/examples/dotnet/SeleniumDocs/Drivers/RemoteWebDriverTest.cs b/examples/dotnet/SeleniumDocs/Drivers/RemoteWebDriverTest.cs index 8f1464e1bd6a..0b2675c03e2f 100644 --- a/examples/dotnet/SeleniumDocs/Drivers/RemoteWebDriverTest.cs +++ b/examples/dotnet/SeleniumDocs/Drivers/RemoteWebDriverTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.IdentityModel.Tokens; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -59,19 +60,20 @@ public void Downloads() { EnableDownloads = true }; + driver = new RemoteWebDriver(GridUrl, options); driver.Url = "https://selenium.dev/selenium/web/downloads/download.html"; driver.FindElement(By.Id("file-1")).Click(); driver.FindElement(By.Id("file-2")).Click(); - WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3)); + WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5)); wait.Until(d => ((RemoteWebDriver)d).GetDownloadableFiles().Contains("file_2.jpg")); - List names = ((RemoteWebDriver)driver).GetDownloadableFiles(); + IReadOnlyList names = ((RemoteWebDriver)driver).GetDownloadableFiles(); Assert.IsTrue(names.Contains("file_1.txt")); Assert.IsTrue(names.Contains("file_2.jpg")); - string downloadableFile = names[0]; + string downloadableFile = names.First(f => f == "file_1.txt"); string targetDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); ((RemoteWebDriver)driver).DownloadFile(downloadableFile, targetDirectory); diff --git a/examples/dotnet/SeleniumDocs/Drivers/ServiceTest.cs b/examples/dotnet/SeleniumDocs/Drivers/ServiceTest.cs index f62a8b595737..4aad3970c97b 100644 --- a/examples/dotnet/SeleniumDocs/Drivers/ServiceTest.cs +++ b/examples/dotnet/SeleniumDocs/Drivers/ServiceTest.cs @@ -1,10 +1,7 @@ -using System; -using System.IO; -using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Firefox; +using SeleniumDocs.TestSupport; namespace SeleniumDocs.Drivers { @@ -18,7 +15,8 @@ public void BasicService() driver = new ChromeDriver(service); } - [TestMethod] + [TestMethodCustom] + [EnabledOnOs("OSX")] public void DriverLocation() { var options = GetLatestChromeOptions(); @@ -38,7 +36,7 @@ public void DriverPort() private static string GetDriverLocation(ChromeOptions options) { - return DriverFinder.FullPath(options); + return new DriverFinder(options).GetDriverPath(); } private static ChromeOptions GetLatestChromeOptions() diff --git a/examples/dotnet/SeleniumDocs/Elements/InformationTest.cs b/examples/dotnet/SeleniumDocs/Elements/InformationTest.cs index f113bce99fee..07cc1149d2c8 100644 --- a/examples/dotnet/SeleniumDocs/Elements/InformationTest.cs +++ b/examples/dotnet/SeleniumDocs/Elements/InformationTest.cs @@ -1,9 +1,69 @@ +using System; +using System.Drawing; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; + namespace SeleniumDocs.Elements { [TestClass] - public class InformationTest : BaseTest + public class InformationTest { + [TestMethod] + public void TestInformationCommands(){ + WebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + // Navigate to Url + driver.Url= "https://www.selenium.dev/selenium/web/inputs.html"; + // isDisplayed + // Get boolean value for is element display + bool isEmailVisible = driver.FindElement(By.Name("email_input")).Displayed; + Assert.AreEqual(isEmailVisible, true); + + // isEnabled + // returns true if element is enabled else returns false + bool isEnabledButton = driver.FindElement(By.Name("button_input")).Enabled; + Assert.AreEqual(isEnabledButton, true); + + // isSelected + // returns true if element is checked else returns false + bool isSelectedCheck = driver.FindElement(By.Name("checkbox_input")).Selected; + Assert.AreEqual(isSelectedCheck, true); + + // TagName + // returns TagName of the element + string tagNameInp = driver.FindElement(By.Name("email_input")).TagName; + Assert.AreEqual(tagNameInp, "input"); + + // Get Location and Size + // Get Location + IWebElement rangeElement = driver.FindElement(By.Name("range_input")); + Point point = rangeElement.Location; + Assert.IsNotNull(point.X); + // Get Size + int height=rangeElement.Size.Height; + Assert.IsNotNull(height); + + // Retrieves the computed style property 'font-size' of field + string cssValue = driver.FindElement(By.Name("color_input")).GetCssValue("font-size"); + Assert.AreEqual(cssValue, "13.3333px"); + + // GetText + // Retrieves the text of the element + string text = driver.FindElement(By.TagName("h1")).Text; + Assert.AreEqual(text, "Testing Inputs"); + + // FetchAttributes + // identify the email text box + IWebElement emailTxt = driver.FindElement(By.Name("email_input")); + // fetch the value property associated with the textbox + string valueInfo = emailTxt.GetAttribute("value"); + Assert.AreEqual(valueInfo, "admin@localhost"); + + //Quit the driver + driver.Quit(); + } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Elements/InteractionTest.cs b/examples/dotnet/SeleniumDocs/Elements/InteractionTest.cs index 16ba341a1b90..e20b94b46929 100644 --- a/examples/dotnet/SeleniumDocs/Elements/InteractionTest.cs +++ b/examples/dotnet/SeleniumDocs/Elements/InteractionTest.cs @@ -1,9 +1,52 @@ +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; namespace SeleniumDocs.Elements { [TestClass] - public class InteractionTest : BaseTest + public class InteractionTest { + [TestMethod] + public void TestInteractionCommands() + { + IWebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + // Navigate to Url + driver.Navigate().GoToUrl("https://www.selenium.dev/selenium/web/inputs.html"); + // Click on the element + IWebElement checkInput = driver.FindElement(By.Name("checkbox_input")); + checkInput.Click(); + + //Verify + Boolean isChecked = checkInput.Selected; + Assert.AreEqual(isChecked, false); + + //SendKeys + // Clear field to empty it from any previous data + IWebElement emailInput = driver.FindElement(By.Name("email_input")); + emailInput.Clear(); + //Enter Text + String email = "admin@localhost.dev"; + emailInput.SendKeys(email); + + //Verify + String data = emailInput.GetAttribute("value"); + Assert.AreEqual(data, email); + + + //Clear Element + // Clear field to empty it from any previous data + emailInput.Clear(); + data = emailInput.GetAttribute("value"); + + //Verify + Assert.AreEqual(data, ""); + + //Quit the browser + driver.Quit(); + } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example.xpi b/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example.xpi index 34b0ae3913f7..dca8e2e12312 100644 Binary files a/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example.xpi and b/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example.xpi differ diff --git a/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example/manifest.json b/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example/manifest.json index e938974a20b1..a8b4fec6e60f 100644 --- a/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example/manifest.json +++ b/examples/dotnet/SeleniumDocs/Extensions/webextensions-selenium-example/manifest.json @@ -1,17 +1,22 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "webextensions-selenium-example", - "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium" , + "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium", "version": "0.1", "content_scripts": [ { - "matches": ["https://*/*","http://*/*"], - "js": ["inject.js"] + "matches": [ + "https://*/*", + "http://*/*" + ], + "js": [ + "inject.js" + ] } ], - "applications": { - "gecko": { - "id": "webextensions-selenium-example@example.com" - } - } -} + "browser_specific_settings": { + "gecko": { + "id": "webextensions-selenium-example-v3@example.com" + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs b/examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs index b5247b3a90c1..4e6975c5a04b 100644 --- a/examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs +++ b/examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs @@ -1,9 +1,101 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; + +namespace SeleniumDocs.Interactions{ + +[TestClass] +public class CookiesTest{ + + WebDriver driver = new ChromeDriver(); + + [TestMethod] + public void addCookie(){ + driver.Url="https://www.selenium.dev/selenium/web/blank.html"; + // Add cookie into current browser context + driver.Manage().Cookies.AddCookie(new Cookie("key", "value")); + driver.Quit(); + } + + [TestMethod] + public void getNamedCookie(){ + driver.Url = "https://www.selenium.dev/selenium/web/blank.html"; + // Add cookie into current browser context + driver.Manage().Cookies.AddCookie(new Cookie("foo", "bar")); + // Get cookie details with named cookie 'foo' + Cookie cookie = driver.Manage().Cookies.GetCookieNamed("foo"); + Assert.AreEqual(cookie.Value, "bar"); + driver.Quit(); + } + + [TestMethod] + public void getAllCookies(){ + driver.Url = "https://www.selenium.dev/selenium/web/blank.html"; + // Add cookies into current browser context + driver.Manage().Cookies.AddCookie(new Cookie("test1", "cookie1")); + driver.Manage().Cookies.AddCookie(new Cookie("test2", "cookie2")); + // Get cookies + var cookies = driver.Manage().Cookies.AllCookies; + foreach (var cookie in cookies){ + if (cookie.Name.Equals("test1")){ + Assert.AreEqual("cookie1", cookie.Value); + } + if (cookie.Name.Equals("test2")){ + Assert.AreEqual("cookie2", cookie.Value); + } + } + driver.Quit(); + } + + [TestMethod] + public void deleteCookieNamed(){ + driver.Url = "https://www.selenium.dev/selenium/web/blank.html"; + driver.Manage().Cookies.AddCookie(new Cookie("test1", "cookie1")); + // delete cookie named + driver.Manage().Cookies.DeleteCookieNamed("test1"); + driver.Quit(); + } -namespace SeleniumDocs.Interactions -{ - [TestClass] - public class CookiesTest : BaseTest - { + [TestMethod] + public void deleteCookieObject(){ + driver.Url = "https://www.selenium.dev/selenium/web/blank.html"; + Cookie cookie = new Cookie("test2", "cookie2"); + driver.Manage().Cookies.AddCookie(cookie); + /* + Selenium CSharp bindings also provides a way to delete + cookie by passing cookie object of current browsing context + */ + driver.Manage().Cookies.DeleteCookie(cookie); + driver.Quit(); + } + + [TestMethod] + public void deleteAllCookies(){ + driver.Url = "https://www.selenium.dev/selenium/web/blank.html"; + // Add cookies into current browser context + driver.Manage().Cookies.AddCookie(new Cookie("test1", "cookie1")); + driver.Manage().Cookies.AddCookie(new Cookie("test2", "cookie2")); + // Delete All cookies + driver.Manage().Cookies.DeleteAllCookies(); + driver.Quit(); + } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs b/examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs index f2ab19af117d..85f7573c883f 100644 --- a/examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs +++ b/examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs @@ -1,9 +1,74 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using System.Collections.Generic; namespace SeleniumDocs.Interactions { - [TestClass] - public class FramesTest : BaseTest + [TestClass] + public class FramesTest { + [TestMethod] + public void TestFrames() + { + WebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + // Navigate to Url + driver.Url= "https://www.selenium.dev/selenium/web/iframes.html"; + //switch To IFrame using Web Element + IWebElement iframe = driver.FindElement(By.Id("iframe1")); + //Switch to the frame + driver.SwitchTo().Frame(iframe); + Assert.AreEqual(true, driver.PageSource.Contains("We Leave From Here")); + //Now we can type text into email field + IWebElement emailE = driver.FindElement(By.Id("email")); + emailE.SendKeys("admin@selenium.dev"); + emailE.Clear(); + driver.SwitchTo().DefaultContent(); + + + //switch To IFrame using name or id + driver.FindElement(By.Name("iframe1-name")); + //Switch to the frame + driver.SwitchTo().Frame(iframe); + Assert.AreEqual(true, driver.PageSource.Contains("We Leave From Here")); + IWebElement email = driver.FindElement(By.Id("email")); + //Now we can type text into email field + email.SendKeys("admin@selenium.dev"); + email.Clear(); + driver.SwitchTo().DefaultContent(); + + + //switch To IFrame using index + driver.SwitchTo().Frame(0); + Assert.AreEqual(true, driver.PageSource.Contains("We Leave From Here")); + + //leave frame + driver.SwitchTo().DefaultContent(); + Assert.AreEqual(true, driver.PageSource.Contains("This page has iframes")); + + //quit the browser + driver.Quit(); + } } } \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Interactions/InteractionsTest.cs b/examples/dotnet/SeleniumDocs/Interactions/InteractionsTest.cs new file mode 100644 index 000000000000..bc20acb09f08 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/Interactions/InteractionsTest.cs @@ -0,0 +1,48 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +namespace SeleniumDocumentation.SeleniumInteractions +{ + [TestClass] + public class InteractionsTest + { + [TestMethod] + public void TestInteractions() + { + WebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + // Navigate to Url + driver.Url="https://www.selenium.dev/"; + //GetTitle + String title = driver.Title; + Assert.AreEqual(title, "Selenium"); + + //GetCurrentURL + String url = driver.Url; + Assert.AreEqual(url, "https://www.selenium.dev/"); + + //quitting driver + driver.Quit(); //close all windows + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Interactions/NavigationTest.cs b/examples/dotnet/SeleniumDocs/Interactions/NavigationTest.cs index e3e190452d61..9b2ce323eba6 100644 --- a/examples/dotnet/SeleniumDocs/Interactions/NavigationTest.cs +++ b/examples/dotnet/SeleniumDocs/Interactions/NavigationTest.cs @@ -1,9 +1,43 @@ +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; -namespace SeleniumDocs.Interactions +namespace SeleniumDocumentation.SeleniumInteractions { [TestClass] - public class NavigationTest : BaseTest + public class NavigationTest { + [TestMethod] + public void TestNavigationCommands() + { + IWebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + //Convenient + driver.Url = "https://selenium.dev/"; + //Longer + driver.Navigate().GoToUrl("https://selenium.dev/"); + var title = driver.Title; + Assert.AreEqual("Selenium", title); + + //Back + driver.Navigate().Back(); + title = driver.Title; + Assert.AreEqual("Selenium", title); + + //Forward + driver.Navigate().Forward(); + title = driver.Title; + Assert.AreEqual("Selenium", title); + + //Refresh + driver.Navigate().Refresh(); + title = driver.Title; + Assert.AreEqual("Selenium", title); + + //Quit the browser + driver.Quit(); + } } -} \ No newline at end of file +} diff --git a/examples/dotnet/SeleniumDocs/Interactions/PrintOptionsTest.cs b/examples/dotnet/SeleniumDocs/Interactions/PrintOptionsTest.cs new file mode 100644 index 000000000000..927b34dff340 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/Interactions/PrintOptionsTest.cs @@ -0,0 +1,90 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; + +namespace SeleniumDocumentation.SeleniumInteractions +{ + [TestClass] + public class PrintOptionsTest + { + [TestMethod] + public void TestOrientation() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.Orientation = PrintOrientation.Landscape; + PrintOrientation currentOrientation = printOptions.Orientation; + } + + [TestMethod] + public void TestRange() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.AddPageRangeToPrint("1-3"); // add range of pages + printOptions.AddPageToPrint(5); // add individual page + } + + [TestMethod] + public void TestSize() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + PrintOptions.PageSize currentDimensions = printOptions.PageDimensions; + } + + [TestMethod] + public void TestBackgrounds() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.OutputBackgroundImages = true; + bool currentBackgrounds = printOptions.OutputBackgroundImages; + } + + [TestMethod] + public void TestMargins() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + PrintOptions.Margins currentMargins = printOptions.PageMargins; + } + + + [TestMethod] + public void TestScale() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.ScaleFactor = 0.5; + double currentScale = printOptions.ScaleFactor; + } + + [TestMethod] + public void TestShrinkToFit() + { + IWebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.ShrinkToFit = true; + bool currentShrinkToFit = printOptions.ShrinkToFit; + } + + [TestMethod] + public void PrintWithPrintsPageTest() + { + WebDriver driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + PrintDocument printedPage = driver.Print(printOptions); + Assert.IsTrue(printedPage.AsBase64EncodedString.StartsWith("JVBER")); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/Interactions/VirtualAuthenticatorTest.cs b/examples/dotnet/SeleniumDocs/Interactions/VirtualAuthenticatorTest.cs index a774f471e282..51fa05b402a5 100644 --- a/examples/dotnet/SeleniumDocs/Interactions/VirtualAuthenticatorTest.cs +++ b/examples/dotnet/SeleniumDocs/Interactions/VirtualAuthenticatorTest.cs @@ -86,7 +86,7 @@ public void ShouldBeAbleToRemoveAuthenticator() ((WebDriver)driver).RemoveVirtualAuthenticator(virtualAuthenticatorId); // Since the authenticator was removed, any operation using it will throw an error - Assert.ThrowsException(() => ((WebDriver)driver).GetCredentials()); + Assert.ThrowsException(() => ((WebDriver)driver).GetCredentials()); } [TestMethod] diff --git a/examples/dotnet/SeleniumDocs/Interactions/WindowsTest.cs b/examples/dotnet/SeleniumDocs/Interactions/WindowsTest.cs index eadb93b523ac..f362ad3ba154 100644 --- a/examples/dotnet/SeleniumDocs/Interactions/WindowsTest.cs +++ b/examples/dotnet/SeleniumDocs/Interactions/WindowsTest.cs @@ -1,9 +1,49 @@ +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; - +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using System.Collections.Generic; namespace SeleniumDocs.Interactions { [TestClass] - public class WindowsTest : BaseTest + public class WindowsTest { + [TestMethod] + public void TestWindowCommands() + { + WebDriver driver = new ChromeDriver(); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(500); + + // Navigate to Url + driver.Url="https://www.selenium.dev/selenium/web/window_switching_tests/page_with_frame.html"; + //fetch handle of this + String currHandle = driver.CurrentWindowHandle; + Assert.IsNotNull(currHandle); + + //click on link to open a new window + driver.FindElement(By.LinkText("Open new window")).Click(); + //fetch handles of all windows, there will be two, [0]- default, [1] - new window + IList windowHandles = new List(driver.WindowHandles); + driver.SwitchTo().Window(windowHandles[1]); + //assert on title of new window + String title = driver.Title; + Assert.AreEqual("Simple Page", title); + + //closing current window + driver.Close(); + //Switch back to the old tab or window + driver.SwitchTo().Window(windowHandles[0]); + + //Opens a new tab and switches to new tab + driver.SwitchTo().NewWindow(WindowType.Tab); + Assert.AreEqual("", driver.Title); + + //Opens a new window and switches to new window + driver.SwitchTo().NewWindow(WindowType.Window); + Assert.AreEqual("", driver.Title); + + //quitting driver + driver.Quit(); //close all windows + } } -} \ No newline at end of file +} diff --git a/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj b/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj index 3ef438532dd7..bb82466b3d91 100644 --- a/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj +++ b/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj @@ -1,17 +1,17 @@ - net6.0 + net8.0 false - - - - - - + + + + + + diff --git a/examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs b/examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs new file mode 100644 index 000000000000..b2a04500df76 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium.Chrome; + +namespace SeleniumDocs.SeleniumManagerTest +{ + [TestClass] + public class UsageTest + { + [TestMethod] + public void TestWithSeleniumManager() + { + // Before + // using var driver = new ChromeDriver("path/to/chromedriver"); + + // Now + using var driver = new ChromeDriver(); + driver.Navigate().GoToUrl("https://www.selenium.dev/documentation/selenium_manager/"); + } + } +} diff --git a/examples/dotnet/SeleniumDocs/Troubleshooting/LoggingTest.cs b/examples/dotnet/SeleniumDocs/Troubleshooting/LoggingTest.cs new file mode 100644 index 000000000000..c226e0e9566c --- /dev/null +++ b/examples/dotnet/SeleniumDocs/Troubleshooting/LoggingTest.cs @@ -0,0 +1,80 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Internal.Logging; +using OpenQA.Selenium.Remote; +using System; +using System.IO; + +namespace SeleniumDocs.Troubleshooting +{ + [TestClass] + public class LoggingTest + { + private const string filePath = "Selenium.log"; + + [TestMethod] + public void Logging() + { + Log.SetLevel(LogEventLevel.Trace); + + Log.Handlers.Add(new FileLogHandler(filePath)); + + Log.SetLevel(typeof(RemoteWebDriver), LogEventLevel.Debug); + Log.SetLevel(typeof(SeleniumManager), LogEventLevel.Info); + + Warn("this is a warning"); + Info("this is useful information"); + Debug("this is detailed debug information"); + + using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var streamReader = new StreamReader(fileStream)) + { + var fileLogContent = streamReader.ReadToEnd(); + + StringAssert.Contains(fileLogContent, "this is a warning"); + StringAssert.Contains(fileLogContent, "this is useful information"); + StringAssert.Contains(fileLogContent, "this is detailed debug information"); + } + } + } + + [TestCleanup] + public void TestCleanup() + { + // reset log to default + Log.SetLevel(LogEventLevel.Info) + .Handlers.Clear() + .Handlers.Add(new ConsoleLogHandler()); + } + + // logging is only for internal usage + // hacking it via reflection + + private void Debug(string message) + { + LogMessage("Debug", message); + } + + private void Warn(string message) + { + LogMessage("Warn", message); + } + + private void Info(string message) + { + LogMessage("Info", message); + } + + private void LogMessage(string methodName, string message) + { + var getLoggerMethod = typeof(Log).GetMethod("GetLogger", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, new Type[] { typeof(Type) }); + + var logger = getLoggerMethod.Invoke(null, new object[] { typeof(LoggingTest) }); + + var emitMethod = logger.GetType().GetMethod(methodName); + + emitMethod.Invoke(logger, new object[] { message }); + } + } +} diff --git a/examples/java/README.md b/examples/java/README.md new file mode 100644 index 000000000000..ebe2468de5af --- /dev/null +++ b/examples/java/README.md @@ -0,0 +1,63 @@ +# Running Selenium Java Tests +The following steps will guide you on how to +run Selenium Java tests using a repository +of `SeleniumHQ/seleniumhq.github.io` examples. + +## Initial Setup + +### Prerequisites + +Ensure that Java Development Kit (JDK) and Maven +are installed on your system. If they are not installed, +you will need to download and install them. You can +find detailed installation guides for both on their +respective official sites. + +### Clone the repository +First, we need to get the Selenium Java examples +on your local machine. This can be done by +cloning the `SeleniumHQ/seleniumhq.github.io` Git repository. +Run the following command in your terminal: + +```bash +git clone https://github.com/SeleniumHQ/seleniumhq.github.io.git +``` +## Navigate to the java directory +After cloning the repository, navigate into the +directory where the Selenium Java examples are +located. Run the following command: + +```bash +cd seleniumhq.github.io/examples/java +``` + +## Running the Tests +### Install dependencies +Before running the tests, we need to install all +necessary dependencies. Maven, a software +project management tool, can do this for us. +Run the following command: + +```bash +mvn test-compile +``` + +### Run all tests +To verify if everything is installed correctly and +functioning properly, we should run all +available tests. This can be done with the following command: + +```bash +mvn test +``` + +Please be patient! If this is your first time running these tests, +it might take a while to download and verify all necessary browser drivers. + +## Execute a specific example +To run a specific Selenium Java example, use the following command: +```bash +mvn exec:java -D"exec.mainClass"="dev.selenium.getting_started.FirstScript" -D"exec.classpathScope"=test +``` + +Make sure to replace `dev.selenium.getting_started.FirstScript` with the path and name of the example you want to run. \ No newline at end of file diff --git a/examples/java/build.gradle b/examples/java/build.gradle index 2a0f71427240..a44c0f4f1839 100644 --- a/examples/java/build.gradle +++ b/examples/java/build.gradle @@ -10,8 +10,8 @@ repositories { } dependencies { - testImplementation 'org.seleniumhq.selenium:selenium-java:4.15.0' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' + testImplementation 'org.seleniumhq.selenium:selenium-java:4.31.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.12.1' } test { diff --git a/examples/java/gradle/wrapper/gradle-wrapper.jar b/examples/java/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4f..9bbc975c742b 100644 Binary files a/examples/java/gradle/wrapper/gradle-wrapper.jar and b/examples/java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/java/gradle/wrapper/gradle-wrapper.properties b/examples/java/gradle/wrapper/gradle-wrapper.properties index 41dfb87909a8..37f853b1c84d 100644 --- a/examples/java/gradle/wrapper/gradle-wrapper.properties +++ b/examples/java/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/java/gradlew b/examples/java/gradlew index 1b6c787337ff..faf93008b77e 100755 --- a/examples/java/gradlew +++ b/examples/java/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +133,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +216,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/examples/java/gradlew.bat b/examples/java/gradlew.bat index 107acd32c4e6..9b42019c7915 100644 --- a/examples/java/gradlew.bat +++ b/examples/java/gradlew.bat @@ -1,89 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/java/pom.xml b/examples/java/pom.xml index 9b7a474bc0de..6b66cc9c188b 100644 --- a/examples/java/pom.xml +++ b/examples/java/pom.xml @@ -10,9 +10,10 @@ 1 - 11 - 11 + 17 + 17 UTF-8 + 4.31.0 @@ -29,17 +30,17 @@ org.seleniumhq.selenium selenium-java - 4.15.0 + ${selenium.version} org.seleniumhq.selenium selenium-grid - 4.15.0 + ${selenium.version} org.junit.jupiter junit-jupiter-engine - 5.10.0 + 5.12.1 test @@ -54,7 +55,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.2 + 3.5.3 @@ -65,6 +66,7 @@ junit.jupiter.execution.parallel.config.fixed.max-pool-size = ${surefire.parallel} + 3 diff --git a/examples/java/src/test/java/dev/selenium/BaseTest.java b/examples/java/src/test/java/dev/selenium/BaseTest.java index 3ff60ac40a50..70ac73b5a63f 100644 --- a/examples/java/src/test/java/dev/selenium/BaseTest.java +++ b/examples/java/src/test/java/dev/selenium/BaseTest.java @@ -4,13 +4,19 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.junit.jupiter.api.AfterEach; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.edge.EdgeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxOptions; import org.openqa.selenium.grid.Main; @@ -22,6 +28,9 @@ public class BaseTest { protected WebDriverWait wait; protected File driverPath; protected File browserPath; + protected String username = "admin"; + protected String password = "myStrongPassword"; + protected String trustStorePassword = "seleniumkeystore"; public WebElement getLocatedElement(WebDriver driver, By by) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5)); @@ -49,6 +58,18 @@ protected ChromeDriver startChromeDriver(ChromeOptions options) { return (ChromeDriver) driver; } + protected static ChromeOptions getDefaultChromeOptions() { + ChromeOptions options = new ChromeOptions(); + options.addArguments("--no-sandbox"); + return options; + } + + protected static EdgeOptions getDefaultEdgeOptions() { + EdgeOptions options = new EdgeOptions(); + options.addArguments("--no-sandbox"); + return options; + } + protected File getTempDirectory(String prefix) { File tempDirectory = null; try { @@ -94,6 +115,46 @@ protected URL startStandaloneGrid() { } } + protected URL startStandaloneGridAdvanced() { + int port = PortProber.findFreePort(); + try { + System.setProperty("javax.net.ssl.trustStore", Path.of("src/test/resources/server.jks").toAbsolutePath().toString()); + System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); + System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true"); + Main.main( + new String[] { + "standalone", + "--port", + String.valueOf(port), + "--selenium-manager", + "true", + "--enable-managed-downloads", + "true", + "--log-level", + "WARNING", + "--username", + username, + "--password", + password, + "--https-certificate", + Path.of("src/test/resources/tls.crt").toAbsolutePath().toString(), + "--https-private-key", + Path.of("src/test/resources/tls.key").toAbsolutePath().toString() + }); + return new URL("https://localhost/" + port); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected void enableLogging() { + Logger logger = Logger.getLogger(""); + logger.setLevel(Level.FINE); + Arrays.stream(logger.getHandlers()).forEach(handler -> { + handler.setLevel(Level.FINE); + }); + } + @AfterEach public void quit() { if (driver != null) { diff --git a/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpApiTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpApiTest.java new file mode 100644 index 000000000000..b7505c31f58b --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpApiTest.java @@ -0,0 +1,75 @@ +package dev.selenium.bidi.cdp; + +import com.google.common.collect.ImmutableMap; +import dev.selenium.BaseTest; +import java.time.Duration; +import java.util.Base64; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.devtools.DevTools; +import org.openqa.selenium.devtools.HasDevTools; +import org.openqa.selenium.devtools.v134.browser.Browser; +import org.openqa.selenium.devtools.v134.network.Network; +import org.openqa.selenium.devtools.v134.network.model.Headers; +import org.openqa.selenium.support.ui.WebDriverWait; + +public class CdpApiTest extends BaseTest { + DevTools devTools; + + @BeforeEach + public void createSession() { + ChromeOptions options = getDefaultChromeOptions(); + options.setBrowserVersion("134"); + driver = new ChromeDriver(options); + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + } + + @Test + public void basicAuth() { + devTools = ((HasDevTools) driver).getDevTools(); + devTools.createSession(); + devTools.send(Network.enable(Optional.of(100000), Optional.of(100000), Optional.of(100000))); + + String encodedAuth = Base64.getEncoder().encodeToString("admin:admin".getBytes()); + Map headers = ImmutableMap.of("Authorization", "Basic " + encodedAuth); + + devTools.send(Network.setExtraHTTPHeaders(new Headers(headers))); + + driver.get("https://the-internet.herokuapp.com/basic_auth"); + + Assertions.assertEquals( + "Congratulations! You must have the proper credentials.", + driver.findElement(By.tagName("p")).getText()); + } + + @Test + public void waitForDownload() { + driver.get("https://www.selenium.dev/selenium/web/downloads/download.html"); + + devTools = ((HasDevTools) driver).getDevTools(); + devTools.createSession(); + devTools.send( + Browser.setDownloadBehavior( + Browser.SetDownloadBehaviorBehavior.ALLOWANDNAME, + Optional.empty(), + Optional.of(""), + Optional.of(true))); + + AtomicBoolean completed = new AtomicBoolean(false); + devTools.addListener( + Browser.downloadProgress(), + e -> completed.set(Objects.equals(e.getState().toString(), "completed"))); + + driver.findElement(By.id("file-2")).click(); + + Assertions.assertDoesNotThrow(() -> wait.until(_d -> completed)); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpTest.java new file mode 100644 index 000000000000..f83c18832d95 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpTest.java @@ -0,0 +1,33 @@ +package dev.selenium.bidi.cdp; + +import dev.selenium.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chromium.HasCdp; + +import java.util.HashMap; +import java.util.Map; + +public class CdpTest extends BaseTest { + @BeforeEach + public void createSession() { + driver = new ChromeDriver(); + } + + @Test + public void setCookie() { + Map cookie = new HashMap<>(); + cookie.put("name", "cheese"); + cookie.put("value", "gouda"); + cookie.put("domain", "www.selenium.dev"); + cookie.put("secure", true); + ((HasCdp) driver).executeCdpCommand("Network.setCookie", cookie); + + driver.get("https://www.selenium.dev/"); + Cookie cheese = driver.manage().getCookieNamed("cheese"); + Assertions.assertEquals("gouda", cheese.getValue()); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidi/cdp/LoggingTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/LoggingTest.java new file mode 100644 index 000000000000..01a0cfa09333 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/LoggingTest.java @@ -0,0 +1,40 @@ +package dev.selenium.bidi.cdp; + +import static org.openqa.selenium.devtools.events.CdpEventTypes.consoleEvent; + +import dev.selenium.BaseTest; + +import java.time.Duration; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.logging.HasLogEvents; +import org.openqa.selenium.support.ui.WebDriverWait; + +public class LoggingTest extends BaseTest { + + @BeforeEach + public void createSession() { + driver = new ChromeDriver(); + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + } + + @Test + public void consoleLogs() { + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + CopyOnWriteArrayList messages = new CopyOnWriteArrayList<>(); + + ((HasLogEvents) driver).onLogEvent(consoleEvent(e -> messages.add(e.getMessages().get(0)))); + + driver.findElement(By.id("consoleLog")).click(); + driver.findElement(By.id("consoleError")).click(); + + wait.until(_d -> messages.size() > 1); + Assertions.assertTrue(messages.contains("Hello, world!")); + Assertions.assertTrue(messages.contains("I am console error")); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/BidiApiTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java similarity index 57% rename from examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/BidiApiTest.java rename to examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java index cb657347d34c..96d0c1ad5ab0 100644 --- a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/BidiApiTest.java +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java @@ -1,29 +1,34 @@ -package dev.selenium.bidirectional.chrome_devtools; - -import static org.openqa.selenium.devtools.events.CdpEventTypes.consoleEvent; -import static org.openqa.selenium.devtools.events.CdpEventTypes.domMutation; +package dev.selenium.bidi.cdp; import com.google.common.net.MediaType; import dev.selenium.BaseTest; import java.net.*; import java.time.Duration; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.function.Supplier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.devtools.DevTools; +import org.openqa.selenium.devtools.HasDevTools; import org.openqa.selenium.devtools.NetworkInterceptor; -import org.openqa.selenium.logging.HasLogEvents; +import org.openqa.selenium.devtools.v134.browser.Browser; +import org.openqa.selenium.devtools.v134.network.Network; +import org.openqa.selenium.devtools.v134.performance.Performance; +import org.openqa.selenium.devtools.v134.performance.model.Metric; import org.openqa.selenium.remote.http.*; import org.openqa.selenium.support.ui.WebDriverWait; -public class BidiApiTest extends BaseTest { +public class NetworkTest extends BaseTest { @BeforeEach public void createSession() { @@ -35,7 +40,6 @@ public void createSession() { public void basicAuthentication() { Predicate uriPredicate = uri -> uri.toString().contains("herokuapp.com"); Supplier authentication = UsernameAndPassword.of("admin", "admin"); - ((HasAuthentication) driver).register(uriPredicate, authentication); driver.get("https://the-internet.herokuapp.com/basic_auth"); @@ -45,46 +49,6 @@ public void basicAuthentication() { Assertions.assertEquals(successMessage, elementMessage.getText()); } - @Test - public void pinScript() { - driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); - WebElement element = driver.findElement(By.id("id1")); - - ScriptKey key = ((JavascriptExecutor) driver).pin("return arguments;"); - List arguments = - (List) ((JavascriptExecutor) driver).executeScript(key, 1, true, element); - - Assertions.assertEquals(List.of(1L, true, element), arguments); - } - - @Test - public void mutatedElements() { - driver.get("https://www.selenium.dev/selenium/web/dynamic.html"); - - CopyOnWriteArrayList mutations = new CopyOnWriteArrayList<>(); - ((HasLogEvents) driver).onLogEvent(domMutation(e -> mutations.add(e.getElement()))); - - driver.findElement(By.id("reveal")).click(); - - wait.until(_d -> !mutations.isEmpty()); - Assertions.assertEquals(mutations.get(0), driver.findElement(By.id("revealed"))); - } - - @Test - public void consoleLogs() { - driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); - - CopyOnWriteArrayList messages = new CopyOnWriteArrayList<>(); - ((HasLogEvents) driver).onLogEvent(consoleEvent(e -> messages.add(e.getMessages().get(0)))); - - driver.findElement(By.id("consoleLog")).click(); - driver.findElement(By.id("consoleError")).click(); - - wait.until(_d -> messages.size() > 1); - Assertions.assertTrue(messages.contains("Hello, world!")); - Assertions.assertTrue(messages.contains("I am console error")); - } - @Test public void recordResponse() { CopyOnWriteArrayList contentType = new CopyOnWriteArrayList<>(); @@ -119,7 +83,6 @@ public void transformResponses() { .setStatus(200) .addHeader("Content-Type", MediaType.HTML_UTF_8.toString()) .setContent(Contents.utf8String("Creamy, delicious cheese!"))))) { - driver.get("https://www.selenium.dev/selenium/web/blank.html"); } @@ -128,7 +91,6 @@ public void transformResponses() { } @Test - @Disabled("Not working yet") public void interceptRequests() { AtomicBoolean completed = new AtomicBoolean(false); @@ -152,4 +114,74 @@ public void interceptRequests() { Assertions.assertEquals("two", driver.findElement(By.id("result")).getText()); } + + @Test + public void performanceMetrics() { + driver.get("https://www.selenium.dev/selenium/web/frameset.html"); + + DevTools devTools = ((HasDevTools) driver).getDevTools(); + devTools.createSession(); + + devTools.send(Performance.enable(Optional.empty())); + List metricList = devTools.send(Performance.getMetrics()); + + Map metrics = new HashMap<>(); + for (Metric metric : metricList) { + metrics.put(metric.getName(), metric.getValue()); + } + + Assertions.assertTrue(metrics.get("DevToolsCommandDuration").doubleValue() > 0); + Assertions.assertEquals(12, metrics.get("Frames").intValue()); + } + + @Test + public void setCookie() { + DevTools devTools = ((HasDevTools) driver).getDevTools(); + devTools.createSession(); + + devTools.send( + Network.setCookie( + "cheese", + "gouda", + Optional.empty(), + Optional.of("www.selenium.dev"), + Optional.empty(), + Optional.of(true), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty())); + + driver.get("https://www.selenium.dev/"); + Cookie cheese = driver.manage().getCookieNamed("cheese"); + Assertions.assertEquals("gouda", cheese.getValue()); + } + + @Test + public void waitForDownload() { + driver.get("https://www.selenium.dev/selenium/web/downloads/download.html"); + + DevTools devTools = ((HasDevTools) driver).getDevTools(); + devTools.createSession(); + + devTools.send( + Browser.setDownloadBehavior( + Browser.SetDownloadBehaviorBehavior.ALLOWANDNAME, + Optional.empty(), + Optional.of(""), + Optional.of(true))); + + AtomicBoolean completed = new AtomicBoolean(false); + devTools.addListener( + Browser.downloadProgress(), + e -> completed.set(Objects.equals(e.getState().toString(), "completed"))); + + driver.findElement(By.id("file-2")).click(); + + Assertions.assertDoesNotThrow(() -> wait.until(_d -> completed)); + } } diff --git a/examples/java/src/test/java/dev/selenium/bidi/cdp/ScriptTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/ScriptTest.java new file mode 100644 index 000000000000..dd6436f4f327 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/ScriptTest.java @@ -0,0 +1,51 @@ +package dev.selenium.bidi.cdp; + +import static org.openqa.selenium.devtools.events.CdpEventTypes.domMutation; + +import dev.selenium.BaseTest; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.logging.HasLogEvents; +import org.openqa.selenium.support.ui.WebDriverWait; + +public class ScriptTest extends BaseTest { + + @BeforeEach + public void createSession() { + driver = new ChromeDriver(); + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + } + + @Test + public void pinScript() { + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + WebElement element = driver.findElement(By.id("id1")); + + ScriptKey key = ((JavascriptExecutor) driver).pin("return arguments;"); + List arguments = + (List) ((JavascriptExecutor) driver).executeScript(key, 1, true, element); + + Assertions.assertEquals(List.of(1L, true, element), arguments); + } + + @Test + public void mutatedElements() { + driver.get("https://www.selenium.dev/selenium/web/dynamic.html"); + CopyOnWriteArrayList mutations = new CopyOnWriteArrayList<>(); + + ((HasLogEvents) driver).onLogEvent(domMutation(e -> mutations.add(e.getElement()))); + + driver.findElement(By.id("reveal")).click(); + + wait.until(_d -> !mutations.isEmpty()); + Assertions.assertEquals(mutations.get(0), driver.findElement(By.id("revealed"))); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpApiTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpApiTest.java deleted file mode 100644 index 9a94e2b2932a..000000000000 --- a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpApiTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package dev.selenium.bidirectional.chrome_devtools; - -import com.google.common.collect.ImmutableMap; -import dev.selenium.BaseTest; -import java.time.Duration; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.Cookie; -import org.openqa.selenium.JavascriptException; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.devtools.DevTools; -import org.openqa.selenium.devtools.HasDevTools; -import org.openqa.selenium.devtools.v118.browser.Browser; -import org.openqa.selenium.devtools.v118.network.Network; -import org.openqa.selenium.devtools.v118.network.model.Headers; -import org.openqa.selenium.devtools.v118.performance.Performance; -import org.openqa.selenium.devtools.v118.performance.model.Metric; -import org.openqa.selenium.devtools.v118.runtime.Runtime; -import org.openqa.selenium.support.ui.WebDriverWait; - -public class CdpApiTest extends BaseTest { - DevTools devTools; - - @BeforeEach - public void createSession() { - ChromeOptions options = new ChromeOptions(); - options.setBrowserVersion("118"); - driver = new ChromeDriver(options); - wait = new WebDriverWait(driver, Duration.ofSeconds(10)); - } - - @Test - public void setCookie() { - devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - - devTools.send( - Network.setCookie( - "cheese", - "gouda", - Optional.empty(), - Optional.of("www.selenium.dev"), - Optional.empty(), - Optional.of(true), - Optional.empty(), - Optional.empty(), - Optional.empty(), - Optional.empty(), - Optional.empty(), - Optional.empty(), - Optional.empty(), - Optional.empty())); - - driver.get("https://www.selenium.dev/"); - Cookie cheese = driver.manage().getCookieNamed("cheese"); - Assertions.assertEquals("gouda", cheese.getValue()); - } - - @Test - @Disabled("4.15 broke the casting") - public void performanceMetrics() { - driver.get("https://www.selenium.dev/selenium/web/frameset.html"); - - devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - devTools.send(Performance.enable(Optional.empty())); - - List metricList = devTools.send(Performance.getMetrics()); - - Map metrics = new HashMap<>(); - for (Metric metric : metricList) { - metrics.put(metric.getName(), metric.getValue()); - } - - Assertions.assertTrue(metrics.get("DevToolsCommandDuration").doubleValue() > 0); - Assertions.assertEquals(12, metrics.get("Frames").intValue()); - } - - @Test - public void basicAuth() { - devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - devTools.send(Network.enable(Optional.of(100000), Optional.of(100000), Optional.of(100000))); - - String encodedAuth = Base64.getEncoder().encodeToString("admin:admin".getBytes()); - Map headers = ImmutableMap.of("Authorization", "Basic " + encodedAuth); - - devTools.send(Network.setExtraHTTPHeaders(new Headers(headers))); - - driver.get("https://the-internet.herokuapp.com/basic_auth"); - - Assertions.assertEquals( - "Congratulations! You must have the proper credentials.", - driver.findElement(By.tagName("p")).getText()); - } - - @Test - public void consoleLogs() { - driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); - - DevTools devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - devTools.send(Runtime.enable()); - - CopyOnWriteArrayList logs = new CopyOnWriteArrayList<>(); - devTools.addListener( - Runtime.consoleAPICalled(), - event -> logs.add((String) event.getArgs().get(0).getValue().orElse(""))); - - driver.findElement(By.id("consoleLog")).click(); - - wait.until(_d -> !logs.isEmpty()); - Assertions.assertEquals("Hello, world!", logs.get(0)); - } - - @Test - public void jsErrors() { - driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); - - DevTools devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - devTools.send(Runtime.enable()); - - CopyOnWriteArrayList errors = new CopyOnWriteArrayList<>(); - devTools.getDomains().events().addJavascriptExceptionListener(errors::add); - - driver.findElement(By.id("jsException")).click(); - - wait.until(_d -> !errors.isEmpty()); - Assertions.assertTrue(errors.get(0).getMessage().contains("Error: Not working")); - } - - @Test - public void waitForDownload() { - driver.get("https://www.selenium.dev/selenium/web/downloads/download.html"); - - devTools = ((HasDevTools) driver).getDevTools(); - devTools.createSession(); - devTools.send( - Browser.setDownloadBehavior( - Browser.SetDownloadBehaviorBehavior.ALLOWANDNAME, - Optional.empty(), - Optional.of(""), - Optional.of(true))); - - AtomicBoolean completed = new AtomicBoolean(false); - devTools.addListener( - Browser.downloadProgress(), - e -> completed.set(Objects.equals(e.getState().toString(), "completed"))); - - driver.findElement(By.id("file-2")).click(); - - Assertions.assertDoesNotThrow(() -> wait.until(_d -> completed)); - } -} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpEndpointTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpEndpointTest.java deleted file mode 100644 index c8845e727d4d..000000000000 --- a/examples/java/src/test/java/dev/selenium/bidirectional/chrome_devtools/CdpEndpointTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package dev.selenium.bidirectional.chrome_devtools; - -import com.google.common.collect.ImmutableMap; -import dev.selenium.BaseTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.Cookie; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.chromium.HasCdp; - -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class CdpEndpointTest extends BaseTest { - @BeforeEach - public void createSession() { - driver = new ChromeDriver(); - } - - @Test - public void setCookie() { - Map cookie = new HashMap<>(); - cookie.put("name", "cheese"); - cookie.put("value", "gouda"); - cookie.put("domain", "www.selenium.dev"); - cookie.put("secure", true); - - ((HasCdp) driver).executeCdpCommand("Network.setCookie", cookie); - - driver.get("https://www.selenium.dev/"); - Cookie cheese = driver.manage().getCookieNamed("cheese"); - Assertions.assertEquals("gouda", cheese.getValue()); - } - - @Test - public void performanceMetrics() { - driver.get("https://www.selenium.dev/selenium/web/frameset.html"); - - ((HasCdp) driver).executeCdpCommand("Performance.enable", new HashMap<>()); - - Map response = - ((HasCdp) driver).executeCdpCommand("Performance.getMetrics", new HashMap<>()); - List> metricList = (List>) response.get("metrics"); - - Map metrics = new HashMap<>(); - for (Map metric : metricList) { - metrics.put((String) metric.get("name"), (Number) metric.get("value")); - } - - Assertions.assertTrue(metrics.get("DevToolsCommandDuration").doubleValue() > 0); - Assertions.assertEquals(12, metrics.get("Frames").intValue()); - } - - @Test - public void basicAuth() { - ((HasCdp) driver).executeCdpCommand("Network.enable", new HashMap<>()); - - String encodedAuth = Base64.getEncoder().encodeToString("admin:admin".getBytes()); - Map headers = - ImmutableMap.of("headers", ImmutableMap.of("authorization", "Basic " + encodedAuth)); - - ((HasCdp) driver).executeCdpCommand("Network.setExtraHTTPHeaders", headers); - - driver.get("https://the-internet.herokuapp.com/basic_auth"); - - Assertions.assertEquals( - "Congratulations! You must have the proper credentials.", - driver.findElement(By.tagName("p")).getText()); - } -} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ActionsTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ActionsTest.java new file mode 100644 index 000000000000..ffc692a8fc9a --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ActionsTest.java @@ -0,0 +1,73 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.bidi.module.Input; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.interactions.Actions; + +class ActionsTest extends BaseTest { + private Input input; + + private String windowHandle; + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + windowHandle = driver.getWindowHandle(); + input = new Input(driver); + } + + @Test + void canPerformInputActions() { + driver.get("https://www.selenium.dev/selenium/web/formSelectionPage.html"); + + List options = driver.findElements(By.tagName("option")); + + Actions actions = new Actions(driver); + Actions selectThreeOptions = + actions.click(options.get(1)).keyDown(Keys.SHIFT).click(options.get(3)).keyUp(Keys.SHIFT); + + input.perform(windowHandle, selectThreeOptions.getSequences()); + + WebElement showButton = driver.findElement(By.name("showselected")); + showButton.click(); + + WebElement resultElement = driver.findElement(By.id("result")); + Assertions.assertTrue(resultElement.getText().contains("roquefort parmigiano cheddar")); + } + + @Test + void canPerformReleaseAction() { + driver.get("https://www.selenium.dev/selenium/web/bidi/release_action.html"); + + WebElement inputTextBox = driver.findElement(By.id("keys")); + + Actions sendLowercase = + new Actions(driver).keyDown(inputTextBox, "a").keyDown(inputTextBox, "b"); + + input.perform(windowHandle, sendLowercase.getSequences()); + ((JavascriptExecutor) driver).executeScript("resetEvents()"); + + input.release(windowHandle); + + List> events = + (List>) + ((JavascriptExecutor) driver).executeScript("return allEvents.events"); + Assertions.assertEquals("KeyB", events.get(0).get("code")); + Assertions.assertEquals("KeyA", events.get(1).get("code")); + } + } diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextInspectorTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextInspectorTest.java new file mode 100644 index 000000000000..bd12d14e2b26 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextInspectorTest.java @@ -0,0 +1,185 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.module.BrowsingContextInspector; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContextInfo; +import org.openqa.selenium.bidi.browsingcontext.NavigationInfo; +import org.openqa.selenium.bidi.browsingcontext.ReadinessState; +import org.openqa.selenium.bidi.browsingcontext.UserPromptClosed; +import org.openqa.selenium.bidi.browsingcontext.UserPromptOpened; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class BrowsingContextInspectorTest extends BaseTest { + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + void canListenToWindowBrowsingContextCreatedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + inspector.onBrowsingContextCreated(future::complete); + + String windowHandle = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle(); + + BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals(windowHandle, browsingContextInfo.getId()); + } + } + + @Test + void canListenToTabBrowsingContextCreatedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + inspector.onBrowsingContextCreated(future::complete); + + String windowHandle = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle(); + + BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals(windowHandle, browsingContextInfo.getId()); + } + } + + @Test + void canListenToDomContentLoadedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + inspector.onDomContentLoaded(future::complete); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + context.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE); + + NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded")); + } + } + + @Test + void canListenToBrowsingContextLoadedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + inspector.onBrowsingContextLoaded(future::complete); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + context.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE); + + NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded")); + } + } + + @Test + void canListenToNavigationStartedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + inspector.onNavigationStarted(future::complete); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + context.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE); + + NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertTrue(navigationInfo.getUrl().contains("bidi/logEntryAdded")); + } + } + + @Test + void canListenToFragmentNavigatedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + context.navigate("https://www.selenium.dev/selenium/web/linked_image.html", ReadinessState.COMPLETE); + + inspector.onFragmentNavigated(future::complete); + + context.navigate("https://www.selenium.dev/selenium/web/linked_image.html#linkToAnchorOnThisPage", ReadinessState.COMPLETE); + + NavigationInfo navigationInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertTrue(navigationInfo.getUrl().contains("linkToAnchorOnThisPage")); + } + } + + @Test + void canListenToUserPromptOpenedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + inspector.onUserPromptOpened(future::complete); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("alert")).click(); + + UserPromptOpened userPromptOpened = future.get(5, TimeUnit.SECONDS); + Assertions.assertEquals(context.getId(), userPromptOpened.getBrowsingContextId()); + } + } + + @Test + void canListenToUserPromptClosedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + inspector.onUserPromptClosed(future::complete); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("prompt")).click(); + + context.handleUserPrompt(true, "selenium"); + + UserPromptClosed userPromptClosed = future.get(5, TimeUnit.SECONDS); + Assertions.assertEquals(context.getId(), userPromptClosed.getBrowsingContextId()); + } + } + + @Test + void canListenToBrowsingContextDestroyedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (BrowsingContextInspector inspector = new BrowsingContextInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + inspector.onBrowsingContextDestroyed(future::complete); + + String windowHandle = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle(); + + driver.close(); + + BrowsingContextInfo browsingContextInfo = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals(windowHandle, browsingContextInfo.getId()); + Assertions.assertTrue(browsingContextInfo.getUrl().contains("about:blank")); + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextTest.java index 6efc1425c899..55e82ed2c20e 100644 --- a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextTest.java +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/BrowsingContextTest.java @@ -4,7 +4,12 @@ import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Rectangle; +import org.openqa.selenium.WebElement; import org.openqa.selenium.WindowType; import org.openqa.selenium.bidi.BiDiException; import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; @@ -13,6 +18,11 @@ import org.openqa.selenium.bidi.browsingcontext.ReadinessState; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.print.PrintOptions; +import org.openqa.selenium.remote.RemoteWebElement; + +import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs; +import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfElementLocated; class BrowsingContextTest extends BaseTest { @@ -141,4 +151,218 @@ void testCloseATab() { Assertions.assertThrows(BiDiException.class, tab2::getTree); } + + @Test + void testActivateABrowsingContext() { + BrowsingContext window1 = new BrowsingContext(driver, driver.getWindowHandle()); + BrowsingContext window2 = new BrowsingContext(driver, WindowType.WINDOW); + + window1.activate(); + + boolean isFocused = (boolean) ((JavascriptExecutor) driver).executeScript("return document.hasFocus();"); + + Assertions.assertTrue(isFocused); + } + + @Test + void testReloadABrowsingContext() { + BrowsingContext browsingContext = new BrowsingContext(driver, WindowType.TAB); + + browsingContext.navigate("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html", ReadinessState.COMPLETE); + + NavigationResult reloadInfo = browsingContext.reload(ReadinessState.INTERACTIVE); + + Assertions.assertNotNull(reloadInfo.getNavigationId()); + Assertions.assertTrue(reloadInfo.getUrl().contains("/bidi/logEntryAdded.html")); + } + + @Test + void testHandleUserPrompt() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("alert")).click(); + + browsingContext.handleUserPrompt(); + + Assertions.assertTrue(driver.getPageSource().contains("Testing Alerts and Stuff")); + } + + @Test + void testAcceptUserPrompt() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("alert")).click(); + + browsingContext.handleUserPrompt("true"); + + Assertions.assertTrue(driver.getPageSource().contains("Testing Alerts and Stuff")); + } + + @Test + void testDismissUserPrompt() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("alert")).click(); + + browsingContext.handleUserPrompt("true"); + + Assertions.assertTrue(driver.getPageSource().contains("Testing Alerts and Stuff")); + } + + @Test + void testPassUserTextToUserPrompt() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("prompt-with-default")).click(); + + String userText = "Selenium automates browsers"; + browsingContext.handleUserPrompt(true, userText); + + Assertions.assertTrue(driver.getPageSource().contains(userText)); + } + + @Test + void testDismissUserPromptWithText() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + driver.findElement(By.id("prompt-with-default")).click(); + + String userText = "Selenium automates browsers"; + browsingContext.handleUserPrompt(false, userText); + + Assertions.assertFalse(driver.getPageSource().contains(userText)); + } + + @Test + void textCaptureScreenshot() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/alerts.html"); + + String screenshot = browsingContext.captureScreenshot(); + + Assertions.assertTrue(screenshot.length() > 0); + } + + @Test + void textCaptureViewportScreenshot() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/coordinates_tests/simple_page.html"); + + WebElement element = driver.findElement(By.id("box")); + Rectangle elementRectangle = element.getRect(); + + String screenshot = + browsingContext.captureBoxScreenshot( + elementRectangle.getX(), elementRectangle.getY(), 5, 5); + + Assertions.assertTrue(screenshot.length() > 0); + } + + @Test + void textCaptureElementScreenshot() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + WebElement element = driver.findElement(By.id("checky")); + + String screenshot = browsingContext.captureElementScreenshot(((RemoteWebElement) element).getId()); + + Assertions.assertTrue(screenshot.length() > 0); + } + + @Test + void textSetViewport() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + + browsingContext.setViewport(250, 300); + + List newViewportSize = + (List) + ((JavascriptExecutor) driver) + .executeScript("return [window.innerWidth, window.innerHeight];"); + + Assertions.assertEquals(250, newViewportSize.get(0)); + Assertions.assertEquals(300, newViewportSize.get(1)); + } + + @Test + @Disabled("Supported by Firefox Nightly 124") + void textSetViewportWithDevicePixelRatio() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + + browsingContext.setViewport(250, 300, 5); + + Long newDevicePixelRatio = + (Long) ((JavascriptExecutor) driver).executeScript("return window.devicePixelRatio"); + + Assertions.assertEquals(5, newDevicePixelRatio); + } + + @Test + void testPrintPage() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + PrintOptions printOptions = new PrintOptions(); + + String printPage = browsingContext.print(printOptions); + + Assertions.assertTrue(printPage.length() > 0); + } + + @Test + @Disabled("Supported by Firefox Nightly 124") + void testNavigateBackInTheBrowserHistory() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + browsingContext.navigate("https://www.selenium.dev/selenium/web/formPage.html", ReadinessState.COMPLETE); + + wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit(); + wait.until(titleIs("We Arrive Here")); + + browsingContext.back(); + Assertions.assertTrue(driver.getPageSource().contains("We Leave From Here")); + } + + @Test + @Disabled("Supported by Firefox Nightly 124") + void canNavigateForwardInTheBrowserHistory() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + browsingContext.navigate("https://www.selenium.dev/selenium/web/formPage.html", ReadinessState.COMPLETE); + + wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit(); + wait.until(titleIs("We Arrive Here")); + + browsingContext.back(); + Assertions.assertTrue(driver.getPageSource().contains("We Leave From Here")); + + browsingContext.forward(); + wait.until(titleIs("We Arrive Here")); + } + + @Test + @Disabled("Supported by Firefox Nightly 124") + void canTraverseBrowserHistory() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + browsingContext.navigate("https://www.selenium.dev/selenium/web/formPage.html", ReadinessState.COMPLETE); + + wait.until(visibilityOfElementLocated(By.id("imageButton"))).submit(); + wait.until(titleIs("We Arrive Here")); + + browsingContext.traverseHistory(-1); + Assertions.assertTrue(driver.getPageSource().contains("We Leave From Here")); + } } diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LocateNodesTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LocateNodesTest.java new file mode 100644 index 000000000000..af0003daacbf --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LocateNodesTest.java @@ -0,0 +1,221 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.bidi.module.Script; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.browsingcontext.LocateNodeParameters; +import org.openqa.selenium.bidi.browsingcontext.Locator; +import org.openqa.selenium.bidi.browsingcontext.ReadinessState; +import org.openqa.selenium.bidi.script.EvaluateResult; +import org.openqa.selenium.bidi.script.EvaluateResultSuccess; +import org.openqa.selenium.bidi.script.LocalValue; +import org.openqa.selenium.bidi.script.NodeProperties; +import org.openqa.selenium.bidi.script.RemoteReference; +import org.openqa.selenium.bidi.script.RemoteValue; +import org.openqa.selenium.bidi.script.ResultOwnership; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class LocateNodesTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + @Disabled + void testCreateABrowsingContextForGivenId() { + String id = driver.getWindowHandle(); + BrowsingContext browsingContext = new BrowsingContext(driver, id); + Assertions.assertEquals(id, browsingContext.getId()); + } + + @Test + @Disabled + void canLocateNodes() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + LocateNodeParameters parameters = new LocateNodeParameters(Locator.css("div")); + + List elements = browsingContext.locateNodes(parameters); + Assertions.assertEquals(13, elements.size()); + } + + @Test + @Disabled + void canLocateNodesWithJustLocator() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + List elements = browsingContext.locateNodes(Locator.css("div")); + Assertions.assertEquals(13, elements.size()); + } + + @Test + @Disabled + void canLocateNode() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + RemoteValue element = browsingContext.locateNode(Locator.css("div")); + Assertions.assertEquals("node", element.getType()); + } + + @Test + @Disabled + void canLocateNodesWithCSSLocator() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.css("div.extraDiv, div.content")) + .setMaxNodeCount(1); + + List elements = browsingContext.locateNodes(parameters); + + RemoteValue value = elements.get(0); + NodeProperties properties = (NodeProperties) value.getValue().get(); + Assertions.assertEquals("div", properties.getLocalName().get()); + Assertions.assertEquals("content", properties.getAttributes().get().get("class")); + } + + @Test + @Disabled + void canLocateNodesWithXPathLocator() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.xpath("/html/body/div[2]")).setMaxNodeCount(1); + + List elements = browsingContext.locateNodes(parameters); + + RemoteValue value = elements.get(0); + NodeProperties properties = (NodeProperties) value.getValue().get(); + Assertions.assertEquals("div", properties.getLocalName().get()); + Assertions.assertEquals("content", properties.getAttributes().get().get("class")); + } + + @Test + @Disabled + void canLocateNodesWithInnerText() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.innerText("Spaced out")).setMaxNodeCount(1); + + List elements = browsingContext.locateNodes(parameters); + + RemoteValue value = elements.get(0); + Assertions.assertEquals("node", value.getType()); + } + + @Test + @Disabled + void canLocateNodesWithMaxNodeCount() { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + driver.get("https://www.selenium.dev/selenium/web/xhtmlTest.html"); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.css("div")).setMaxNodeCount(4); + + List elements = browsingContext.locateNodes(parameters); + Assertions.assertEquals(4, elements.size()); + } + + @Test + @Disabled + void canLocateNodesGivenStartNodes() { + String handle = driver.getWindowHandle(); + BrowsingContext browsingContext = new BrowsingContext(driver, handle); + + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + + Script script = new Script(driver); + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + handle, + "document.querySelectorAll(\"form\")", + false, + Optional.of(ResultOwnership.ROOT)); + + EvaluateResultSuccess resultSuccess = (EvaluateResultSuccess) result; + List startNodes = new ArrayList<>(); + + RemoteValue remoteValue = resultSuccess.getResult(); + List remoteValues = (List) remoteValue.getValue().get(); + + remoteValues.forEach( + value -> + startNodes.add( + new RemoteReference(RemoteReference.Type.SHARED_ID, value.getSharedId().get()))); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.css("input")) + .setStartNodes(startNodes) + .setMaxNodeCount(50); + + List elements = browsingContext.locateNodes(parameters); + Assertions.assertEquals(35, elements.size()); + } + + @Test + @Disabled + void canLocateNodesInAGivenSandbox() { + String sandbox = "sandbox"; + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + + browsingContext.navigate("https://www.selenium.dev/selenium/web/xhtmlTest.html", ReadinessState.COMPLETE); + + LocateNodeParameters parameters = + new LocateNodeParameters(Locator.css("div")) + .setSandbox(sandbox) + .setMaxNodeCount(1); + + List elements = browsingContext.locateNodes(parameters); + + String nodeId = elements.get(0).getSharedId().get(); + + List arguments = new ArrayList<>(); + + LocalValue value = LocalValue.mapValue(Map.of("sharedId", LocalValue.stringValue(nodeId))); + arguments.add(value); + + Script script = new Script(driver); + + // Since the node was present in the sandbox, the script run in the same sandbox should be able + // to retrieve it + EvaluateResult result = + script.callFunctionInBrowsingContext( + driver.getWindowHandle(), + sandbox, + "function(){ return arguments[0]; }", + true, + Optional.of(arguments), + Optional.empty(), + Optional.empty()); + + Map sharedIdMap = + (Map) ((EvaluateResultSuccess) result).getResult().getValue().get(); + + String sharedId = (String) ((RemoteValue) sharedIdMap.get("sharedId")).getValue().get(); + Assertions.assertEquals(nodeId, sharedId); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LogTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LogTest.java index c5d634576faa..11ccec612536 100644 --- a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LogTest.java +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/LogTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; -import org.openqa.selenium.bidi.LogInspector; +import org.openqa.selenium.bidi.module.LogInspector; import org.openqa.selenium.bidi.log.ConsoleLogEntry; import org.openqa.selenium.bidi.log.JavascriptLogEntry; import org.openqa.selenium.bidi.log.LogLevel; @@ -27,21 +27,6 @@ public void setup() { driver = new FirefoxDriver(options); } - @Test - public void jsErrors() { - CopyOnWriteArrayList logs = new CopyOnWriteArrayList<>(); - - try (LogInspector logInspector = new LogInspector(driver)) { - logInspector.onConsoleEntry(logs::add); - } - - driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); - driver.findElement(By.id("consoleLog")).click(); - - new WebDriverWait(driver, Duration.ofSeconds(5)).until(_d -> !logs.isEmpty()); - Assertions.assertEquals("Hello, world!", logs.get(0).getText()); - } - @Test void testListenToConsoleLog() throws ExecutionException, InterruptedException, TimeoutException { try (LogInspector logInspector = new LogInspector(driver)) { @@ -54,7 +39,7 @@ void testListenToConsoleLog() throws ExecutionException, InterruptedException, T ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS); Assertions.assertEquals("Hello, world!", logEntry.getText()); - Assertions.assertNull(logEntry.getRealm()); + Assertions.assertEquals(1, logEntry.getArgs().size()); Assertions.assertEquals("console", logEntry.getType()); Assertions.assertEquals("log", logEntry.getMethod()); diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkCommandsTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkCommandsTest.java new file mode 100644 index 000000000000..8050892d27ca --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkCommandsTest.java @@ -0,0 +1,112 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.UsernameAndPassword; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.bidi.module.Network; +import org.openqa.selenium.bidi.network.AddInterceptParameters; +import org.openqa.selenium.bidi.network.InterceptPhase; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +class NetworkCommandsTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + } + + @Test + @Disabled + void canAddIntercept() { + try (Network network = new Network(driver)) { + String intercept = + network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT)); + Assertions.assertNotNull(intercept); + } + } + + @Test + @Disabled + void canRemoveIntercept() { + try (Network network = new Network(driver)) { + String intercept = + network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT)); + Assertions.assertNotNull(intercept); + network.removeIntercept(intercept); + } + } + + @Test + @Disabled + void canContinueWithAuthCredentials() { + try (Network network = new Network(driver)) { + network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)); + network.onAuthRequired( + responseDetails -> + network.continueWithAuth( + responseDetails.getRequest().getRequestId(), + new UsernameAndPassword("admin", "admin"))); + driver.get("https://the-internet.herokuapp.com/basic_auth"); + String successMessage = "Congratulations! You must have the proper credentials."; + WebElement elementMessage = driver.findElement(By.tagName("p")); + Assertions.assertEquals(successMessage, elementMessage.getText()); + } + } + + @Test + @Disabled + void canContinueWithoutAuthCredentials() { + try (Network network = new Network(driver)) { + network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)); + network.onAuthRequired( + responseDetails -> + // Does not handle the alert + network.continueWithAuthNoCredentials(responseDetails.getRequest().getRequestId())); + driver.get("https://the-internet.herokuapp.com/basic_auth"); + Alert alert = wait.until(ExpectedConditions.alertIsPresent()); + alert.dismiss(); + Assertions.assertTrue(driver.getPageSource().contains("Not authorized")); + } + } + + @Test + @Disabled + void canCancelAuth() { + try (Network network = new Network(driver)) { + network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)); + network.onAuthRequired( + responseDetails -> + // Does not handle the alert + network.cancelAuth(responseDetails.getRequest().getRequestId())); + driver.get("https://the-internet.herokuapp.com/basic_auth"); + Assertions.assertTrue(driver.getPageSource().contains("Not authorized")); + } + } + + @Test + @Disabled + void canFailRequest() { + try (Network network = new Network(driver)) { + network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT)); + network.onBeforeRequestSent( + responseDetails -> network.failRequest(responseDetails.getRequest().getRequestId())); + driver.manage().timeouts().pageLoadTimeout(Duration.of(5, ChronoUnit.SECONDS)); + Assertions.assertThrows(TimeoutException.class, () -> driver.get("https://the-internet.herokuapp.com/basic_auth")); + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkEventsTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkEventsTest.java new file mode 100644 index 000000000000..d00d5af9ce8d --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/NetworkEventsTest.java @@ -0,0 +1,113 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.bidi.module.Network; +import org.openqa.selenium.bidi.network.BeforeRequestSent; +import org.openqa.selenium.bidi.network.ResponseDetails; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class NetworkEventsTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + void canListenToBeforeRequestSentEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Network network = new Network(driver)) { + CompletableFuture future = new CompletableFuture<>(); + network.onBeforeRequestSent(future::complete); + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + + BeforeRequestSent requestSent = future.get(5, TimeUnit.SECONDS); + String windowHandle = driver.getWindowHandle(); + Assertions.assertEquals(windowHandle, requestSent.getBrowsingContextId()); + Assertions.assertEquals("get", requestSent.getRequest().getMethod().toLowerCase()); + } + } + + @Test + void canListenToResponseStartedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Network network = new Network(driver)) { + CompletableFuture future = new CompletableFuture<>(); + network.onResponseStarted(future::complete); + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + + ResponseDetails response = future.get(5, TimeUnit.SECONDS); + String windowHandle = driver.getWindowHandle(); + + Assertions.assertEquals(windowHandle, response.getBrowsingContextId()); + Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase()); + Assertions.assertEquals(200L, response.getResponseData().getStatus()); + } + } + + @Test + void canListenToResponseCompletedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Network network = new Network(driver)) { + CompletableFuture future = new CompletableFuture<>(); + network.onResponseCompleted(future::complete); + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + + ResponseDetails response = future.get(5, TimeUnit.SECONDS); + String windowHandle = driver.getWindowHandle(); + + Assertions.assertEquals(windowHandle, response.getBrowsingContextId()); + Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase()); + Assertions.assertEquals(200L, response.getResponseData().getStatus()); + } + } + + @Test + void canListenToResponseCompletedEventWithCookie() + throws ExecutionException, InterruptedException, TimeoutException { + try (Network network = new Network(driver)) { + CompletableFuture future = new CompletableFuture<>(); + + driver.get("https://www.selenium.dev/selenium/web/blankPage"); + driver.manage().addCookie(new Cookie("foo", "bar")); + network.onBeforeRequestSent(future::complete); + driver.navigate().refresh(); + + BeforeRequestSent requestSent = future.get(5, TimeUnit.SECONDS); + String windowHandle = driver.getWindowHandle(); + + Assertions.assertEquals(windowHandle, requestSent.getBrowsingContextId()); + Assertions.assertEquals("get", requestSent.getRequest().getMethod().toLowerCase()); + + Assertions.assertEquals("foo", requestSent.getRequest().getCookies().get(0).getName()); + Assertions.assertEquals("bar", requestSent.getRequest().getCookies().get(0).getValue().getValue()); + } + } + + @Test + void canListenToOnAuthRequiredEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Network network = new Network(driver)) { + CompletableFuture future = new CompletableFuture<>(); + network.onAuthRequired(future::complete); + driver.get("https://the-internet.herokuapp.com/basic_auth"); + + ResponseDetails response = future.get(5, TimeUnit.SECONDS); + String windowHandle = driver.getWindowHandle(); + Assertions.assertEquals(windowHandle, response.getBrowsingContextId()); + Assertions.assertEquals("get", response.getRequest().getMethod().toLowerCase()); + Assertions.assertEquals(401L, response.getResponseData().getStatus()); + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptEventsTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptEventsTest.java new file mode 100644 index 000000000000..b3f1ffae952e --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptEventsTest.java @@ -0,0 +1,87 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.openqa.selenium.bidi.module.Script; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.script.LocalValue; +import org.openqa.selenium.bidi.script.Message; +import org.openqa.selenium.bidi.script.RealmInfo; +import org.openqa.selenium.bidi.script.RealmType; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class ScriptEventsTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + void canListenToChannelMessage() + throws ExecutionException, InterruptedException, TimeoutException { + try (Script script = new Script(driver)) { + CompletableFuture future = new CompletableFuture<>(); + script.onMessage(future::complete); + + script.callFunctionInBrowsingContext( + driver.getWindowHandle(), + "(channel) => channel('foo')", + false, + Optional.of(List.of(LocalValue.channelValue("channel_name"))), + Optional.empty(), + Optional.empty()); + + Message message = future.get(5, TimeUnit.SECONDS); + Assertions.assertEquals("channel_name", message.getChannel()); + } + } + + @Test + @DisabledOnOs(value = OS.MAC, disabledReason = "Works locally, times out on CI") + void canListenToRealmCreatedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Script script = new Script(driver)) { + CompletableFuture future = new CompletableFuture<>(); + script.onRealmCreated(future::complete); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + + context.navigate("https://www.selenium.dev/selenium/blankPage"); + RealmInfo realmInfo = future.get(5, TimeUnit.SECONDS); + Assertions.assertNotNull(realmInfo.getRealmId()); + Assertions.assertEquals(RealmType.WINDOW, realmInfo.getRealmType()); + } + } + + @Test + @Disabled + void canListenToRealmDestroyedEvent() + throws ExecutionException, InterruptedException, TimeoutException { + try (Script script = new Script(driver)) { + CompletableFuture future = new CompletableFuture<>(); + script.onRealmDestroyed(future::complete); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + + context.close(); + RealmInfo realmInfo = future.get(5, TimeUnit.SECONDS); + Assertions.assertNotNull(realmInfo.getRealmId()); + Assertions.assertEquals(RealmType.WINDOW, realmInfo.getRealmType()); + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptTest.java new file mode 100644 index 000000000000..2e1ab65e9355 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/ScriptTest.java @@ -0,0 +1,494 @@ +package dev.selenium.bidirectional.webdriver_bidi; + +import dev.selenium.BaseTest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.module.LogInspector; +import org.openqa.selenium.bidi.module.Script; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.browsingcontext.ReadinessState; +import org.openqa.selenium.bidi.log.ConsoleLogEntry; +import org.openqa.selenium.bidi.script.ChannelValue; +import org.openqa.selenium.bidi.script.EvaluateResult; +import org.openqa.selenium.bidi.script.EvaluateResultExceptionValue; +import org.openqa.selenium.bidi.script.EvaluateResultSuccess; +import org.openqa.selenium.bidi.script.LocalValue; +import org.openqa.selenium.bidi.script.ObjectLocalValue; +import org.openqa.selenium.bidi.script.PrimitiveProtocolValue; +import org.openqa.selenium.bidi.script.RealmInfo; +import org.openqa.selenium.bidi.script.RealmType; +import org.openqa.selenium.bidi.script.RemoteReference; +import org.openqa.selenium.bidi.script.ResultOwnership; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class ScriptTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + void canCallFunction() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + List arguments = new ArrayList<>(); + arguments.add(PrimitiveProtocolValue.numberValue(22)); + + Map value = new HashMap<>(); + value.put("some_property", LocalValue.numberValue(42)); + LocalValue thisParameter = LocalValue.objectValue(value); + + arguments.add(thisParameter); + + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "function processWithPromise(argument) {\n" + + " return new Promise((resolve, reject) => {\n" + + " setTimeout(() => {\n" + + " resolve(argument + this.some_property);\n" + + " }, 1000)\n" + + " })\n" + + "}", + true, + Optional.of(arguments), + Optional.of(thisParameter), + Optional.of(ResultOwnership.ROOT)); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals(64L, (Long) successResult.getResult().getValue().get()); + } + } + + @Test + void canCallFunctionWithDeclaration() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, "()=>{return 1+2;}", false, Optional.empty(), Optional.empty(), Optional.empty()); + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals(3L, (Long) successResult.getResult().getValue().get()); + } + } + + @Test + void canCallFunctionWithArguments() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + List arguments = new ArrayList<>(); + LocalValue value1 = PrimitiveProtocolValue.stringValue("ARGUMENT_STRING_VALUE"); + LocalValue value2 = PrimitiveProtocolValue.numberValue(42); + arguments.add(value1); + arguments.add(value2); + + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "(...args)=>{return args}", + false, + Optional.of(arguments), + Optional.empty(), + Optional.empty()); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals(2, ((List) successResult.getResult().getValue().get()).size()); + } + } + + @Test + void canCallFunctionWithAwaitPromise() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "async function() {{\n" + + " await new Promise(r => setTimeout(() => r(), 0));\n" + + " return \"SOME_DELAYED_RESULT\";\n" + + " }}", + true, + Optional.empty(), + Optional.empty(), + Optional.empty()); + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals("SOME_DELAYED_RESULT", (String) successResult.getResult().getValue().get()); + } + } + + @Test + void canCallFunctionWithThisParameter() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + Map value = new HashMap<>(); + value.put("some_property", PrimitiveProtocolValue.numberValue(42)); + LocalValue thisParameter = LocalValue.objectValue(value); + + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "function(){return this.some_property}", + false, + Optional.empty(), + Optional.of(thisParameter), + Optional.empty()); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals("number", successResult.getResult().getType()); + Assertions.assertEquals(42L, (Long) successResult.getResult().getValue().get()); + } + } + + @Test + void canCallFunctionWithOwnershipRoot() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "async function(){return {a:1}}", + true, + Optional.empty(), + Optional.empty(), + Optional.of(ResultOwnership.ROOT)); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertTrue(successResult.getResult().getHandle().isPresent()); + } + } + + @Test + void canCallFunctionThatThrowsException() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "))) !!@@## some invalid JS script (((", + false, + Optional.empty(), + Optional.empty(), + Optional.empty()); + EvaluateResultExceptionValue exception = (EvaluateResultExceptionValue) result; + Assertions.assertEquals("error", exception.getExceptionDetails().getException().getType()); + Assertions.assertEquals( + "SyntaxError: expected expression, got ')'", exception.getExceptionDetails().getText()); + } + } + + @Test + void canCallFunctionInASandBox() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.callFunctionInBrowsingContext( + id, + "sandbox", + "() => window.foo", + true, + Optional.empty(), + Optional.empty(), + Optional.empty()); + + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + } + } + + @Test + void canCallFunctionInARealm() { + String tab = driver.getWindowHandle(); + try (Script script = new Script(tab, driver)) { + List realms = script.getAllRealms(); + String realmId = realms.get(0).getRealmId(); + + EvaluateResult result = script.callFunctionInRealm( + realmId, + "() => { window.foo = 3; }", + true, + Optional.empty(), + Optional.empty(), + Optional.empty()); + + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + } + } + + @Test + void canEvaluateScript() { + String id = driver.getWindowHandle(); + + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.evaluateFunctionInBrowsingContext(id, "1 + 2", true, Optional.empty()); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + Assertions.assertEquals(3L, (Long) successResult.getResult().getValue().get()); + } + } + + @Test + void canEvaluateScriptThatThrowsException() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + id, "))) !!@@## some invalid JS script (((", false, Optional.empty()); + + EvaluateResultExceptionValue exception = (EvaluateResultExceptionValue) result; + Assertions.assertEquals("error", exception.getExceptionDetails().getException().getType()); + Assertions.assertEquals( + "SyntaxError: expected expression, got ')'", exception.getExceptionDetails().getText()); + } + } + + @Test + void canEvaluateScriptWithResulWithOwnership() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + id, "Promise.resolve({a:1})", true, Optional.of(ResultOwnership.ROOT)); + + EvaluateResultSuccess successResult = (EvaluateResultSuccess) result; + Assertions.assertTrue(successResult.getResult().getHandle().isPresent()); + } + } + + @Test + void canEvaluateInASandBox() { + String id = driver.getWindowHandle(); + try (Script script = new Script(id, driver)) { + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + id, "sandbox", "window.foo", true, Optional.empty()); + + + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + } + } + + @Test + void canEvaluateInARealm() { + String tab = driver.getWindowHandle(); + try (Script script = new Script(tab, driver)) { + List realms = script.getAllRealms(); + String realmId = realms.get(0).getRealmId(); + + EvaluateResult result = + script.evaluateFunctionInRealm( + realmId, "window.foo", true, Optional.empty()); + + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + } + } + + @Test + void canDisownHandle() { + String window = driver.getWindowHandle(); + try (Script script = new Script(window, driver)) { + BrowsingContext context = new BrowsingContext(driver, window); + + context.navigate("https://www.selenium.dev/selenium/web/dynamic.html", ReadinessState.COMPLETE); + + driver.findElement(By.id("adder")).click(); + + getLocatedElement(driver, By.id("box0")); + + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + window, "document.querySelector('.redbox');", false, Optional.of(ResultOwnership.ROOT)); + String boxId = ((EvaluateResultSuccess)result).getResult().getHandle().get(); + + script.disownBrowsingContextScript( + window, List.of(boxId)); + + LocalValue value = + LocalValue.remoteReference( + RemoteReference.Type.HANDLE, boxId); + + // Since the handle is now eligible for garbage collections, it is no longer available to be used. + Assertions.assertThrows(WebDriverException.class, () -> script.callFunctionInBrowsingContext( + window, + "arg => arg.a", + false, Optional.of(List.of(value)), + Optional.empty(), + Optional.empty())); + } + } + + @Test + void canDisownHandleInARealm() { + String window = driver.getWindowHandle(); + try (Script script = new Script(window, driver)) { + BrowsingContext context = new BrowsingContext(driver, window); + + context.navigate("https://www.selenium.dev/selenium/web/dynamic.html", ReadinessState.COMPLETE); + + driver.findElement(By.id("adder")).click(); + + getLocatedElement(driver, By.id("box0")); + + List realms = script.getAllRealms(); + String realmId = realms.get(0).getRealmId(); + + // Retrieve the handle for the element added to DOM + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + window, "document.querySelector('.redbox');", false, Optional.of(ResultOwnership.ROOT)); + String boxId = ((EvaluateResultSuccess)result).getResult().getHandle().get(); + + LocalValue value = + LocalValue.remoteReference( + RemoteReference.Type.HANDLE, boxId); + + EvaluateResult checkHandle = script.callFunctionInBrowsingContext( + window, + "arg => arg.a", + false, Optional.of(List.of(value)), + Optional.empty(), + Optional.empty()); + + // The handle is present in memory, else it would result in exception + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, checkHandle.getResultType()); + + // Useful to memory management in a dynamic webpage where DOM mutations happen often + script.disownRealmScript(realmId, List.of(boxId)); + + // Since the handle is now eligible for garbage collections, it is no longer available to be used. + Assertions.assertThrows(WebDriverException.class, () -> script.callFunctionInBrowsingContext( + window, + "arg => arg.a", + false, Optional.of(List.of(value)), + Optional.empty(), + Optional.empty())); + } + } + + @Test + void canGetAllRealms() { + String firstWindow = driver.getWindowHandle(); + String secondWindow = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle(); + try (Script script = new Script(firstWindow, driver)) { + List realms = script.getAllRealms(); + Assertions.assertEquals(2, realms.size()); + } + } + + @Test + void canGetRealmByType() { + String firstWindow = driver.getWindowHandle(); + String secondWindow = driver.switchTo().newWindow(WindowType.WINDOW).getWindowHandle(); + try (Script script = new Script(firstWindow, driver)) { + List realms = script.getRealmsByType(RealmType.WINDOW); + Assertions.assertEquals(2, realms.size()); + } + } + + @Test + void canGetRealmInBrowsingContext() { + String windowId = driver.getWindowHandle(); + String tabId = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle(); + try (Script script = new Script(windowId, driver)) { + List realms = script.getRealmsInBrowsingContext(tabId); + Assertions.assertEquals(1, realms.size()); + } + } + + @Test + void canGetRealmInBrowsingContextByType() { + String windowId = driver.getWindowHandle(); + String tabId = driver.switchTo().newWindow(WindowType.TAB).getWindowHandle(); + try (Script script = new Script(windowId, driver)) { + List windowRealms = + script.getRealmsInBrowsingContextByType(windowId, RealmType.WINDOW); + Assertions.assertEquals(1, windowRealms.size()); + } + } + + @Test + void canAddPreloadScript() throws ExecutionException, InterruptedException, TimeoutException { + try (Script script = new Script(driver)) { + String id = + script.addPreloadScript("() => {{ console.log('{preload_script_console_text}') }}"); + + Assertions.assertNotNull(id); + + try (LogInspector logInspector = new LogInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + logInspector.onConsoleEntry(future::complete); + + driver.get("https://www.selenium.dev/selenium/blankPage"); + + ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals("{preload_script_console_text}", logEntry.getText()); + } + } + } + + @Test + void canAddPreloadScriptWithArguments() { + try (Script script = new Script(driver)) { + String id = + script.addPreloadScript( + "(channel) => channel('will_be_send', 'will_be_ignored')", + List.of(new ChannelValue("channel_name"))); + Assertions.assertNotNull(id); + } + } + + @Test + void canAddPreloadScriptInASandbox() { + try (Script script = new Script(driver)) { + String id = script.addPreloadScript("() => { window.bar=2; }", "sandbox"); + Assertions.assertNotNull(id); + driver.get("https://www.selenium.dev/selenium/blankPage"); + + EvaluateResult result = + script.evaluateFunctionInBrowsingContext( + driver.getWindowHandle(), "sandbox", "window.bar", true, Optional.empty()); + Assertions.assertEquals(EvaluateResult.Type.SUCCESS, result.getResultType()); + } + } + + @Test + void canRemovePreloadScript() throws ExecutionException, InterruptedException, TimeoutException { + try (Script script = new Script(driver)) { + String id = + script.addPreloadScript("() => {{ console.log('{preload_script_console_text}') }}"); + + Assertions.assertNotNull(id); + + try (LogInspector logInspector = new LogInspector(driver)) { + CompletableFuture future = new CompletableFuture<>(); + logInspector.onConsoleEntry(future::complete); + + script.removePreloadScript(id); + + driver.get("https://www.selenium.dev/selenium/blankPage"); + + Assertions.assertThrows(TimeoutException.class, () -> future.get(5, TimeUnit.SECONDS)); + } + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/high_level/LogTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/high_level/LogTest.java new file mode 100644 index 000000000000..62d37c431306 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/high_level/LogTest.java @@ -0,0 +1,89 @@ +package dev.selenium.bidirectional.webdriver_bidi.high_level; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.bidi.log.ConsoleLogEntry; +import org.openqa.selenium.bidi.log.JavascriptLogEntry; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.remote.RemoteWebDriver; + +import dev.selenium.BaseTest; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +class LogTest extends BaseTest { + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + } + + @Test + void canAddConsoleMessageHandler() + throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture future = new CompletableFuture<>(); + + long id = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(future::complete); + + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + driver.findElement(By.id("consoleLog")).click(); + + ConsoleLogEntry logEntry = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals("Hello, world!", logEntry.getText()); + + ((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id); + } + + @Test + void canRemoveConsoleMessageHandler() { + CopyOnWriteArrayList logs = new CopyOnWriteArrayList<>(); + + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + + long id = ((RemoteWebDriver) driver).script().addConsoleMessageHandler(logs::add); + ((RemoteWebDriver) driver).script().removeConsoleMessageHandler(id); + + driver.findElement(By.id("consoleLog")).click(); + + Assertions.assertEquals(0, logs.size()); + } + + @Test + void canAddJsErrorHandler() throws ExecutionException, InterruptedException, TimeoutException { + CompletableFuture future = new CompletableFuture<>(); + + long id = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(future::complete); + + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + driver.findElement(By.id("jsException")).click(); + + JavascriptLogEntry logEntry = future.get(5, TimeUnit.SECONDS); + + Assertions.assertEquals("Error: Not working", logEntry.getText()); + + ((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id); + } + + @Test + void canRemoveJsErrorHandler() { + CopyOnWriteArrayList logs = new CopyOnWriteArrayList<>(); + + long id = ((RemoteWebDriver) driver).script().addJavaScriptErrorHandler(logs::add); + ((RemoteWebDriver) driver).script().removeJavaScriptErrorHandler(id); + + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + driver.findElement(By.id("jsException")).click(); + + Assertions.assertEquals(0, logs.size()); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/MultipleInstanceParallelTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/MultipleInstanceParallelTest.java new file mode 100644 index 000000000000..7f5b5a6ccc1d --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/MultipleInstanceParallelTest.java @@ -0,0 +1,92 @@ +package dev.selenium.bidirectional.webdriver_bidi.user_context; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; + +class MultipleInstanceParallelTest { + + private WebDriver driver; + + @BeforeEach + public void setup() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + options.addArguments("-private"); + driver = new FirefoxDriver(options); + } + + @Test + void canSwitchToBlue() { + driver.get("https://www.selenium.dev/selenium/web/cookie-background.html"); + + WebElement body = driver.findElement(By.tagName("body")); + String bgColor = body.getCssValue("background-color"); + + String expectedColor = "rgb(255, 255, 255)"; + // Background color is white + Assertions.assertEquals(bgColor, expectedColor); + + driver.get("https://www.selenium.dev/selenium/web/cookie-background.html"); + + driver.findElement(By.id("blue-btn")).click(); + body = driver.findElement(By.tagName("body")); + bgColor = body.getCssValue("background-color"); + + expectedColor = "rgb(173, 216, 230)"; + // Background color is blue + Assertions.assertEquals(bgColor, expectedColor); + + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @Test + void canSwitchToGreen() { + driver.get("https://www.selenium.dev/selenium/web/cookie-background.html"); + + WebElement body = driver.findElement(By.tagName("body")); + String bgColor = body.getCssValue("background-color"); + + String expectedColor = "rgb(255, 255, 255)"; + Assertions.assertEquals(bgColor, expectedColor); + + driver.findElement(By.id("green-btn")).click(); + body = driver.findElement(By.tagName("body")); + bgColor = body.getCssValue("background-color"); + + expectedColor = "rgb(144, 238, 144)"; + Assertions.assertEquals(bgColor, expectedColor); + + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @Test + void canHaveTheDefaultBackgroundColor() { + driver.get("https://www.selenium.dev/selenium/web/cookie-background.html"); + + WebElement body = driver.findElement(By.tagName("body")); + String bgColor = body.getCssValue("background-color"); + + String expectedColor = "rgb(255, 255, 255)"; + Assertions.assertEquals(bgColor, expectedColor); + + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @AfterEach + public void cleanup() { + driver.quit(); + } +} diff --git a/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/SingleInstanceCookieParallelTest.java b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/SingleInstanceCookieParallelTest.java new file mode 100644 index 000000000000..a40a6da5a85c --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/bidirectional/webdriver_bidi/user_context/SingleInstanceCookieParallelTest.java @@ -0,0 +1,133 @@ +package dev.selenium.bidirectional.webdriver_bidi.user_context; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.browsingcontext.CreateContextParameters; +import org.openqa.selenium.bidi.browsingcontext.Locator; +import org.openqa.selenium.bidi.browsingcontext.ReadinessState; +import org.openqa.selenium.bidi.module.Browser; +import org.openqa.selenium.bidi.module.Input; +import org.openqa.selenium.bidi.script.NodeProperties; +import org.openqa.selenium.bidi.script.RemoteValue; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.remote.RemoteWebElement; + +class SingleInstanceCookieParallelTest { + + private static WebDriver driver; + BrowsingContext context; + + @BeforeAll + public static void beforeAll() { + FirefoxOptions options = new FirefoxOptions(); + options.setCapability("webSocketUrl", true); + driver = new FirefoxDriver(options); + +// To use Grid uncomment the lines below + +// driver = new RemoteWebDriver( +// new URL("http://localhost:4444/"), +// options, false); +// +// Augmenter augmenter = new Augmenter(); +// driver = augmenter.augment(driver); + } + + @BeforeEach + public void setup() { + Browser browser = new Browser(driver); + String userContext = browser.createUserContext(); + + CreateContextParameters parameters = new CreateContextParameters(WindowType.TAB); + parameters.userContext(userContext); + + context = new BrowsingContext(driver, parameters); + } + + @Test + void canSwitchToBlue() { + context.navigate("https://www.selenium.dev/selenium/web/cookie-background.html", ReadinessState.COMPLETE); + + RemoteValue value = context.locateNode(Locator.xpath("/html/body/button[1]")); + + Input inputModule = new Input(driver); + Actions actions = new Actions(driver); + + RemoteWebElement element = new RemoteWebElement(); + element.setId(value.getSharedId().get()); + actions.moveToElement(element).click(); + + inputModule.perform(context.getId(), actions.getSequences()); + + value = context.locateNode(Locator.xpath("/html/body")); + + NodeProperties properties = (NodeProperties) value.getValue().get(); + String bgColor = properties.getAttributes().get().get("style"); + + Assertions.assertEquals(bgColor, "background-color: lightblue;"); + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @Test + void canSwitchToGreen() { + context.navigate("https://www.selenium.dev/selenium/web/cookie-background.html", ReadinessState.COMPLETE); + + RemoteValue value = context.locateNode(Locator.xpath("/html/body")); + + NodeProperties properties = (NodeProperties) value.getValue().get(); + String bgColor = properties.getAttributes().get().get("style"); + + Assertions.assertEquals(bgColor, "background-color: white;"); + + value = context.locateNode(Locator.xpath("/html/body/button[2]")); + + Input inputModule = new Input(driver); + Actions actions = new Actions(driver); + + RemoteWebElement element = new RemoteWebElement(); + element.setId(value.getSharedId().get()); + actions.moveToElement(element).click(); + + inputModule.perform(context.getId(), actions.getSequences()); + + value = context.locateNode(Locator.xpath("/html/body")); + + properties = (NodeProperties) value.getValue().get(); + bgColor = properties.getAttributes().get().get("style"); + + Assertions.assertEquals(bgColor, "background-color: lightgreen;"); + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @Test + void canHaveTheDefaultBackgroundColor() { + context.navigate("https://www.selenium.dev/selenium/web/cookie-background.html", ReadinessState.COMPLETE); + + RemoteValue value = context.locateNode(Locator.xpath("/html/body")); + + NodeProperties properties = (NodeProperties) value.getValue().get(); + String bgColor = properties.getAttributes().get().get("style"); + + Assertions.assertEquals(bgColor, "background-color: white;"); + System.out.println( + Thread.currentThread().getName() + " " + Thread.currentThread().getStackTrace()[1] + .getMethodName() + " => executed successfully"); + } + + @AfterAll + public static void cleanup() { + driver.quit(); + } +} diff --git a/examples/java/src/test/java/dev/selenium/browsers/ChromeTest.java b/examples/java/src/test/java/dev/selenium/browsers/ChromeTest.java index a9f35e484cad..0161c6ceec34 100644 --- a/examples/java/src/test/java/dev/selenium/browsers/ChromeTest.java +++ b/examples/java/src/test/java/dev/selenium/browsers/ChromeTest.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.regex.Pattern; import org.junit.jupiter.api.AfterEach; @@ -19,12 +20,11 @@ import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.chromium.ChromiumDriverLogLevel; -import org.openqa.selenium.logging.LogEntries; -import org.openqa.selenium.logging.LogType; -import org.openqa.selenium.logging.LoggingPreferences; -import org.openqa.selenium.manager.SeleniumManagerOutput; +import org.openqa.selenium.chromium.ChromiumNetworkConditions; +import org.openqa.selenium.logging.*; import org.openqa.selenium.remote.service.DriverFinder; + public class ChromeTest extends BaseTest { @AfterEach public void clearProperties() { @@ -34,13 +34,13 @@ public void clearProperties() { @Test public void basicOptions() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); driver = new ChromeDriver(options); } @Test public void arguments() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.addArguments("--start-maximized"); @@ -49,7 +49,7 @@ public void arguments() { @Test public void setBrowserLocation() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setBinary(getChromeLocation()); @@ -58,7 +58,7 @@ public void setBrowserLocation() { @Test public void extensionOptions() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); Path path = Paths.get("src/test/resources/extensions/webextensions-selenium-example.crx"); File extensionFilePath = new File(path.toUri()); @@ -73,7 +73,7 @@ public void extensionOptions() { @Test public void excludeSwitches() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setExperimentalOption("excludeSwitches", List.of("disable-popup-blocking")); @@ -82,7 +82,7 @@ public void excludeSwitches() { @Test public void loggingPreferences() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); LoggingPreferences logPrefs = new LoggingPreferences(); logPrefs.enable(LogType.PERFORMANCE, Level.ALL); options.setCapability(ChromeOptions.LOGGING_PREFS, logPrefs); @@ -175,10 +175,87 @@ public void disableBuildChecks() throws IOException { } private File getChromeLocation() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setBrowserVersion("stable"); - SeleniumManagerOutput.Result output = - DriverFinder.getPath(ChromeDriverService.createDefaultService(), options); - return new File(output.getBrowserPath()); + DriverFinder finder = new DriverFinder(ChromeDriverService.createDefaultService(), options); + return new File(finder.getBrowserPath()); + } + + @Test + public void setPermission() { + ChromeDriver driver = new ChromeDriver(); + driver.get("https://www.selenium.dev/"); + + driver.setPermission("camera", "denied"); + + // Verify the permission state is 'denied' + String script = "return navigator.permissions.query({ name: 'camera' })" + + " .then(permissionStatus => permissionStatus.state);"; + String permissionState = (String) driver.executeScript(script); + + Assertions.assertEquals("denied", permissionState); + driver.quit(); + } + + @Test + public void setNetworkConditions() { + driver = new ChromeDriver(); + + ChromiumNetworkConditions networkConditions = new ChromiumNetworkConditions(); + networkConditions.setOffline(false); + networkConditions.setLatency(java.time.Duration.ofMillis(20)); // 20 ms of latency + networkConditions.setDownloadThroughput(2000 * 1024 / 8); // 2000 kbps + networkConditions.setUploadThroughput(2000 * 1024 / 8); // 2000 kbps + + ((ChromeDriver) driver).setNetworkConditions(networkConditions); + + driver.get("https://www.selenium.dev/"); + + // Assert the network conditions are set as expected + ChromiumNetworkConditions actualConditions = ((ChromeDriver) driver).getNetworkConditions(); + Assertions.assertAll( + () -> Assertions.assertEquals(networkConditions.getOffline(), actualConditions.getOffline()), + () -> Assertions.assertEquals(networkConditions.getLatency(), actualConditions.getLatency()), + () -> Assertions.assertEquals(networkConditions.getDownloadThroughput(), actualConditions.getDownloadThroughput()), + () -> Assertions.assertEquals(networkConditions.getUploadThroughput(), actualConditions.getUploadThroughput()) + ); + ((ChromeDriver) driver).deleteNetworkConditions(); + driver.quit(); + } + + @Test + public void castFeatures() { + ChromeDriver driver = new ChromeDriver(); + + List> sinks = driver.getCastSinks(); + if (!sinks.isEmpty()) { + String sinkName = sinks.get(0).get("name"); + driver.startTabMirroring(sinkName); + driver.stopCasting(sinkName); + } + + driver.quit(); + } + + @Test + public void getBrowserLogs() { + ChromeDriver driver = new ChromeDriver(); + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + WebElement consoleLogButton = driver.findElement(By.id("consoleError")); + consoleLogButton.click(); + + LogEntries logs = driver.manage().logs().get(LogType.BROWSER); + + // Assert that at least one log contains the expected message + boolean logFound = false; + for (LogEntry log : logs) { + if (log.getMessage().contains("I am console error")) { + logFound = true; + break; + } + } + + Assertions.assertTrue(logFound, "No matching log message found."); + driver.quit(); } } diff --git a/examples/java/src/test/java/dev/selenium/browsers/EdgeTest.java b/examples/java/src/test/java/dev/selenium/browsers/EdgeTest.java index 071dd4f871fb..534d78a4cb6f 100644 --- a/examples/java/src/test/java/dev/selenium/browsers/EdgeTest.java +++ b/examples/java/src/test/java/dev/selenium/browsers/EdgeTest.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.regex.Pattern; import org.junit.jupiter.api.AfterEach; @@ -16,15 +17,15 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.chromium.ChromiumDriverLogLevel; +import org.openqa.selenium.chromium.ChromiumNetworkConditions; import org.openqa.selenium.edge.EdgeDriver; import org.openqa.selenium.edge.EdgeDriverService; import org.openqa.selenium.edge.EdgeOptions; -import org.openqa.selenium.logging.LogEntries; -import org.openqa.selenium.logging.LogType; -import org.openqa.selenium.logging.LoggingPreferences; -import org.openqa.selenium.manager.SeleniumManagerOutput; +import org.openqa.selenium.logging.*; import org.openqa.selenium.remote.service.DriverFinder; + + public class EdgeTest extends BaseTest { @AfterEach public void clearProperties() { @@ -34,13 +35,13 @@ public void clearProperties() { @Test public void basicOptions() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); driver = new EdgeDriver(options); } @Test public void arguments() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); options.addArguments("--start-maximized"); @@ -49,7 +50,7 @@ public void arguments() { @Test public void setBrowserLocation() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); options.setBinary(getEdgeLocation()); @@ -58,7 +59,7 @@ public void setBrowserLocation() { @Test public void extensionOptions() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); Path path = Paths.get("src/test/resources/extensions/webextensions-selenium-example.crx"); File extensionFilePath = new File(path.toUri()); @@ -73,7 +74,7 @@ public void extensionOptions() { @Test public void excludeSwitches() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); options.setExperimentalOption("excludeSwitches", List.of("disable-popup-blocking")); @@ -82,7 +83,7 @@ public void excludeSwitches() { @Test public void loggingPreferences() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); LoggingPreferences logPrefs = new LoggingPreferences(); logPrefs.enable(LogType.PERFORMANCE, Level.ALL); options.setCapability(EdgeOptions.LOGGING_PREFS, logPrefs); @@ -169,10 +170,87 @@ public void disableBuildChecks() throws IOException { } private File getEdgeLocation() { - EdgeOptions options = new EdgeOptions(); + EdgeOptions options = getDefaultEdgeOptions(); options.setBrowserVersion("stable"); - SeleniumManagerOutput.Result output = - DriverFinder.getPath(EdgeDriverService.createDefaultService(), options); - return new File(output.getBrowserPath()); + DriverFinder finder = new DriverFinder(EdgeDriverService.createDefaultService(), options); + return new File(finder.getBrowserPath()); + } + + @Test + public void setPermissions() { + EdgeDriver driver = new EdgeDriver(); + driver.get("https://www.selenium.dev/"); + + driver.setPermission("camera", "denied"); + + // Verify the permission state is 'denied' + String script = "return navigator.permissions.query({ name: 'camera' })" + + " .then(permissionStatus => permissionStatus.state);"; + String permissionState = (String) driver.executeScript(script); + + Assertions.assertEquals("denied", permissionState); + driver.quit(); + } + + @Test + public void setNetworkConditions() { + driver = new EdgeDriver(); + + ChromiumNetworkConditions networkConditions = new ChromiumNetworkConditions(); + networkConditions.setOffline(false); + networkConditions.setLatency(java.time.Duration.ofMillis(20)); // 20 ms of latency + networkConditions.setDownloadThroughput(2000 * 1024 / 8); // 2000 kbps + networkConditions.setUploadThroughput(2000 * 1024 / 8); // 2000 kbps + + ((EdgeDriver) driver).setNetworkConditions(networkConditions); + + driver.get("https://www.selenium.dev/"); + + // Assert the network conditions are set as expected + ChromiumNetworkConditions actualConditions = ((EdgeDriver) driver).getNetworkConditions(); + Assertions.assertAll( + () -> Assertions.assertEquals(networkConditions.getOffline(), actualConditions.getOffline()), + () -> Assertions.assertEquals(networkConditions.getLatency(), actualConditions.getLatency()), + () -> Assertions.assertEquals(networkConditions.getDownloadThroughput(), actualConditions.getDownloadThroughput()), + () -> Assertions.assertEquals(networkConditions.getUploadThroughput(), actualConditions.getUploadThroughput()) + ); + ((EdgeDriver) driver).deleteNetworkConditions(); + driver.quit(); + } + + @Test + public void castFeatures() { + EdgeDriver driver = new EdgeDriver(); + + List> sinks = driver.getCastSinks(); + if (!sinks.isEmpty()) { + String sinkName = sinks.get(0).get("name"); + driver.startTabMirroring(sinkName); + driver.stopCasting(sinkName); + } + + driver.quit(); + } + + @Test + public void getBrowserLogs() { + EdgeDriver driver = new EdgeDriver(); + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html"); + WebElement consoleLogButton = driver.findElement(By.id("consoleError")); + consoleLogButton.click(); + + LogEntries logs = driver.manage().logs().get(LogType.BROWSER); + + // Assert that at least one log contains the expected message + boolean logFound = false; + for (LogEntry log : logs) { + if (log.getMessage().contains("I am console error")) { + logFound = true; + break; + } + } + + Assertions.assertTrue(logFound, "No matching log message found."); + driver.quit(); } } diff --git a/examples/java/src/test/java/dev/selenium/browsers/FirefoxTest.java b/examples/java/src/test/java/dev/selenium/browsers/FirefoxTest.java index 77a1db789971..28166e124804 100644 --- a/examples/java/src/test/java/dev/selenium/browsers/FirefoxTest.java +++ b/examples/java/src/test/java/dev/selenium/browsers/FirefoxTest.java @@ -13,22 +13,22 @@ import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.openqa.selenium.By; +import org.openqa.selenium.OutputType; import org.openqa.selenium.WebElement; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.firefox.FirefoxDriverLogLevel; -import org.openqa.selenium.firefox.FirefoxDriverService; -import org.openqa.selenium.firefox.FirefoxOptions; -import org.openqa.selenium.firefox.GeckoDriverService; -import org.openqa.selenium.manager.SeleniumManagerOutput; +import org.openqa.selenium.firefox.*; import org.openqa.selenium.remote.service.DriverFinder; + + + + public class FirefoxTest extends BaseTest { private FirefoxDriver driver; @AfterEach public void clearProperties() { System.clearProperty(GeckoDriverService.GECKO_DRIVER_LOG_PROPERTY); - System.clearProperty(GeckoDriverService.GECKO_DRIVER_LOG_LEVEL_PROPERTY); + System.clearProperty(GeckoDriverService.GECKO_DRIVER_LOG_LEVEL_PROPERTY);driver.quit(); } @Test @@ -124,6 +124,7 @@ public void setProfileLocation() { Assertions.assertTrue(location.contains(profileDirectory.getAbsolutePath())); } + @Test public void installAddon() { driver = startFirefoxDriver(); @@ -137,6 +138,7 @@ public void installAddon() { "Content injected by webextensions-selenium-example", injected.getText()); } + @Test public void uninstallAddon() { driver = startFirefoxDriver(); @@ -149,6 +151,7 @@ public void uninstallAddon() { Assertions.assertEquals(driver.findElements(By.id("webextensions-selenium-example")).size(), 0); } + @Test public void installUnsignedAddonPath() { driver = startFirefoxDriver(); @@ -165,8 +168,53 @@ public void installUnsignedAddonPath() { private Path getFirefoxLocation() { FirefoxOptions options = new FirefoxOptions(); options.setBrowserVersion("stable"); - SeleniumManagerOutput.Result output = - DriverFinder.getPath(GeckoDriverService.createDefaultService(), options); - return Path.of(output.getBrowserPath()); + DriverFinder finder = new DriverFinder(GeckoDriverService.createDefaultService(), options); + return Path.of(finder.getBrowserPath()); + } + + @Test + public void fullPageScreenshot() throws Exception { + driver = startFirefoxDriver(); + + driver.get("https://www.selenium.dev/"); + + File screenshot = driver.getFullPageScreenshotAs(OutputType.FILE); + + File targetFile = new File("full_page_screenshot.png"); + Files.move(screenshot.toPath(), targetFile.toPath()); + + // Verify the screenshot file exists + Assertions.assertTrue(targetFile.exists(), "The full page screenshot file should exist"); + Files.deleteIfExists(targetFile.toPath()); + + driver.quit(); + } + + @Test + public void setContext() { + driver = startFirefoxDriver(); + + ((HasContext) driver).setContext(FirefoxCommandContext.CHROME); + driver.executeScript("console.log('Inside Chrome context');"); + + // Verify the context is back to "content" + Assertions.assertEquals( + FirefoxCommandContext.CHROME, ((HasContext) driver).getContext(), + "The context should be 'chrome'" + ); + + driver.quit(); + } + + @Test + public void firefoxProfile() { + FirefoxProfile profile = new FirefoxProfile(); + FirefoxOptions options = new FirefoxOptions(); + profile.setPreference("javascript.enabled", "False"); + options.setProfile(profile); + + driver = new FirefoxDriver(options); + + driver.quit(); } } diff --git a/examples/java/src/test/java/dev/selenium/browsers/SafariTest.java b/examples/java/src/test/java/dev/selenium/browsers/SafariTest.java index 7e12aa362699..a579b2170e44 100644 --- a/examples/java/src/test/java/dev/selenium/browsers/SafariTest.java +++ b/examples/java/src/test/java/dev/selenium/browsers/SafariTest.java @@ -33,4 +33,10 @@ public void enableLogs() { driver = new SafariDriver(service); } + + public void safariTechnologyPreview() { + SafariOptions options = new SafariOptions(); + options.setUseTechnologyPreview(true); + driver = new SafariDriver(options); + } } diff --git a/examples/java/src/test/java/dev/selenium/drivers/HttpClientTest.java b/examples/java/src/test/java/dev/selenium/drivers/HttpClientTest.java index 2bf0923f6cbb..16b36c91432c 100644 --- a/examples/java/src/test/java/dev/selenium/drivers/HttpClientTest.java +++ b/examples/java/src/test/java/dev/selenium/drivers/HttpClientTest.java @@ -2,6 +2,128 @@ import dev.selenium.BaseTest; +import org.openqa.selenium.remote.http.ClientConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.RemoteWebDriver; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.FileInputStream; +import java.net.URL; +import java.nio.file.Path; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.Duration; + +import org.openqa.selenium.UsernameAndPassword; + +import static java.net.http.HttpClient.Version.HTTP_1_1; + public class HttpClientTest extends BaseTest { + URL gridUrl; + + @BeforeEach + public void startGrid() { + gridUrl = startStandaloneGridAdvanced(); + } + + @Test + public void remoteWebDriverWithClientConfig() throws Exception { + ClientConfig clientConfig = ClientConfig.defaultConfig() + .withRetries() + .sslContext(createSSLContextWithCA(Path.of("src/test/resources/tls.crt").toAbsolutePath().toString())) + .connectionTimeout(Duration.ofSeconds(300)) + .readTimeout(Duration.ofSeconds(3600)) + .authenticateAs(new UsernameAndPassword("admin", "myStrongPassword")) + .version(HTTP_1_1.toString()); + ChromeOptions options = getDefaultChromeOptions(); + options.setEnableDownloads(true); + driver = RemoteWebDriver.builder() + .oneOf(options) + .address(gridUrl) + .config(clientConfig) + .build(); + driver.quit(); + } + + @Test + public void remoteWebDriverIgnoreSSL() throws Exception { + ClientConfig clientConfig = ClientConfig.defaultConfig() + .withRetries() + .sslContext(createIgnoreSSLContext()) + .connectionTimeout(Duration.ofSeconds(300)) + .readTimeout(Duration.ofSeconds(3600)) + .authenticateAs(new UsernameAndPassword("admin", "myStrongPassword")) + .version(HTTP_1_1.toString()); + ChromeOptions options = getDefaultChromeOptions(); + options.setEnableDownloads(true); + driver = RemoteWebDriver.builder() + .oneOf(options) + .address(gridUrl) + .config(clientConfig) + .build(); + driver.quit(); + } + + @Test + public void remoteWebDriverWithEmbedAuthUrl() throws Exception { + ClientConfig clientConfig = ClientConfig.defaultConfig() + .withRetries() + .sslContext(createSSLContextWithCA(Path.of("src/test/resources/tls.crt").toAbsolutePath().toString())) + .connectionTimeout(Duration.ofSeconds(300)) + .readTimeout(Duration.ofSeconds(3600)) + .version(HTTP_1_1.toString()); + ChromeOptions options = getDefaultChromeOptions(); + options.setEnableDownloads(true); + driver = RemoteWebDriver.builder() + .oneOf(options) + .address(embedAuthToUrl(gridUrl, "admin", "myStrongPassword")) + .config(clientConfig) + .build(); + driver.quit(); + } + + private URL embedAuthToUrl(URL url, String username, String password) throws Exception { + String userInfo = username + ":" + password; + String urlWithAuth = url.getProtocol() + "://" + userInfo + "@" + url.getHost() + ":" + url.getPort() + url.getPath(); + return new URL(urlWithAuth); + } + + public static SSLContext createSSLContextWithCA(String caCertPath) throws Exception { + FileInputStream fis = new FileInputStream(caCertPath); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate caCert = (X509Certificate) cf.generateCertificate(fis); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + keyStore.setCertificateEntry("caCert", caCert); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + return sslContext; + } + + public static SSLContext createIgnoreSSLContext() throws Exception { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + return sslContext; + } } diff --git a/examples/java/src/test/java/dev/selenium/drivers/OptionsTest.java b/examples/java/src/test/java/dev/selenium/drivers/OptionsTest.java index af650a3ed754..0ebf3e7d103d 100644 --- a/examples/java/src/test/java/dev/selenium/drivers/OptionsTest.java +++ b/examples/java/src/test/java/dev/selenium/drivers/OptionsTest.java @@ -2,6 +2,172 @@ import dev.selenium.BaseTest; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.PageLoadStrategy; +import org.openqa.selenium.UnexpectedAlertBehaviour; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.CapabilityType; +import org.openqa.selenium.chrome.ChromeDriver; + public class OptionsTest extends BaseTest { + @Test + public void setPageLoadStrategyNormal() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setPageLoadStrategy(PageLoadStrategy.NORMAL); + WebDriver driver = new ChromeDriver(chromeOptions); + try { + // Navigate to Url + driver.get("https://selenium.dev/"); + } finally { + driver.quit(); + } + } + + @Test + public void setPageLoadStrategyEager() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER); + WebDriver driver = new ChromeDriver(chromeOptions); + try { + // Navigate to Url + driver.get("https://selenium.dev/"); + } finally { + driver.quit(); + } + } + + @Test + public void setPageLoadStrategyNone() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setPageLoadStrategy(PageLoadStrategy.NONE); + WebDriver driver = new ChromeDriver(chromeOptions); + try { + // Navigate to Url + driver.get("https://selenium.dev/"); + } finally { + driver.quit(); + } + } + + @Test + public void setAcceptInsecureCerts() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setAcceptInsecureCerts(true); + WebDriver driver = new ChromeDriver(chromeOptions); + try { + // Navigate to Url + driver.get("https://selenium.dev/"); + } finally { + driver.quit(); + } + } + + @Test + public void getBrowserName() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + String name = chromeOptions.getBrowserName(); + Assertions.assertFalse(name.isEmpty(), "Browser name should not be empty"); + } + + @Test + public void setBrowserVersion() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + String version = "latest"; + chromeOptions.setBrowserVersion(version); + Assertions.assertEquals(version, chromeOptions.getBrowserVersion()); + } + + @Test + public void setPlatformName() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + String platform = "OS X 10.6"; + chromeOptions.setPlatformName(platform); + Assertions.assertEquals(platform, chromeOptions.getPlatformName().toString()); + } + + @Test + public void setScriptTimeout() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + Duration duration = Duration.of(5, ChronoUnit.SECONDS); + chromeOptions.setScriptTimeout(duration); + + WebDriver driver = new ChromeDriver(chromeOptions); + try { + Duration timeout = driver.manage().timeouts().getScriptTimeout(); + Assertions.assertEquals(timeout, duration, "The script timeout should be set to 5 seconds."); + } finally { + driver.quit(); + } + } + + @Test + public void setPageLoadTimeout() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + Duration duration = Duration.of(5, ChronoUnit.SECONDS); + chromeOptions.setPageLoadTimeout(duration); + + WebDriver driver = new ChromeDriver(chromeOptions); + try { + Duration timeout = driver.manage().timeouts().getPageLoadTimeout(); + Assertions.assertEquals(timeout, duration, "The page load timeout should be set to 5 seconds."); + } finally { + driver.quit(); + } + } + + @Test + public void setImplicitWaitTimeout() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + Duration duration = Duration.of(5, ChronoUnit.SECONDS); + chromeOptions.setImplicitWaitTimeout(duration); + + WebDriver driver = new ChromeDriver(chromeOptions); + try { + Duration timeout = driver.manage().timeouts().getImplicitWaitTimeout(); + Assertions.assertEquals(timeout, duration, "The implicit wait timeout should be set to 5 seconds."); + } finally { + driver.quit(); + } + } + + @Test + public void setUnhandledPromptBehaviour() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.DISMISS_AND_NOTIFY); + //verify the capability object is not null + Object capabilityObject = chromeOptions.getCapability(CapabilityType.UNHANDLED_PROMPT_BEHAVIOUR); + Assertions.assertNotNull(capabilityObject, "Capability UNHANDLED_PROMPT_BEHAVIOUR should not be null."); + Assertions.assertEquals(capabilityObject.toString(), UnexpectedAlertBehaviour.DISMISS_AND_NOTIFY.toString()); + } + + @Test + public void setWindowRect() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setCapability(CapabilityType.SET_WINDOW_RECT, true); + //verify the capability object is not null + Object capabilityObject = chromeOptions.getCapability(CapabilityType.SET_WINDOW_RECT); + Assertions.assertNotNull(capabilityObject, "Capability SET_WINDOW_RECT should not be null."); + + Boolean capability = (Boolean) capabilityObject; + Assertions.assertTrue(capability, "The capability SET_WINDOW_RECT should be set to true."); + } + + @Test + public void setStrictFileInteractability() { + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.setCapability(CapabilityType.STRICT_FILE_INTERACTABILITY, true); + //verify the capability object is not null + Object capabilityObject = chromeOptions.getCapability(CapabilityType.STRICT_FILE_INTERACTABILITY); + Assertions.assertNotNull(capabilityObject, "Capability STRICT_FILE_INTERACTABILITY should not be null."); + + Boolean capability = (Boolean) capabilityObject; + Assertions.assertTrue(capability, "The capability STRICT_FILE_INTERACTABILITY should be set to true."); + } } + diff --git a/examples/java/src/test/java/dev/selenium/drivers/RemoteWebDriverTest.java b/examples/java/src/test/java/dev/selenium/drivers/RemoteWebDriverTest.java index 260818b4287c..cf3851e5284c 100644 --- a/examples/java/src/test/java/dev/selenium/drivers/RemoteWebDriverTest.java +++ b/examples/java/src/test/java/dev/selenium/drivers/RemoteWebDriverTest.java @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; @@ -34,13 +35,13 @@ public void startGrid() { @Test public void runRemote() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); driver = new RemoteWebDriver(gridUrl, options); } @Test public void uploads() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); driver = new RemoteWebDriver(gridUrl, options); driver.get("https://the-internet.herokuapp.com/upload"); File uploadFile = new File("src/test/resources/selenium-snapshot.png"); @@ -56,7 +57,7 @@ public void uploads() { @Test public void downloads() throws IOException { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setEnableDownloads(true); driver = new RemoteWebDriver(gridUrl, options); @@ -71,6 +72,10 @@ public void downloads() throws IOException { List files = ((HasDownloads) driver).getDownloadableFiles(); + // Sorting them to avoid differences when comparing the files + fileNames.sort(Comparator.naturalOrder()); + files.sort(Comparator.naturalOrder()); + Assertions.assertEquals(fileNames, files); String downloadableFile = files.get(0); Path targetDirectory = Files.createTempDirectory("download"); @@ -87,7 +92,7 @@ public void downloads() throws IOException { @Test public void augment() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); driver = new RemoteWebDriver(gridUrl, options); driver = new Augmenter().augment(driver); @@ -100,7 +105,7 @@ public void remoteWebDriverBuilder() { driver = RemoteWebDriver.builder() .address(gridUrl) - .oneOf(new ChromeOptions()) + .oneOf(getDefaultChromeOptions()) .setCapability("ext:options", Map.of("key", "value")) .config(ClientConfig.defaultConfig()) .build(); diff --git a/examples/java/src/test/java/dev/selenium/drivers/ServiceTest.java b/examples/java/src/test/java/dev/selenium/drivers/ServiceTest.java index e343e6e7b8b0..4a2eef8507bc 100644 --- a/examples/java/src/test/java/dev/selenium/drivers/ServiceTest.java +++ b/examples/java/src/test/java/dev/selenium/drivers/ServiceTest.java @@ -6,10 +6,10 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.manager.SeleniumManagerOutput; import org.openqa.selenium.remote.service.DriverFinder; public class ServiceTest extends BaseTest { + @Test public void defaultService() { ChromeDriverService service = new ChromeDriverService.Builder().build(); @@ -19,7 +19,7 @@ public void defaultService() { @Test public void setDriverLocation() { setBinaryPaths(); - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setBinary(browserPath); ChromeDriverService service = @@ -36,11 +36,10 @@ public void setPort() { } private void setBinaryPaths() { - ChromeOptions options = new ChromeOptions(); + ChromeOptions options = getDefaultChromeOptions(); options.setBrowserVersion("stable"); - SeleniumManagerOutput.Result location = - DriverFinder.getPath(ChromeDriverService.createDefaultService(), options); - driverPath = new File(location.getDriverPath()); - browserPath = new File(location.getBrowserPath()); + DriverFinder finder = new DriverFinder(ChromeDriverService.createDefaultService(), options); + driverPath = new File(finder.getDriverPath()); + browserPath = new File(finder.getBrowserPath()); } } diff --git a/examples/java/src/test/java/dev/selenium/elements/InformationTest.java b/examples/java/src/test/java/dev/selenium/elements/InformationTest.java index 562b76397604..ac56c43e97bb 100644 --- a/examples/java/src/test/java/dev/selenium/elements/InformationTest.java +++ b/examples/java/src/test/java/dev/selenium/elements/InformationTest.java @@ -1,7 +1,71 @@ package dev.selenium.elements; -import dev.selenium.BaseTest; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Rectangle; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import java.time.Duration; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class InformationTest extends BaseTest { +public class InformationTest { -} + @Test + public void informationWithElements() { + + WebDriver driver = new ChromeDriver(); + driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + // Navigate to Url + driver.get("https://www.selenium.dev/selenium/web/inputs.html"); + + // isDisplayed + // Get boolean value for is element display + boolean isEmailVisible = driver.findElement(By.name("email_input")).isDisplayed(); + assertEquals(isEmailVisible,true); + + // isEnabled + // returns true if element is enabled else returns false + boolean isEnabledButton = driver.findElement(By.name("button_input")).isEnabled(); + assertEquals(isEnabledButton,true); + + // isSelected + // returns true if element is checked else returns false + boolean isSelectedCheck = driver.findElement(By.name("checkbox_input")).isSelected(); + assertEquals(isSelectedCheck,true); + + // TagName + // returns TagName of the element + String tagNameInp = driver.findElement(By.name("email_input")).getTagName(); + assertEquals(tagNameInp,"input"); + + // GetRect + // Returns height, width, x and y coordinates referenced element + Rectangle res = driver.findElement(By.name("range_input")).getRect(); + // Rectangle class provides getX,getY, getWidth, getHeight methods + assertEquals(res.getX(),10); + + + // Retrieves the computed style property 'font-size' of field + String cssValue = driver.findElement(By.name("color_input")).getCssValue("font-size"); + assertEquals(cssValue, "13.3333px"); + + + // GetText + // Retrieves the text of the element + String text = driver.findElement(By.tagName("h1")).getText(); + assertEquals(text, "Testing Inputs"); + + + // FetchAttributes + // identify the email text box + WebElement emailTxt = driver.findElement(By.name(("email_input"))); + // fetch the value property associated with the textbox + String valueInfo = emailTxt.getAttribute("value"); + assertEquals(valueInfo,"admin@localhost"); + + + driver.quit(); + } + +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/elements/InteractionTest.java b/examples/java/src/test/java/dev/selenium/elements/InteractionTest.java index 90653f472806..1dadef48758c 100644 --- a/examples/java/src/test/java/dev/selenium/elements/InteractionTest.java +++ b/examples/java/src/test/java/dev/selenium/elements/InteractionTest.java @@ -8,7 +8,7 @@ import java.time.Duration; import static org.junit.jupiter.api.Assertions.assertEquals; -public class InteractionTest { +public class InteractionTest{ @Test public void interactWithElements() { diff --git a/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java b/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java index 08f73a7384d6..bcc473f7a8be 100644 --- a/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java +++ b/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java @@ -1,7 +1,44 @@ package dev.selenium.elements; - +import org.openqa.selenium.By; +import org.openqa.selenium.support.pagefactory.ByAll; +import org.openqa.selenium.support.pagefactory.ByChained; import dev.selenium.BaseTest; +import java.util.List; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; public class LocatorsTest extends BaseTest { + + public void ByAllTest() { + // Create instance of ChromeDriver + WebDriver driver = new ChromeDriver(); + // Navigate to Url + driver.get("https://www.selenium.dev/selenium/web/login.html"); + + // get both logins + By example = new ByAll(By.id("password-field"), By.id("username-field")); + List login_inputs = driver.findElements(example); + + //send them both input + login_inputs.get(0).sendKeys("username"); + login_inputs.get(1).sendKeys("password"); + } + + public String ByChainedTest() { + // Create instance of ChromeDriver + WebDriver driver = new ChromeDriver(); + // Navigate to Url + driver.get("https://www.selenium.dev/selenium/web/login.html"); + + // Find username-field inside of login-form + By example = new ByChained(By.id("login-form"), By.id("username-field")); + WebElement username_input = driver.findElement(example); + + //return placeholder text + String placeholder = username_input.getAttribute("placeholder"); + return placeholder; + } } diff --git a/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java b/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java index 5b314f68be9d..26ddc5229767 100644 --- a/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java +++ b/examples/java/src/test/java/dev/selenium/getting_started/UsingSeleniumTest.java @@ -1,38 +1,50 @@ package dev.selenium.getting_started; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.Duration; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; -import java.time.Duration; +public class UsingSeleniumTest { -import static org.junit.jupiter.api.Assertions.assertEquals; + WebDriver driver; -public class UsingSeleniumTest { + @BeforeEach + public void setup() { + driver = new ChromeDriver(); + } + + @Test + public void eightComponents() { - @Test - public void eightComponents() { - WebDriver driver = new ChromeDriver(); - driver.get("https://www.selenium.dev/selenium/web/web-form.html"); + driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + driver.get("https://www.selenium.dev/selenium/web/web-form.html"); - String title = driver.getTitle(); - assertEquals("Web form", title); + String title = driver.getTitle(); + assertEquals("Web form", title); - driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + WebElement textBox = driver.findElement(By.name("my-text")); + WebElement submitButton = driver.findElement(By.cssSelector("button")); - WebElement textBox = driver.findElement(By.name("my-text")); - WebElement submitButton = driver.findElement(By.cssSelector("button")); + textBox.sendKeys("Selenium"); + submitButton.click(); - textBox.sendKeys("Selenium"); - submitButton.click(); + WebElement message = driver.findElement(By.id("message")); + String value = message.getText(); + assertEquals("Received!", value); - WebElement message = driver.findElement(By.id("message")); - String value = message.getText(); - assertEquals("Received!", value); + } - driver.quit(); - } + @AfterEach + public void teardown() { + driver.quit(); + } } diff --git a/examples/java/src/test/java/dev/selenium/interactions/AlertsTest.java b/examples/java/src/test/java/dev/selenium/interactions/AlertsTest.java index 34835005d867..5b42e0299f12 100644 --- a/examples/java/src/test/java/dev/selenium/interactions/AlertsTest.java +++ b/examples/java/src/test/java/dev/selenium/interactions/AlertsTest.java @@ -1,7 +1,93 @@ -package dev.selenium.interactions; +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. import dev.selenium.BaseTest; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class AlertsTest extends BaseTest { -} + @Test + public void testForAlerts() throws Exception { + + ChromeOptions chromeOptions = getDefaultChromeOptions(); + chromeOptions.addArguments("disable-search-engine-choice-screen"); + WebDriver driver = new ChromeDriver(chromeOptions); + + driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + driver.manage().window().maximize(); + //Navigate to Url + driver.get("https://www.selenium.dev/documentation/webdriver/interactions/alerts/"); + + //Simple Alert + //Click the link to activate the alert + JavascriptExecutor js = (JavascriptExecutor) driver; + //execute js for alert + js.executeScript("alert('Sample Alert');"); + WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30)); + //Wait for the alert to be displayed and store it in a variable + wait.until(ExpectedConditions.alertIsPresent()); + + Alert alert = driver.switchTo().alert(); + //Store the alert text in a variable and verify it + String text = alert.getText(); + assertEquals(text, "Sample Alert"); + //Press the OK button + alert.accept(); + + //Confirm + //execute js for confirm + js.executeScript("confirm('Are you sure?');"); + //Wait for the alert to be displayed + wait = new WebDriverWait(driver, Duration.ofSeconds(30)); + wait.until(ExpectedConditions.alertIsPresent()); + + + alert = driver.switchTo().alert(); + //Store the alert text in a variable and verify it + text = alert.getText(); + assertEquals(text, "Are you sure?"); + //Press the Cancel button + alert.dismiss(); + + //Prompt + //execute js for prompt + js.executeScript("prompt('What is your name?');"); + //Wait for the alert to be displayed and store it in a variable + wait = new WebDriverWait(driver, Duration.ofSeconds(30)); + wait.until(ExpectedConditions.alertIsPresent()); + + alert = driver.switchTo().alert(); + //Store the alert text in a variable and verify it + text = alert.getText(); + assertEquals(text, "What is your name?"); + //Type your message + alert.sendKeys("Selenium"); + //Press the OK button + alert.accept(); + //quit the browser + driver.quit(); + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/interactions/CookiesTest.java b/examples/java/src/test/java/dev/selenium/interactions/CookiesTest.java index a7fab56ac673..20ea34c4de84 100644 --- a/examples/java/src/test/java/dev/selenium/interactions/CookiesTest.java +++ b/examples/java/src/test/java/dev/selenium/interactions/CookiesTest.java @@ -1,7 +1,125 @@ -package dev.selenium.interactions; +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. -import dev.selenium.BaseTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import java.util.Set; -public class CookiesTest extends BaseTest { +public class CookiesTest { + WebDriver driver = new ChromeDriver(); + @Test + public void addCookie() { + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + // Add cookie into current browser context + driver.manage().addCookie(new Cookie("key", "value")); + driver.quit(); + } + @Test + public void getNamedCookie() { + + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + // Add cookie into current browser context + driver.manage().addCookie(new Cookie("foo", "bar")); + // Get cookie details with named cookie 'foo' + Cookie cookie = driver.manage().getCookieNamed("foo"); + Assertions.assertEquals(cookie.getValue(), "bar"); + + driver.quit(); + } + + + @Test + public void getAllCookies() { + + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + // Add cookies into current browser context + driver.manage().addCookie(new Cookie("test1", "cookie1")); + driver.manage().addCookie(new Cookie("test2", "cookie2")); + // Get cookies + Set cookies = driver.manage().getCookies(); + for (Cookie cookie : cookies) { + if (cookie.getName().equals("test1")) { + Assertions.assertEquals(cookie.getValue(), "cookie1"); + } + + if (cookie.getName().equals("test2")) { + Assertions.assertEquals(cookie.getValue(), "cookie2"); + } + } + driver.quit(); + } + + + @Test + public void deleteCookieNamed() { + + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + driver.manage().addCookie(new Cookie("test1", "cookie1")); + // delete cookie named + driver.manage().deleteCookieNamed("test1"); + driver.quit(); + } + + @Test + public void deleteCookieObject() { + + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + Cookie cookie = new Cookie("test2", "cookie2"); + driver.manage().addCookie(cookie); + /* + Selenium Java bindings also provides a way to delete + cookie by passing cookie object of current browsing context + */ + driver.manage().deleteCookie(cookie); + + driver.quit(); + } + + + @Test + public void deleteAllCookies() { + + driver.get("https://www.selenium.dev/selenium/web/blank.html"); + // Add cookies into current browser context + driver.manage().addCookie(new Cookie("test1", "cookie1")); + driver.manage().addCookie(new Cookie("test2", "cookie2")); + // Delete All cookies + driver.manage().deleteAllCookies(); + + driver.quit(); + } + + @Test + public void sameSiteCookie() { + driver.get("http://www.example.com/"); + + Cookie cookie = new Cookie.Builder("key", "value").sameSite("Strict").build(); + Cookie cookie1 = new Cookie.Builder("key", "value").sameSite("Lax").build(); + + driver.manage().addCookie(cookie); + driver.manage().addCookie(cookie1); + + System.out.println(cookie.getSameSite()); + System.out.println(cookie1.getSameSite()); + + driver.quit(); + } } diff --git a/examples/java/src/test/java/dev/selenium/interactions/FramesTest.java b/examples/java/src/test/java/dev/selenium/interactions/FramesTest.java index bf92911e3642..b6ff81ae052f 100644 --- a/examples/java/src/test/java/dev/selenium/interactions/FramesTest.java +++ b/examples/java/src/test/java/dev/selenium/interactions/FramesTest.java @@ -1,7 +1,74 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package dev.selenium.interactions; -import dev.selenium.BaseTest; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import java.time.Duration; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FramesTest{ -public class FramesTest extends BaseTest { + @Test + public void informationWithElements() { + + WebDriver driver = new ChromeDriver(); + driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + + // Navigate to Url + driver.get("https://www.selenium.dev/selenium/web/iframes.html"); + + + //switch To IFrame using Web Element + WebElement iframe = driver.findElement(By.id("iframe1")); + //Switch to the frame + driver.switchTo().frame(iframe); + assertEquals(true, driver.getPageSource().contains("We Leave From Here")); + //Now we can type text into email field + WebElement emailE = driver.findElement(By.id("email")); + emailE.sendKeys("admin@selenium.dev"); + emailE.clear(); + driver.switchTo().defaultContent(); + + + //switch To IFrame using name or id + driver.findElement(By.name("iframe1-name")); + //Switch to the frame + driver.switchTo().frame(iframe); + assertEquals(true, driver.getPageSource().contains("We Leave From Here")); + WebElement email = driver.findElement(By.id("email")); + //Now we can type text into email field + email.sendKeys("admin@selenium.dev"); + email.clear(); + driver.switchTo().defaultContent(); + + + //switch To IFrame using index + driver.switchTo().frame(0); + assertEquals(true, driver.getPageSource().contains("We Leave From Here")); + + //leave frame + driver.switchTo().defaultContent(); + assertEquals(true, driver.getPageSource().contains("This page has iframes")); + + //quit the browser + driver.quit(); + } } diff --git a/examples/java/src/test/java/dev/selenium/interactions/InteractionsTest.java b/examples/java/src/test/java/dev/selenium/interactions/InteractionsTest.java new file mode 100644 index 000000000000..4d7b857fe304 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/interactions/InteractionsTest.java @@ -0,0 +1,32 @@ +package dev.selenium.interactions; + +import dev.selenium.BaseChromeTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Cookie; +import java.util.Set; + +public class InteractionsTest extends BaseChromeTest { + @Test + public void getTitle() { + try { + driver.get("https://www.selenium.dev/"); + // get title + String title = driver.getTitle(); + Assertions.assertEquals(title, "Selenium"); + } finally { + driver.quit(); + } + } + @Test + public void getCurrentUrl() { + try { + driver.get("https://www.selenium.dev/"); + // get current url + String url = driver.getCurrentUrl(); + Assertions.assertEquals(url, "https://www.selenium.dev/"); + } finally { + driver.quit(); + } + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/interactions/NavigationTest.java b/examples/java/src/test/java/dev/selenium/interactions/NavigationTest.java index bb8a40ff263f..ad4dc07df050 100644 --- a/examples/java/src/test/java/dev/selenium/interactions/NavigationTest.java +++ b/examples/java/src/test/java/dev/selenium/interactions/NavigationTest.java @@ -1,7 +1,39 @@ package dev.selenium.interactions; -import dev.selenium.BaseTest; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class NavigationTest extends BaseTest { +public class NavigationTest { + @Test + public void navigateBrowser() { + + WebDriver driver = new ChromeDriver(); + + //Convenient + driver.get("https://selenium.dev/"); + + //Longer way + driver.navigate().to("https://selenium.dev/"); + String title = driver.getTitle(); + assertEquals(title, "Selenium"); + + //Back + driver.navigate().back(); + title = driver.getTitle(); + assertEquals(title, "Selenium"); + + //Forward + driver.navigate().forward(); + title = driver.getTitle(); + assertEquals(title, "Selenium"); + //Refresh + driver.navigate().refresh(); + title = driver.getTitle(); + assertEquals(title, "Selenium"); + + driver.quit(); + } } diff --git a/examples/java/src/test/java/dev/selenium/interactions/PrintOptionsTest.java b/examples/java/src/test/java/dev/selenium/interactions/PrintOptionsTest.java new file mode 100644 index 000000000000..f6e532e1e4da --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/interactions/PrintOptionsTest.java @@ -0,0 +1,77 @@ +package dev.selenium.interactions; + +import org.junit.jupiter.api.Test; +import org.openqa.selenium.print.PageMargin; +import org.openqa.selenium.print.PrintOptions; +import org.openqa.selenium.print.PageSize; +import dev.selenium.BaseChromeTest; + +public class PrintOptionsTest extends BaseChromeTest { + + @Test + public void TestOrientation() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setOrientation(PrintOptions.Orientation.LANDSCAPE); + PrintOptions.Orientation current_orientation = printOptions.getOrientation(); + } + + @Test + public void TestRange() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setPageRanges("1-2"); + String[] current_range = printOptions.getPageRanges(); + } + + @Test + public void TestSize() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setPageSize(new PageSize(27.94, 21.59)); // A4 size in cm + double currentHeight = printOptions.getPageSize().getHeight(); // use getWidth() to retrieve width + } + + @Test + public void TestMargins() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + PageMargin margins = new PageMargin(1.0,1.0,1.0,1.0); + printOptions.setPageMargin(margins); + double topMargin = margins.getTop(); + double bottomMargin = margins.getBottom(); + double leftMargin = margins.getLeft(); + double rightMargin = margins.getRight(); + } + + @Test + public void TestScale() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setScale(.50); + double current_scale = printOptions.getScale(); + } + + @Test + public void TestBackground() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setBackground(true); + boolean current_background = printOptions.getBackground(); + } + + @Test + public void TestShrinkToFit() + { + driver.get("https://www.selenium.dev/"); + PrintOptions printOptions = new PrintOptions(); + printOptions.setShrinkToFit(true); + boolean current_shrink_to_fit = printOptions.getShrinkToFit(); + } +} diff --git a/examples/java/src/test/java/dev/selenium/interactions/PrintsPageTest.java b/examples/java/src/test/java/dev/selenium/interactions/PrintsPageTest.java new file mode 100644 index 000000000000..b6b6dae776f3 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/interactions/PrintsPageTest.java @@ -0,0 +1,43 @@ +package dev.selenium.interactions; + +import org.openqa.selenium.Pdf; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.openqa.selenium.print.PageMargin; +import org.openqa.selenium.print.PrintOptions; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.PrintsPage; +import dev.selenium.BaseTest; + +public class PrintsPageTest extends BaseTest{ + + @BeforeEach + public void setup() { + ChromeOptions options = getDefaultChromeOptions(); + options.setCapability("webSocketUrl", true); + driver = new ChromeDriver(options); + } + + @Test + public void PrintWithPrintsPageTest() + { + driver.get("https://www.selenium.dev/"); + PrintsPage printer = (PrintsPage) driver; + PrintOptions printOptions = new PrintOptions(); + Pdf printedPage = printer.print(printOptions); + Assertions.assertNotNull(printedPage); + } + + @Test + public void PrintWithBrowsingContextTest() + { + BrowsingContext browsingContext = new BrowsingContext(driver, driver.getWindowHandle()); + driver.get("https://www.selenium.dev/selenium/web/formPage.html"); + PrintOptions printOptions = new PrintOptions(); + String printPage = browsingContext.print(printOptions); + Assertions.assertTrue(printPage.length() > 0); + } +} diff --git a/examples/java/src/test/java/dev/selenium/interactions/WindowsTest.java b/examples/java/src/test/java/dev/selenium/interactions/WindowsTest.java index 2e686aa0fb6a..0ea2402da943 100644 --- a/examples/java/src/test/java/dev/selenium/interactions/WindowsTest.java +++ b/examples/java/src/test/java/dev/selenium/interactions/WindowsTest.java @@ -1,7 +1,48 @@ package dev.selenium.interactions; -import dev.selenium.BaseTest; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -public class WindowsTest extends BaseTest { +public class WindowsTest { -} + @Test + public void windowsExampleCode() { + + WebDriver driver = new ChromeDriver(); + driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500)); + // Navigate to Url + driver.get("https://www.selenium.dev/selenium/web/window_switching_tests/page_with_frame.html"); + //fetch handle of this + String currHandle=driver.getWindowHandle(); + assertNotNull(currHandle); + + //click on link to open a new window + driver.findElement(By.linkText("Open new window")).click(); + //fetch handles of all windows, there will be two, [0]- default, [1] - new window + Object[] windowHandles=driver.getWindowHandles().toArray(); + driver.switchTo().window((String) windowHandles[1]); + //assert on title of new window + String title=driver.getTitle(); + assertEquals("Simple Page",title); + + //closing current window + driver.close(); + //Switch back to the old tab or window + driver.switchTo().window((String) windowHandles[0]); + + //Opens a new tab and switches to new tab + driver.switchTo().newWindow(WindowType.TAB); + assertEquals("",driver.getTitle()); + + //Opens a new window and switches to new window + driver.switchTo().newWindow(WindowType.WINDOW); + assertEquals("",driver.getTitle()); + + //quitting driver + driver.quit(); //close all windows + + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/selenium_manager/SeleniumManagerUsageDemo.java b/examples/java/src/test/java/dev/selenium/selenium_manager/SeleniumManagerUsageDemo.java new file mode 100644 index 000000000000..857c4a746f3f --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/selenium_manager/SeleniumManagerUsageDemo.java @@ -0,0 +1,24 @@ +package dev.selenium.selenium_manager; + +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; + +public class SeleniumManagerUsageDemo { + + @Test + public void testSetupWithoutManager() { + System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver"); + WebDriver driver = new ChromeDriver(); + driver.get("https://www.selenium.dev/documentation/selenium_manager/"); + driver.quit(); + } + + @Test + public void testSetupWithManager() { + WebDriver driver = new ChromeDriver(); + driver.get("https://www.selenium.dev/documentation/selenium_manager/"); + driver.quit(); + } + +} diff --git a/examples/java/src/test/resources/extensions/selenium-example.xpi b/examples/java/src/test/resources/extensions/selenium-example.xpi index 34b0ae3913f7..dca8e2e12312 100644 Binary files a/examples/java/src/test/resources/extensions/selenium-example.xpi and b/examples/java/src/test/resources/extensions/selenium-example.xpi differ diff --git a/examples/java/src/test/resources/extensions/selenium-example/manifest.json b/examples/java/src/test/resources/extensions/selenium-example/manifest.json index e938974a20b1..a8b4fec6e60f 100644 --- a/examples/java/src/test/resources/extensions/selenium-example/manifest.json +++ b/examples/java/src/test/resources/extensions/selenium-example/manifest.json @@ -1,17 +1,22 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "webextensions-selenium-example", - "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium" , + "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium", "version": "0.1", "content_scripts": [ { - "matches": ["https://*/*","http://*/*"], - "js": ["inject.js"] + "matches": [ + "https://*/*", + "http://*/*" + ], + "js": [ + "inject.js" + ] } ], - "applications": { - "gecko": { - "id": "webextensions-selenium-example@example.com" - } - } -} + "browser_specific_settings": { + "gecko": { + "id": "webextensions-selenium-example-v3@example.com" + } + } +} \ No newline at end of file diff --git a/examples/java/src/test/resources/server.jks b/examples/java/src/test/resources/server.jks new file mode 100644 index 000000000000..76579e1776c1 Binary files /dev/null and b/examples/java/src/test/resources/server.jks differ diff --git a/examples/java/src/test/resources/tls.crt b/examples/java/src/test/resources/tls.crt new file mode 100644 index 000000000000..58a511093dde --- /dev/null +++ b/examples/java/src/test/resources/tls.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIIPgWI/2ppJPowDQYJKoZIhvcNAQELBQAwgYcxEDAOBgNV +BAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24x +EzARBgNVBAoTClNlbGVuaXVtSFExJTAjBgNVBAsTHFNvZnR3YXJlIEZyZWVkb20g +Q29uc2VydmFuY3kxEzARBgNVBAMTClNlbGVuaXVtSFEwHhcNMjQxMTAzMDkwMDUz +WhcNMzQxMTAxMDkwMDUzWjCBhzEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMH +VW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjETMBEGA1UEChMKU2VsZW5pdW1IUTEl +MCMGA1UECxMcU29mdHdhcmUgRnJlZWRvbSBDb25zZXJ2YW5jeTETMBEGA1UEAxMK +U2VsZW5pdW1IUTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKVTx0e5 +6/75QgE5E6rTYPlTkIxDjZylOMT2YBNuB8vIFZkSaCtLEqto0XTVV6dQc8Ge41QV +rkt7DID1oN40rvWZdla9/2bVhCsWsRiXlvrKDbjoUi5kiLcfKJW+erUWs28xnLOw +bvGNLLAjEUitKKGpR1vsSMOuvMN9VnsSkn9smAHLT2y41CjKpvdkq+OCUdnqfYju +vV6OthRPXFMsDb1fYqZfE7fZhLc806Rg31qLssNVPwxt6VeNYi1/e5cWYeKIJQoj +sFkqIdvu7xHtR7Qu1tNdeQoiDhMS7VLdZDsnAAtQLHvyAVEBicBX95VrGnOTlKdk ++UDwyOP6djCISzUCAwEAAaNxMG8wHQYDVR0OBBYEFNrLCgZ7d2vfurWaJ4wa8O/T +PfXPME4GA1UdEQEB/wREMEKCCWxvY2FsaG9zdIITc2VsZW5pdW0tZ3JpZC5sb2Nh +bIISc2VsZW5pdW0tZ3JpZC5wcm9kggxzZWxlbml1bS5kZXYwDQYJKoZIhvcNAQEL +BQADggEBABtxoPrVrPO5ELzUuSXbvYKHQG9YEuoAisXsiOWmldXRRvT/yTr3nzJn +bC4dJywMW5unPdq1NoMxza0AF0KBFp1GzLDW5/KcA26R4IQi2xfQKVyRzb4vu0CY +BDbnzF7Bisj50sSI4WThtF4xLEHVmzJ2GWNp6SgAScIrdGeB320aTqUIDO8YHH+y +oeSu6qQfEcDiBWh3KD85vCIx0+L4AM3WKkP5nDq2FL6nwCdxqV7bo5/BZJEODMiW +xv/hw0r1OBn2T2Z6o3pRI92zu9sjj6PzPP80DUPl7+fqAaRlLFglXd8b+Qxojm9o +B0QN+gEM717L6WqmJGr1VC6HWQCvcCc= +-----END CERTIFICATE----- diff --git a/examples/java/src/test/resources/tls.key b/examples/java/src/test/resources/tls.key new file mode 100644 index 000000000000..d97038cd2ef8 --- /dev/null +++ b/examples/java/src/test/resources/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQClU8dHuev++UIB +OROq02D5U5CMQ42cpTjE9mATbgfLyBWZEmgrSxKraNF01VenUHPBnuNUFa5LewyA +9aDeNK71mXZWvf9m1YQrFrEYl5b6yg246FIuZIi3HyiVvnq1FrNvMZyzsG7xjSyw +IxFIrSihqUdb7EjDrrzDfVZ7EpJ/bJgBy09suNQoyqb3ZKvjglHZ6n2I7r1ejrYU +T1xTLA29X2KmXxO32YS3PNOkYN9ai7LDVT8MbelXjWItf3uXFmHiiCUKI7BZKiHb +7u8R7Ue0LtbTXXkKIg4TEu1S3WQ7JwALUCx78gFRAYnAV/eVaxpzk5SnZPlA8Mjj ++nYwiEs1AgMBAAECggEAA2D+tT3SGlmG9Tube2CLaRUW4shSVDBWmcSXn+teBUFv +MDwdfRMGoZJdw4eaXWz0wgaItV7QZjJbMKXfK44ZQaOTtP/4QLuzkjuKE4tXloO7 +e5BjS5eaPrSIPGU9S0cDPvjH2oP22dYi4sJYt6ry+2ODC0Mn6o3p8Dc3Ja1HvrXA +SNImimok7YemXVMbdPyaqbu2eXjPvWAA8W9/OW2L1n4U4neM0S5Nt3tVl5sMELj5 +iFC7Z+M3ZLon/54137h3xPmHYQMiPIX+PulaRLOJYSbR0dtMHhPMyWtR7GwEK4Aw +EgtDLKfa6qMv5BYsI2E0bPHRDaj39UXGeWX5/2xzyQKBgQDcMUL3sEbRmeBKhYlT +xv5ea2P4P247DDWObTDw5jLhwfmOycFcJVlaiXICpGR6hqWY8wI7kKxbQQVKFob6 +JVpIHmkkRqsV8JfXVAcaH1thlKAS4NVZsOJIVBHO3JdPaCUFq7HHbBA3436aJLtC +HiINkuiNXd2dDMuDwOsfhsRFzQKBgQDANnK1P7sZSP7dJHinA2sPSbGAK8ZTbYWD +8oe/lYlLkw6qM9i8vIKCfTpfi4vh4qfjQUczdy1w2zFbxriC2+uxhEqDN2tud3/P +0CYrO0SGQKYCROrYUh0Pl1MswBeu8yT5AdrIBK3t92wfYqTWK7VUZQaUQ7YJWfXS +usbz5qIzCQKBgH8ICHt+/gxUOtqjWYu0pPFyATWp2n1EWO13PyHrnHU0BDaFXQE9 +JuSdoOG3V6R8Y7Lul14n49etllCc2Hgd7ozmxn/AKVm5+M+oUYSXjI+qQANEJLHe +410Y60EtcDnGen1gBWtog57KpzJkeIf3fGvaUkGkYoMFa6/yL3N7u2YNAoGADH29 +WKAKpasDvRVYrenf9D9ixKSTn+pXKesB/WZXZMzqwA7cf+90P8yplXn5HjXfmTot +yV9uWY41F/TDGuX13DRvrzVTyvsDGFs7j8WrP1pGL5GQ/XvgnZnE8vyMzXbJqVEA +ic0cDIHuyd9cPPrcLt7d3ZbE5ris7APtV/5d/hkCgYAMFCYoKcCh+9/2HOgwQ1b6 +16CS71TvDBCx7+D1V3WXrIOWkNzW2SIZtnhQwglU9L7PFw6ViJAY4sB2p9hDDtcZ +e7Lotmnbrb75QQpWUyaoZMsw8l23MOGPzHKPqNiT57uOorjcFrePi9EOdERSG9+4 +lRKqCFhaNBUwQ4idzO0rWA== +-----END PRIVATE KEY----- diff --git a/examples/javascript/README.md b/examples/javascript/README.md index 5bd5430d781b..f8c5f5ba10e7 100755 --- a/examples/javascript/README.md +++ b/examples/javascript/README.md @@ -26,4 +26,12 @@ npm install npm test ``` -> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers \ No newline at end of file +> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers + +# Execute a javascript test + +Use this command to run a JavaScript and follow the first script example + +``` +node example_script.spec.js +``` \ No newline at end of file diff --git a/examples/javascript/package-lock.json b/examples/javascript/package-lock.json index 0797ecafc034..b068602c0bfd 100644 --- a/examples/javascript/package-lock.json +++ b/examples/javascript/package-lock.json @@ -9,20 +9,123 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "assert": "^2.0.0", - "selenium-webdriver": "^4.15.0" + "assert": "2.1.0", + "selenium-webdriver": "4.32.0" }, "devDependencies": { - "mocha": "^10.2.0" + "mocha": "11.2.2" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@bazel/runfiles": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.3.1.tgz", + "integrity": "sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA==", + "license": "Apache-2.0" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">=6" + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" } }, "node_modules/ansi-regex": { @@ -49,19 +152,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -69,14 +159,15 @@ "dev": true }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/available-typed-arrays": { @@ -93,16 +184,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -113,18 +196,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -132,12 +203,13 @@ "dev": true }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -184,41 +256,34 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/color-convert": { @@ -239,20 +304,29 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -278,11 +352,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -294,30 +382,32 @@ } }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" - }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -334,18 +424,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -379,101 +457,87 @@ "is-callable": "^1.1.3" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" + "node": ">=16 || 14 >=14.17" }, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/gopd": { @@ -487,17 +551,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -518,6 +571,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -543,6 +607,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -557,15 +632,6 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -586,18 +652,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -609,15 +663,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -641,18 +686,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -668,15 +701,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -721,6 +745,27 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -783,10 +828,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -795,44 +846,49 @@ "node": ">=10" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz", + "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/ms": { @@ -847,27 +903,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -891,12 +926,21 @@ "node": ">= 0.4" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dependencies": { - "wrappy": "1" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/p-limit": { @@ -929,6 +973,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -943,26 +993,38 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=8.6" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -992,15 +1054,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/require-directory": { @@ -1008,56 +1072,102 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/selenium-webdriver": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.15.0.tgz", - "integrity": "sha512-BNG1bq+KWiBGHcJ/wULi0eKY0yaDqFIbEmtbsYJmfaEghdCkXBsx1akgOorhNwjBipOr0uwpvNXqT6/nzl+zjg==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.32.0.tgz", + "integrity": "sha512-dG48JJnB96Aea1iVaZOKGmd6yT6aemeI1heWI/i8DtfD3pDX7uIlwpDBoGauNhtXAaFaamP+U4hIab8zZkg3Ag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/SeleniumHQ" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/selenium" + } + ], + "license": "Apache-2.0", "dependencies": { + "@bazel/runfiles": "^6.3.1", "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.14.2" + "tmp": "^0.2.3", + "ws": "^8.18.0" }, "engines": { - "node": ">= 14.20.0" + "node": ">= 18.20.5" } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1066,7 +1176,22 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/string-width": { + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", @@ -1092,6 +1217,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1120,26 +1258,11 @@ } }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "engines": { - "node": ">=8.0" + "node": ">=14.14" } }, "node_modules/util": { @@ -1159,6 +1282,21 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", @@ -1179,9 +1317,9 @@ } }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -1189,6 +1327,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1201,15 +1340,28 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -1231,35 +1383,38 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -1291,11 +1446,82 @@ } }, "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "@bazel/runfiles": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.3.1.tgz", + "integrity": "sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA==" + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true }, "ansi-regex": { "version": "5.0.1", @@ -1312,16 +1538,6 @@ "color-convert": "^2.0.1" } }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1329,14 +1545,15 @@ "dev": true }, "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "available-typed-arrays": { @@ -1347,12 +1564,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "brace-expansion": { @@ -1364,15 +1576,6 @@ "balanced-match": "^1.0.0" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -1380,12 +1583,13 @@ "dev": true }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" } }, "camelcase": { @@ -1416,29 +1620,22 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" } }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -1457,20 +1654,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "requires": { "ms": "2.1.2" @@ -1482,19 +1685,36 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, + "define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "emoji-regex": { @@ -1503,15 +1723,10 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" - }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -1520,15 +1735,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1553,22 +1759,20 @@ "is-callable": "^1.1.3" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, - "optional": true + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "get-caller-file": { "version": "2.0.5", @@ -1577,56 +1781,41 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "dependencies": { "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } } } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -1635,14 +1824,6 @@ "get-intrinsic": "^1.1.3" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1657,6 +1838,11 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -1670,6 +1856,14 @@ "has-symbols": "^1.0.2" } }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1681,15 +1875,6 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1704,26 +1889,11 @@ "has-tostringtag": "^1.0.0" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1738,15 +1908,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, "is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -1756,12 +1917,6 @@ "define-properties": "^1.1.3" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -1791,6 +1946,22 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1838,42 +2009,53 @@ "is-unicode-supported": "^0.1.0" } }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, "mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz", + "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==", "dev": true, "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "dependencies": { "ms": { @@ -1890,18 +2072,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -1916,12 +2086,15 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "requires": { - "wrappy": "1" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" } }, "p-limit": { @@ -1942,6 +2115,12 @@ "p-limit": "^3.0.2" } }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -1953,15 +2132,26 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "process-nextick-args": { @@ -1993,13 +2183,10 @@ } }, "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true }, "require-directory": { "version": "2.1.1", @@ -2007,43 +2194,68 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "selenium-webdriver": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.15.0.tgz", - "integrity": "sha512-BNG1bq+KWiBGHcJ/wULi0eKY0yaDqFIbEmtbsYJmfaEghdCkXBsx1akgOorhNwjBipOr0uwpvNXqT6/nzl+zjg==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.32.0.tgz", + "integrity": "sha512-dG48JJnB96Aea1iVaZOKGmd6yT6aemeI1heWI/i8DtfD3pDX7uIlwpDBoGauNhtXAaFaamP+U4hIab8zZkg3Ag==", "requires": { + "@bazel/runfiles": "^6.3.1", "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.14.2" + "tmp": "^0.2.3", + "ws": "^8.18.0" } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, + "set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2063,6 +2275,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2072,6 +2295,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2088,21 +2320,9 @@ } }, "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==" }, "util": { "version": "0.12.5", @@ -2121,6 +2341,15 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", @@ -2135,9 +2364,9 @@ } }, "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { @@ -2151,15 +2380,21 @@ "strip-ansi": "^6.0.0" } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } }, "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "requires": {} }, "y18n": { @@ -2169,24 +2404,24 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { diff --git a/examples/javascript/package.json b/examples/javascript/package.json index 0f73e4364123..dc76bb8d3cd5 100644 --- a/examples/javascript/package.json +++ b/examples/javascript/package.json @@ -2,15 +2,15 @@ "name": "javascript-examples", "version": "1.0.0", "scripts": { - "test": "npx mocha test/**/*.spec.js --timeout 60000" + "test": "npx mocha test/**/*.spec.js --timeout 90000" }, "author": "The Selenium project", "license": "Apache-2.0", "dependencies": { - "assert": "^2.0.0", - "selenium-webdriver": "^4.15.0" + "assert": "2.1.0", + "selenium-webdriver": "4.32.0" }, "devDependencies": { - "mocha": "^10.2.0" + "mocha": "11.2.2" } } diff --git a/examples/javascript/test/actionsApi/actionsTest.spec.js b/examples/javascript/test/actionsApi/actionsTest.spec.js index c2214108c10c..b67cb668914a 100644 --- a/examples/javascript/test/actionsApi/actionsTest.spec.js +++ b/examples/javascript/test/actionsApi/actionsTest.spec.js @@ -1,52 +1,49 @@ -const { By, Key, Browser} = require('selenium-webdriver') -const { suite } = require('selenium-webdriver/testing') +const { By, Key, Browser, Builder} = require('selenium-webdriver') const assert = require('assert') -suite(function(env) { - describe('Actions API - Pause and Release All Actions', function() { - let driver +describe('Actions API - Pause and Release All Actions', function() { + let driver - before(async function() { - driver = await env.builder().build() - }) + before(async function() { + driver = await new Builder().forBrowser('chrome').build(); + }) - after(() => driver.quit()) + after(async () => await driver.quit()) - it('Pause', async function() { - await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + it('Pause', async function() { + await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') - const start = Date.now() + const start = Date.now() - const clickable = await driver.findElement(By.id('clickable')) - await driver.actions() - .move({ origin: clickable }) - .pause(1000) - .press() - .pause(1000) - .sendKeys('abc') - .perform() + const clickable = await driver.findElement(By.id('clickable')) + await driver.actions() + .move({ origin: clickable }) + .pause(1000) + .press() + .pause(1000) + .sendKeys('abc') + .perform() - const end = Date.now() - start - assert.ok(end > 2000) - assert.ok(end < 4000) - }) + const end = Date.now() - start + assert.ok(end > 2000) + assert.ok(end < 4000) + }) - it('Clear', async function() { - await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + it('Clear', async function() { + await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') - const clickable = driver.findElement(By.id('clickable')) - await driver.actions() - .click(clickable) - .keyDown(Key.SHIFT) - .sendKeys('a') - .perform() + const clickable = driver.findElement(By.id('clickable')) + await driver.actions() + .click(clickable) + .keyDown(Key.SHIFT) + .sendKeys('a') + .perform() - await driver.actions().clear() - await driver.actions().sendKeys('a').perform() + await driver.actions().clear() + await driver.actions().sendKeys('a').perform() - const value = await clickable.getAttribute('value') - assert.deepStrictEqual('A', value.substring(0, 1)) - assert.deepStrictEqual('a', value.substring(1, 2)) - }) + const value = await clickable.getAttribute('value') + assert.deepStrictEqual('A', value.substring(0, 1)) + assert.deepStrictEqual('a', value.substring(1, 2)) }) -}, { browsers: [Browser.CHROME]}) +}) diff --git a/examples/javascript/test/actionsApi/keysTest.spec.js b/examples/javascript/test/actionsApi/keysTest.spec.js index 36a45767e841..30682b9065ea 100644 --- a/examples/javascript/test/actionsApi/keysTest.spec.js +++ b/examples/javascript/test/actionsApi/keysTest.spec.js @@ -1,92 +1,89 @@ -const { By, Key, Browser} = require('selenium-webdriver') -const { suite } = require('selenium-webdriver/testing') +const { By, Key, Browser, Builder} = require('selenium-webdriver') const assert = require('assert') const { platform } = require('node:process') -suite(function(env) { - describe('Keyboard Action - Keys test', function() { - let driver +describe('Keyboard Action - Keys test', function() { + let driver - before(async function() { - driver = await env.builder().build() - }) + before(async function() { + driver = await new Builder().forBrowser('chrome').build(); + }) - after(() => driver.quit()) + after(async () => await driver.quit()) - it('KeyDown', async function() { - await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') + it('KeyDown', async function() { + await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') - await driver.actions() - .keyDown(Key.SHIFT) - .sendKeys('a') - .perform() + await driver.actions() + .keyDown(Key.SHIFT) + .sendKeys('a') + .perform() - const textField = driver.findElement(By.id('textInput')) - assert.deepStrictEqual(await textField.getAttribute('value'), 'A') - }) + const textField = driver.findElement(By.id('textInput')) + assert.deepStrictEqual(await textField.getAttribute('value'), 'A') + }) - it('KeyUp', async function() { - await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') + it('KeyUp', async function() { + await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') - const textField = driver.findElement(By.id('textInput')) - await textField.click() + const textField = driver.findElement(By.id('textInput')) + await textField.click() - await driver.actions() - .keyDown(Key.SHIFT) - .sendKeys('a') - .keyUp(Key.SHIFT) - .sendKeys('b') - .perform() + await driver.actions() + .keyDown(Key.SHIFT) + .sendKeys('a') + .keyUp(Key.SHIFT) + .sendKeys('b') + .perform() - assert.deepStrictEqual(await textField.getAttribute('value'), 'Ab') - }) + assert.deepStrictEqual(await textField.getAttribute('value'), 'Ab') + }) - it('sendKeys', async function() { - await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') + it('sendKeys', async function() { + await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') - const textField = driver.findElement(By.id('textInput')) - await textField.click() + const textField = driver.findElement(By.id('textInput')) + await textField.click() - await driver.actions() - .sendKeys('abc') - .perform() + await driver.actions() + .sendKeys('abc') + .perform() - assert.deepStrictEqual(await textField.getAttribute('value'), 'abc') - }) + assert.deepStrictEqual(await textField.getAttribute('value'), 'abc') + }) - it('Designated Element', async function() { - await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') + it('Designated Element', async function() { + await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') - await driver.findElement(By.css('body')).click() - const textField = await driver.findElement(By.id('textInput')) + await driver.findElement(By.css('body')).click() + const textField = await driver.findElement(By.id('textInput')) - await driver.actions() - .sendKeys(textField, 'abc') - .perform() + await driver.actions() + .sendKeys(textField, 'abc') + .perform() - assert.deepStrictEqual(await textField.getAttribute('value'), 'abc') - }) + assert.deepStrictEqual(await textField.getAttribute('value'), 'abc') + }) - it('Copy and Paste', async function() { - await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') + it('Copy and Paste', async function() { + await driver.get('https://www.selenium.dev/selenium/web/single_text_input.html') - const textField = await driver.findElement(By.id('textInput')) + const textField = await driver.findElement(By.id('textInput')) - const cmdCtrl = platform.includes('darwin') ? Key.COMMAND : Key.CONTROL + const cmdCtrl = platform.includes('darwin') ? Key.COMMAND : Key.CONTROL - await driver.actions() - .click(textField) - .sendKeys('Selenium!') - .sendKeys(Key.ARROW_LEFT) - .keyDown(Key.SHIFT) - .sendKeys(Key.ARROW_UP) - .keyUp(Key.SHIFT) - .keyDown(cmdCtrl) - .sendKeys('xvv') - .keyUp(cmdCtrl) - .perform() + await driver.actions() + .click(textField) + .sendKeys('Selenium!') + .sendKeys(Key.ARROW_LEFT) + .keyDown(Key.SHIFT) + .sendKeys(Key.ARROW_UP) + .keyUp(Key.SHIFT) + .keyDown(cmdCtrl) + .sendKeys('xvv') + .keyUp(cmdCtrl) + .perform() - assert.deepStrictEqual(await textField.getAttribute('value'), 'SeleniumSelenium!') - }) + assert.deepStrictEqual(await textField.getAttribute('value'), 'SeleniumSelenium!') }) -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}) +}) diff --git a/examples/javascript/test/actionsApi/mouse/backAndForwardClick.spec.js b/examples/javascript/test/actionsApi/mouse/backAndForwardClick.spec.js index 7d669c23b241..c3236c887f02 100644 --- a/examples/javascript/test/actionsApi/mouse/backAndForwardClick.spec.js +++ b/examples/javascript/test/actionsApi/mouse/backAndForwardClick.spec.js @@ -1,40 +1,37 @@ -const {By, Button, Browser} = require('selenium-webdriver'); -const {suite, ignore} = require('selenium-webdriver/testing'); +const {By, Button, Browser, Builder} = require('selenium-webdriver'); const assert = require('assert'); -suite(function (env) { - describe('Should be able to perform BACK click and FORWARD click', function () { - let driver; +describe('Should be able to perform BACK click and FORWARD click', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - ignore(env.browsers(Browser.FIREFOX, Browser.SAFARI)).it('Back click', async function () { - await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); - await driver.findElement(By.id("click")).click(); + it('Back click', async function () { + await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); + await driver.findElement(By.id("click")).click(); - assert.deepStrictEqual(await driver.getTitle(), `We Arrive Here`) + assert.deepStrictEqual(await driver.getTitle(), `We Arrive Here`) - const actions = driver.actions({async: true}); - await actions.press(Button.BACK).release(Button.BACK).perform() + const actions = driver.actions({async: true}); + await actions.press(Button.BACK).release(Button.BACK).perform() - assert.deepStrictEqual(await driver.getTitle(), `BasicMouseInterfaceTest`) - }); + assert.deepStrictEqual(await driver.getTitle(), `BasicMouseInterfaceTest`) + }); - ignore(env.browsers(Browser.FIREFOX, Browser.SAFARI)).it('Forward click', async function () { - await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); - await driver.findElement(By.id("click")).click(); - await driver.navigate().back(); + it('Forward click', async function () { + await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); + await driver.findElement(By.id("click")).click(); + await driver.navigate().back(); - assert.deepStrictEqual(await driver.getTitle(), `BasicMouseInterfaceTest`) + assert.deepStrictEqual(await driver.getTitle(), `BasicMouseInterfaceTest`) - const actions = driver.actions({async: true}); - await actions.press(Button.FORWARD).release(Button.FORWARD).perform() + const actions = driver.actions({async: true}); + await actions.press(Button.FORWARD).release(Button.FORWARD).perform() - assert.deepStrictEqual(await driver.getTitle(), `We Arrive Here`) - }); + assert.deepStrictEqual(await driver.getTitle(), `We Arrive Here`) }); -}, { browsers: [Browser.CHROME]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/clickAndHold.spec.js b/examples/javascript/test/actionsApi/mouse/clickAndHold.spec.js index 80a190ce3ba6..2e6dbf286afa 100644 --- a/examples/javascript/test/actionsApi/mouse/clickAndHold.spec.js +++ b/examples/javascript/test/actionsApi/mouse/clickAndHold.spec.js @@ -1,22 +1,18 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Builder} = require('selenium-webdriver'); -suite(function (env) { - describe('Click and hold', function () { - let driver; +describe('Click and hold', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); - - after(() => driver.quit()); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - it('Mouse move and mouseDown on an element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - let clickable = driver.findElement(By.id("clickable")); - const actions = driver.actions({async: true}); - await actions.move({origin: clickable}).press().perform(); - }); + after(async () => await driver.quit()); + it('Mouse move and mouseDown on an element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + let clickable = driver.findElement(By.id("clickable")); + const actions = driver.actions({async: true}); + await actions.move({origin: clickable}).press().perform(); }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/clickAndRelease.spec.js b/examples/javascript/test/actionsApi/mouse/clickAndRelease.spec.js index 86ae72234132..97e2b2ceb5cc 100644 --- a/examples/javascript/test/actionsApi/mouse/clickAndRelease.spec.js +++ b/examples/javascript/test/actionsApi/mouse/clickAndRelease.spec.js @@ -1,22 +1,18 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By,Builder} = require('selenium-webdriver'); -suite(function (env) { - describe('Click and release', function () { - let driver; +describe('Click and release', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); - - after(() => driver.quit()); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - it('Mouse move and click on an element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - let click = driver.findElement(By.id("click")); - const actions = driver.actions({async: true}); - await actions.move({origin: click}).click().perform(); - }); + after(async () => await driver.quit()); + it('Mouse move and click on an element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + let click = driver.findElement(By.id("click")); + const actions = driver.actions({async: true}); + await actions.move({origin: click}).click().perform(); }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/doubleClick.spec.js b/examples/javascript/test/actionsApi/mouse/doubleClick.spec.js index 3efd915b3379..e7bd3aaa278f 100644 --- a/examples/javascript/test/actionsApi/mouse/doubleClick.spec.js +++ b/examples/javascript/test/actionsApi/mouse/doubleClick.spec.js @@ -1,26 +1,23 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Builder} = require('selenium-webdriver'); const assert = require("assert"); -suite(function (env) { - describe('Double click', function () { - let driver; +describe('Double click', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - it('Double-click on an element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const clickable = driver.findElement(By.id("clickable")); - const actions = driver.actions({async: true}); - await actions.doubleClick(clickable).perform(); + it('Double-click on an element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const clickable = driver.findElement(By.id("clickable")); + const actions = driver.actions({async: true}); + await actions.doubleClick(clickable).perform(); - await driver.sleep(500); - const status = await driver.findElement(By.id('click-status')).getText(); - assert.deepStrictEqual(status, `double-clicked`) - }); + await driver.sleep(500); + const status = await driver.findElement(By.id('click-status')).getText(); + assert.deepStrictEqual(status, `double-clicked`) }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/dragAndDrop.spec.js b/examples/javascript/test/actionsApi/mouse/dragAndDrop.spec.js index 09fe0d76fc61..e83673ff11af 100644 --- a/examples/javascript/test/actionsApi/mouse/dragAndDrop.spec.js +++ b/examples/javascript/test/actionsApi/mouse/dragAndDrop.spec.js @@ -1,38 +1,35 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Builder} = require('selenium-webdriver'); const assert = require('assert'); -suite(function (env) { - describe('Drag and Drop', function () { - let driver; +describe('Drag and Drop', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - it('By Offset', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const draggable = driver.findElement(By.id("draggable")); - let start = await draggable.getRect(); - let finish = await driver.findElement(By.id("droppable")).getRect(); - const actions = driver.actions({async: true}); - await actions.dragAndDrop(draggable, {x: finish.x - start.x, y: finish.y - start.y}).perform(); + it('By Offset', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const draggable = driver.findElement(By.id("draggable")); + let start = await draggable.getRect(); + let finish = await driver.findElement(By.id("droppable")).getRect(); + const actions = driver.actions({async: true}); + await actions.dragAndDrop(draggable, {x: finish.x - start.x, y: finish.y - start.y}).perform(); - let result = await driver.findElement(By.id("drop-status")).getText(); - assert.deepStrictEqual('dropped', result) - }); + let result = await driver.findElement(By.id("drop-status")).getText(); + assert.deepStrictEqual('dropped', result) + }); - it('Onto Element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const draggable = driver.findElement(By.id("draggable")); - const droppable = await driver.findElement(By.id("droppable")); - const actions = driver.actions({async: true}); - await actions.dragAndDrop(draggable, droppable).perform(); + it('Onto Element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const draggable = driver.findElement(By.id("draggable")); + const droppable = await driver.findElement(By.id("droppable")); + const actions = driver.actions({async: true}); + await actions.dragAndDrop(draggable, droppable).perform(); - let result = await driver.findElement(By.id("drop-status")).getText(); - assert.deepStrictEqual('dropped', result) - }); + let result = await driver.findElement(By.id("drop-status")).getText(); + assert.deepStrictEqual('dropped', result) }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/moveByOffset.spec.js b/examples/javascript/test/actionsApi/mouse/moveByOffset.spec.js index e6c6e7c2c857..f73986e81ec2 100644 --- a/examples/javascript/test/actionsApi/mouse/moveByOffset.spec.js +++ b/examples/javascript/test/actionsApi/mouse/moveByOffset.spec.js @@ -1,50 +1,47 @@ -const {By, Origin, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Origin, Builder} = require('selenium-webdriver'); const assert = require('assert'); -suite(function (env) { - describe('Mouse move by offset', function () { - let driver; - - before(async function () { - driver = await env.builder().build(); - }); - - after(async () => await driver.quit()); - - it('From element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const mouseTracker = driver.findElement(By.id("mouse-tracker")); - const actions = driver.actions({async: true}); - await actions.move({x: 8, y: 0, origin: mouseTracker}).perform(); - - await driver.sleep(500); - let result = await driver.findElement(By.id('relative-location')).getText(); - result = result.split(', '); - assert.deepStrictEqual((Math.abs(parseInt(result[0]) - 100 - 8) < 2), true) - }); - - it('From viewport origin', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const actions = driver.actions({async: true}); - await actions.move({x: 8, y: 0}).perform(); - - let result = await driver.findElement(By.id('absolute-location')).getText(); - result = result.split(', '); - assert.deepStrictEqual((Math.abs(parseInt(result[0]) - 8) < 2), true) - }); - - it('From current pointer location', async function () { - await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); - const actions = driver.actions({async: true}); - await actions.move({x: 6, y: 3}).perform() - - await actions.move({x: 13, y: 15, origin: Origin.POINTER}).perform() - - let result = await driver.findElement(By.id('absolute-location')).getText(); - result = result.split(', '); - assert.deepStrictEqual(Math.abs(parseInt(result[0]) - 6 - 13) < 2, true) - assert.deepStrictEqual(Math.abs(parseInt(result[1]) - 3 - 15) < 2, true) - }); +describe('Mouse move by offset', function () { + let driver; + + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); + + after(async () => await driver.quit()); + + it('From element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const mouseTracker = driver.findElement(By.id("mouse-tracker")); + const actions = driver.actions({async: true}); + await actions.move({x: 8, y: 0, origin: mouseTracker}).perform(); + + await driver.sleep(500); + let result = await driver.findElement(By.id('relative-location')).getText(); + result = result.split(', '); + assert.deepStrictEqual((Math.abs(parseInt(result[0]) - 100 - 8) < 2), true) + }); + + it('From viewport origin', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const actions = driver.actions({async: true}); + await actions.move({x: 8, y: 0}).perform(); + + let result = await driver.findElement(By.id('absolute-location')).getText(); + result = result.split(', '); + assert.deepStrictEqual((Math.abs(parseInt(result[0]) - 8) < 2), true) + }); + + it('From current pointer location', async function () { + await driver.get('https://selenium.dev/selenium/web/mouse_interaction.html'); + const actions = driver.actions({async: true}); + await actions.move({x: 6, y: 3}).perform() + + await actions.move({x: 13, y: 15, origin: Origin.POINTER}).perform() + + let result = await driver.findElement(By.id('absolute-location')).getText(); + result = result.split(', '); + assert.deepStrictEqual(Math.abs(parseInt(result[0]) - 6 - 13) < 2, true) + assert.deepStrictEqual(Math.abs(parseInt(result[1]) - 3 - 15) < 2, true) }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/moveToElement.spec.js b/examples/javascript/test/actionsApi/mouse/moveToElement.spec.js index a29acac9045c..9c994a6e2b9e 100644 --- a/examples/javascript/test/actionsApi/mouse/moveToElement.spec.js +++ b/examples/javascript/test/actionsApi/mouse/moveToElement.spec.js @@ -1,25 +1,22 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Builder} = require('selenium-webdriver'); const assert = require("assert"); -suite(function (env) { - describe('Move to element', function () { - let driver; +describe('Move to element', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - it('Mouse move into an element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const hoverable = driver.findElement(By.id("hover")); - const actions = driver.actions({async: true}); - await actions.move({origin: hoverable}).perform(); + it('Mouse move into an element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const hoverable = driver.findElement(By.id("hover")); + const actions = driver.actions({async: true}); + await actions.move({origin: hoverable}).perform(); - const status = await driver.findElement(By.id('move-status')).getText(); - assert.deepStrictEqual(status, `hovered`) - }); + const status = await driver.findElement(By.id('move-status')).getText(); + assert.deepStrictEqual(status, `hovered`) }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/mouse/rightClick.spec.js b/examples/javascript/test/actionsApi/mouse/rightClick.spec.js index c09d30ad716b..fbad07649e02 100644 --- a/examples/javascript/test/actionsApi/mouse/rightClick.spec.js +++ b/examples/javascript/test/actionsApi/mouse/rightClick.spec.js @@ -1,26 +1,23 @@ -const {By, Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {By, Builder} = require('selenium-webdriver'); const assert = require('assert'); -suite(function (env) { - describe('Right click', function () { - let driver; +describe('Right click', function () { + let driver; - before(async function () { - driver = await env.builder().build(); - }); + before(async function () { + driver = new Builder().forBrowser('chrome').build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - it('Mouse move and right click on an element', async function () { - await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); - const clickable = driver.findElement(By.id("clickable")); - const actions = driver.actions({async: true}); - await actions.contextClick(clickable).perform(); + it('Mouse move and right click on an element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/mouse_interaction.html'); + const clickable = driver.findElement(By.id("clickable")); + const actions = driver.actions({async: true}); + await actions.contextClick(clickable).perform(); - await driver.sleep(500); - const clicked = await driver.findElement(By.id('click-status')).getText(); - assert.deepStrictEqual(clicked, `context-clicked`) - }); + await driver.sleep(500); + const clicked = await driver.findElement(By.id('click-status')).getText(); + assert.deepStrictEqual(clicked, `context-clicked`) }); -},{ browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); diff --git a/examples/javascript/test/actionsApi/wheel/scrollByAmount.spec.js b/examples/javascript/test/actionsApi/wheel/scrollByAmount.spec.js index 201167f8dd60..8bcb307fe475 100644 --- a/examples/javascript/test/actionsApi/wheel/scrollByAmount.spec.js +++ b/examples/javascript/test/actionsApi/wheel/scrollByAmount.spec.js @@ -1,28 +1,21 @@ -const { By, Builder, Browser} = require('selenium-webdriver'); -const { suite } = require('selenium-webdriver/testing'); - -suite(function(env) { - describe('Scroll by given amount from element', function() { - let driver; - - before(async function() { - driver = await new Builder().forBrowser('chrome').build(); - }); - - after(() => driver.quit()); - - it('Scroll to element by 300', async function() { - await driver.manage().window().setRect({ width: 500, height: 400 }); - - // Navigate to url - await driver.get('https://crossbrowsertesting.github.io/selenium_example_page.html'); - - // Store element - let element = await driver.findElement(By.linkText('Go To Page 2')); - - // Scroll to element by 300 - await driver.actions().scroll(0, 0, 0, 300, element).perform(); - }); - - }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +const { By, Builder} = require('selenium-webdriver'); + +describe('Scroll by given amount from element', function() { + let driver; + + before(async function() { + driver = await new Builder().forBrowser('chrome').build(); + }); + + after(async() => await driver.quit()); + + it('Scroll to element by 300', async function() { + await driver.manage().window().setRect({ width: 500, height: 400 }); + // Navigate to url + await driver.get('https://crossbrowsertesting.github.io/selenium_example_page.html'); + // Store element + let element = await driver.findElement(By.linkText('Go To Page 2')); + // Scroll to element by 300 + await driver.actions().scroll(0, 0, 0, 300, element).perform(); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/actionsApi/wheel/wheelScrollToElement.spec.js b/examples/javascript/test/actionsApi/wheel/wheelScrollToElement.spec.js index f9abaffdf85e..4341d237359a 100644 --- a/examples/javascript/test/actionsApi/wheel/wheelScrollToElement.spec.js +++ b/examples/javascript/test/actionsApi/wheel/wheelScrollToElement.spec.js @@ -1,26 +1,20 @@ -const { By, Builder, Browser} = require('selenium-webdriver'); -const { suite } = require('selenium-webdriver/testing'); +const { By, Builder} = require('selenium-webdriver'); -suite(function(env) { - describe('Whell scroll to element', function() { - let driver; +describe('Wheel scroll to element', function() { + let driver; - before(async function() { - driver = await new Builder().forBrowser('chrome').build(); - }); + before(async function() { + driver = await new Builder().forBrowser('chrome').build(); + }); - after(() => driver.quit()); + after(async () => await driver.quit()); - it('Scroll to the element', async function() { - // Navigate to the url - await driver.get('https://crossbrowsertesting.github.io/selenium_example_page.html'); - - // Find close button element - let element = driver.findElement(By.id('closepopup')); - - // Scroll to the element - await driver.actions().scroll(0, 0, 0, 0, element).perform(); - }); - - }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file + it('Scroll to the element', async function() { + // Navigate to the url + await driver.get('https://crossbrowsertesting.github.io/selenium_example_page.html'); + // Find close button element + let element = driver.findElement(By.id('closepopup')); + // Scroll to the element + await driver.actions().scroll(0, 0, 0, 0, element).perform(); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/actionsApi/wheelTest.spec.js b/examples/javascript/test/actionsApi/wheelTest.spec.js index 6fdc103d299f..666e576f5cda 100644 --- a/examples/javascript/test/actionsApi/wheelTest.spec.js +++ b/examples/javascript/test/actionsApi/wheelTest.spec.js @@ -1,95 +1,89 @@ -const { By, Browser} = require('selenium-webdriver') -const { suite } = require('selenium-webdriver/testing') +const { By, Browser, Builder} = require('selenium-webdriver') const assert = require('assert') -suite(function (env) { - describe('Actions API - Wheel Tests', function () { - let driver +describe('Actions API - Wheel Tests', function () { + let driver - before(async function () { - driver = await env.builder().build() - }) + before(async function () { + driver = await new Builder().forBrowser('chrome').build(); + }) - after(async() => await driver.quit()) + after(async() => await driver.quit()) - it('Scroll to element', async function () { - await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") + it('Scroll to element', async function () { + await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") - const iframe = await driver.findElement(By.css("iframe")) + const iframe = await driver.findElement(By.css("iframe")) + await driver.actions() + .scroll(0, 0, 0, 0, iframe) + .perform() + assert.ok(await inViewport(iframe)) + }) - await driver.actions() - .scroll(0, 0, 0, 0, iframe) - .perform() + it('Scroll by given amount', async function () { + await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") - assert.ok(await inViewport(iframe)) - }) + const footer = await driver.findElement(By.css("footer")) + const deltaY = (await footer.getRect()).y - it('Scroll by given amount', async function () { - await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") + await driver.actions() + .scroll(0, 0, 0, deltaY) + .perform() - const footer = await driver.findElement(By.css("footer")) - const deltaY = (await footer.getRect()).y + await driver.sleep(500) + assert.ok(await inViewport(footer)) + }) - await driver.actions() - .scroll(0, 0, 0, deltaY) - .perform() + it('Scroll from an element by a given amount', async function () { + await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") - await driver.sleep(500) + const iframe = await driver.findElement(By.css("iframe")) - assert.ok(await inViewport(footer)) - }) + await driver.actions() + .scroll(0, 0, 0, 200, iframe) + .perform() - it('Scroll from an element by a given amount', async function () { - await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") + await driver.sleep(500) - const iframe = await driver.findElement(By.css("iframe")) + await driver.switchTo().frame(iframe) + const checkbox = await driver.findElement(By.name('scroll_checkbox')) + assert.ok(await inViewport(checkbox)) + }) - await driver.actions() - .scroll(0, 0, 0, 200, iframe) - .perform() + it('Scroll from an element with an offset', async function () { + await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") - await driver.sleep(500) + const iframe = await driver.findElement(By.css("iframe")) + const footer = await driver.findElement(By.css("footer")) - await driver.switchTo().frame(iframe) - const checkbox = await driver.findElement(By.name('scroll_checkbox')) - assert.ok(await inViewport(checkbox)) - }) + await driver.actions() + .scroll(0, -50, 0, 200, footer) + .perform() - it('Scroll from an element with an offset', async function () { - await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html") + await driver.sleep(500) - const iframe = await driver.findElement(By.css("iframe")) - const footer = await driver.findElement(By.css("footer")) + await driver.switchTo().frame(iframe) + const checkbox = await driver.findElement(By.name('scroll_checkbox')) + assert.ok(await inViewport(checkbox)) + }) - await driver.actions() - .scroll(0, -50, 0, 200, footer) - .perform() + it('Scroll from an offset of origin (element) by given amount', async function () { + await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame.html") - await driver.sleep(500) + const iframe = await driver.findElement(By.css("iframe")) - await driver.switchTo().frame(iframe) - const checkbox = await driver.findElement(By.name('scroll_checkbox')) - assert.ok(await inViewport(checkbox)) - }) + await driver.actions() + .scroll(10, 10, 0, 200) + .perform() - it('Scroll from an offset of origin (element) by given amount', async function () { - await driver.get("https://www.selenium.dev/selenium/web/scrolling_tests/frame_with_nested_scrolling_frame.html") - - const iframe = await driver.findElement(By.css("iframe")) - - await driver.actions() - .scroll(10, 10, 0, 200) - .perform() - - await driver.sleep(500) - - await driver.switchTo().frame(iframe) - const checkbox = await driver.findElement(By.name('scroll_checkbox')) - assert.ok(await inViewport(checkbox)) - }) + await driver.sleep(500) - function inViewport(element) { - return driver.executeScript("for(var e=arguments[0],f=e.offsetTop,t=e.offsetLeft,o=e.offsetWidth,n=e.offsetHeight;\ne.offsetParent;)f+=(e=e.offsetParent).offsetTop,t+=e.offsetLeft;\nreturn f\nwindow.pageYOffset&&t+o>window.pageXOffset", element) - } + await driver.switchTo().frame(iframe) + const checkbox = await driver.findElement(By.name('scroll_checkbox')) + assert.ok(await inViewport(checkbox)) }) -}, { browsers: [Browser.CHROME]}) \ No newline at end of file + + function inViewport(element) { + return driver.executeScript("for(var e=arguments[0],f=e.offsetTop,t=e.offsetLeft,o=e.offsetWidth,n=e.offsetHeight;\ne.offsetParent;)f+=(e=e.offsetParent).offsetTop,t+=e.offsetLeft;\nreturn f\nwindow.pageYOffset&&t+o>window.pageXOffset", element) + } +}) \ No newline at end of file diff --git a/examples/javascript/test/bidirectional/bidi.spec.js b/examples/javascript/test/bidirectional/bidi.spec.js index bcb8277d750c..d540a88757c3 100644 --- a/examples/javascript/test/bidirectional/bidi.spec.js +++ b/examples/javascript/test/bidirectional/bidi.spec.js @@ -1,52 +1,49 @@ -const { suite } = require('selenium-webdriver/testing'); const assert = require("assert"); +const {until, Builder} = require('selenium-webdriver'); const firefox = require('selenium-webdriver/firefox'); const LogInspector = require('selenium-webdriver/bidi/logInspector'); const BrowsingContext = require('selenium-webdriver/bidi/browsingContext'); -const { until } = require('selenium-webdriver'); - -suite(function(env) { - describe('Integration Tests', function() { - let driver - - beforeEach(async function () { - driver = await env - .builder() - .setFirefoxOptions(new firefox.Options().enableBidi()) - .build() - }) - - afterEach(async function () { - await driver.quit() - }) - - it('test navigate and listen to errors', async function () { - let logEntry = null - const inspector = await LogInspector(driver) - await inspector.onJavascriptException(function (log) { - logEntry = log - }) - - const id = await driver.getWindowHandle() - const browsingContext = await BrowsingContext(driver, { - browsingContextId: id, - }) - - let info = await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - - assert.notEqual(browsingContext.id, null) - assert.equal(info.navigationId, null) - assert(info.url.includes('/bidi/logEntryAdded.html')) - - await driver.wait(until.urlIs('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')) - await driver.findElement({ id: 'jsException' }).click() - - assert.equal(logEntry.text, 'Error: Not working') - assert.equal(logEntry.type, 'javascript') - assert.equal(logEntry.level, 'error') - - await inspector.close() - await browsingContext.close() - }) + +describe('Integration Tests', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('test navigate and listen to errors', async function () { + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onJavascriptException(function (log) { + logEntry = log + }) + + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, }) -}, { browsers: ['firefox'] }) \ No newline at end of file + + let info = await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + assert.notEqual(browsingContext.id, null) + assert.notEqual(info.navigationId, null) + assert(info.url.includes('/bidi/logEntryAdded.html')) + + await driver.wait(until.urlIs('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html')) + await driver.findElement({id: 'jsException'}).click() + + assert.equal(logEntry.text, 'Error: Not working') + assert.equal(logEntry.type, 'javascript') + assert.equal(logEntry.level, 'error') + + await inspector.close() + await browsingContext.close() + }) +}) \ No newline at end of file diff --git a/examples/javascript/test/bidirectional/browsingContext.spec.js b/examples/javascript/test/bidirectional/browsingContext.spec.js index 5f635ccb0051..ffaa11143201 100644 --- a/examples/javascript/test/bidirectional/browsingContext.spec.js +++ b/examples/javascript/test/bidirectional/browsingContext.spec.js @@ -1,135 +1,434 @@ -const { suite } = require('selenium-webdriver/testing'); -const assert = require("assert"); +const {By, until, Builder} = require("selenium-webdriver"); const firefox = require('selenium-webdriver/firefox'); const BrowsingContext = require('selenium-webdriver/bidi/browsingContext'); +const assert = require("assert"); + +describe('Browsing Context', function () { + let driver + let startIndex = 0 + let endIndex = 5 + let pdfMagicNumber = 'JVBER' + let pngMagicNumber = 'iVBOR' + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('test create a browsing context for given id', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + assert.equal(browsingContext.id, id) + }) + + it('test create a window', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'window', + }) + assert.notEqual(browsingContext.id, null) + }) + + it('test create a window with a reference context', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'window', + referenceContext: await driver.getWindowHandle(), + }) + assert.notEqual(browsingContext.id, null) + }) + + it('test create a tab', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'tab', + }) + assert.notEqual(browsingContext.id, null) + }) + + it('test create a tab with a reference context', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'tab', + referenceContext: await driver.getWindowHandle(), + }) + assert.notEqual(browsingContext.id, null) + }) + + it('test navigate to a url', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'tab', + }) + + let info = await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + assert.notEqual(browsingContext.id, null) + assert.notEqual(info.navigationId, null) + assert(info.url.includes('/bidi/logEntryAdded.html')) + }) + + it('test navigate to a url with readiness state', async function () { + const browsingContext = await BrowsingContext(driver, { + type: 'tab', + }) + + const info = await browsingContext.navigate( + 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', + 'complete' + ) + + assert.notEqual(browsingContext.id, null) + assert.notEqual(info.navigationId, null) + assert(info.url.includes('/bidi/logEntryAdded.html')) + }) + + it('test get tree with a child', async function () { + const browsingContextId = await driver.getWindowHandle() + const parentWindow = await BrowsingContext(driver, { + browsingContextId: browsingContextId, + }) + await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete') + + const contextInfo = await parentWindow.getTree() + assert.equal(contextInfo.children.length, 1) + assert.equal(contextInfo.id, browsingContextId) + assert(contextInfo.children[0]['url'].includes('formPage.html')) + }) + + it('test get tree with depth', async function () { + const browsingContextId = await driver.getWindowHandle() + const parentWindow = await BrowsingContext(driver, { + browsingContextId: browsingContextId, + }) + await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete') + + const contextInfo = await parentWindow.getTree(0) + assert.equal(contextInfo.children, null) + assert.equal(contextInfo.id, browsingContextId) + }) + + it('test close a window', async function () { + const window1 = await BrowsingContext(driver, {type: 'window'}) + const window2 = await BrowsingContext(driver, {type: 'window'}) + + await window2.close() + + assert.doesNotThrow(async function () { + await window1.getTree() + }) + await assert.rejects(window2.getTree(), {message: 'no such frame'}) + }) + + it('test close a tab', async function () { + const tab1 = await BrowsingContext(driver, {type: 'tab'}) + const tab2 = await BrowsingContext(driver, {type: 'tab'}) + + await tab2.close() + + assert.doesNotThrow(async function () { + await tab1.getTree() + }) + await assert.rejects(tab2.getTree(), {message: 'no such frame'}) + }) + + it('can print PDF with all valid parameters', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/printPage.html") + const result = await browsingContext.printPage({ + orientation: 'landscape', + scale: 1, + background: true, + width: 30, + height: 30, + top: 1, + bottom: 1, + left: 1, + right: 1, + shrinkToFit: true, + pageRanges: ['1-2'], + }) + + let base64Code = result.data.slice(0, 5) + assert.strictEqual(base64Code, 'JVBER') + }) + + it('can take box screenshot', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + const response = await browsingContext.captureBoxScreenshot(5, 5, 10, 10) + + const base64code = response.slice(0, 5) + assert.equal(base64code, 'iVBOR') + }) + + it('can take element screenshot', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/formPage.html") + const element = await driver.findElement(By.id('checky')) + const elementId = await element.getId() + const response = await browsingContext.captureElementScreenshot(elementId) + + const base64code = response.slice(0, 5) + assert.equal(base64code, 'iVBOR') + }) + + it('can activate a browsing context', async function () { + const id = await driver.getWindowHandle() + const window1 = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await BrowsingContext(driver, { + type: 'window', + }) + + const result = await driver.executeScript('return document.hasFocus();') + + assert.equal(result, false) + + await window1.activate() + const result2 = await driver.executeScript('return document.hasFocus();') + + assert.equal(result2, true) + }) + + it('can handle user prompt', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('alert')).click() + + await driver.wait(until.alertIsPresent()) + + await browsingContext.handleUserPrompt() + + const result = await driver.getTitle() + + assert.equal(result, 'Testing Alerts') + }) + + it('can accept user prompt', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('alert')).click() + + await driver.wait(until.alertIsPresent()) -suite(function(env) { - describe('Browsing Context', function() { - let driver - - beforeEach(async function () { - driver = await env - .builder() - .setFirefoxOptions(new firefox.Options().enableBidi()) - .build() - }) - - afterEach(async function () { - await driver.quit() - }) - - it('test create a browsing context for given id', async function () { - const id = await driver.getWindowHandle() - const browsingContext = await BrowsingContext(driver, { - browsingContextId: id, - }) - assert.equal(browsingContext.id, id) - }) - - it('test create a window', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'window', - }) - assert.notEqual(browsingContext.id, null) - }) - - it('test create a window with a reference context', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'window', - referenceContext: await driver.getWindowHandle(), - }) - assert.notEqual(browsingContext.id, null) - }) - - it('test create a tab', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'tab', - }) - assert.notEqual(browsingContext.id, null) - }) - - it('test create a tab with a reference context', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'tab', - referenceContext: await driver.getWindowHandle(), - }) - assert.notEqual(browsingContext.id, null) - }) - - it('test navigate to a url', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'tab', - }) - - let info = await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - - assert.notEqual(browsingContext.id, null) - assert.equal(info.navigationId, null) - assert(info.url.includes('/bidi/logEntryAdded.html')) - }) - - it('test navigate to a url with readiness state', async function () { - const browsingContext = await BrowsingContext(driver, { - type: 'tab', - }) - - const info = await browsingContext.navigate( - 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', - 'complete' - ) - - assert.notEqual(browsingContext.id, null) - assert.equal(info.navigationId, null) - assert(info.url.includes('/bidi/logEntryAdded.html')) - }) - - it('test get tree with a child', async function () { - const browsingContextId = await driver.getWindowHandle() - const parentWindow = await BrowsingContext(driver, { - browsingContextId: browsingContextId, - }) - await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete') - - const contextInfo = await parentWindow.getTree() - assert.equal(contextInfo.children.length, 1) - assert.equal(contextInfo.id, browsingContextId) - assert(contextInfo.children[0]['url'].includes('formPage.html')) - }) - - it('test get tree with depth', async function () { - const browsingContextId = await driver.getWindowHandle() - const parentWindow = await BrowsingContext(driver, { - browsingContextId: browsingContextId, - }) - await parentWindow.navigate('https://www.selenium.dev/selenium/web/iframes.html', 'complete') - - const contextInfo = await parentWindow.getTree(0) - assert.equal(contextInfo.children, null) - assert.equal(contextInfo.id, browsingContextId) - }) - - it('test close a window', async function () { - const window1 = await BrowsingContext(driver, { type: 'window' }) - const window2 = await BrowsingContext(driver, { type: 'window' }) - - await window2.close() - - assert.doesNotThrow(async function () { - await window1.getTree() - }) - await assert.rejects(window2.getTree(), { message: 'no such frame' }) - }) - - it('test close a tab', async function () { - const tab1 = await BrowsingContext(driver, { type: 'tab' }) - const tab2 = await BrowsingContext(driver, { type: 'tab' }) - - await tab2.close() - - assert.doesNotThrow(async function () { - await tab1.getTree() - }) - await assert.rejects(tab2.getTree(), { message: 'no such frame' }) - }) - }) -}, { browsers: ['firefox'] }) + await browsingContext.handleUserPrompt(true) + + const result = await driver.getTitle() + + assert.equal(result, 'Testing Alerts') + }) + + it('can dismiss user prompt', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('alert')).click() + + await driver.wait(until.alertIsPresent()) + + await browsingContext.handleUserPrompt(false) + + const result = await driver.getTitle() + + assert.equal(result, 'Testing Alerts') + }) + + it('can pass user text to user prompt', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('prompt')).click() + + await driver.wait(until.alertIsPresent()) + + const userText = 'Selenium automates browsers' + + await browsingContext.handleUserPrompt(undefined, userText) + + const result = await driver.getPageSource() + assert.equal(result.includes(userText), true) + }) + + it('can accept user prompt with user text', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('prompt')).click() + + await driver.wait(until.alertIsPresent()) + + const userText = 'Selenium automates browsers' + + await browsingContext.handleUserPrompt(true, userText) + + const result = await driver.getPageSource() + assert.equal(result.includes(userText), true) + }) + + it('can dismiss user prompt with user text', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/alerts.html") + + await driver.findElement(By.id('alert')).click() + + await driver.wait(until.alertIsPresent()) + + const userText = 'Selenium automates browsers' + + await browsingContext.handleUserPrompt(false, userText) + + const result = await driver.getPageSource() + assert.equal(result.includes(userText), false) + }) + + it('can set viewport', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get("https://www.selenium.dev/selenium/web/blank.html") + + await browsingContext.setViewport(250, 300) + + const result = await driver.executeScript('return [window.innerWidth, window.innerHeight];') + assert.equal(result[0], 250) + assert.equal(result[1], 300) + }) + + it('can reload a browsing context', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + const result = await browsingContext.navigate( + 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', 'complete') + + await browsingContext.reload(undefined, 'complete') + assert.notEqual(result.navigationId, null) + assert(result.url.includes('/bidi/logEntryAdded.html')) + }) + + it('can take screenshot', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + const response = await browsingContext.captureScreenshot() + const base64code = response.slice(startIndex, endIndex) + assert.equal(base64code, pngMagicNumber) + }) + + it('can traverse browser history', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/formPage.html', 'complete') + + await driver.wait(until.elementLocated(By.id('imageButton'))).submit() + await driver.wait(until.titleIs('We Arrive Here'), 2500) + + await browsingContext.traverseHistory(-1) + + const source = await driver.getPageSource() + + assert.equal(source.includes('We Leave From Here'), true) + }) + + it('can navigate back in browser history', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/formPage.html', 'complete') + + await driver.wait(until.elementLocated(By.id('imageButton'))).submit() + await driver.wait(until.titleIs('We Arrive Here'), 2500) + + await browsingContext.back() + + const source = await driver.getPageSource() + + assert.equal(source.includes('We Leave From Here'), true) + }) + + it('can navigate forward in browser history', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/formPage.html', 'complete') + + await driver.wait(until.elementLocated(By.id('imageButton'))).submit() + await driver.wait(until.titleIs('We Arrive Here'), 2500) + + await browsingContext.back() + + const source = await driver.getPageSource() + + assert.equal(source.includes('We Leave From Here'), true) + + await browsingContext.forward() + + await driver.wait(until.titleIs('We Arrive Here'), 2500) + }); + + it.skip('Get All Top level browsing contexts', async () => { + const id = await driver.getWindowHandle() + const window1 = await BrowsingContext(driver, { + browsingContextId: id, + }) + await BrowsingContext(driver, { type: 'window' }) + const res = await window1.getTopLevelContexts() + assert.equal(res.length, 2) + }) +}) diff --git a/examples/javascript/test/bidirectional/browsingContextInspector.spec.js b/examples/javascript/test/bidirectional/browsingContextInspector.spec.js new file mode 100644 index 000000000000..045b464eb054 --- /dev/null +++ b/examples/javascript/test/bidirectional/browsingContextInspector.spec.js @@ -0,0 +1,120 @@ +const BrowsingContextInspector = require("selenium-webdriver/bidi/browsingContextInspector"); +const BrowsingContext = require("selenium-webdriver/bidi/browsingContext"); +const assert = require("assert"); +const firefox = require('selenium-webdriver/firefox'); +const {Builder} = require("selenium-webdriver"); + +describe('Browsing Context Inspector', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('can listen to window browsing context created event', async function () { + let contextInfo = null + const browsingContextInspector = await BrowsingContextInspector(driver) + await browsingContextInspector.onBrowsingContextCreated((entry) => { + contextInfo = entry + }) + + await driver.switchTo().newWindow('window') + const windowHandle = await driver.getWindowHandle() + assert.equal(contextInfo.id, windowHandle) + assert.equal(contextInfo.url, 'about:blank') + assert.equal(contextInfo.children, null) + assert.equal(contextInfo.parentBrowsingContext, null) + }) + + it('can listen to tab browsing context created event', async function () { + let contextInfo = null + const browsingContextInspector = await BrowsingContextInspector(driver) + await browsingContextInspector.onBrowsingContextCreated((entry) => { + contextInfo = entry + }) + + await driver.switchTo().newWindow('tab') + const tabHandle = await driver.getWindowHandle() + + assert.equal(contextInfo.id, tabHandle) + assert.equal(contextInfo.url, 'about:blank') + assert.equal(contextInfo.children, null) + assert.equal(contextInfo.parentBrowsingContext, null) + }) + + it('can listen to dom content loaded event', async function () { + const browsingContextInspector = await BrowsingContextInspector(driver) + let navigationInfo = null + await browsingContextInspector.onDomContentLoaded((entry) => { + navigationInfo = entry + }) + + const browsingContext = await BrowsingContext(driver, { + browsingContextId: await driver.getWindowHandle(), + }) + await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', 'complete') + + assert.equal(navigationInfo.browsingContextId, browsingContext.id) + assert.strictEqual(navigationInfo.url.includes('/bidi/logEntryAdded.html'), true) + }) + + it('can listen to browsing context loaded event', async function () { + let navigationInfo = null + const browsingContextInspector = await BrowsingContextInspector(driver) + + await browsingContextInspector.onBrowsingContextLoaded((entry) => { + navigationInfo = entry + }) + const browsingContext = await BrowsingContext(driver, { + browsingContextId: await driver.getWindowHandle(), + }) + await browsingContext.navigate('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html', 'complete') + + assert.equal(navigationInfo.browsingContextId, browsingContext.id) + assert.strictEqual(navigationInfo.url.includes('/bidi/logEntryAdded.html'), true) + }) + + it('can listen to fragment navigated event', async function () { + let navigationInfo = null + const browsingContextInspector = await BrowsingContextInspector(driver) + + const browsingContext = await BrowsingContext(driver, { + browsingContextId: await driver.getWindowHandle(), + }) + await browsingContext.navigate('https://www.selenium.dev/selenium/web/linked_image.html', 'complete') + + await browsingContextInspector.onFragmentNavigated((entry) => { + navigationInfo = entry + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/linked_image.html#linkToAnchorOnThisPage', 'complete') + + assert.equal(navigationInfo.browsingContextId, browsingContext.id) + assert.strictEqual(navigationInfo.url.includes('linkToAnchorOnThisPage'), true) + }) + + it('can listen to browsing context destroyed event', async function () { + let contextInfo = null + const browsingContextInspector = await BrowsingContextInspector(driver) + await browsingContextInspector.onBrowsingContextDestroyed((entry) => { + contextInfo = entry + }) + + await driver.switchTo().newWindow('window') + + const windowHandle = await driver.getWindowHandle() + await driver.close() + + assert.equal(contextInfo.id, windowHandle) + assert.equal(contextInfo.url, 'about:blank') + assert.equal(contextInfo.children, null) + assert.equal(contextInfo.parentBrowsingContext, null) + }) +}) diff --git a/examples/javascript/test/bidirectional/emulateGeoLocation.spec.js b/examples/javascript/test/bidirectional/emulateGeoLocation.spec.js index 776a156a18e3..2fc607d537ca 100644 --- a/examples/javascript/test/bidirectional/emulateGeoLocation.spec.js +++ b/examples/javascript/test/bidirectional/emulateGeoLocation.spec.js @@ -1,33 +1,28 @@ -const { By, Key, Browser} = require('selenium-webdriver'); -const { suite } = require('selenium-webdriver/testing'); -const assert = require("assert"); +const { Builder } = require('selenium-webdriver'); -suite(function(env) { - describe('Emulate geolocation', function() { - let driver; +describe('Emulate geolocation', function() { + let driver; - before(async function() { - driver = await env.builder().build(); - }); + before(async function() { + driver = new Builder().forBrowser('chrome').build(); + }); - after(() => driver.quit()); + after(async () => await driver.quit()); - it('Emulate coordinates of Tokyo', async function() { - const cdpConnection = await driver.createCDPConnection('page'); + it('Emulate coordinates of Tokyo', async function() { + const cdpConnection = await driver.createCDPConnection('page'); - // Latitude and longitude of Tokyo, Japan - const coordinates = { - latitude: 35.689487, - longitude: 139.691706, - accuracy: 100, - }; + // Latitude and longitude of Tokyo, Japan + const coordinates = { + latitude: 35.689487, + longitude: 139.691706, + accuracy: 100, + }; - await cdpConnection.execute( - "Emulation.setGeolocationOverride", - coordinates - ); - await driver.get("https://kawasaki-india.com/dealer-locator/"); - }); - - }); -},{ browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file + await cdpConnection.execute( + "Emulation.setGeolocationOverride", + coordinates + ); + await driver.get("https://kawasaki-india.com/dealer-locator/"); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/bidirectional/input.spec.js b/examples/javascript/test/bidirectional/input.spec.js new file mode 100644 index 000000000000..40573bd15648 --- /dev/null +++ b/examples/javascript/test/bidirectional/input.spec.js @@ -0,0 +1,62 @@ +const assert = require("assert") +const firefox = require('selenium-webdriver/firefox') +const {By, Key, Builder} = require("selenium-webdriver") +const Input = require('selenium-webdriver/bidi/input') + +describe('Input module', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('can perform input action', async function () { + const browsingContextId = await driver.getWindowHandle() + const input = await Input(driver) + await driver.get('https://www.selenium.dev/selenium/web/formSelectionPage.html') + + let options = await driver.findElements(By.tagName('option')) + + const actions = driver.actions().click(options[1]).keyDown(Key.SHIFT).click(options[3]).keyUp(Key.SHIFT).getSequences() + + await input.perform(browsingContextId, actions) + + let showButton = await driver.findElement(By.name('showselected')) + showButton.click() + + let resultElement = await driver.findElement(By.id('result')) + await resultElement.getText().then(function (text) { + assert(text.includes('oquefort parmigiano cheddar')) + }) + }) + + it('can execute release in browsing context', async function () { + const browsingContextId = await driver.getWindowHandle() + const input = await Input(driver) + await driver.get('https://www.selenium.dev/selenium/web/bidi/release_action.html') + + let inputTextBox = await driver.findElement(By.id('keys')) + + await driver.executeScript('arguments[0].focus()', inputTextBox) + + const actions = driver.actions().keyDown('a').keyDown('b').getSequences() + + await input.perform(browsingContextId, actions) + + await driver.executeScript('resetEvents()') + + await input.release(browsingContextId) + + const events = await driver.executeScript('return allEvents.events') + + assert.strictEqual(events[0].code, 'KeyB') + assert.strictEqual(events[1].code, 'KeyA') + }) +}) diff --git a/examples/javascript/test/bidirectional/locateNodes.spec.js b/examples/javascript/test/bidirectional/locateNodes.spec.js new file mode 100644 index 000000000000..4d1534dfa864 --- /dev/null +++ b/examples/javascript/test/bidirectional/locateNodes.spec.js @@ -0,0 +1,242 @@ +const assert = require("assert"); +const firefox = require('selenium-webdriver/firefox'); +const {BrowsingContext, Builder} = require("selenium-webdriver"); +const {Locator} = require("selenium-webdriver/bidi/browsingContext"); +const {LocalValue} = require("selenium-webdriver/bidi/protocolValue"); +const {ArgumentValue} = require("selenium-webdriver/bidi/argumentValue"); + +describe('Locate Nodes', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + xit('can locate nodes', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const element = await browsingContext.locateNodes(Locator.css('div')) + assert.strictEqual(element.length, 13) + }) + + xit('can locate node', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const element = await browsingContext.locateNode(Locator.css('div')) + assert.strictEqual(element.type, 'node') + }) + + xit('can locate node with css locator', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const elements = await browsingContext.locateNodes(Locator.css('div.extraDiv, div.content'), 1) + const element = elements[0] + assert.strictEqual(element.type, 'node') + assert.notEqual(element.value, undefined) + assert.strictEqual(element.value.localName, 'div') + assert.strictEqual(element.value.attributes.class, 'content') + assert.notEqual(element.sharedId, undefined) + }) + + xit('can locate node with xpath locator', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + const elements = await browsingContext.locateNodes(Locator.xpath('/html/body/div[2]'), 1) + + const element = elements[0] + assert.strictEqual(element.type, 'node') + assert.notEqual(element.value, undefined) + assert.strictEqual(element.value.localName, 'div') + assert.strictEqual(element.value.attributes.class, 'content') + assert.notEqual(element.sharedId, undefined) + }) + + xit('can locate node with inner test locator', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + const elements = await browsingContext.locateNodes(Locator.innerText('Spaced out'), 1) + + const element = elements[0] + assert.strictEqual(element.type, 'node') + assert.notEqual(element.value, undefined) + assert.strictEqual(element.value.localName, 'div') + assert.strictEqual(element.value.attributes.class, 'content') + assert.notEqual(element.sharedId, undefined) + }) + + xit('can locate node with max node count', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const elements = await browsingContext.locateNodes(Locator.css('div'), 4) + assert.strictEqual(elements.length, 4) + }) + + xit('can locate node with none ownership value', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const elements = await browsingContext.locateNodes(Locator.css('div'), undefined, 'none') + assert.strictEqual(elements.length, 13) + assert.strictEqual(elements[0].handle, null) + }) + + xit('can locate node with root ownership value', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const elements = await browsingContext.locateNodes(Locator.css('div'), undefined, 'root') + assert.strictEqual(elements.length, 13) + assert.notEqual(elements[0].handle, null) + }) + + xit('can locate node with given start nodes', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get(Pages.formPage) + + const script = await ScriptManager(id, driver) + + const result = await script.evaluateFunctionInBrowsingContext( + id, + "document.querySelectorAll('form')", + false, + 'root', + ) + + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.notEqual(result.realmId, null) + assert.equal(result.result.type, 'nodelist') + + const value = result.result.value + + const startNodes = [] + + value.forEach((node) => { + startNodes.push(new ReferenceValue(node.handle, node.sharedId)) + }) + + const elements = await browsingContext.locateNodes( + Locator.css('input'), + 50, + 'none', + undefined, + undefined, + startNodes, + ) + + assert.strictEqual(elements.length, 35) + }) + + xit('can locate nodes in a given sandbox', async function () { + const sandbox = 'sandbox' + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/xhtmlTest.html', 'complete') + + const elements = await browsingContext.locateNodes(Locator.css('div'), 1, undefined, sandbox) + + assert.strictEqual(elements.length, 1) + + const nodeId = elements[0].sharedId + + const script = await ScriptManager(id, driver) + + let argumentValues = [] + let mapValue = { sharedId: LocalValue.createStringValue(nodeId) } + argumentValues.push(new ArgumentValue(LocalValue.createMapValue(mapValue))) + + const response = await script.callFunctionInBrowsingContext( + id, + 'function(){ return arguments[0]; }', + false, + argumentValues, + undefined, + undefined, + sandbox, + ) + + assert.equal(response.resultType, EvaluateResultType.SUCCESS) + assert.equal(response.result.type, 'map') + + const sharedId = response.result.value.sharedId + + assert.strictEqual(sharedId.value, nodeId) + }) + + xit('can find element', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const element = await browsingContext.locateElement(Locator.css('p')) + const elementText = await element.getText() + assert.strictEqual(elementText, 'Open new window') + }) + + xit('can find elements', async function () { + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + + const elements = await browsingContext.locateElements(Locator.css('div')) + assert.strictEqual(elements.length, 13) + + const elementText = await elements[0].getText() + assert.strictEqual(elementText.includes('Open new window'), true) + }) +}) diff --git a/examples/javascript/test/bidirectional/logInspector.spec.js b/examples/javascript/test/bidirectional/logInspector.spec.js index d6596302ebf4..fc0923d8c0fb 100644 --- a/examples/javascript/test/bidirectional/logInspector.spec.js +++ b/examples/javascript/test/bidirectional/logInspector.spec.js @@ -1,101 +1,100 @@ -const { suite } = require('selenium-webdriver/testing'); const assert = require("assert"); const firefox = require('selenium-webdriver/firefox'); const LogInspector = require('selenium-webdriver/bidi/logInspector'); +const {Builder} = require("selenium-webdriver"); -suite(function(env) { - describe('Log Inspector', function() { - let driver - - beforeEach(async function () { - driver = await env - .builder() - .setFirefoxOptions(new firefox.Options().enableBidi()) - .build() - }) - - afterEach(async function () { - await driver.quit() - }) - - it('test listen to console log', async function () { - let logEntry = null - const inspector = await LogInspector(driver) - await inspector.onConsoleEntry(function (log) { - logEntry = log - }) - - await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - await driver.findElement({ id: 'consoleLog' }).click() - - assert.equal(logEntry.text, 'Hello, world!') - assert.equal(logEntry.realm, null) - assert.equal(logEntry.type, 'console') - assert.equal(logEntry.level, 'info') - assert.equal(logEntry.method, 'log') - assert.equal(logEntry.stackTrace, null) - assert.equal(logEntry.args.length, 1) - - await inspector.close() - }) - - it('test listen to javascript error log', async function () { - let logEntry = null - const inspector = await LogInspector(driver) - await inspector.onJavascriptException(function (log) { - logEntry = log - }) - - await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - await driver.findElement({ id: 'jsException' }).click() - - assert.equal(logEntry.text, 'Error: Not working') - assert.equal(logEntry.type, 'javascript') - assert.equal(logEntry.level, 'error') - - await inspector.close() - }) - - it('test retrieve stack trace for a log', async function () { - let logEntry = null - const inspector = await LogInspector(driver) - await inspector.onJavascriptException(function (log) { - logEntry = log - }) - - await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - await driver.findElement({ id: 'jsException' }).click() - - const stackTrace = logEntry.stackTrace - assert.notEqual(stackTrace, null) - assert.equal(stackTrace.callFrames.length, 3) - - await inspector.close() - }) - - it('test listen to logs with multiple consumers', async function () { - let logEntry1 = null - let logEntry2 = null - const inspector = await LogInspector(driver) - await inspector.onJavascriptException(function (log) { - logEntry1 = log - }) - await inspector.onJavascriptException(function (log) { - logEntry2 = log - }) - - await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - await driver.findElement({ id: 'jsException' }).click() - - assert.equal(logEntry1.text, 'Error: Not working') - assert.equal(logEntry1.type, 'javascript') - assert.equal(logEntry1.level, 'error') - - assert.equal(logEntry2.text, 'Error: Not working') - assert.equal(logEntry2.type, 'javascript') - assert.equal(logEntry2.level, 'error') - - await inspector.close() - }) + +describe('Log Inspector', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('test listen to console log', async function () { + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onConsoleEntry(function (log) { + logEntry = log + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({id: 'consoleLog'}).click() + + assert.equal(logEntry.text, 'Hello, world!') + assert.equal(logEntry.realm, null) + assert.equal(logEntry.type, 'console') + assert.equal(logEntry.level, 'info') + assert.equal(logEntry.method, 'log') + assert.equal(logEntry.stackTrace, null) + assert.equal(logEntry.args.length, 1) + + await inspector.close() + }) + + it('test listen to javascript error log', async function () { + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onJavascriptException(function (log) { + logEntry = log + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({id: 'jsException'}).click() + + assert.equal(logEntry.text, 'Error: Not working') + assert.equal(logEntry.type, 'javascript') + assert.equal(logEntry.level, 'error') + + await inspector.close() + }) + + it('test retrieve stack trace for a log', async function () { + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onJavascriptException(function (log) { + logEntry = log }) -}, { browsers: ['firefox'] }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({id: 'jsException'}).click() + + const stackTrace = logEntry.stackTrace + assert.notEqual(stackTrace, null) + assert.equal(stackTrace.callFrames.length, 3) + + await inspector.close() + }) + + it('test listen to logs with multiple consumers', async function () { + let logEntry1 = null + let logEntry2 = null + const inspector = await LogInspector(driver) + await inspector.onJavascriptException(function (log) { + logEntry1 = log + }) + await inspector.onJavascriptException(function (log) { + logEntry2 = log + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({id: 'jsException'}).click() + + assert.equal(logEntry1.text, 'Error: Not working') + assert.equal(logEntry1.type, 'javascript') + assert.equal(logEntry1.level, 'error') + + assert.equal(logEntry2.text, 'Error: Not working') + assert.equal(logEntry2.type, 'javascript') + assert.equal(logEntry2.level, 'error') + + await inspector.close() + }) +}) \ No newline at end of file diff --git a/examples/javascript/test/bidirectional/network_commands.spec.js b/examples/javascript/test/bidirectional/network_commands.spec.js new file mode 100644 index 000000000000..563016460f08 --- /dev/null +++ b/examples/javascript/test/bidirectional/network_commands.spec.js @@ -0,0 +1,81 @@ +const assert = require("assert") +const firefox = require('selenium-webdriver/firefox') +const Network = require('selenium-webdriver/bidi/network') +const {until, By, Builder} = require('selenium-webdriver') +const {AddInterceptParameters} = require("selenium-webdriver/bidi/addInterceptParameters"); +const {InterceptPhase} = require("selenium-webdriver/bidi/interceptPhase"); + + +describe('Network commands', function () { + let driver + let network + + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + + network = await Network(driver) + }) + + afterEach(async function () { + await network.close() + await driver.quit() + }) + + xit('can add intercept', async function () { + const intercept = await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT)) + assert.notEqual(intercept, null) + }) + + xit('can remove intercept', async function () { + const network = await Network(driver) + const intercept = await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT)) + assert.notEqual(intercept, null) + + await network.removeIntercept(intercept) + }) + + xit('can continue with auth credentials ', async function () { + await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)) + + await network.authRequired(async (event) => { + await network.continueWithAuth(event.request.request, 'admin','admin') + }) + await driver.get('https://the-internet.herokuapp.com/basic_auth') + + const successMessage = 'Congratulations! You must have the proper credentials.' + + let elementMessage = await driver.findElement(By.tagName('p')).getText() + assert.equal(elementMessage, successMessage) + }) + + xit('can continue without auth credentials ', async function () { + await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)) + + await network.authRequired(async (event) => { + await network.continueWithAuthNoCredentials(event.request.request) + }) + + await driver.get('https://the-internet.herokuapp.com/basic_auth') + const alert = await driver.wait(until.alertIsPresent()) + await alert.dismiss() + + let source = await driver.getPageSource() + assert.equal(source.includes('Not authorized'), true) + }) + + xit('can cancel auth ', async function () { + await network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)) + + await network.authRequired(async (event) => { + await network.cancelAuth(event.request.request) + }) + + await driver.get('https://the-internet.herokuapp.com/basic_auth') + let source = await driver.getPageSource() + assert.equal(source.includes('Not authorized'), true) + }) +}) diff --git a/examples/javascript/test/bidirectional/network_events.spec.js b/examples/javascript/test/bidirectional/network_events.spec.js new file mode 100644 index 000000000000..931a5f2e7087 --- /dev/null +++ b/examples/javascript/test/bidirectional/network_events.spec.js @@ -0,0 +1,110 @@ +const assert = require("assert"); +const firefox = require('selenium-webdriver/firefox'); +const { Network } = require("selenium-webdriver/bidi/network"); +const {until, Builder} = require("selenium-webdriver"); + + +describe('Network events', function () { + let driver + + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('can listen to event before request is sent', async function () { + let beforeRequestEvent = null + const network = await Network(driver) + await network.beforeRequestSent(function (event) { + beforeRequestEvent = event + }) + + await driver.get('https://www.selenium.dev/selenium/web/blank.html') + + assert.equal(beforeRequestEvent.request.method, 'GET') + const url = beforeRequestEvent.request.url + assert.equal(url, await driver.getCurrentUrl()) + }) + + it('can request cookies', async function () { + const network = await Network(driver) + let beforeRequestEvent = null + await network.beforeRequestSent(function (event) { + beforeRequestEvent = event + }) + + await driver.get('https://www.selenium.dev/selenium/web/blank.html') + await driver.manage().addCookie({ + name: 'north', + value: 'biryani', + }) + await driver.navigate().refresh() + + assert.equal(beforeRequestEvent.request.method, 'GET') + assert.equal(beforeRequestEvent.request.cookies[0].name, 'north') + assert.equal(beforeRequestEvent.request.cookies[0].value.value, 'biryani') + const url = beforeRequestEvent.request.url + assert.equal(url, await driver.getCurrentUrl()) + + await driver.manage().addCookie({ + name: 'south', + value: 'dosa', + }) + await driver.navigate().refresh() + + assert.equal(beforeRequestEvent.request.cookies[1].name, 'south') + assert.equal(beforeRequestEvent.request.cookies[1].value.value, 'dosa') + }) + + it('can redirect http equiv', async function () { + let beforeRequestEvent = [] + const network = await Network(driver) + await network.beforeRequestSent(function (event) { + beforeRequestEvent.push(event) + }) + + await driver.get('http://www.selenium.dev/selenium/web/bidi/redirected_http_equiv.html') + await driver.wait(until.urlContains('redirected.html'), 1000) + + assert.equal(beforeRequestEvent[0].request.method, 'GET') + assert(beforeRequestEvent[0].request.url.includes('redirected_http_equiv.html')) + assert.equal(beforeRequestEvent[2].request.method, 'GET') + assert(beforeRequestEvent[3].request.url.includes('redirected.html')) + }) + + it('can subscribe to response started', async function () { + let onResponseStarted = [] + const network = await Network(driver) + await network.responseStarted(function (event) { + onResponseStarted.push(event) + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + assert.equal(onResponseStarted[0].request.method, 'GET') + assert.equal(onResponseStarted[0].request.url, await driver.getCurrentUrl()) + assert.equal(onResponseStarted[0].response.url, await driver.getCurrentUrl()) + }) + + it('can subscribe to response completed', async function () { + let onResponseCompleted = [] + const network = await Network(driver) + await network.responseCompleted(function (event) { + onResponseCompleted.push(event) + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + assert.equal(onResponseCompleted[0].request.method, 'GET') + assert.equal(onResponseCompleted[0].request.url, await driver.getCurrentUrl()) + assert.equal(onResponseCompleted[0].response.fromCache, false) + assert.equal(onResponseCompleted[0].response.status, 200) + }) +}) diff --git a/examples/javascript/test/bidirectional/script_commands.spec.js b/examples/javascript/test/bidirectional/script_commands.spec.js new file mode 100644 index 000000000000..0625ab0db146 --- /dev/null +++ b/examples/javascript/test/bidirectional/script_commands.spec.js @@ -0,0 +1,477 @@ +const assert = require("assert") +const firefox = require('selenium-webdriver/firefox') +const {By, until, Builder} = require("selenium-webdriver") +const ScriptManager = require('selenium-webdriver/bidi/scriptManager') +const {ResultOwnership} = require("selenium-webdriver/bidi/resultOwnership"); +const {ArgumentValue} = require("selenium-webdriver/bidi/argumentValue"); +const {LocalValue, RemoteReferenceType, ReferenceValue} = require("selenium-webdriver/bidi/protocolValue"); +const {EvaluateResultType} = require("selenium-webdriver/bidi/evaluateResult"); +const BrowsingContext = require("selenium-webdriver/bidi/browsingContext"); +const {WebDriverError} = require("selenium-webdriver/lib/error"); +const {RealmType} = require("selenium-webdriver/bidi/realmInfo"); +const LogInspector = require("selenium-webdriver/bidi/logInspector"); + + + +describe('Script commands', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('can call function', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + let argumentValues = [] + let value = new ArgumentValue(LocalValue.createNumberValue(22)) + argumentValues.push(value) + + let mapValue = {some_property: LocalValue.createNumberValue(42)} + let thisParameter = new ArgumentValue(LocalValue.createObjectValue(mapValue)).asMap() + + const result = await manager.callFunctionInBrowsingContext( + id, + 'function processWithPromise(argument) {' + + 'return new Promise((resolve, reject) => {' + + 'setTimeout(() => {' + + 'resolve(argument + this.some_property);' + + '}, 1000)' + + '})' + + '}', + true, + argumentValues, + thisParameter, + ResultOwnership.ROOT) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 64) + }) + + it('can call function with declaration', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext(id, '()=>{return 1+2;}', false) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 3) + }) + + it('can call function to get element', async function () { + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded') + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext( + id, + '() => document.getElementById("consoleLog")', + false, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.notEqual(result.realmId, null) + assert.equal(result.result.type, 'node') + assert.notEqual(result.result.value, null) + assert.notEqual(result.result.value.nodeType, null) + }) + + it('can call function with arguments', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + let argumentValues = [] + let value1 = new ArgumentValue(LocalValue.createStringValue('ARGUMENT_STRING_VALUE')) + let value2 = new ArgumentValue(LocalValue.createNumberValue(42)) + argumentValues.push(value1) + argumentValues.push(value2) + + const result = await manager.callFunctionInBrowsingContext( + id, + '(...args)=>{return args}', + false, + argumentValues, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value.length, 2) + }) + + it('can call function with await promise', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext( + id, + 'async function() {{\n' + + ' await new Promise(r => setTimeout(() => r(), 0));\n' + + ' return "SOME_DELAYED_RESULT";\n' + + ' }}', + true, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 'SOME_DELAYED_RESULT') + }) + + it('can call function with await promise false', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext( + id, + 'async function() {{\n' + + ' await new Promise(r => setTimeout(() => r(), 0));\n' + + ' return "SOME_DELAYED_RESULT";\n' + + ' }}', + false, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.type, 'promise') + }) + + it('can call function with this parameter', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + let mapValue = {some_property: LocalValue.createNumberValue(42)} + let thisParameter = new ArgumentValue(LocalValue.createObjectValue(mapValue)).asMap() + + const result = await manager.callFunctionInBrowsingContext( + id, + 'function(){return this.some_property}', + false, + null, + thisParameter, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 42) + }) + + it('can call function with ownership root', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext( + id, + 'async function(){return {a:1}}', + true, + null, + null, + ResultOwnership.ROOT, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + }) + + it('can call function with ownership none', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext( + id, + 'async function(){return {a:1}}', + true, + null, + null, + ResultOwnership.NONE, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.notEqual(result.realmId, null) + assert.equal(result.result.handle, undefined) + assert.notEqual(result.result.value, null) + }) + + it('can call function that throws exception', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.callFunctionInBrowsingContext(id, '))) !!@@## some invalid JS script (((', false) + assert.equal(result.resultType, EvaluateResultType.EXCEPTION) + + assert.equal(result.exceptionDetails.exception.type, 'error') + assert.equal(result.exceptionDetails.text, "SyntaxError: expected expression, got ')'") + assert.equal(result.exceptionDetails.columnNumber, 39) + assert.equal(result.exceptionDetails.stackTrace.callFrames.length, 0) + }) + + it('can call function in a sandbox', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + await manager.callFunctionInBrowsingContext(id, '() => { window.foo = 2; }', true, null, null, null, 'sandbox') + + const resultInSandbox = await manager.callFunctionInBrowsingContext( + id, + '() => window.foo', + true, + null, + null, + null, + 'sandbox', + ) + + assert.equal(resultInSandbox.resultType, EvaluateResultType.SUCCESS) + }) + + it('can call function in a realm', async function () { + const firstTab = await driver.getWindowHandle() + await driver.switchTo().newWindow('tab') + const manager = await ScriptManager(firstTab, driver) + + const realms = await manager.getAllRealms() + const realmId = realms[0].realmId + + await manager.callFunctionInRealm(realmId, '() => { window.foo = 3; }', true) + + const result = await manager.callFunctionInRealm(realmId, '() => window.foo', true) + + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 3) + }) + + it('can evaluate script', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.evaluateFunctionInBrowsingContext(id, '1 + 2', true) + + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 3) + }) + + it('can evaluate script that throws exception', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.evaluateFunctionInBrowsingContext( + id, + '))) !!@@## some invalid JS script (((', + false, + ) + + assert.equal(result.resultType, EvaluateResultType.EXCEPTION) + assert.equal(result.exceptionDetails.exception.type, 'error') + assert.equal(result.exceptionDetails.text, "SyntaxError: expected expression, got ')'") + assert.equal(result.exceptionDetails.columnNumber, 39) + assert.equal(result.exceptionDetails.stackTrace.callFrames.length, 0) + }) + + it('can evaluate script with result ownership', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const result = await manager.evaluateFunctionInBrowsingContext( + id, + 'Promise.resolve({a:1})', + true, + ResultOwnership.ROOT, + ) + + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.notEqual(result.result.handle, null) + }) + + it('can evaluate in a sandbox', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + await manager.evaluateFunctionInBrowsingContext(id, 'window.foo = 2', true, null, 'sandbox') + + const resultInSandbox = await manager.evaluateFunctionInBrowsingContext(id, 'window.foo', true, null, 'sandbox') + + assert.equal(resultInSandbox.resultType, EvaluateResultType.SUCCESS) + assert.equal(resultInSandbox.result.value, 2) + }) + + it('can evaluate in a realm', async function () { + const firstTab = await driver.getWindowHandle() + await driver.switchTo().newWindow('tab') + const manager = await ScriptManager(firstTab, driver) + + const realms = await manager.getAllRealms() + const realmId = realms[0].realmId + + await manager.evaluateFunctionInRealm(realmId, 'window.foo = 3', true) + + const result = await manager.evaluateFunctionInRealm(realmId, 'window.foo', true) + + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.equal(result.result.value, 3) + }) + + it('can disown handles', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const browsingContext = await BrowsingContext(driver, {browsingContextId: id}) + + const info = await browsingContext.navigate( + 'https://www.selenium.dev/selenium/web/dynamic.html', + 'complete' + ) + + await driver.findElement(By.id('adder')).click() + + await driver.wait(until.elementLocated(By.id('box0')), 10000) + + const evaluateResult = await manager.evaluateFunctionInBrowsingContext( + id, + "document.querySelector('.redbox');", + false, + ResultOwnership.ROOT, + ) + + assert.equal(evaluateResult.resultType, EvaluateResultType.SUCCESS) + let boxId = evaluateResult.result.handle + + await manager.disownBrowsingContextScript(id, boxId) + + await manager.callFunctionInBrowsingContext(id, 'arg => arg.a', false).catch((error) => { + assert(error instanceof WebDriverError) + }) + }) + + it('can disown handles in realm', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const browsingContext = await BrowsingContext(driver, {browsingContextId: id}) + + const info = await browsingContext.navigate( + 'https://www.selenium.dev/selenium/web/dynamic.html', + 'complete' + ) + + await driver.findElement(By.id('adder')).click() + + await driver.wait(until.elementLocated(By.id('box0')), 10000) + + const realms = await manager.getAllRealms() + const realmId = realms[0].realmId + + const evaluateResult = await manager.evaluateFunctionInBrowsingContext( + id, + "document.querySelector('.redbox');", + false, + ResultOwnership.ROOT, + ) + + assert.equal(evaluateResult.resultType, EvaluateResultType.SUCCESS) + let boxId = evaluateResult.result.handle + + let argumentValues = [] + let value1 = new ArgumentValue(new ReferenceValue(RemoteReferenceType.HANDLE, boxId)) + argumentValues.push(value1) + let checkHandle = await manager.callFunctionInBrowsingContext(id, 'arg => arg.a', false, argumentValues) + + assert.equal(checkHandle.resultType, EvaluateResultType.SUCCESS) + + await manager.disownRealmScript(realmId, boxId) + + await manager.callFunctionInBrowsingContext(id, 'arg => arg.a', false).catch((error) => { + assert(error instanceof WebDriverError) + }) + }) + + it('can get all realms', async function () { + const firstWindow = await driver.getWindowHandle() + await driver.switchTo().newWindow('window') + const secondWindow = await driver.getWindowHandle() + const manager = await ScriptManager(firstWindow, driver) + + const realms = await manager.getAllRealms() + assert.equal(realms.length, 2) + }) + + it('can get realm by type', async function () { + const firstWindow = await driver.getWindowHandle() + await driver.switchTo().newWindow('window') + const secondWindow = await driver.getWindowHandle() + const manager = await ScriptManager(firstWindow, driver) + + const realms = await manager.getRealmsByType(RealmType.WINDOW) + assert.equal(realms.length, 2) + }) + + it('can get realm in browsing context', async function () { + const windowId = await driver.getWindowHandle() + await driver.switchTo().newWindow('tab') + const tabId = await driver.getWindowHandle() + const manager = await ScriptManager(windowId, driver) + + const realms = await manager.getRealmsInBrowsingContext(tabId) + + const tabRealm = realms[0] + assert.equal(tabRealm.realmType, RealmType.WINDOW) + }) + + it('can get realm in browsing context by type', async function () { + const windowId = await driver.getWindowHandle() + await driver.switchTo().newWindow('tab') + const manager = await ScriptManager(windowId, driver) + + const realms = await manager.getRealmsInBrowsingContextByType(windowId, RealmType.WINDOW) + + const windowRealm = realms[0] + assert.equal(windowRealm.realmType, RealmType.WINDOW) + }) + + it('can add preload script', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const scriptId = await manager.addPreloadScript('() => {{ console.log(\'{preload_script_console_text}\') }}') + + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onConsoleEntry(function (log) { + logEntry = log + }) + + await driver.get('https://www.selenium.dev/selenium/blank') + + assert.equal(logEntry.text, '{preload_script_console_text}') + }) + + it('can add preload script to sandbox', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + await manager.addPreloadScript('() => { window.bar = 2; }', undefined, 'sandbox') + + await driver.get('https://www.selenium.dev/selenium/blank') + + let result_in_sandbox = await manager.evaluateFunctionInBrowsingContext( + id, + 'window.bar', + true, + null, + 'sandbox', + ) + + assert.equal(result_in_sandbox.result.type, 'number') + assert.equal(result_in_sandbox.result.value, 2) + }) + + it('can remove preload script', async function () { + const id = await driver.getWindowHandle() + const manager = await ScriptManager(id, driver) + + const scriptId = await manager.addPreloadScript('() => {{ console.log(\'{preload_script_console_text}\') }}') + + let logEntry = null + const inspector = await LogInspector(driver) + await inspector.onConsoleEntry(function (log) { + logEntry = log + }) + + await manager.removePreloadScript(scriptId) + + await driver.get('https://www.selenium.dev/selenium/blank') + + assert.equal(logEntry, null) + }) +}) diff --git a/examples/javascript/test/bidirectional/script_events.spec.js b/examples/javascript/test/bidirectional/script_events.spec.js new file mode 100644 index 000000000000..f87312531e61 --- /dev/null +++ b/examples/javascript/test/bidirectional/script_events.spec.js @@ -0,0 +1,92 @@ +const assert = require("assert") +const firefox = require('selenium-webdriver/firefox') +const {ScriptManager, BrowsingContext, Builder} = require("selenium-webdriver") +const {ArgumentValue} = require("selenium-webdriver/bidi/argumentValue") +const {RealmType} = require("selenium-webdriver/bidi/realmInfo") +const {LocalValue, ChannelValue} = require("selenium-webdriver/bidi/protocolValue") +const {EvaluateResultType} = require("selenium-webdriver/bidi/evaluateResult"); + + + +describe('Script events', function () { + let driver + + beforeEach(async function () { + driver = new Builder() + .forBrowser('firefox') + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() + }) + + afterEach(async function () { + await driver.quit() + }) + + it('can listen to channel message', async function () { + const manager = await ScriptManager(undefined, driver) + + let message = null + + await manager.onMessage((m) => { + message = m + }) + + let argumentValues = [] + let value = new ArgumentValue(LocalValue.createChannelValue(new ChannelValue('channel_name'))) + argumentValues.push(value) + + const result = await manager.callFunctionInBrowsingContext( + await driver.getWindowHandle(), + '(channel) => channel("foo")', + false, + argumentValues, + ) + assert.equal(result.resultType, EvaluateResultType.SUCCESS) + assert.notEqual(message, null) + assert.equal(message.channel, 'channel_name') + assert.equal(message.data.type, 'string') + assert.equal(message.data.value, 'foo') + }) + + it('can listen to realm created message', async function () { + const manager = await ScriptManager(undefined, driver) + + let realmInfo = null + + await manager.onRealmCreated((result) => { + realmInfo = result + }) + + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.navigate('https://www.selenium.dev/selenium/web/blank', 'complete') + + assert.notEqual(realmInfo, null) + assert.notEqual(realmInfo.realmId, null) + assert.equal(realmInfo.realmType, RealmType.WINDOW) + }) + + xit('can listen to realm destroyed message', async function () { + const manager = await ScriptManager(undefined, driver) + + let realmInfo = null + + await manager.onRealmDestroyed((result) => { + realmInfo = result + }) + + const id = await driver.getWindowHandle() + const browsingContext = await BrowsingContext(driver, { + browsingContextId: id, + }) + + await browsingContext.close() + + assert.notEqual(realmInfo, null) + assert.notEqual(realmInfo.realmId, null) + assert.equal(realmInfo.realmType, RealmType.WINDOW) + }) +}) diff --git a/examples/javascript/test/bidirectional/w3c/log.spec.js b/examples/javascript/test/bidirectional/w3c/log.spec.js new file mode 100644 index 000000000000..41ca36fb8040 --- /dev/null +++ b/examples/javascript/test/bidirectional/w3c/log.spec.js @@ -0,0 +1,89 @@ + +const assert = require("assert"); +const firefox = require('selenium-webdriver/firefox'); +const {until, Builder} = require("selenium-webdriver"); + +let driver + +beforeEach(async function () { + driver = new Builder() + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() +}) + +afterEach(async function () { + await driver.quit() +}) + +function delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +describe('BiDi Logging', function () { + it('can listen to console log', async function () { + let log = null + const handler = await driver.script().addConsoleMessageHandler((logEntry) => { + log = logEntry + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({ id: 'consoleLog' }).click() + + await delay(3000) + + assert.equal(log.text, 'Hello, world!') + await driver.script().removeConsoleMessageHandler(handler) + }) + + it('can remove console log handler', async function () { + let log = null + const handler = await driver.script().addConsoleMessageHandler((logEntry) => { + log = logEntry + }) + + await driver.script().removeConsoleMessageHandler(handler) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({ id: 'consoleLog' }).click() + + await delay(3000) + + assert.equal(log, null) + }) + + it('can listen to javascript error', async function () { + let log = null + const handler = await driver.script().addJavaScriptErrorHandler((logEntry) => { + log = logEntry + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({ id: 'jsException' }).click() + + await delay(3000) + + assert.equal(log.text, 'Error: Not working') + assert.equal(log.type, 'javascript') + assert.equal(log.level, 'error') + + await driver.script().removeJavaScriptErrorHandler(handler) + }) + + it('can remove to javascript error handler', async function () { + let log = null + const handler = await driver.script().addJavaScriptErrorHandler((logEntry) => { + log = logEntry + }) + + await driver.script().removeJavaScriptErrorHandler(handler) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + await driver.findElement({ id: 'jsException' }).click() + + await delay(3000) + + assert.equal(log, null) + }) +}) + + diff --git a/examples/javascript/test/bidirectional/w3c/script.spec.js b/examples/javascript/test/bidirectional/w3c/script.spec.js new file mode 100644 index 000000000000..7b29ba4a65be --- /dev/null +++ b/examples/javascript/test/bidirectional/w3c/script.spec.js @@ -0,0 +1,93 @@ + +const assert = require("assert"); +const firefox = require('selenium-webdriver/firefox'); +const {until, Builder} = require("selenium-webdriver"); + +let driver + +beforeEach(async function () { + driver = new Builder() + .setFirefoxOptions(new firefox.Options().enableBidi()) + .build() +}) + +afterEach(async function () { + await driver.quit() +}) + +function delay(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +describe('BiDi Script', function () { + + it('can listen to dom mutations', async function () { + let message = null + await driver.script().addDomMutationHandler((m) => { + message = m + }) + + await driver.get('https://www.selenium.dev/selenium/web/dynamic') + + let element = driver.findElement({ id: 'reveal' }) + await element.click() + let revealed = driver.findElement({ id: 'revealed' }) + await driver.wait(until.elementIsVisible(revealed), 5000) + + assert.strictEqual(message['attribute_name'], 'style') + assert.strictEqual(message['current_value'], '') + assert.strictEqual(message['old_value'], 'display:none;') + }) + + it('can remove to dom mutation handler', async function () { + let message = null + let id = await driver.script().addDomMutationHandler((m) => { + message = m + }) + + await driver.script().removeDomMutationHandler(id) + + await driver.get('https://www.selenium.dev/selenium/web/dynamic') + + let element = driver.findElement({ id: 'reveal' }) + await element.click() + let revealed = driver.findElement({ id: 'revealed' }) + await driver.wait(until.elementIsVisible(revealed), 5000) + + assert.strictEqual(message, null) + }) + + it('can pin script', async function () { + await driver.script().pin("() => { console.log('Hello!'); }") + let log + + await driver.script().addConsoleMessageHandler((logEntry) => { + log = logEntry + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + await delay(3000) + + assert.equal(log.text, 'Hello!') + }) + + it('can unpin script', async function () { + const id = await driver.script().pin("() => { console.log('Hello!'); }") + + let count = 0 + await driver.script().addConsoleMessageHandler((logEntry) => { + count++ + }) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + await driver.script().unpin(id) + + await driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + assert.equal(count, 1) + }) +}) + + diff --git a/examples/javascript/test/browser/chromeSpecificCaps.spec.js b/examples/javascript/test/browser/chromeSpecificCaps.spec.js index 29ad798ed060..04175656d870 100644 --- a/examples/javascript/test/browser/chromeSpecificCaps.spec.js +++ b/examples/javascript/test/browser/chromeSpecificCaps.spec.js @@ -1,72 +1,71 @@ const Chrome = require('selenium-webdriver/chrome'); -const {suite} = require('selenium-webdriver/testing'); -const {Browser} = require("selenium-webdriver"); +const { Browser, Builder } = require("selenium-webdriver"); const options = new Chrome.Options(); -suite(function (env) { - describe('Should be able to Test Command line arguments', function () { - it('headless', async function () { - let driver = await env - .builder() - .setChromeOptions(options.addArguments('--headless=new')) - .build(); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); - it('exclude switches', async function () { - let driver = await env - .builder() - .setChromeOptions(options.excludeSwitches('enable-automation')) - .build(); +describe('Should be able to Test Command line arguments', function () { + it('headless', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.addArguments('--headless=new')) + .build(); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); - it('Keep browser open - set detach to true ', async function () { - let driver = await env - .builder() - .setChromeOptions(options.detachDriver(true)) - .build(); + it('exclude switches', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.excludeSwitches('enable-automation')) + .build(); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); - // As tests runs in ci, quitting the driver instance to avoid any failures - await driver.quit(); - }); + it('Keep browser open - set detach to true ', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.detachDriver(true)) + .build(); - xit('Start browser from specified location ', async function () { - let driver = await env - .builder() - .setChromeOptions(options.setChromeBinaryPath(`Path to chrome binary`)) - .build(); + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + // As tests runs in ci, quitting the driver instance to avoid any failures + await driver.quit(); + }); - it('Basic Chrome test', async function () { - const Options = new Chrome.Options(); - let driver = await env - .builder() - .setChromeOptions(Options) - .build(); + xit('Start browser from specified location ', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.setChromeBinaryPath(`Path to chrome binary`)) + .build(); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Basic Chrome test', async function () { + const Options = new Chrome.Options(); + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(Options) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); - it('Add Extension', async function () { - const options = new Chrome.Options(); - let driver = await env - .builder() - .setChromeOptions(options.addExtensions(['./test/resources/extensions/webextensions-selenium-example.crx'])) - .build(); + it('Add Extension', async function () { + const options = new Chrome.Options(); + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.addExtensions(['./test/resources/extensions/webextensions-selenium-example.crx'])) + .build(); - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); }); -}, { browsers: [Browser.CHROME]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/browser/cookies.spec.js b/examples/javascript/test/browser/cookies.spec.js deleted file mode 100644 index dab19465e94b..000000000000 --- a/examples/javascript/test/browser/cookies.spec.js +++ /dev/null @@ -1,82 +0,0 @@ -const { suite } = require('selenium-webdriver/testing'); -const {Browser} = require("selenium-webdriver"); - -suite(function(env) { - describe('Cookies', function() { - let driver; - - before(async function() { - driver = await env.builder().build(); - }); - - after(() => driver.quit()); - - it('Create a cookie', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // set a cookie on the current domain - await driver.manage().addCookie({ name: 'key', value: 'value' }); - }); - - it('Create cookies with sameSite', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // set a cookie on the current domain with sameSite 'Strict' (or) 'Lax' - await driver.manage().addCookie({ name: 'key', value: 'value', sameSite: 'Strict' }); - await driver.manage().addCookie({ name: 'key', value: 'value', sameSite: 'Lax' }); - }); - - it('Read cookie', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // set a cookie on the current domain - await driver.manage().addCookie({ name: 'foo', value: 'bar' }); - - // Get cookie details with named cookie 'foo' - await driver.manage().getCookie('foo').then(function(cookie) { - console.log('cookie details => ', cookie); - }); - }); - - it('Read all cookies', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // Add few cookies - await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); - await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); - - // Get all Available cookies - await driver.manage().getCookies().then(function(cookies) { - console.log('cookie details => ', cookies); - }); - }); - - it('Delete a cookie', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // Add few cookies - await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); - await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); - - // Delete a cookie with name 'test1' - await driver.manage().deleteCookie('test1'); - - // Get all Available cookies - await driver.manage().getCookies().then(function(cookies) { - console.log('cookie details => ', cookies); - }); - }); - - it('Delete all cookies', async function() { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // Add few cookies - await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); - await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); - - // Delete all cookies - await driver.manage().deleteAllCookies(); - }); - - }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file diff --git a/examples/javascript/test/browser/edgeSpecificCaps.spec.js b/examples/javascript/test/browser/edgeSpecificCaps.spec.js index d05e94cfb743..4f059f5c79a5 100644 --- a/examples/javascript/test/browser/edgeSpecificCaps.spec.js +++ b/examples/javascript/test/browser/edgeSpecificCaps.spec.js @@ -1,51 +1,63 @@ -const {Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {Browser, By, Builder } = require('selenium-webdriver'); const edge = require('selenium-webdriver/edge'); const options = new edge.Options(); +const assert = require("assert"); -suite(function (env) { - describe('Should be able to Test Command line arguments', function () { - it('headless', async function () { - let driver = await env - .builder() - .setEdgeOptions(options.addArguments('--headless=new')) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); - - it('exclude switches', async function () { - let driver = await env - .builder() - .setEdgeOptions(options.excludeSwitches('enable-automation')) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); - - it('Keep browser open - set detach to true ', async function () { - let driver = await env - .builder() - .setEdgeOptions(options.detachDriver(true)) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - - // As tests runs in ci, quitting the driver instance to avoid any failures - await driver.quit(); - }); - - it('Basic edge test', async function () { - const Options = new edge.Options(); - let driver = await env - .builder() - .setEdgeOptions(Options) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + + +describe('Should be able to Test Command line arguments', function () { + it('headless', async function () { + let driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(options.addArguments('--headless=new')) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('exclude switches', async function () { + let driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(options.excludeSwitches('enable-automation')) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Keep browser open - set detach to true ', async function () { + let driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(options.detachDriver(true)) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // As tests runs in ci, quitting the driver instance to avoid any failures + await driver.quit(); + }); + + it('Basic edge test', async function () { + const Options = new edge.Options(); + let driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(Options) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Add Extension', async function () { + let driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(options.addExtensions(['./test/resources/extensions/webextensions-selenium-example.crx'])) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + let injected = await driver.findElement(By.id('webextensions-selenium-example')); + assert.equal(await injected.getText(), `Content injected by webextensions-selenium-example`) + await driver.quit(); }); -}, { browsers: [Browser.EDGE]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/browser/firefoxSpecificFunctionalities.js b/examples/javascript/test/browser/firefoxSpecificFunctionalities.spec.js similarity index 54% rename from examples/javascript/test/browser/firefoxSpecificFunctionalities.js rename to examples/javascript/test/browser/firefoxSpecificFunctionalities.spec.js index 8382759921fd..4fefa794e833 100644 --- a/examples/javascript/test/browser/firefoxSpecificFunctionalities.js +++ b/examples/javascript/test/browser/firefoxSpecificFunctionalities.spec.js @@ -1,15 +1,14 @@ -const {Browser, By} = require('selenium-webdriver'); +const {Browser, By, Builder} = require('selenium-webdriver'); const Firefox = require('selenium-webdriver/firefox'); const options = new Firefox.Options(); const path = require('path'); -const {suite} = require("selenium-webdriver/testing"); const assert = require("assert"); -suite(function (env) { describe('Should be able to Test Command line arguments', function () { it('headless', async function () { - let driver = await env.builder() + let driver = new Builder() + .forBrowser(Browser.FIREFOX) .setFirefoxOptions(options.addArguments('--headless')) .build(); @@ -20,7 +19,9 @@ describe('Should be able to Test Command line arguments', function () { it('Should be able to add extension', async function () { const xpiPath = path.resolve('./test/resources/extensions/selenium-example.xpi') - let driver = await env.builder().build(); + let driver = new Builder() + .forBrowser(Browser.FIREFOX) + .build() let id = await driver.installAddon(xpiPath); await driver.uninstallAddon(id); @@ -30,5 +31,20 @@ describe('Should be able to Test Command line arguments', function () { assert.equal(ele.length, 0); await driver.quit(); }); -}); -}, { browsers: [Browser.FIREFOX]}); \ No newline at end of file + + it('Should be able to install unsigned addon', async function () { + + const xpiPath = path.resolve('./test/resources/extensions/selenium-example') + let driver = new Builder() + .forBrowser(Browser.FIREFOX) + .build() + let id = await driver.installAddon(xpiPath, true); + await driver.uninstallAddon(id); + + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + const ele = await driver.findElements(By.id("webextensions-selenium-example")); + assert.equal(ele.length, 0); + await driver.quit(); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/browser/safariSpecificCap.spec.js b/examples/javascript/test/browser/safariSpecificCap.spec.js index e4ace0fc717d..8bda5240cdf0 100644 --- a/examples/javascript/test/browser/safariSpecificCap.spec.js +++ b/examples/javascript/test/browser/safariSpecificCap.spec.js @@ -1,18 +1,16 @@ const safari = require('selenium-webdriver/safari'); -const {Browser} = require("selenium-webdriver"); -const { suite } = require('selenium-webdriver/testing') +const {Browser, Builder} = require("selenium-webdriver"); const options = new safari.Options(); const process = require('node:process'); -suite(function(env) { - describe('Should be able to Test Command line arguments', function () { - (process.platform === 'darwin' ? it : it.skip)('headless', async function () { - let driver = await env.builder() +describe('Should be able to Test Command line arguments', function () { + (process.platform === 'darwin' ? it : it.skip)('headless', async function () { + let driver = new Builder() + .forBrowser(Browser.SAFARI) .setSafariOptions(options) .build(); await driver.get('https://www.selenium.dev/selenium/web/blank.html'); await driver.quit(); - }); }); -}, { browsers: [Browser.SAFARI]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/capabilities/pageLoading.spec.js b/examples/javascript/test/capabilities/pageLoading.spec.js index 0c6605864452..d2eb0a91b08d 100644 --- a/examples/javascript/test/capabilities/pageLoading.spec.js +++ b/examples/javascript/test/capabilities/pageLoading.spec.js @@ -1,38 +1,46 @@ const Chrome = require('selenium-webdriver/chrome'); -const {suite} = require('selenium-webdriver/testing'); -const {Browser} = require("selenium-webdriver"); +const {Browser, Builder} = require("selenium-webdriver"); const options = new Chrome.Options() -suite(function (env) { - describe('Page loading strategies', function () { - it('Navigate using eager page loading strategy', async function () { - let driver = await env - .builder() - .setChromeOptions(options.setPageLoadStrategy('eager')) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); - - it('Navigate using none page loading strategy', async function () { - let driver = await env - .builder() - .setChromeOptions(options.setPageLoadStrategy('none')) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); - - it('Navigate using normal page loading strategy', async function () { - let driver = await env - .builder() - .setChromeOptions(options.setPageLoadStrategy('normal')) - .build(); - - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - await driver.quit(); - }); + +describe('Page loading strategies', function () { + it('Navigate using eager page loading strategy', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.setPageLoadStrategy('eager')) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Navigate using none page loading strategy', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.setPageLoadStrategy('none')) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Navigate using normal page loading strategy', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.setPageLoadStrategy('normal')) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Should be able to accept certs', async function () { + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options.setAcceptInsecureCerts(true)) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/drivers/service.spec.js b/examples/javascript/test/drivers/service.spec.js new file mode 100644 index 000000000000..f0772239eb68 --- /dev/null +++ b/examples/javascript/test/drivers/service.spec.js @@ -0,0 +1,53 @@ +const Chrome = require('selenium-webdriver/chrome'); +const {Browser, Builder} = require("selenium-webdriver"); +const {getBinaryPaths} = require("selenium-webdriver/common/driverFinder"); +const options = new Chrome.Options(); + +describe('Service Test', function () { + it('Default service', async function () { + let service = new Chrome.ServiceBuilder() + + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeService(service) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Set Driver Location', async function () { + + let options = new Chrome.Options(); + options.setBrowserVersion("stable") + + let paths = getBinaryPaths(options) + let driverPath = paths.driverPath; + let browserPath = paths.browserPath; + + options.setChromeBinaryPath(browserPath) + + let service = new Chrome.ServiceBuilder().setPath(driverPath) + + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeOptions(options) + .setChromeService(service) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + it('Set port', async function () { + let service = new Chrome.ServiceBuilder().setPort(1234) + + let driver = new Builder() + .forBrowser(Browser.CHROME) + .setChromeService(service) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/elements/fileUpload.spec.js b/examples/javascript/test/elements/fileUpload.spec.js index cafa37e7f68d..feb7d0402cfb 100644 --- a/examples/javascript/test/elements/fileUpload.spec.js +++ b/examples/javascript/test/elements/fileUpload.spec.js @@ -1,35 +1,35 @@ -const { suite } = require('selenium-webdriver/testing'); -const {Browser, By, until} = require("selenium-webdriver"); + +const {Browser, By, until, Builder} = require("selenium-webdriver"); const path = require("path"); const assert = require('node:assert'); -suite(function(env) { - describe('File Upload Test', function() { - let driver; - before(async function() { - driver = await env.builder().build(); - }); +describe('File Upload Test', function() { + let driver; + + before(async function() { + driver = new Builder() + .forBrowser(Browser.CHROME) + .build(); + }); + + after(async() => await driver.quit()); - after(() => driver.quit()); + it('Should be able to upload a file successfully', async function() { + const image = path.resolve('./test/resources/selenium-snapshot.png') - it('Should be able to upload a file successfully', async function() { - const image = path.resolve('./test/resources/selenium-snapshot.png') + await driver.manage().setTimeouts({implicit: 5000}); - await driver.manage().setTimeouts({implicit: 5000}); + // Navigate to URL + await driver.get('https://the-internet.herokuapp.com/upload'); + // Upload snapshot + await driver.findElement(By.id("file-upload")).sendKeys(image); + await driver.findElement(By.id("file-submit")).submit(); - // Navigate to URL - await driver.get('https://the-internet.herokuapp.com/upload'); - // Upload snapshot - await driver.findElement(By.id("file-upload")).sendKeys(image); - await driver.findElement(By.id("file-submit")).submit(); - - const revealed = await driver.findElement(By.id('uploaded-files')) - await driver.wait(until.elementIsVisible(revealed), 2000); - const data = await driver.findElement(By.css('h3')); - - assert.equal(await data.getText(), `File Uploaded!`); - }); + const revealed = await driver.findElement(By.id('uploaded-files')) + await driver.wait(until.elementIsVisible(revealed), 2000); + const data = await driver.findElement(By.css('h3')); + assert.equal(await data.getText(), `File Uploaded!`); }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/elements/interactions.spec.js b/examples/javascript/test/elements/interactions.spec.js index cca6e5b960a0..005974f85129 100644 --- a/examples/javascript/test/elements/interactions.spec.js +++ b/examples/javascript/test/elements/interactions.spec.js @@ -1,28 +1,30 @@ -const {suite} = require('selenium-webdriver/testing'); -const {By, Browser} = require('selenium-webdriver'); + +const {By, Browser, Builder} = require('selenium-webdriver'); const assert = require("node:assert"); -suite(function (env) { - describe('Element Interactions', function () { - let driver; - before(async function () { - driver = await env.builder().build(); - }); +describe('Element Interactions', function () { + let driver; + + before(async function () { + driver = new Builder() + .forBrowser(Browser.CHROME) + .build(); + }); - after(async () => await driver.quit()); + after(async () => await driver.quit()); - it('should Clear input and send keys into input field', async function () { + it('should Clear input and send keys into input field', async function () { - try { - await driver.get('https://www.selenium.dev/selenium/web/inputs.html'); - let inputField = await driver.findElement(By.name('no_type')); - await inputField.clear(); - await inputField.sendKeys('Selenium'); - assert.strictEqual(await inputField.getText(), "Selenium"); - } catch (e) { - console.log(e) - } - }); + try { + await driver.get('https://www.selenium.dev/selenium/web/inputs.html'); + let inputField = await driver.findElement(By.name('no_type')); + await inputField.clear(); + await inputField.sendKeys('Selenium'); + const text = await inputField.getAttribute('value'); + assert.strictEqual(text, "Selenium"); + } catch (e) { + console.log(e) + } }); -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/getting_started/firstScript.spec.js b/examples/javascript/test/getting_started/firstScript.spec.js index 0aa870f331b6..4c0a2a496d90 100644 --- a/examples/javascript/test/getting_started/firstScript.spec.js +++ b/examples/javascript/test/getting_started/firstScript.spec.js @@ -1,11 +1,11 @@ -const {By, Builder} = require('selenium-webdriver'); +const {By, Builder, Browser} = require('selenium-webdriver'); const assert = require("assert"); (async function firstTest() { let driver; try { - driver = await new Builder().forBrowser('chrome').build(); + driver = await new Builder().forBrowser(Browser.CHROME).build(); await driver.get('https://www.selenium.dev/selenium/web/web-form.html'); let title = await driver.getTitle(); @@ -27,4 +27,4 @@ const assert = require("assert"); } finally { await driver.quit(); } -}()) \ No newline at end of file +}()) diff --git a/examples/javascript/test/getting_started/openEdgeTest.spec.js b/examples/javascript/test/getting_started/openEdgeTest.spec.js index 24a053ed0461..001337c356ec 100644 --- a/examples/javascript/test/getting_started/openEdgeTest.spec.js +++ b/examples/javascript/test/getting_started/openEdgeTest.spec.js @@ -1,22 +1,23 @@ -const {Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {Browser, Builder} = require('selenium-webdriver'); const edge = require('selenium-webdriver/edge'); -suite(function (env) { - describe('Open Edge', function () { - let driver; - before(async function () { - let options = new edge.Options(); - driver = await env.builder() - .setEdgeOptions(options) - .build(); - }); +describe('Open Edge', function () { + let driver; - after(async () => await driver.quit()); - it('Basic Edge test', async function () { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - }); + + before(async function () { + let options = new edge.Options(); + driver = new Builder() + .forBrowser(Browser.EDGE) + .setEdgeOptions(options) + .build(); + }); + + after(async () => await driver.quit()); + + it('Basic Edge test', async function () { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); }); -}, { browsers: [Browser.EDGE]}); +}); diff --git a/examples/javascript/test/getting_started/openFirefoxTest.spec.js b/examples/javascript/test/getting_started/openFirefoxTest.spec.js index 5de1112df72f..023006fda961 100644 --- a/examples/javascript/test/getting_started/openFirefoxTest.spec.js +++ b/examples/javascript/test/getting_started/openFirefoxTest.spec.js @@ -1,22 +1,21 @@ -const {Browser} = require('selenium-webdriver'); -const {suite} = require('selenium-webdriver/testing'); +const {Browser, Builder} = require('selenium-webdriver'); const firefox = require('selenium-webdriver/firefox'); -suite(function (env) { - describe('Open Firefox', function () { - let driver; - before(async function () { - let options = new firefox.Options(); - driver = await env.builder() - .setFirefoxOptions(options) - .build(); - }); +describe('Open Firefox', function () { + let driver; - after(async () => await driver.quit()); + before(async function () { + let options = new firefox.Options(); + driver = new Builder() + .forBrowser(Browser.FIREFOX) + .setFirefoxOptions(options) + .build(); + }); + + after(async () => await driver.quit()); - it('Basic Firefox test', async function () { - await driver.get('https://www.selenium.dev/selenium/web/blank.html'); - }); + it('Basic Firefox test', async function () { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); }); -}, { browsers: [Browser.FIREFOX]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/getting_started/runningTests.spec.js b/examples/javascript/test/getting_started/runningTests.spec.js index 31aa8732396e..764515add235 100644 --- a/examples/javascript/test/getting_started/runningTests.spec.js +++ b/examples/javascript/test/getting_started/runningTests.spec.js @@ -1,31 +1,31 @@ const {By, Builder} = require('selenium-webdriver'); const assert = require("assert"); - describe('First script', function () { - let driver; - - before(async function () { - driver = await new Builder().forBrowser('chrome').build(); - }); - - it('First Selenium script with mocha', async function () { - await driver.get('https://www.selenium.dev/selenium/web/web-form.html'); - - let title = await driver.getTitle(); - assert.equal("Web form", title); - - await driver.manage().setTimeouts({implicit: 500}); - - let textBox = await driver.findElement(By.name('my-text')); - let submitButton = await driver.findElement(By.css('button')); - - await textBox.sendKeys('Selenium'); - await submitButton.click(); - - let message = await driver.findElement(By.id('message')); - let value = await message.getText(); - assert.equal("Received!", value); - }); - - after(async () => await driver.quit()); - }); \ No newline at end of file +describe('First script', function () { + let driver; + + before(async function () { + driver = await new Builder().forBrowser('chrome').build(); + }); + + it('First Selenium script with mocha', async function () { + await driver.get('https://www.selenium.dev/selenium/web/web-form.html'); + + let title = await driver.getTitle(); + assert.equal("Web form", title); + + await driver.manage().setTimeouts({implicit: 500}); + + let textBox = await driver.findElement(By.name('my-text')); + let submitButton = await driver.findElement(By.css('button')); + + await textBox.sendKeys('Selenium'); + await submitButton.click(); + + let message = await driver.findElement(By.id('message')); + let value = await message.getText(); + assert.equal("Received!", value); + }); + + after(async () => await driver.quit()); +}); \ No newline at end of file diff --git a/examples/javascript/test/hello/helloSelenium.js b/examples/javascript/test/hello/helloSelenium.js index 41e2b701e659..732205a424bd 100644 --- a/examples/javascript/test/hello/helloSelenium.js +++ b/examples/javascript/test/hello/helloSelenium.js @@ -1,7 +1,7 @@ -const {Builder} = require('selenium-webdriver'); +const {Builder, Browser} = require('selenium-webdriver'); (async function helloSelenium() { - let driver = await new Builder().forBrowser('chrome').build(); + let driver = await new Builder().forBrowser(Browser.CHROME).build(); await driver.get('https://selenium.dev/'); diff --git a/examples/javascript/test/interactions/alert.spec.js b/examples/javascript/test/interactions/alert.spec.js new file mode 100644 index 000000000000..ea503eb817d0 --- /dev/null +++ b/examples/javascript/test/interactions/alert.spec.js @@ -0,0 +1,50 @@ + +const { By, Builder, until } = require('selenium-webdriver'); +const assert = require("node:assert"); + + +describe('Interactions - Alerts', function () { + let driver; + + before(async function () { + driver = await new Builder().forBrowser('chrome').build(); + }); + + after(async () => await driver.quit()); + + it('Should be able to getText from alert and accept', async function () { + await driver.get('https://www.selenium.dev/selenium/web/alerts.html'); + await driver.findElement(By.id("alert")).click(); + await driver.wait(until.alertIsPresent()); + let alert = await driver.switchTo().alert(); + let alertText = await alert.getText(); + await alert.accept(); + // Verify + assert.equal(alertText, "cheese"); + }); + + it('Should be able to getText from alert and dismiss', async function () { + await driver.get('https://www.selenium.dev/selenium/web/alerts.html'); + await driver.findElement(By.id("confirm")).click(); + await driver.wait(until.alertIsPresent()); + let alert = await driver.switchTo().alert(); + let alertText = await alert.getText(); + await alert.dismiss(); + // Verify + assert.equal(alertText, "Are you sure?"); + }); + + it('Should be able to enter text in alert prompt', async function () { + let text = 'Selenium'; + await driver.get('https://www.selenium.dev/selenium/web/alerts.html'); + await driver.findElement(By.id("prompt")).click(); + await driver.wait(until.alertIsPresent()); + let alert = await driver.switchTo().alert(); + //Type your message + await alert.sendKeys(text); + await alert.accept(); + + let enteredText = await driver.findElement(By.id('text')); + assert.equal(await enteredText.getText(), text); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/interactions/cookies.spec.js b/examples/javascript/test/interactions/cookies.spec.js new file mode 100644 index 000000000000..a954f39310c5 --- /dev/null +++ b/examples/javascript/test/interactions/cookies.spec.js @@ -0,0 +1,82 @@ + +const {Browser, Builder} = require("selenium-webdriver"); + + +describe('Cookies', function() { + let driver; + + before(async function() { + driver = new Builder() + .forBrowser(Browser.CHROME) + .build(); + }); + + after(async () => await driver.quit()); + + it('Create a cookie', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // set a cookie on the current domain + await driver.manage().addCookie({ name: 'key', value: 'value' }); + }); + + it('Create cookies with sameSite', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // set a cookie on the current domain with sameSite 'Strict' (or) 'Lax' + await driver.manage().addCookie({ name: 'key', value: 'value', sameSite: 'Strict' }); + await driver.manage().addCookie({ name: 'key', value: 'value', sameSite: 'Lax' }); + }); + + it('Read cookie', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // set a cookie on the current domain + await driver.manage().addCookie({ name: 'foo', value: 'bar' }); + + // Get cookie details with named cookie 'foo' + await driver.manage().getCookie('foo').then(function(cookie) { + console.log('cookie details => ', cookie); + }); + }); + + it('Read all cookies', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // Add few cookies + await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); + await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); + + // Get all Available cookies + await driver.manage().getCookies().then(function(cookies) { + console.log('cookie details => ', cookies); + }); + }); + + it('Delete a cookie', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // Add few cookies + await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); + await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); + + // Delete a cookie with name 'test1' + await driver.manage().deleteCookie('test1'); + + // Get all Available cookies + await driver.manage().getCookies().then(function(cookies) { + console.log('cookie details => ', cookies); + }); + }); + + it('Delete all cookies', async function() { + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + + // Add few cookies + await driver.manage().addCookie({ name: 'test1', value: 'cookie1' }); + await driver.manage().addCookie({ name: 'test2', value: 'cookie2' }); + + // Delete all cookies + await driver.manage().deleteAllCookies(); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/interactions/interactionsIndex.spec.js b/examples/javascript/test/interactions/interactionsIndex.spec.js new file mode 100644 index 000000000000..8dea313d6620 --- /dev/null +++ b/examples/javascript/test/interactions/interactionsIndex.spec.js @@ -0,0 +1,27 @@ +const {Builder } = require('selenium-webdriver'); +const assert = require("node:assert"); + +describe('Interactions', function () { + let driver; + + before(async function () { + driver = new Builder() + .forBrowser('chrome') + .build(); + }); + + after(async () => await driver.quit()); + + it('Should be able to get title and current url', async function () { + const url = 'https://www.selenium.dev/'; + await driver.get(url); + + //Get Current title + let title = await driver.getTitle(); + assert.equal(title, "Selenium"); + + //Get Current url + let currentUrl = await driver.getCurrentUrl(); + assert.equal(currentUrl, url); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/interactions/navigation.spec.js b/examples/javascript/test/interactions/navigation.spec.js new file mode 100644 index 000000000000..07fbd4be7705 --- /dev/null +++ b/examples/javascript/test/interactions/navigation.spec.js @@ -0,0 +1,39 @@ +const {Builder } = require('selenium-webdriver'); +const assert = require("node:assert"); + +describe('Interactions - Navigation', function () { + let driver; + + before(async function () { + driver = new Builder() + .forBrowser('chrome') + .build(); + }); + + after(async () => await driver.quit()); + + it('Browser navigation test', async function () { + //Convenient + await driver.get('https://www.selenium.dev/'); + + //Longer way + await driver.navigate().to("https://www.selenium.dev/selenium/web/index.html"); + let title = await driver.getTitle(); + assert.equal(title, "Index of Available Pages"); + + //Back + await driver.navigate().back(); + title = await driver.getTitle(); + assert.equal(title, "Selenium"); + + //Forward + await driver.navigate().forward(); + title = await driver.getTitle(); + assert.equal(title, "Index of Available Pages"); + + //Refresh + await driver.navigate().refresh(); + title = await driver.getTitle(); + assert.equal(title, "Index of Available Pages"); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/interactions/windows.spec.js b/examples/javascript/test/interactions/windows.spec.js new file mode 100644 index 000000000000..6907c7d33ba8 --- /dev/null +++ b/examples/javascript/test/interactions/windows.spec.js @@ -0,0 +1,118 @@ +const {Builder, By} = require('selenium-webdriver'); +const chrome = require('selenium-webdriver/chrome'); +const assert = require("node:assert"); +let opts = new chrome.Options(); +opts.addArguments('--headless'); +let startIndex = 0 +let endIndex = 5 +let pdfMagicNumber = 'JVBER' +let imgMagicNumber = 'iVBOR' +let base64Code + +describe('Interactions - Windows', function () { + let driver; + before(async function () { + driver = await new Builder().forBrowser('chrome').setChromeOptions(opts).build(); + }); + + after(async () => await driver.quit()); + + it('Should be able to print page to pdf', async function () { + + await driver.get('https://www.selenium.dev/selenium/web/alerts.html'); + let base64 = await driver.printPage({pageRanges: ["1-2"]}); + // page can be saved as a PDF as below + // await fs.writeFileSync('./test.pdf', base64, 'base64'); + + base64Code = base64.slice(startIndex, endIndex) + assert.strictEqual(base64Code, pdfMagicNumber) + }); + + it('Should be able to get text using executeScript', async function () { + await driver.get('https://www.selenium.dev/selenium/web/javascriptPage.html'); + // Stores the header element + let header = await driver.findElement(By.css('h1')); + + // Executing JavaScript to capture innerText of header element + let text = await driver.executeScript('return arguments[0].innerText', header); + assert.strictEqual(text, `Type Stuff`) + }); + + it('Should be able to take Element Screenshot', async function () { + await driver.get('https://www.selenium.dev/selenium/web/javascriptPage.html'); + + let header = await driver.findElement(By.css('h1')); + // Captures the element screenshot + let encodedString = await header.takeScreenshot(true); + // save screenshot as below + // await fs.writeFileSync('./image.png', encodedString, 'base64'); + base64Code = encodedString.slice(startIndex, endIndex) + assert.strictEqual(base64Code, imgMagicNumber) + }); + + it('Should be able to takeScreenshot', async function () { + await driver.get('https://www.selenium.dev/selenium/web/javascriptPage.html'); + + // Captures the screenshot + let encodedString = await driver.takeScreenshot(); + // save screenshot as below + // await fs.writeFileSync('./image.png', encodedString, 'base64'); + base64Code = encodedString.slice(startIndex, endIndex) + assert.strictEqual(base64Code, imgMagicNumber) + }); + + it('Should be able to switch to newWindow and newTab and close', async function () { + await driver.get('https://www.selenium.dev/selenium/web/'); + const initialWindow = await driver.getAllWindowHandles(); + assert.strictEqual(initialWindow.length, 1) + + // Opens a new tab and switches to new tab + await driver.switchTo().newWindow('tab'); + const browserTabs = await driver.getAllWindowHandles(); + assert.strictEqual(browserTabs.length, 2) + + // Opens a new window and switches to new window + await driver.switchTo().newWindow('window'); + const windows = await driver.getAllWindowHandles(); + assert.strictEqual(windows.length, 3) + + //Close the tab or window + await driver.close(); + + //Switch back to the old tab or window + await driver.switchTo().window(windows[1]); + + const windowsAfterClose = await driver.getAllWindowHandles(); + assert.strictEqual(windowsAfterClose.length, 2); + }); + + it('Should be able to getWindow Size', async function () { + await driver.get('https://www.selenium.dev/selenium/web/'); + + // Access each dimension individually + const { width, height } = await driver.manage().window().getRect(); + + // Or store the dimensions and query them later + const rect = await driver.manage().window().getRect(); + const windowWidth = rect.width; + const windowHeight = rect.height; + + assert.ok(windowWidth>0); + assert.ok(windowHeight>0); + }); + + it('Should be able to getWindow position', async function () { + await driver.get('https://www.selenium.dev/selenium/web/'); + + // Access each dimension individually + const { x, y } = await driver.manage().window().getRect(); + + // Or store the dimensions and query them later + const rect = await driver.manage().window().getRect(); + const x1 = rect.x; + const y1 = rect.y; + + assert.ok(x1>=0); + assert.ok(y1>=0); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/resources/extensions/selenium-example.xpi b/examples/javascript/test/resources/extensions/selenium-example.xpi index 34b0ae3913f7..dca8e2e12312 100644 Binary files a/examples/javascript/test/resources/extensions/selenium-example.xpi and b/examples/javascript/test/resources/extensions/selenium-example.xpi differ diff --git a/examples/javascript/test/resources/extensions/selenium-example/manifest.json b/examples/javascript/test/resources/extensions/selenium-example/manifest.json index e938974a20b1..a8b4fec6e60f 100644 --- a/examples/javascript/test/resources/extensions/selenium-example/manifest.json +++ b/examples/javascript/test/resources/extensions/selenium-example/manifest.json @@ -1,17 +1,22 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "webextensions-selenium-example", - "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium" , + "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium", "version": "0.1", "content_scripts": [ { - "matches": ["https://*/*","http://*/*"], - "js": ["inject.js"] + "matches": [ + "https://*/*", + "http://*/*" + ], + "js": [ + "inject.js" + ] } ], - "applications": { - "gecko": { - "id": "webextensions-selenium-example@example.com" - } - } -} + "browser_specific_settings": { + "gecko": { + "id": "webextensions-selenium-example-v3@example.com" + } + } +} \ No newline at end of file diff --git a/examples/javascript/test/select/selectListTest.spec.js b/examples/javascript/test/select/selectListTest.spec.js index 4fe88d79cf9d..840ea2bd2935 100644 --- a/examples/javascript/test/select/selectListTest.spec.js +++ b/examples/javascript/test/select/selectListTest.spec.js @@ -1,81 +1,82 @@ -const {By, Browser} = require('selenium-webdriver') -const {suite} = require('selenium-webdriver/testing') + +const {By, Browser, Builder} = require('selenium-webdriver') const assert = require('assert/strict') const {Select} = require('selenium-webdriver') -suite(function (env) { - describe('Select Tests', async function () { - let driver - before(async function () { - driver = await env.builder().build() - await driver.get('https://www.selenium.dev/selenium/web/formPage.html') - }) +describe('Select Tests', async function () { + let driver - after(async () => await driver.quit()) + before(async function () { + driver = new Builder() + .forBrowser(Browser.FIREFOX) + .build() + await driver.get('https://www.selenium.dev/selenium/web/formPage.html') + }) - it('Select an option', async function () { - const selectElement = await driver.findElement(By.name('selectomatic')) - const select = new Select(selectElement) + after(async () => await driver.quit()) - const twoElement = await driver.findElement(By.css('option[value=two]')) - const fourElement = await driver.findElement(By.css('option[value=four]')) - const countElement = await driver.findElement(By.css("option[value='still learning how to count, apparently']")) + it('Select an option', async function () { + const selectElement = await driver.findElement(By.name('selectomatic')) + const select = new Select(selectElement) - await select.selectByVisibleText('Four') - assert.equal(true, await fourElement.isSelected()) + const twoElement = await driver.findElement(By.css('option[value=two]')) + const fourElement = await driver.findElement(By.css('option[value=four]')) + const countElement = await driver.findElement(By.css("option[value='still learning how to count, apparently']")) - await select.selectByValue('two') - assert.equal(true, await twoElement.isSelected()) + await select.selectByVisibleText('Four') + assert.equal(true, await fourElement.isSelected()) - await select.selectByIndex(3) - assert.equal(true, await countElement.isSelected()) - }) + await select.selectByValue('two') + assert.equal(true, await twoElement.isSelected()) - it('Select by multiple options', async function () { - const selectElement = await driver.findElement(By.name('multi')) - const select = await new Select(selectElement) - - const hamElement = await driver.findElement(By.css('option[value=ham]')) - const gravyElement = await driver.findElement(By.css("option[value='onion gravy']")) - const eggElement = await driver.findElement(By.css('option[value=eggs]')) - const sausageElement = await driver.findElement(By.css("option[value='sausages']")) - - const optionElements = await selectElement.findElements(By.css('option')) - const optionList = await select.getOptions() - assert.equal(optionList.length, optionElements.length) - for (const index in optionList) { - assert.equal(await optionList[index].getText(), await optionElements[index].getText()) - } - - const selectedOptionList = await select.getAllSelectedOptions() - const expectedSelection = [eggElement, sausageElement] - assert.equal(expectedSelection.length, selectedOptionList.length) - for (const index in selectedOptionList) { - assert.equal(await selectedOptionList[index].getText(), await expectedSelection[index].getText()) - } - - await select.selectByValue('ham') - await select.selectByValue('onion gravy') - assert.equal(true, await hamElement.isSelected()) - assert.equal(true, await gravyElement.isSelected()) - - await select.deselectByValue('eggs') - await select.deselectByValue('sausages') - assert.equal(false, await eggElement.isSelected()) - assert.equal(false, await sausageElement.isSelected()) - }) + await select.selectByIndex(3) + assert.equal(true, await countElement.isSelected()) + }) + + it('Select by multiple options', async function () { + const selectElement = await driver.findElement(By.name('multi')) + const select = await new Select(selectElement) + + const hamElement = await driver.findElement(By.css('option[value=ham]')) + const gravyElement = await driver.findElement(By.css("option[value='onion gravy']")) + const eggElement = await driver.findElement(By.css('option[value=eggs]')) + const sausageElement = await driver.findElement(By.css("option[value='sausages']")) + + const optionElements = await selectElement.findElements(By.css('option')) + const optionList = await select.getOptions() + assert.equal(optionList.length, optionElements.length) + for (const index in optionList) { + assert.equal(await optionList[index].getText(), await optionElements[index].getText()) + } + + const selectedOptionList = await select.getAllSelectedOptions() + const expectedSelection = [eggElement, sausageElement] + assert.equal(expectedSelection.length, selectedOptionList.length) + for (const index in selectedOptionList) { + assert.equal(await selectedOptionList[index].getText(), await expectedSelection[index].getText()) + } + + await select.selectByValue('ham') + await select.selectByValue('onion gravy') + assert.equal(true, await hamElement.isSelected()) + assert.equal(true, await gravyElement.isSelected()) + + await select.deselectByValue('eggs') + await select.deselectByValue('sausages') + assert.equal(false, await eggElement.isSelected()) + assert.equal(false, await sausageElement.isSelected()) + }) - it('Try selecting disabled option', async function () { - const selectElement = await driver.findElement(By.name('single_disabled')) - const select = await new Select(selectElement) + it('Try selecting disabled option', async function () { + const selectElement = await driver.findElement(By.name('single_disabled')) + const select = await new Select(selectElement) - await assert.rejects(async () => { - await select.selectByValue("disabled") - }, { - name: 'UnsupportedOperationError', - message: 'You may not select a disabled option' - }) + await assert.rejects(async () => { + await select.selectByValue("disabled") + }, { + name: 'UnsupportedOperationError', + message: 'You may not select a disabled option' }) }) -}, { browsers: [Browser.CHROME, Browser.FIREFOX]}) \ No newline at end of file +}) \ No newline at end of file diff --git a/examples/javascript/test/selenium_manager/usage.spec.js b/examples/javascript/test/selenium_manager/usage.spec.js new file mode 100644 index 000000000000..9ef17707aeb5 --- /dev/null +++ b/examples/javascript/test/selenium_manager/usage.spec.js @@ -0,0 +1,32 @@ +const Chrome = require('selenium-webdriver/chrome'); +const {Browser, Builder} = require("selenium-webdriver"); +const options = new Chrome.Options(); + +describe('Usage Test', function () { + it('Creates driver wit Selenium Manager', async function () { + + let driver = new Builder() + .forBrowser(Browser.CHROME) + .build(); + + await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + await driver.quit(); + }); + + // it('Creates driver with Selenium Manager', async function () { + // let driverPath = '/path/to/chromedriver'; + // let browserPath = '/path/to/chrome'; + + // options.setChromeBinaryPath(browserPath) + + // let service = new Chrome.ServiceBuilder().setPath(driverPath) + + // let driver = new Builder() + // .forBrowser(Browser.CHROME) + // .setChromeService(service) + // .build(); + + // await driver.get('https://www.selenium.dev/selenium/web/blank.html'); + // await driver.quit(); + // }); +}); diff --git a/examples/javascript/test/virtual_authenticator/virtualAuthenticator.spec.js b/examples/javascript/test/virtual_authenticator/virtualAuthenticator.spec.js index 43bf056c3c4e..7e6b5ea13340 100644 --- a/examples/javascript/test/virtual_authenticator/virtualAuthenticator.spec.js +++ b/examples/javascript/test/virtual_authenticator/virtualAuthenticator.spec.js @@ -1,197 +1,200 @@ -const { Builder, Browser} = require("selenium-webdriver"); + +const { Builder} = require("selenium-webdriver"); const { Credential, VirtualAuthenticatorOptions, Transport, Protocol } = require("selenium-webdriver/lib/virtual_authenticator"); -const { suite } = require('selenium-webdriver/testing'); const assert = require('assert') const { InvalidArgumentError } = require("selenium-webdriver/lib/error"); -suite(function(env) { - describe('Virtual authenticator', function() { - const BASE64_ENCODED_PK = - "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbBOu5Lhs4vpowbCnmCyLUpIE7JM9sm9QXzye2G+jr+Kr" + - "MsinWohEce47BFPJlTaDzHSvOW2eeunBO89ZcvvVc8RLz4qyQ8rO98xS1jtgqi1NcBPETDrtzthODu/gd0sjB2Tk3TLuBGV" + - "oPXt54a+Oo4JbBJ6h3s0+5eAfGplCbSNq6hN3Jh9YOTw5ZA6GCEy5l8zBaOgjXytd2v2OdSVoEDNiNQRkjJd2rmS2oi9AyQ" + - "FR3B7BrPSiDlCcITZFOWgLF5C31Wp/PSHwQhlnh7/6YhnE2y9tzsUvzx0wJXrBADW13+oMxrneDK3WGbxTNYgIi1PvSqXlq" + - "GjHtCK+R2QkXAgMBAAECggEAVc6bu7VAnP6v0gDOeX4razv4FX/adCao9ZsHZ+WPX8PQxtmWYqykH5CY4TSfsuizAgyPuQ0" + - "+j4Vjssr9VODLqFoanspT6YXsvaKanncUYbasNgUJnfnLnw3an2XpU2XdmXTNYckCPRX9nsAAURWT3/n9ljc/XYY22ecYxM" + - "8sDWnHu2uKZ1B7M3X60bQYL5T/lVXkKdD6xgSNLeP4AkRx0H4egaop68hoW8FIwmDPVWYVAvo8etzWCtibRXz5FcNld9MgD" + - "/Ai7ycKy4Q1KhX5GBFI79MVVaHkSQfxPHpr7/XcmpQOEAr+BMPon4s4vnKqAGdGB3j/E3d/+4F2swykoQKBgQD8hCsp6FIQ" + - "5umJlk9/j/nGsMl85LgLaNVYpWlPRKPc54YNumtvj5vx1BG+zMbT7qIE3nmUPTCHP7qb5ERZG4CdMCS6S64/qzZEqijLCqe" + - "pwj6j4fV5SyPWEcpxf6ehNdmcfgzVB3Wolfwh1ydhx/96L1jHJcTKchdJJzlfTvq8wwKBgQDeCnKws1t5GapfE1rmC/h4ol" + - "L2qZTth9oQmbrXYohVnoqNFslDa43ePZwL9Jmd9kYb0axOTNMmyrP0NTj41uCfgDS0cJnNTc63ojKjegxHIyYDKRZNVUR/d" + - "xAYB/vPfBYZUS7M89pO6LLsHhzS3qpu3/hppo/Uc/AM/r8PSflNHQKBgDnWgBh6OQncChPUlOLv9FMZPR1ZOfqLCYrjYEqi" + - "uzGm6iKM13zXFO4AGAxu1P/IAd5BovFcTpg79Z8tWqZaUUwvscnl+cRlj+mMXAmdqCeO8VASOmqM1ml667axeZDIR867ZG8" + - "K5V029Wg+4qtX5uFypNAAi6GfHkxIKrD04yOHAoGACdh4wXESi0oiDdkz3KOHPwIjn6BhZC7z8mx+pnJODU3cYukxv3WTct" + - "lUhAsyjJiQ/0bK1yX87ulqFVgO0Knmh+wNajrb9wiONAJTMICG7tiWJOm7fW5cfTJwWkBwYADmkfTRmHDvqzQSSvoC2S7aa" + - "9QulbC3C/qgGFNrcWgcT9kCgYAZTa1P9bFCDU7hJc2mHwJwAW7/FQKEJg8SL33KINpLwcR8fqaYOdAHWWz636osVEqosRrH" + - "zJOGpf9x2RSWzQJ+dq8+6fACgfFZOVpN644+sAHfNPAI/gnNKU5OfUv+eav8fBnzlf1A3y3GIkyMyzFN3DE7e0n/lyqxE4H" + - "BYGpI8g=="; - - const base64EncodedPK = - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q" + - "hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU" + - "RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB"; - - let options; - let driver; - - before(async function() { - options = new VirtualAuthenticatorOptions(); - driver = await new Builder().forBrowser('chrome').build(); - }); - - after(() => driver.quit()); - - function arraysEqual(array1, array2) { - return (array1.length === array2.length && - array1.every((item) => array2.includes(item)) && - array2.every((item) => array1.includes(item))); - } - - it('Register a virtual authenticator', async function() { - options.setProtocol(Protocol['U2F']); - options.setHasResidentKey(false); - - // Register a virtual authenticator - await driver.addVirtualAuthenticator(options); - let credentialList = await driver.getCredentials(); - - assert.equal(0, credentialList.length); - }); - - it('Remove authenticator', async function() { - await driver.addVirtualAuthenticator(options); - await driver.removeVirtualAuthenticator(); - - // Since the authenticator was removed, any operation using it will throw an error - try { - await driver.getCredentials() - } - catch (e) { - if (e instanceof InvalidArgumentError) { - assert(true) - } - else { - assert(false) - } - } - }); - - it('Createa and add residential key', async function() { - options.setProtocol(Protocol['CTAP2']); - options.setHasResidentKey(true); - options.setHasUserVerification(true); - options.setIsUserVerified(true); - - await driver.addVirtualAuthenticator(options); - - let residentCredential = new Credential().createResidentCredential( - new Uint8Array([1, 2, 3, 4]), - 'localhost', - new Uint8Array([1]), - Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), - 0); - - await driver.addCredential(residentCredential); - let credentialList = await driver.getCredentials(); - assert.equal(1, credentialList.length); - - let credential_id = credentialList[0].id(); - let test_id = new Uint8Array([1, 2, 3, 4]); - - assert(arraysEqual(credential_id, test_id)); - }); - - it('Add resident credential not supported when authenticator uses U2F protocol', async function() { - options.setProtocol(Protocol['U2F']); - options.setHasResidentKey(true); - - await driver.addVirtualAuthenticator(options); - - let credential = new Credential().createResidentCredential( - new Uint8Array([1, 2, 3, 4]), - 'localhost', - new Uint8Array([1]), - Buffer.from(base64EncodedPK, 'base64').toString('binary'), - 0); - - try { - await driver.addCredential(credential) - } - catch (e) { - if (e instanceof InvalidArgumentError) { - assert(true) - } - else { - assert(false) - } - } - }); - - it('Create and add non residential key', async function() { - options.setProtocol(Protocol['U2F']); - options.setHasResidentKey(false); - - await driver.addVirtualAuthenticator(options); - - let nonResidentCredential = new Credential().createNonResidentCredential( - new Uint8Array([1, 2, 3, 4]), - 'localhost', - Buffer.from(base64EncodedPK, 'base64').toString('binary'), - 0); - - await driver.addCredential(nonResidentCredential); - - let credentialList = await driver.getCredentials(); - assert.equal(1, credentialList.length); - - let credential_id = credentialList[0].id(); - let test_id = new Uint8Array([1, 2, 3, 4]); - - assert(arraysEqual(credential_id, test_id)); - }); - - it('Get credential', async function() { - options.setProtocol(Protocol['CTAP2']); - options.setHasResidentKey(true); - options.setHasUserVerification(true); - options.setIsUserVerified(true); - - await driver.addVirtualAuthenticator(options); - - let residentCredential = new Credential().createResidentCredential( - new Uint8Array([1, 2, 3, 4]), - 'localhost', - new Uint8Array([1]), - Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), - 0); - - await driver.addCredential(residentCredential); - - let credentialList = await driver.getCredentials(); - assert.equal(1, credentialList.length); - - let credential_id = credentialList[0].id(); - let test_id = new Uint8Array([1, 2, 3, 4]); - - assert(arraysEqual(credential_id, test_id)); - assert.equal(BASE64_ENCODED_PK, Buffer.from(credentialList[0].privateKey(), 'binary').toString('base64')); - }); - - it('Remove all credentials', async function() { - await driver.addVirtualAuthenticator(options); - - let nonResidentCredential = new Credential().createNonResidentCredential( - new Uint8Array([1, 2, 3, 4]), - 'localhost', - Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), - 0); - - await driver.addCredential(nonResidentCredential); - driver.removeAllCredentials(); - - let credentialList = await driver.getCredentials(); - assert.equal(0, credentialList.length); - }); - }); -}, { browsers: [Browser.CHROME]}); \ No newline at end of file +describe('Virtual authenticator', function() { + const BASE64_ENCODED_PK = + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbBOu5Lhs4vpowbCnmCyLUpIE7JM9sm9QXzye2G+jr+Kr" + + "MsinWohEce47BFPJlTaDzHSvOW2eeunBO89ZcvvVc8RLz4qyQ8rO98xS1jtgqi1NcBPETDrtzthODu/gd0sjB2Tk3TLuBGV" + + "oPXt54a+Oo4JbBJ6h3s0+5eAfGplCbSNq6hN3Jh9YOTw5ZA6GCEy5l8zBaOgjXytd2v2OdSVoEDNiNQRkjJd2rmS2oi9AyQ" + + "FR3B7BrPSiDlCcITZFOWgLF5C31Wp/PSHwQhlnh7/6YhnE2y9tzsUvzx0wJXrBADW13+oMxrneDK3WGbxTNYgIi1PvSqXlq" + + "GjHtCK+R2QkXAgMBAAECggEAVc6bu7VAnP6v0gDOeX4razv4FX/adCao9ZsHZ+WPX8PQxtmWYqykH5CY4TSfsuizAgyPuQ0" + + "+j4Vjssr9VODLqFoanspT6YXsvaKanncUYbasNgUJnfnLnw3an2XpU2XdmXTNYckCPRX9nsAAURWT3/n9ljc/XYY22ecYxM" + + "8sDWnHu2uKZ1B7M3X60bQYL5T/lVXkKdD6xgSNLeP4AkRx0H4egaop68hoW8FIwmDPVWYVAvo8etzWCtibRXz5FcNld9MgD" + + "/Ai7ycKy4Q1KhX5GBFI79MVVaHkSQfxPHpr7/XcmpQOEAr+BMPon4s4vnKqAGdGB3j/E3d/+4F2swykoQKBgQD8hCsp6FIQ" + + "5umJlk9/j/nGsMl85LgLaNVYpWlPRKPc54YNumtvj5vx1BG+zMbT7qIE3nmUPTCHP7qb5ERZG4CdMCS6S64/qzZEqijLCqe" + + "pwj6j4fV5SyPWEcpxf6ehNdmcfgzVB3Wolfwh1ydhx/96L1jHJcTKchdJJzlfTvq8wwKBgQDeCnKws1t5GapfE1rmC/h4ol" + + "L2qZTth9oQmbrXYohVnoqNFslDa43ePZwL9Jmd9kYb0axOTNMmyrP0NTj41uCfgDS0cJnNTc63ojKjegxHIyYDKRZNVUR/d" + + "xAYB/vPfBYZUS7M89pO6LLsHhzS3qpu3/hppo/Uc/AM/r8PSflNHQKBgDnWgBh6OQncChPUlOLv9FMZPR1ZOfqLCYrjYEqi" + + "uzGm6iKM13zXFO4AGAxu1P/IAd5BovFcTpg79Z8tWqZaUUwvscnl+cRlj+mMXAmdqCeO8VASOmqM1ml667axeZDIR867ZG8" + + "K5V029Wg+4qtX5uFypNAAi6GfHkxIKrD04yOHAoGACdh4wXESi0oiDdkz3KOHPwIjn6BhZC7z8mx+pnJODU3cYukxv3WTct" + + "lUhAsyjJiQ/0bK1yX87ulqFVgO0Knmh+wNajrb9wiONAJTMICG7tiWJOm7fW5cfTJwWkBwYADmkfTRmHDvqzQSSvoC2S7aa" + + "9QulbC3C/qgGFNrcWgcT9kCgYAZTa1P9bFCDU7hJc2mHwJwAW7/FQKEJg8SL33KINpLwcR8fqaYOdAHWWz636osVEqosRrH" + + "zJOGpf9x2RSWzQJ+dq8+6fACgfFZOVpN644+sAHfNPAI/gnNKU5OfUv+eav8fBnzlf1A3y3GIkyMyzFN3DE7e0n/lyqxE4H" + + "BYGpI8g=="; + + const base64EncodedPK = + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q" + + "hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU" + + "RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB"; + + let options; + let driver; + + before(async function() { + options = new VirtualAuthenticatorOptions(); + driver = await new Builder().forBrowser('chrome').build(); + }); + + after(async() => await driver.quit()); + + function arraysEqual(array1, array2) { + return (array1.length === array2.length && + array1.every((item) => array2.includes(item)) && + array2.every((item) => array1.includes(item))); + } + + it('Register a virtual authenticator', async function() { + options.setProtocol(Protocol['U2F']); + options.setHasResidentKey(false); + + // Register a virtual authenticator + await driver.addVirtualAuthenticator(options); + let credentialList = await driver.getCredentials(); + + assert.equal(0, credentialList.length); + }); + + it('Remove authenticator', async function() { + await driver.addVirtualAuthenticator(options); + await driver.removeVirtualAuthenticator(); + + // Since the authenticator was removed, any operation using it will throw an error + try { + await driver.getCredentials() + } + catch (e) { + if (e instanceof InvalidArgumentError) { + assert(true) + } + else { + assert(false) + } + } + }); + + it('Createa and add residential key', async function() { + options.setProtocol(Protocol['CTAP2']); + options.setHasResidentKey(true); + options.setHasUserVerification(true); + options.setIsUserVerified(true); + + await driver.addVirtualAuthenticator(options); + + let residentCredential = new Credential().createResidentCredential( + new Uint8Array([1, 2, 3, 4]), + 'localhost', + new Uint8Array([1]), + Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), + 0); + + await driver.addCredential(residentCredential); + let credentialList = await driver.getCredentials(); + assert.equal(1, credentialList.length); + + let credential_id = credentialList[0].id(); + let test_id = new Uint8Array([1, 2, 3, 4]); + + assert(arraysEqual(credential_id, test_id)); + }); + + it('Add resident credential not supported when authenticator uses U2F protocol', async function() { + options.setProtocol(Protocol['U2F']); + options.setHasResidentKey(true); + + await driver.addVirtualAuthenticator(options); + + let credential = new Credential().createResidentCredential( + new Uint8Array([1, 2, 3, 4]), + 'localhost', + new Uint8Array([1]), + Buffer.from(base64EncodedPK, 'base64').toString('binary'), + 0); + + try { + await driver.addCredential(credential) + } + catch (e) { + if (e instanceof InvalidArgumentError) { + assert(true) + } + else { + assert(false) + } + } + }); + + it('Create and add non residential key', async function() { + options.setProtocol(Protocol['U2F']); + options.setHasResidentKey(false); + + await driver.addVirtualAuthenticator(options); + + let nonResidentCredential = new Credential().createNonResidentCredential( + new Uint8Array([1, 2, 3, 4]), + 'localhost', + Buffer.from(base64EncodedPK, 'base64').toString('binary'), + 0); + + await driver.addCredential(nonResidentCredential); + + let credentialList = await driver.getCredentials(); + assert.equal(1, credentialList.length); + + let credential_id = credentialList[0].id(); + let test_id = new Uint8Array([1, 2, 3, 4]); + + assert(arraysEqual(credential_id, test_id)); + }); + + it('Get credential', async function() { + options.setProtocol(Protocol['CTAP2']); + options.setHasResidentKey(true); + options.setHasUserVerification(true); + options.setIsUserVerified(true); + + await driver.addVirtualAuthenticator(options); + + let residentCredential = new Credential().createResidentCredential( + new Uint8Array([1, 2, 3, 4]), + 'localhost', + new Uint8Array([1]), + Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), + 0); + + await driver.addCredential(residentCredential); + + let credentialList = await driver.getCredentials(); + assert.equal(1, credentialList.length); + + let credential_id = credentialList[0].id(); + let test_id = new Uint8Array([1, 2, 3, 4]); + + assert(arraysEqual(credential_id, test_id)); + assert.equal(BASE64_ENCODED_PK, Buffer.from(credentialList[0].privateKey(), 'binary').toString('base64')); + }); + + it('Remove all credentials', async function() { + await driver.addVirtualAuthenticator(options); + + let nonResidentCredential = new Credential().createNonResidentCredential( + new Uint8Array([1, 2, 3, 4]), + 'localhost', + Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'), + 0); + + await driver.addCredential(nonResidentCredential); + await driver.removeAllCredentials(); + + let credentialList = await driver.getCredentials(); + assert.equal(0, credentialList.length); + }); + + it('Set is user verified', async function() { + options.setIsUserVerified(true); + assert.equal(options.getIsUserVerified(), true); + }); +}); \ No newline at end of file diff --git a/examples/javascript/test/virtual_authenticator/virtualAuthenticatorOptions.spec.js b/examples/javascript/test/virtual_authenticator/virtualAuthenticatorOptions.spec.js index c44f4c234f2b..9848db83eed8 100644 --- a/examples/javascript/test/virtual_authenticator/virtualAuthenticatorOptions.spec.js +++ b/examples/javascript/test/virtual_authenticator/virtualAuthenticatorOptions.spec.js @@ -1,29 +1,25 @@ const {VirtualAuthenticatorOptions, Transport, Protocol} = require("selenium-webdriver/lib/virtual_authenticator"); -const {suite} = require('selenium-webdriver/testing'); const assert = require('assert') -const {Browser} = require("selenium-webdriver"); -suite(function () { - describe('Virtual authenticator options', function () { - let options; - it('Virtual options', async function () { - options = new VirtualAuthenticatorOptions(); - options.setIsUserVerified(true); - options.setHasUserVerification(true); - options.setIsUserConsenting(true); - options.setTransport(Transport['USB']); - options.setProtocol(Protocol['U2F']); - options.setHasResidentKey(false); +describe('Virtual authenticator options', function () { + let options; - assert(Object.keys(options).length === 6); - }); + it('Virtual options', async function () { + options = new VirtualAuthenticatorOptions(); + options.setIsUserVerified(true); + options.setHasUserVerification(true); + options.setIsUserConsenting(true); + options.setTransport(Transport['USB']); + options.setProtocol(Protocol['U2F']); + options.setHasResidentKey(false); - it('User verified', async function () { - options.setIsUserVerified(true); + assert(Object.keys(options).length === 6); + }); - assert(options.toDict()['isUserVerified']); - }); + it('User verified', async function () { + options.setIsUserVerified(true); + assert(options.toDict()['isUserVerified']); }); -}, { browsers: [Browser.CHROME]}); \ No newline at end of file +}); \ No newline at end of file diff --git a/examples/javascript/test/waits/waits.spec.js b/examples/javascript/test/waits/waits.spec.js index 59a7d91677fb..dbc3c83e93f2 100644 --- a/examples/javascript/test/waits/waits.spec.js +++ b/examples/javascript/test/waits/waits.spec.js @@ -1,55 +1,56 @@ -const { suite } = require('selenium-webdriver/testing'); -const { By, Browser, until } = require('selenium-webdriver'); + +const { By, Browser, until, Builder} = require('selenium-webdriver'); const assert = require("node:assert"); -suite(function (env) { - describe('Element Interactions', function () { - let driver; - before(async function () { - driver = await env.builder().build(); - }); +describe('Waits', function () { + let driver; - after(async () => await driver.quit()); + before(async function () { + driver = new Builder() + .forBrowser(Browser.CHROME) + .build(); + }); - it('fail', async function () { - await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); - await driver.findElement(By.id("adder")).click(); + after(async () => await driver.quit()); - await assert.rejects(async () => { - await driver.findElement(By.id("box0")) - }, - Error - ) - }); + it('fail', async function () { + await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); + await driver.findElement(By.id("adder")).click(); - it('sleep', async function () { - await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); - await driver.findElement(By.id("adder")).click(); + await assert.rejects(async () => { + await driver.findElement(By.id("box0")) + }, + Error + ) + }); - await driver.sleep(2000); - let added = await driver.findElement(By.id("box0")); + it('sleep', async function () { + await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); + await driver.findElement(By.id("adder")).click(); - assert.equal(await added.getAttribute('class'), "redbox") - }); + await driver.sleep(2000); + let added = await driver.findElement(By.id("box0")); - it('implicit', async function () { - await driver.manage().setTimeouts({ implicit: 2000 }); - await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); - await driver.findElement(By.id("adder")).click(); + assert.equal(await added.getAttribute('class'), "redbox") + }); - let added = await driver.findElement(By.id("box0")); + it('implicit', async function () { + await driver.manage().setTimeouts({ implicit: 2000 }); + await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); + await driver.findElement(By.id("adder")).click(); - assert.equal(await added.getAttribute('class'), "redbox") - }); + let added = await driver.findElement(By.id("box0")); - it('explicit', async function () { - await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); - let revealed = await driver.findElement(By.id("revealed")); - await driver.findElement(By.id("reveal")).click(); - await driver.wait(until.elementIsVisible(revealed), 2000); - await revealed.sendKeys("Displayed"); - assert.equal(await revealed.getAttribute("value"), "Displayed") - }) + assert.equal(await added.getAttribute('class'), "redbox") }); -}, { browsers: [Browser.CHROME] }); \ No newline at end of file + + it('explicit', async function () { + await driver.get('https://www.selenium.dev/selenium/web/dynamic.html'); + let revealed = await driver.findElement(By.id("revealed")); + await driver.findElement(By.id("reveal")).click(); + await driver.wait(until.elementIsVisible(revealed), 2000); + await revealed.sendKeys("Displayed"); + assert.equal(await revealed.getAttribute("value"), "Displayed") + }) +}); \ No newline at end of file diff --git a/examples/kotlin/pom.xml b/examples/kotlin/pom.xml index 3a3dbf1060df..bc714bc589cb 100644 --- a/examples/kotlin/pom.xml +++ b/examples/kotlin/pom.xml @@ -9,18 +9,17 @@ 1.0.0 - 1.7.10 + 2.1.20 - 1.7.36 - 1.2.11 + 2.0.17 + 1.5.18 - 5.9.0 - 5.2.3 + 5.12.1 - 3.0.0-M7 + 3.5.3 - 1.8 - 4.15.0 + 11 + 4.31.0 ${java.version} ${java.version} diff --git a/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/ActionsTest.kt b/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/ActionsTest.kt index 4ba3d47e178b..435b006a5ffa 100644 --- a/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/ActionsTest.kt +++ b/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/ActionsTest.kt @@ -47,7 +47,7 @@ class ActionsTest : BaseTest() { (driver as RemoteWebDriver).resetInputState() actions.sendKeys("a").perform() - Assertions.assertEquals("A", clickable.getAttribute("value").get(0).toString()) - Assertions.assertEquals("a", clickable.getAttribute("value").get(1).toString()) + Assertions.assertEquals("A", clickable.getAttribute("value")!!.get(0).toString()) + Assertions.assertEquals("a", clickable.getAttribute("value")!!.get(1).toString()) } } diff --git a/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/MouseTest.kt b/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/MouseTest.kt index 8281943430d8..03887f71d769 100644 --- a/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/MouseTest.kt +++ b/examples/kotlin/src/test/kotlin/dev/selenium/actions_api/MouseTest.kt @@ -37,7 +37,7 @@ class MouseTest : BaseTest() { .click(clickable) .perform() - Assertions.assertTrue(driver.getCurrentUrl().contains("resultPage.html")) + Assertions.assertTrue(driver.getCurrentUrl()!!.contains("resultPage.html")) } @Test diff --git a/examples/python/README.md b/examples/python/README.md index 7b0abd2b0ba5..9650c07cbb5e 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -1,30 +1,61 @@ -# Running all tests from Selenium python example +# Running tests from Selenium Python examples -Follow these steps to run all test example from selenium python - -1. Clone this repository +#### 1. Clone this repository ``` git clone https://github.com/SeleniumHQ/seleniumhq.github.io.git ``` -2. Navigate to `python` directory +#### 2. Navigate to `python` directory ``` cd seleniumhq.github.io/examples/python ``` -3. Install dependencies using pip +#### 3. Create a virtual environment + +- On Windows: + +``` +py -m venv venv +venv\Scripts\activate +``` + +- On Linux/Mac: + +``` +python3 -m venv venv +source venv/bin/activate +``` + +#### 4. Install dependencies: ``` pip install -r requirements.txt ``` -> if you are on a different python version, for example python3.x you may have to replace `pip` with `pip3` -4. Run all all tests +> for help, see: https://packaging.python.org/en/latest/tutorials/installing-packages + +#### 5. Run tests + +- Run all tests with the default Python interpreter: ``` pytest ``` -> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers +- Run all tests with every installed/supported Python interpreter: + +``` +tox +``` + +> Please have some patience - If you are doing it for the first time, it will take a little while to download the browser drivers + +- Run a specific example: + +``` +pytest path/to/test_script.py +``` + +> Make sure to replace `path/to/test_script.py` with the path and name of the example you want to run diff --git a/examples/python/requirements.txt b/examples/python/requirements.txt index f17a9587cecc..b8b5db656ee3 100644 --- a/examples/python/requirements.txt +++ b/examples/python/requirements.txt @@ -1,5 +1,8 @@ -selenium==4.15.2 -pytest -trio -pytest-trio -flake8 +selenium==4.31.0 +pytest==8.3.5 +trio==0.30.0 +pytest-trio==0.8.0 +pytest-rerunfailures==15.0 +flake8==7.2.0 +requests==2.32.3 +tox==4.25.0 diff --git a/examples/python/tests/actions_api/test_mouse.py b/examples/python/tests/actions_api/test_mouse.py index cc114389ccf4..9df04fd599f7 100644 --- a/examples/python/tests/actions_api/test_mouse.py +++ b/examples/python/tests/actions_api/test_mouse.py @@ -1,17 +1,19 @@ +import pytest from time import sleep - from selenium.webdriver import ActionChains from selenium.webdriver.common.actions.action_builder import ActionBuilder from selenium.webdriver.common.actions.mouse_button import MouseButton from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC def test_click_and_hold(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') clickable = driver.find_element(By.ID, "clickable") - ActionChains(driver)\ - .click_and_hold(clickable)\ + ActionChains(driver) \ + .click_and_hold(clickable) \ .perform() sleep(0.5) @@ -22,8 +24,8 @@ def test_click_and_release(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') clickable = driver.find_element(By.ID, "click") - ActionChains(driver)\ - .click(clickable)\ + ActionChains(driver) \ + .click(clickable) \ .perform() assert "resultPage.html" in driver.current_url @@ -33,8 +35,8 @@ def test_right_click(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') clickable = driver.find_element(By.ID, "clickable") - ActionChains(driver)\ - .context_click(clickable)\ + ActionChains(driver) \ + .context_click(clickable) \ .perform() sleep(0.5) @@ -72,8 +74,8 @@ def test_double_click(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') clickable = driver.find_element(By.ID, "clickable") - ActionChains(driver)\ - .double_click(clickable)\ + ActionChains(driver) \ + .double_click(clickable) \ .perform() assert driver.find_element(By.ID, "click-status").text == "double-clicked" @@ -83,8 +85,8 @@ def test_hover(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') hoverable = driver.find_element(By.ID, "hover") - ActionChains(driver)\ - .move_to_element(hoverable)\ + ActionChains(driver) \ + .move_to_element(hoverable) \ .perform() assert driver.find_element(By.ID, "move-status").text == "hovered" @@ -94,8 +96,8 @@ def test_move_by_offset_from_element(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') mouse_tracker = driver.find_element(By.ID, "mouse-tracker") - ActionChains(driver)\ - .move_to_element_with_offset(mouse_tracker, 8, 0)\ + ActionChains(driver) \ + .move_to_element_with_offset(mouse_tracker, 8, 0) \ .perform() coordinates = driver.find_element(By.ID, "relative-location").text.split(", ") @@ -104,7 +106,7 @@ def test_move_by_offset_from_element(driver): def test_move_by_offset_from_viewport_origin_ab(driver): driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') - + WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "absolute-location"))) action = ActionBuilder(driver) action.pointer_action.move_to_location(8, 0) action.perform() @@ -121,8 +123,8 @@ def test_move_by_offset_from_current_pointer_ab(driver): action.pointer_action.move_to_location(6, 3) action.perform() - ActionChains(driver)\ - .move_by_offset( 13, 15)\ + ActionChains(driver) \ + .move_by_offset(13, 15) \ .perform() coordinates = driver.find_element(By.ID, "absolute-location").text.split(", ") @@ -136,8 +138,8 @@ def test_drag_and_drop_onto_element(driver): draggable = driver.find_element(By.ID, "draggable") droppable = driver.find_element(By.ID, "droppable") - ActionChains(driver)\ - .drag_and_drop(draggable, droppable)\ + ActionChains(driver) \ + .drag_and_drop(draggable, droppable) \ .perform() assert driver.find_element(By.ID, "drop-status").text == "dropped" @@ -149,15 +151,8 @@ def test_drag_and_drop_by_offset(driver): draggable = driver.find_element(By.ID, "draggable") start = draggable.location finish = driver.find_element(By.ID, "droppable").location - ActionChains(driver)\ - .drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y'])\ + ActionChains(driver) \ + .drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y']) \ .perform() assert driver.find_element(By.ID, "drop-status").text == "dropped" - - - - - - - diff --git a/examples/python/tests/bidi/cdp/test_cdp.py b/examples/python/tests/bidi/cdp/test_cdp.py new file mode 100644 index 000000000000..97664812f8fd --- /dev/null +++ b/examples/python/tests/bidi/cdp/test_cdp.py @@ -0,0 +1,12 @@ +def test_set_cookie(driver): + cookie = {'name': 'cheese', + 'value': 'gouda', + 'domain': 'www.selenium.dev', + 'secure': True} + + driver.execute_cdp_cmd('Network.setCookie', cookie) + + driver.get('https://www.selenium.dev/') + cheese = driver.get_cookie(cookie['name']) + + assert cheese['value'] == 'gouda' diff --git a/examples/python/tests/bidirectional/chrome_devtools/test_bidi_api.py b/examples/python/tests/bidi/cdp/test_logs.py similarity index 58% rename from examples/python/tests/bidirectional/chrome_devtools/test_bidi_api.py rename to examples/python/tests/bidi/cdp/test_logs.py index b7e7a89b728a..573ead1e578a 100644 --- a/examples/python/tests/bidirectional/chrome_devtools/test_bidi_api.py +++ b/examples/python/tests/bidi/cdp/test_logs.py @@ -4,26 +4,12 @@ from selenium.webdriver.common.log import Log -@pytest.mark.trio -async def test_mutation(driver): - async with driver.bidi_connection() as session: - log = Log(driver, session) - - async with log.mutation_events() as event: - driver.get('https://www.selenium.dev/selenium/web/dynamic.html') - driver.find_element(By.ID, "reveal").click() - - assert event["element"] == driver.find_element(By.ID, "revealed") - - @pytest.mark.trio async def test_console_log(driver): driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') async with driver.bidi_connection() as session: - log = Log(driver, session) - - async with log.add_listener(Console.ALL) as messages: + async with Log(driver, session).add_listener(Console.ALL) as messages: driver.find_element(by=By.ID, value='consoleLog').click() assert messages["message"] == "Hello, world!" @@ -34,9 +20,7 @@ async def test_js_error(driver): driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') async with driver.bidi_connection() as session: - log = Log(driver, session) - - async with log.add_js_error_listener() as messages: + async with Log(driver, session).add_js_error_listener() as messages: driver.find_element(by=By.ID, value='jsException').click() assert "Error: Not working" in messages.exception_details.exception.description diff --git a/examples/python/tests/bidirectional/chrome_devtools/test_cdp_api.py b/examples/python/tests/bidi/cdp/test_network.py similarity index 96% rename from examples/python/tests/bidirectional/chrome_devtools/test_cdp_api.py rename to examples/python/tests/bidi/cdp/test_network.py index ff8a639b78d1..c8fe2d4011c9 100644 --- a/examples/python/tests/bidirectional/chrome_devtools/test_cdp_api.py +++ b/examples/python/tests/bidi/cdp/test_network.py @@ -2,26 +2,22 @@ import pytest from selenium.webdriver.common.by import By -from selenium.webdriver.common.devtools.v118.network import Headers +from selenium.webdriver.common.devtools.v134.network import Headers @pytest.mark.trio -async def test_set_cookie(driver): +async def test_basic_auth(driver): async with driver.bidi_connection() as connection: - execution = connection.devtools.network.set_cookie( - name="cheese", - value="gouda", - domain="www.selenium.dev", - secure=True - ) - - await connection.session.execute(execution) + await connection.session.execute(connection.devtools.network.enable()) - driver.get("https://www.selenium.dev/") - cheese = driver.get_cookie("cheese") + credentials = base64.b64encode("admin:admin".encode()).decode() + auth = {'authorization': 'Basic ' + credentials} + await connection.session.execute(connection.devtools.network.set_extra_http_headers(Headers(auth))) - assert cheese["value"] == "gouda" + driver.get('https://the-internet.herokuapp.com/basic_auth') + success = driver.find_element(by=By.TAG_NAME, value='p') + assert success.text == 'Congratulations! You must have the proper credentials.' @pytest.mark.trio async def test_performance(driver): @@ -29,7 +25,6 @@ async def test_performance(driver): async with driver.bidi_connection() as connection: await connection.session.execute(connection.devtools.performance.enable()) - metric_list = await connection.session.execute(connection.devtools.performance.get_metrics()) metrics = {metric.name: metric.value for metric in metric_list} @@ -37,18 +32,18 @@ async def test_performance(driver): assert metrics["DevToolsCommandDuration"] > 0 assert metrics["Frames"] == 12 - @pytest.mark.trio -async def test_basic_auth(driver): +async def test_set_cookie(driver): async with driver.bidi_connection() as connection: - await connection.session.execute(connection.devtools.network.enable()) - - credentials = base64.b64encode("admin:admin".encode()).decode() - auth = {'authorization': 'Basic ' + credentials} - - await connection.session.execute(connection.devtools.network.set_extra_http_headers(Headers(auth))) + execution = connection.devtools.network.set_cookie( + name="cheese", + value="gouda", + domain="www.selenium.dev", + secure=True + ) + await connection.session.execute(execution) - driver.get('https://the-internet.herokuapp.com/basic_auth') + driver.get("https://www.selenium.dev/") + cheese = driver.get_cookie("cheese") - success = driver.find_element(by=By.TAG_NAME, value='p') - assert success.text == 'Congratulations! You must have the proper credentials.' + assert cheese["value"] == "gouda" diff --git a/examples/python/tests/bidi/cdp/test_script.py b/examples/python/tests/bidi/cdp/test_script.py new file mode 100644 index 000000000000..9c837c75e227 --- /dev/null +++ b/examples/python/tests/bidi/cdp/test_script.py @@ -0,0 +1,16 @@ +import pytest +import trio +from selenium.webdriver.common.by import By +from selenium.webdriver.common.log import Log +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +@pytest.mark.trio +async def test_mutation(driver): + async with driver.bidi_connection() as session: + async with Log(driver, session).mutation_events() as event: + await trio.to_thread.run_sync(lambda: driver.get('https://www.selenium.dev/selenium/web/dynamic.html')) + await trio.to_thread.run_sync(lambda: WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "reveal")))) + await trio.to_thread.run_sync(lambda: driver.find_element(By.ID, "reveal").click()) + + assert event["element"] == driver.find_element(By.ID, "revealed") diff --git a/examples/python/tests/bidi/test_bidi_logging.py b/examples/python/tests/bidi/test_bidi_logging.py new file mode 100644 index 000000000000..c2269ba28379 --- /dev/null +++ b/examples/python/tests/bidi/test_bidi_logging.py @@ -0,0 +1,51 @@ +import pytest +from selenium.webdriver.common.by import By +from selenium.webdriver.support.wait import WebDriverWait + + +@pytest.mark.driver_type("bidi") +def test_add_console_log_handler(driver): + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + log_entries = [] + + driver.script.add_console_message_handler(log_entries.append) + + driver.find_element(By.ID, "consoleLog").click() + WebDriverWait(driver, 5).until(lambda _: log_entries) + assert log_entries[0].text == "Hello, world!" + + +@pytest.mark.driver_type("bidi") +def test_remove_console_log_handler(driver): + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + log_entries = [] + + id = driver.script.add_console_message_handler(log_entries.append) + driver.script.remove_console_message_handler(id) + + driver.find_element(By.ID, "consoleLog").click() + assert len(log_entries) == 0 + + +@pytest.mark.driver_type("bidi") +def test_add_js_exception_handler(driver): + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + log_entries = [] + + driver.script.add_javascript_error_handler(log_entries.append) + + driver.find_element(By.ID, "jsException").click() + WebDriverWait(driver, 5).until(lambda _: log_entries) + assert log_entries[0].text == "Error: Not working" + + +@pytest.mark.driver_type("bidi") +def test_remove_js_exception_handler(driver): + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + log_entries = [] + + id = driver.script.add_javascript_error_handler(log_entries.append) + driver.script.remove_javascript_error_handler(id) + + driver.find_element(By.ID, "consoleLog").click() + assert len(log_entries) == 0 diff --git a/examples/python/tests/bidirectional/chrome_devtools/test_cdp_endpoint.py b/examples/python/tests/bidirectional/chrome_devtools/test_cdp_endpoint.py deleted file mode 100644 index 85f3b295d439..000000000000 --- a/examples/python/tests/bidirectional/chrome_devtools/test_cdp_endpoint.py +++ /dev/null @@ -1,44 +0,0 @@ -import base64 - -from selenium.webdriver.common.by import By - - -def test_set_cookie(driver): - cookie = {'name': 'cheese', - 'value': 'gouda', - 'domain': 'www.selenium.dev', - 'secure': True} - - driver.execute_cdp_cmd('Network.setCookie', cookie) - - driver.get('https://www.selenium.dev/') - cheese = driver.get_cookie(cookie['name']) - - assert cheese['value'] == 'gouda' - - -def test_performance(driver): - driver.get('https://www.selenium.dev/selenium/web/frameset.html') - - driver.execute_cdp_cmd('Performance.enable', {}) - - metric_list = driver.execute_cdp_cmd('Performance.getMetrics', {})["metrics"] - - metrics = {metric["name"]: metric["value"] for metric in metric_list} - - assert metrics["DevToolsCommandDuration"] > 0 - assert metrics["Frames"] == 12 - - -def test_basic_auth(driver): - driver.execute_cdp_cmd("Network.enable", {}) - - credentials = base64.b64encode("admin:admin".encode()).decode() - headers = {'headers': {'authorization': 'Basic ' + credentials}} - - driver.execute_cdp_cmd('Network.setExtraHTTPHeaders', headers) - - driver.get('https://the-internet.herokuapp.com/basic_auth') - - success = driver.find_element(by=By.TAG_NAME, value='p') - assert success.text == 'Congratulations! You must have the proper credentials.' diff --git a/examples/python/tests/bidirectional/webdriver_bidi/test_browsing_context.py b/examples/python/tests/bidirectional/webdriver_bidi/test_browsing_context.py deleted file mode 100644 index 53b695b6fc83..000000000000 --- a/examples/python/tests/bidirectional/webdriver_bidi/test_browsing_context.py +++ /dev/null @@ -1,2 +0,0 @@ -from selenium import webdriver - diff --git a/examples/python/tests/bidirectional/webdriver_bidi/test_log.py b/examples/python/tests/bidirectional/webdriver_bidi/test_log.py deleted file mode 100644 index 53b695b6fc83..000000000000 --- a/examples/python/tests/bidirectional/webdriver_bidi/test_log.py +++ /dev/null @@ -1,2 +0,0 @@ -from selenium import webdriver - diff --git a/examples/python/tests/browsers/test_chrome.py b/examples/python/tests/browsers/test_chrome.py index 5bf81e3fca26..05ed61e44d3d 100644 --- a/examples/python/tests/browsers/test_chrome.py +++ b/examples/python/tests/browsers/test_chrome.py @@ -1,19 +1,19 @@ import os import re import subprocess - +import pytest from selenium import webdriver - +from selenium.webdriver.common.by import By def test_basic_options(): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() driver = webdriver.Chrome(options=options) driver.quit() def test_args(): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.add_argument("--start-maximized") @@ -24,7 +24,7 @@ def test_args(): def test_set_browser_location(chrome_bin): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.binary_location = chrome_bin @@ -34,7 +34,7 @@ def test_set_browser_location(chrome_bin): def test_add_extension(): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() extension_file_path = os.path.abspath("tests/extensions/webextensions-selenium-example.crx") options.add_extension(extension_file_path) @@ -46,7 +46,7 @@ def test_add_extension(): def test_keep_browser_open(): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.add_experimental_option("detach", True) @@ -57,7 +57,7 @@ def test_keep_browser_open(): def test_exclude_switches(): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.add_experimental_option('excludeSwitches', ['disable-popup-blocking']) @@ -121,3 +121,75 @@ def test_build_checks(capfd): assert expected in err driver.quit() + + +def test_set_network_conditions(): + driver = webdriver.Chrome() + + network_conditions = { + "offline": False, + "latency": 20, # 20 ms of latency + "download_throughput": 2000 * 1024 / 8, # 2000 kbps + "upload_throughput": 2000 * 1024 / 8, # 2000 kbps + } + driver.set_network_conditions(**network_conditions) + + driver.get("https://www.selenium.dev/") + + # check whether the network conditions are set + assert driver.get_network_conditions() == network_conditions + + driver.quit() + + +def test_set_permissions(): + driver = webdriver.Chrome() + driver.get('https://www.selenium.dev/') + + driver.set_permissions('camera', 'denied') + + assert get_permission_state(driver, 'camera') == 'denied' + driver.quit() + + +def get_permission_state(driver, name): + """Helper function to query the permission state.""" + script = """ + const callback = arguments[arguments.length - 1]; + navigator.permissions.query({name: arguments[0]}).then(permissionStatus => { + callback(permissionStatus.state); + }); + """ + return driver.execute_async_script(script, name) + + +def test_cast_features(): + driver = webdriver.Chrome() + + try: + sinks = driver.get_sinks() + if sinks: + sink_name = sinks[0]['name'] + driver.start_tab_mirroring(sink_name) + driver.stop_casting(sink_name) + else: + pytest.skip("No available Cast sinks to test with.") + finally: + driver.quit() + + +def test_get_browser_logs(): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html") + driver.find_element(By.ID, "consoleError").click() + + logs = driver.get_log("browser") + + # Assert that at least one log contains the expected message + assert any("I am console error" in log['message'] for log in logs), "No matching log message found." + driver.quit() + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/browsers/test_edge.py b/examples/python/tests/browsers/test_edge.py index ac0224ebd66c..457742ca4163 100644 --- a/examples/python/tests/browsers/test_edge.py +++ b/examples/python/tests/browsers/test_edge.py @@ -1,19 +1,19 @@ import os import re import subprocess - +import pytest from selenium import webdriver - +from selenium.webdriver.common.by import By def test_basic_options(): - options = webdriver.EdgeOptions() + options = get_default_edge_options() driver = webdriver.Edge(options=options) driver.quit() def test_args(): - options = webdriver.EdgeOptions() + options = get_default_edge_options() options.add_argument("--start-maximized") @@ -24,7 +24,7 @@ def test_args(): def test_set_browser_location(edge_bin): - options = webdriver.EdgeOptions() + options = get_default_edge_options() options.binary_location = edge_bin @@ -34,7 +34,7 @@ def test_set_browser_location(edge_bin): def test_add_extension(): - options = webdriver.EdgeOptions() + options = get_default_edge_options() extension_file_path = os.path.abspath("tests/extensions/webextensions-selenium-example.crx") options.add_extension(extension_file_path) @@ -46,7 +46,7 @@ def test_add_extension(): def test_keep_browser_open(): - options = webdriver.EdgeOptions() + options = get_default_edge_options() options.add_experimental_option("detach", True) @@ -57,7 +57,7 @@ def test_keep_browser_open(): def test_exclude_switches(): - options = webdriver.EdgeOptions() + options = get_default_edge_options() options.add_experimental_option('excludeSwitches', ['disable-popup-blocking']) @@ -122,3 +122,74 @@ def test_build_checks(log_path): driver.quit() + +def test_set_network_conditions(): + driver = webdriver.Edge() + + network_conditions = { + "offline": False, + "latency": 20, # 20 ms of latency + "download_throughput": 2000 * 1024 / 8, # 2000 kbps + "upload_throughput": 2000 * 1024 / 8, # 2000 kbps + } + driver.set_network_conditions(**network_conditions) + + driver.get("https://www.selenium.dev/") + + # check whether the network conditions are set + assert driver.get_network_conditions() == network_conditions + + driver.quit() + + +def test_set_permissions(): + driver = webdriver.Edge() + driver.get('https://www.selenium.dev/') + + driver.set_permissions('camera', 'denied') + + assert get_permission_state(driver, 'camera') == 'denied' + driver.quit() + + +def get_permission_state(driver, name): + """Helper function to query the permission state.""" + script = """ + const callback = arguments[arguments.length - 1]; + navigator.permissions.query({name: arguments[0]}).then(permissionStatus => { + callback(permissionStatus.state); + }); + """ + return driver.execute_async_script(script, name) + + +def test_cast_features(): + driver = webdriver.Edge() + + try: + sinks = driver.get_sinks() + if sinks: + sink_name = sinks[0]['name'] + driver.start_tab_mirroring(sink_name) + driver.stop_casting(sink_name) + else: + pytest.skip("No available Cast sinks to test with.") + finally: + driver.quit() + + +def test_get_browser_logs(): + driver = webdriver.Edge() + driver.get("https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html") + driver.find_element(By.ID, "consoleError").click() + + logs = driver.get_log("browser") + + # Assert that at least one log contains the expected message + assert any("I am console error" in log['message'] for log in logs), "No matching log message found." + driver.quit() + +def get_default_edge_options(): + options = webdriver.EdgeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/browsers/test_firefox.py b/examples/python/tests/browsers/test_firefox.py index 925bd5e457fb..5780e16106fd 100644 --- a/examples/python/tests/browsers/test_firefox.py +++ b/examples/python/tests/browsers/test_firefox.py @@ -88,10 +88,10 @@ def test_profile_location(temp_dir): driver.quit() -def test_install_addon(firefox_driver, addon_path): +def test_install_addon(firefox_driver, addon_path_xpi): driver = firefox_driver - driver.install_addon(addon_path) + driver.install_addon(addon_path_xpi) driver.get("https://www.selenium.dev/selenium/web/blank.html") injected = driver.find_element(webdriver.common.by.By.ID, "webextensions-selenium-example") @@ -99,22 +99,69 @@ def test_install_addon(firefox_driver, addon_path): assert injected.text == "Content injected by webextensions-selenium-example" -def test_uninstall_addon(firefox_driver, addon_path): +def test_uninstall_addon(firefox_driver, addon_path_xpi): driver = firefox_driver - id = driver.install_addon(addon_path) + id = driver.install_addon(addon_path_xpi) driver.uninstall_addon(id) driver.get("https://www.selenium.dev/selenium/web/blank.html") assert len(driver.find_elements(webdriver.common.by.By.ID, "webextensions-selenium-example")) == 0 -def test_install_unsigned_addon_directory(firefox_driver, addon_path): +def test_install_unsigned_addon_directory(firefox_driver, addon_path_dir): driver = firefox_driver - driver.install_addon(addon_path, temporary=True) + driver.install_addon(addon_path_dir, temporary=True) driver.get("https://www.selenium.dev/selenium/web/blank.html") injected = driver.find_element(webdriver.common.by.By.ID, "webextensions-selenium-example") assert injected.text == "Content injected by webextensions-selenium-example" + + +def test_install_unsigned_addon_directory_slash(firefox_driver, addon_path_dir_slash): + driver = firefox_driver + + driver.install_addon(addon_path_dir_slash, temporary=True) + + driver.get("https://www.selenium.dev/selenium/web/blank.html") + injected = driver.find_element(webdriver.common.by.By.ID, "webextensions-selenium-example") + + assert injected.text == "Content injected by webextensions-selenium-example" + + +def test_full_page_screenshot(firefox_driver): + driver = firefox_driver + + driver.get("https://www.selenium.dev/") + + driver.save_full_page_screenshot("full_page_screenshot.png") + + assert os.path.exists("full_page_screenshot.png") + + driver.quit() + + +def test_set_context(firefox_driver): + driver = firefox_driver + + with driver.context(driver.CONTEXT_CHROME): + driver.execute_script("console.log('Inside Chrome context');") + + # Check if the context is back to content + assert driver.execute("GET_CONTEXT")["value"] == "content" + + +def test_firefox_profile(): + from selenium.webdriver.firefox.options import Options + from selenium.webdriver.firefox.firefox_profile import FirefoxProfile + + options = Options() + firefox_profile = FirefoxProfile() + firefox_profile.set_preference("javascript.enabled", False) + options.profile = firefox_profile + + driver = webdriver.Firefox(options=options) + + driver.quit() diff --git a/examples/python/tests/browsers/test_internet_explorer.py b/examples/python/tests/browsers/test_internet_explorer.py index c3efd343cdf6..617222e4ab30 100644 --- a/examples/python/tests/browsers/test_internet_explorer.py +++ b/examples/python/tests/browsers/test_internet_explorer.py @@ -23,14 +23,82 @@ def test_basic_options_win11(): driver.quit() +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_file_upload_timeout(): + options = webdriver.IeOptions() + options.file_upload_timeout = 2000 + + driver = webdriver.Ie(options=options) + + driver.quit() + + +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_ensure_clean_session(): + options = webdriver.IeOptions() + options.ensure_clean_session = True + + driver = webdriver.Ie(options=options) + + driver.quit() + + +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_ignore_zoom_level(): + options = webdriver.IeOptions() + options.ignore_zoom_level = True + + driver = webdriver.Ie(options=options) + + driver.quit() + + +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_ignore_protected_mode_settings(): + options = webdriver.IeOptions() + options.ignore_protected_mode_settings = True + + driver = webdriver.Ie(options=options) + + driver.quit() + + +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_silent(): + service = webdriver.IeService(service_args=["--silent"]) + driver = webdriver.Ie(service=service) + + driver.quit() + + +@pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") +def test_cmd_options(): + options = webdriver.IeOptions() + options.add_argument("-private") + + driver = webdriver.Ie(options=options) + + driver.quit() + +# Skipping this as it fails on Windows because the value of registry setting in +# HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\TabProcGrowth must be '0' +@pytest.mark.skip +def test_force_create_process_api(): + options = webdriver.IeOptions() + options.force_create_process_api = True + + driver = webdriver.Ie(options=options) + + driver.quit() + @pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") def test_log_to_file(log_path): - service = webdriver.IeService(log_output=log_path, log_level='INFO') + service = webdriver.IeService(log_output=log_path, log_level="INFO") driver = webdriver.Ie(service=service) - with open(log_path, 'r') as fp: + with open(log_path, "r") as fp: assert "Starting WebDriver server" in fp.readline() driver.quit() @@ -50,19 +118,19 @@ def test_log_to_stdout(capfd): @pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") def test_log_level(log_path): - service = webdriver.IeService(log_output=log_path, log_level='WARN') + service = webdriver.IeService(log_output=log_path, log_level="WARN") driver = webdriver.Ie(service=service) - with open(log_path, 'r') as fp: - assert 'Started InternetExplorerDriver server (32-bit)' in fp.readline() + with open(log_path, "r") as fp: + assert "Started InternetExplorerDriver server (32-bit)" in fp.readline() driver.quit() @pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") def test_supporting_files(temp_dir): - service = webdriver.IeService(service_args=["–extract-path="+temp_dir]) + service = webdriver.IeService(service_args=["–extract-path=" + temp_dir]) driver = webdriver.Ie(service=service) diff --git a/examples/python/tests/browsers/test_safari.py b/examples/python/tests/browsers/test_safari.py index ba2677ef0daf..9ad01bcf30f1 100644 --- a/examples/python/tests/browsers/test_safari.py +++ b/examples/python/tests/browsers/test_safari.py @@ -14,7 +14,7 @@ def test_basic_options(): @pytest.mark.skipif(sys.platform != "darwin", reason="requires Mac") def test_enable_logging(): - service = webdriver.SafariService(service_args=["--diagnose"]) + service = webdriver.SafariService(enable_logging=True) driver = webdriver.Safari(service=service) diff --git a/examples/python/tests/conftest.py b/examples/python/tests/conftest.py index 94b05f4ae67e..4bcfb23090c4 100644 --- a/examples/python/tests/conftest.py +++ b/examples/python/tests/conftest.py @@ -7,14 +7,32 @@ from selenium.webdriver.common.utils import free_port from datetime import datetime from urllib.request import urlopen +import requests +from requests.auth import HTTPBasicAuth import pytest from selenium import webdriver +def pytest_configure(config): + config.addinivalue_line( + "markers", "driver_type(type): marks tests to use driver type ('bidi', 'firefox', etc)" + ) + + @pytest.fixture(scope='function') -def driver(): - driver = webdriver.Chrome() +def driver(request): + marker = request.node.get_closest_marker("driver_type") + driver_type = marker.args[0] if marker else None + + if driver_type == "bidi": + options = get_default_chrome_options() + options.enable_bidi = True + driver = webdriver.Chrome(options=options) + elif driver_type == "firefox": + driver = webdriver.Firefox() + else: + driver = webdriver.Chrome() yield driver @@ -23,37 +41,34 @@ def driver(): @pytest.fixture(scope='function') def chromedriver_bin(): - service = webdriver.chrome.service.Service() - options = webdriver.ChromeOptions() + service = webdriver.ChromeService() + options = get_default_chrome_options() options.browser_version = 'stable' - yield webdriver.common.driver_finder.DriverFinder().get_path(service=service, options=options) + yield webdriver.common.driver_finder.DriverFinder(service=service, options=options).get_driver_path() @pytest.fixture(scope='function') def chrome_bin(): - service = webdriver.chrome.service.Service() - options = webdriver.ChromeOptions() + service = webdriver.ChromeService() + options = get_default_chrome_options() options.browser_version = 'stable' - webdriver.common.driver_finder.DriverFinder().get_path(service=service, options=options) - yield options.binary_location + yield webdriver.common.driver_finder.DriverFinder(service=service, options=options).get_browser_path() @pytest.fixture(scope='function') def edge_bin(): - service = webdriver.edge.service.Service() - options = webdriver.EdgeOptions() + service = webdriver.EdgeService() + options = get_default_edge_options() options.browser_version = 'stable' - webdriver.common.driver_finder.DriverFinder().get_path(service=service, options=options) - yield options.binary_location + yield webdriver.common.driver_finder.DriverFinder(service=service, options=options).get_browser_path() @pytest.fixture(scope='function') def firefox_bin(): - service = webdriver.firefox.service.Service() + service = webdriver.FirefoxService() options = webdriver.FirefoxOptions() options.browser_version = 'stable' - webdriver.common.driver_finder.DriverFinder().get_path(service=service, options=options) - yield options.binary_location + yield webdriver.common.driver_finder.DriverFinder(service=service, options=options).get_browser_path() @pytest.fixture(scope='function') @@ -72,6 +87,12 @@ def firefox_driver(): driver.quit() +@pytest.fixture(scope='function') +def log(): + logging.basicConfig(level=logging.WARN) + logging.getLogger('selenium').setLevel(logging.DEBUG) + + @pytest.fixture(scope='function') def log_path(): suffix = datetime.now().strftime("%y%m%d_%H%M%S") @@ -88,7 +109,7 @@ def log_path(): @pytest.fixture(scope='function') -def addon_path(): +def addon_path_xpi(): if os.path.abspath("").endswith("tests"): path = os.path.abspath("extensions/webextensions-selenium-example.xpi") else: @@ -97,6 +118,26 @@ def addon_path(): yield path +@pytest.fixture(scope='function') +def addon_path_dir(): + if os.path.abspath("").endswith("tests"): + path = os.path.abspath("extensions/webextensions-selenium-example") + else: + path = os.path.abspath("tests/extensions/webextensions-selenium-example") + + yield path + + +@pytest.fixture(scope='function') +def addon_path_dir_slash(): + if os.path.abspath("").endswith("tests"): + path = os.path.abspath("extensions/webextensions-selenium-example/") + else: + path = os.path.abspath("tests/extensions/webextensions-selenium-example/") + + yield path + + @pytest.fixture(scope="function") def server_old(request): _host = "localhost" @@ -107,7 +148,7 @@ def server_old(request): os.path.abspath(__file__) ) ), - "selenium-server-4.15.0.jar", + "selenium-server-4.32.0.jar", ) def wait_for_server(url, timeout): @@ -165,7 +206,7 @@ def server(): ) ) ), - "selenium-server-4.15.0.jar", + "selenium-server-4.32.0.jar", ) args = [ @@ -183,7 +224,7 @@ def server(): process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - def wait_for_server(url, timeout=20): + def wait_for_server(url, timeout=60): start = time.time() while time.time() - start < timeout: try: @@ -203,3 +244,98 @@ def wait_for_server(url, timeout=20): process.wait(timeout=10) except subprocess.TimeoutExpired: process.kill() + + +def _get_resource_path(file_name: str): + if os.path.abspath("").endswith("tests"): + path = os.path.abspath(f"resources/{file_name}") + else: + path = os.path.join( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__) + ) + ), + f"tests/resources/{file_name}", + ) + return path + + +@pytest.fixture(scope="function") +def grid_server(): + _host = "localhost" + _port = free_port() + _username = "admin" + _password = "myStrongPassword" + _path_cert = _get_resource_path("tls.crt") + _path_key = _get_resource_path("tls.key") + _path_jks = _get_resource_path("server.jks") + _truststore_pass = "seleniumkeystore" + _path = os.path.join( + os.path.dirname( + os.path.dirname( + os.path.dirname( + os.path.abspath(__file__) + ) + ) + ), + "selenium-server-4.32.0.jar", + ) + + args = [ + "java", + f"-Djavax.net.ssl.trustStore={_path_jks}", + f"-Djavax.net.ssl.trustStorePassword={_truststore_pass}", + "-Djdk.internal.httpclient.disableHostnameVerification=true", + "-jar", + _path, + "standalone", + "--port", + str(_port), + "--selenium-manager", + "true", + "--enable-managed-downloads", + "true", + "--username", + _username, + "--password", + _password, + "--https-certificate", + _path_cert, + "--https-private-key", + _path_key, + ] + + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + def wait_for_server(url, timeout=60): + start = time.time() + while time.time() - start < timeout: + try: + requests.get(url, verify=_path_cert, auth=HTTPBasicAuth(_username, _password)) + return True + except OSError as e: + print(e) + time.sleep(0.2) + return False + + if not wait_for_server(f"https://{_host}:{_port}/status"): + raise RuntimeError(f"Selenium server did not start within the allotted time.") + + yield f"https://{_host}:{_port}" + + process.terminate() + try: + process.wait(timeout=10) + except subprocess.TimeoutExpired: + process.kill() + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options + +def get_default_edge_options(): + options = webdriver.EdgeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/design_strategy/using_best_practice.py b/examples/python/tests/design_strategy/using_best_practice.py new file mode 100644 index 000000000000..29063b8f6a90 --- /dev/null +++ b/examples/python/tests/design_strategy/using_best_practice.py @@ -0,0 +1,266 @@ +""" +An example of `python + pytest + selenium` +which implemented "**Action Bot**, **Loadable Component** and **Page Object**". +""" + +import pytest +from selenium import webdriver +from selenium.common import ( + ElementNotInteractableException, + NoSuchElementException, + StaleElementReferenceException, +) +from selenium.webdriver import ActionChains +from selenium.webdriver.common.by import By +from selenium.webdriver.remote.webelement import WebElement +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + + +@pytest.fixture(scope="function") +def chrome_driver(): + with webdriver.Chrome() as driver: + driver.set_window_size(1024, 768) + driver.implicitly_wait(0.5) + yield driver + + +class ActionBot: + def __init__(self, driver) -> None: + self.driver = driver + self.wait = WebDriverWait( + driver, + timeout=10, + poll_frequency=2, + ignored_exceptions=[ + NoSuchElementException, + StaleElementReferenceException, + ElementNotInteractableException, + ], + ) + + def element(self, locator: tuple) -> WebElement: + self.wait.until(lambda driver: driver.find_element(*locator)) + return self.driver.find_element(*locator) + + def elements(self, locator: tuple) -> list[WebElement]: + return self.driver.find_elements(*locator) + + def hover(self, locator: tuple) -> None: + element = self.element(locator) + ActionChains(self.driver).move_to_element(element).perform() + + def click(self, locator: tuple) -> None: + element = self.element(locator) + element.click() + + def type(self, locator: tuple, value: str) -> None: + element = self.element(locator) + element.clear() + element.send_keys(value) + + def text(self, locator: tuple) -> str: + element = self.element(locator) + return element.text + + +class LoadableComponent: + def load(self): + raise NotImplementedError("Subclasses must implement this method") + + def is_loaded(self): + raise NotImplementedError("Subclasses must implement this method") + + def get(self): + if not self.is_loaded(): + self.load() + if not self.is_loaded(): + raise Exception("Page not loaded properly.") + return self + + +class TodoPage(LoadableComponent): + url = "https://todomvc.com/examples/react/dist/" + + new_todo_by = (By.CSS_SELECTOR, "input.new-todo") + count_todo_left_by = (By.CSS_SELECTOR, "span.todo-count") + todo_items_by = (By.CSS_SELECTOR, "ul.todo-list>li") + + view_all_by = (By.LINK_TEXT, "All") + view_active_by = (By.LINK_TEXT, "Active") + view_completed_by = (By.LINK_TEXT, "Completed") + + toggle_all_by = (By.CSS_SELECTOR, "input.toggle-all") + clear_completed_by = (By.CSS_SELECTOR, "button.clear-completed") + + @staticmethod + def build_todo_by(s: str) -> tuple: + p = f"//li[.//label[contains(text(), '{s}')]]" + return By.XPATH, p + + @staticmethod + def build_todo_item_label_by(s: str) -> tuple: + p = f"//label[contains(text(), '{s}')]" + return By.XPATH, p + + @staticmethod + def build_todo_item_toggle_by(s: str) -> tuple: + by, using = TodoPage.build_todo_item_label_by(s) + p = f"{using}/../input[@class='toggle']" + return by, p + + @staticmethod + def build_todo_item_delete_by(s: str) -> tuple: + by, using = TodoPage.build_todo_item_label_by(s) + p = f"{using}/../button[@class='destroy']" + return by, p + + def build_count_todo_left(self, count: int) -> str: + if count == 1: + return "1 item left!" + else: + return f"{count} items left!" + + def __init__(self, driver): + self.driver = driver + self.bot = ActionBot(driver) + + def load(self): + self.driver.get(self.url) + + def is_loaded(self): + try: + WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located(self.new_todo_by)) + return True + except: + return False + + # business domain below + def count_todo_items_left(self) -> str: + return self.bot.text(self.count_todo_left_by) + + def todo_count(self) -> int: + return len(self.bot.elements(self.todo_items_by)) + + def new_todo(self, s: str): + self.bot.type(self.new_todo_by, s + "\n") + + def toggle_todo(self, s: str): + self.bot.click(self.build_todo_item_toggle_by(s)) + + def hover_todo(self, s: str) -> None: + self.bot.hover(self.build_todo_by(s)) + + def delete_todo(self, s: str): + self.hover_todo(s) + self.bot.click(self.build_todo_item_delete_by(s)) + + def clear_completed_todo(self): + self.bot.click(self.clear_completed_by) + + def toggle_all_todo(self): + self.bot.click(self.toggle_all_by) + + def view_all_todo(self): + self.bot.click(self.view_all_by) + + def view_active_todo(self): + self.bot.click(self.view_active_by) + + def view_completed_todo(self): + self.bot.click(self.view_completed_by) + + +@pytest.fixture +def page(chrome_driver) -> TodoPage: + driver = chrome_driver + return TodoPage(driver).get() + + +class TestTodoPage: + def test_new_todo(self, page: TodoPage): + assert page.todo_count() == 0 + page.new_todo("aaa") + assert page.count_todo_items_left() == page.build_count_todo_left(1) + + def test_todo_toggle(self, page: TodoPage): + s = "aaa" + page.new_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(1) + + page.toggle_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(0) + + page.toggle_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(1) + + def test_todo_delete(self, page: TodoPage): + s1 = "aaa" + s2 = "bbb" + page.new_todo(s1) + page.new_todo(s2) + assert page.count_todo_items_left() == page.build_count_todo_left(2) + + page.delete_todo(s1) + assert page.count_todo_items_left() == page.build_count_todo_left(1) + + page.delete_todo(s2) + assert page.todo_count() == 0 + + def test_new_100_todo(self, page: TodoPage): + for i in range(100): + s = f"ToDo{i}" + page.new_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(100) + + def test_toggle_all_todo(self, page: TodoPage): + for i in range(10): + s = f"ToDo{i}" + page.new_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(10) + assert page.todo_count() == 10 + + page.toggle_all_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(0) + assert page.todo_count() == 10 + + page.toggle_all_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(10) + assert page.todo_count() == 10 + + def test_clear_completed_todo(self, page: TodoPage): + for i in range(10): + s = f"ToDo{i}" + page.new_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(10) + assert page.todo_count() == 10 + + for i in range(5): + s = f"ToDo{i}" + page.toggle_todo(s) + assert page.count_todo_items_left() == page.build_count_todo_left(5) + assert page.todo_count() == 10 + + page.clear_completed_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(5) + assert page.todo_count() == 5 + + def test_view_todo(self, page: TodoPage): + for i in range(10): + s = f"ToDo{i}" + page.new_todo(s) + for i in range(4): + s = f"ToDo{i}" + page.toggle_todo(s) + + page.view_all_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(6) + assert page.todo_count() == 10 + + page.view_active_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(6) + assert page.todo_count() == 6 + + page.view_completed_todo() + assert page.count_todo_items_left() == page.build_count_todo_left(6) + assert page.todo_count() == 4 diff --git a/examples/python/tests/drivers/test_http_client.py b/examples/python/tests/drivers/test_http_client.py index 53b695b6fc83..2a86dd725147 100644 --- a/examples/python/tests/drivers/test_http_client.py +++ b/examples/python/tests/drivers/test_http_client.py @@ -1,2 +1,52 @@ +import os +import pytest +import sys +from urllib3.util import Retry, Timeout from selenium import webdriver +from selenium.webdriver.common.proxy import Proxy +from selenium.webdriver.common.proxy import ProxyType +from selenium.webdriver.remote.client_config import ClientConfig + +@pytest.mark.skipif(sys.platform == "win32", reason="Gets stuck on Windows, passes locally") +def test_start_remote_with_client_config(grid_server): + proxy = Proxy({"proxyType": ProxyType.AUTODETECT}) + retries = Retry(connect=2, read=2, redirect=2) + timeout = Timeout(connect=300, read=3600) + client_config = ClientConfig(remote_server_addr=grid_server, + proxy=proxy, + init_args_for_pool_manager={ + "init_args_for_pool_manager": {"retries": retries, "timeout": timeout}}, + ca_certs=_get_resource_path("tls.crt"), + username="admin", password="myStrongPassword") + options = get_default_chrome_options() + driver = webdriver.Remote(command_executor=grid_server, options=options, client_config=client_config) + driver.get("https://www.selenium.dev/") + driver.quit() + + +@pytest.mark.skipif(sys.platform == "win32", reason="Gets stuck on Windows, passes locally") +def test_start_remote_ignore_certs(grid_server): + proxy = Proxy({"proxyType": ProxyType.AUTODETECT}) + client_config = ClientConfig(remote_server_addr=grid_server, + proxy=proxy, + timeout=3600, + ignore_certificates=True, + username="admin", password="myStrongPassword") + options = get_default_chrome_options() + driver = webdriver.Remote(command_executor=grid_server, options=options, client_config=client_config) + driver.get("https://www.selenium.dev/") + driver.quit() + + +def _get_resource_path(file_name: str): + if os.path.abspath("").endswith("tests"): + path = os.path.abspath(f"resources/{file_name}") + else: + path = os.path.abspath(f"tests/resources/{file_name}") + return path + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/drivers/test_options.py b/examples/python/tests/drivers/test_options.py index 53b695b6fc83..5501fe6f17c4 100644 --- a/examples/python/tests/drivers/test_options.py +++ b/examples/python/tests/drivers/test_options.py @@ -1,2 +1,110 @@ from selenium import webdriver +from selenium.webdriver.common.proxy import Proxy +from selenium.webdriver.common.proxy import ProxyType + +def test_page_load_strategy_normal(): + options = get_default_chrome_options() + options.page_load_strategy = 'normal' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + + +def test_page_load_strategy_eager(): + options = get_default_chrome_options() + options.page_load_strategy = 'eager' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + + +def test_page_load_strategy_none(): + options = get_default_chrome_options() + options.page_load_strategy = 'none' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_timeouts_script(): + options = get_default_chrome_options() + options.timeouts = { 'script': 5000 } + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_timeouts_page_load(): + options = get_default_chrome_options() + options.timeouts = { 'pageLoad': 5000 } + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_timeouts_implicit_wait(): + options = get_default_chrome_options() + options.timeouts = { 'implicit': 5000 } + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_unhandled_prompt(): + options = get_default_chrome_options() + options.unhandled_prompt_behavior = 'accept' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_set_window_rect(): + options = webdriver.FirefoxOptions() + options.set_window_rect = True # Full support in Firefox + driver = webdriver.Firefox(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_strict_file_interactability(): + options = get_default_chrome_options() + options.strict_file_interactability = True + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_proxy(): + options = get_default_chrome_options() + options.proxy = Proxy({ 'proxyType': ProxyType.MANUAL, 'httpProxy' : 'http.proxy:1234'}) + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_set_browser_name(): + options = get_default_chrome_options() + assert options.capabilities['browserName'] == 'chrome' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_set_browser_version(): + options = get_default_chrome_options() + options.browser_version = 'stable' + assert options.capabilities['browserVersion'] == 'stable' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_platform_name(): + options = get_default_chrome_options() + options.platform_name = 'any' + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def test_accept_insecure_certs(): + options = get_default_chrome_options() + options.accept_insecure_certs = True + driver = webdriver.Chrome(options=options) + driver.get("https://www.selenium.dev/") + driver.quit() + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/drivers/test_remote_webdriver.py b/examples/python/tests/drivers/test_remote_webdriver.py index 927278a40bb8..036c2a223f0f 100644 --- a/examples/python/tests/drivers/test_remote_webdriver.py +++ b/examples/python/tests/drivers/test_remote_webdriver.py @@ -1,21 +1,25 @@ import os +import sys +import pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.remote.file_detector import LocalFileDetector from selenium.webdriver.support.wait import WebDriverWait +@pytest.mark.skipif(sys.platform == "win32", reason="Gets stuck on Windows, passes locally") def test_start_remote(server): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() driver = webdriver.Remote(command_executor=server, options=options) - assert "localhost" in driver.command_executor._url + assert "localhost" in driver.command_executor._client_config.remote_server_addr driver.quit() +@pytest.mark.skipif(sys.platform == "win32", reason="Gets stuck on Windows, passes locally") def test_uploads(server): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() driver = webdriver.Remote(command_executor=server, options=options) driver.get("https://the-internet.herokuapp.com/upload") @@ -33,8 +37,9 @@ def test_uploads(server): assert file_name == "selenium-snapshot.png" +@pytest.mark.skipif(sys.platform == "win32", reason="Gets stuck on Windows, passes locally") def test_downloads(server, temp_dir): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.enable_downloads = True driver = webdriver.Remote(command_executor=server, options=options) @@ -46,8 +51,8 @@ def test_downloads(server, temp_dir): files = driver.get_downloadable_files() - assert files == file_names - downloadable_file = files[0] + assert sorted(files) == sorted(file_names) + downloadable_file = file_names[0] target_directory = temp_dir driver.download_file(downloadable_file, target_directory) @@ -59,3 +64,8 @@ def test_downloads(server, temp_dir): driver.delete_downloadable_files() assert not driver.get_downloadable_files() + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/drivers/test_service.py b/examples/python/tests/drivers/test_service.py index 1152d21789a2..98cfe0e2c9be 100644 --- a/examples/python/tests/drivers/test_service.py +++ b/examples/python/tests/drivers/test_service.py @@ -9,7 +9,7 @@ def test_basic_service(): def test_driver_location(chromedriver_bin, chrome_bin): - options = webdriver.ChromeOptions() + options = get_default_chrome_options() options.binary_location = chrome_bin service = webdriver.ChromeService(executable_path=chromedriver_bin) @@ -25,3 +25,8 @@ def test_driver_port(): driver = webdriver.Chrome(service=service) driver.quit() + +def get_default_chrome_options(): + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + return options diff --git a/examples/python/tests/elements/test_information.py b/examples/python/tests/elements/test_information.py index 53b695b6fc83..a2d5d030af81 100644 --- a/examples/python/tests/elements/test_information.py +++ b/examples/python/tests/elements/test_information.py @@ -1,2 +1,47 @@ from selenium import webdriver +from selenium.webdriver.common.by import By +import pytest + + +def test_informarion(): + # Initialize WebDriver + driver = webdriver.Chrome() + driver.implicitly_wait(0.5) + + driver.get("https://www.selenium.dev/selenium/web/inputs.html") + + # isDisplayed + is_email_visible = driver.find_element(By.NAME, "email_input").is_displayed() + assert is_email_visible == True + + # isEnabled + is_enabled_button = driver.find_element(By.NAME, "button_input").is_enabled() + assert is_enabled_button == True + + # isSelected + is_selected_check = driver.find_element(By.NAME, "checkbox_input").is_selected() + assert is_selected_check == True + + # TagName + tag_name_inp = driver.find_element(By.NAME, "email_input").tag_name + assert tag_name_inp == "input" + + # GetRect + rect = driver.find_element(By.NAME, "range_input").rect + assert rect["x"] == 10 + + # CSS Value + css_value = driver.find_element(By.NAME, "color_input").value_of_css_property( + "font-size" + ) + assert css_value == "13.3333px" + + # GetText + text = driver.find_element(By.TAG_NAME, "h1").text + assert text == "Testing Inputs" + + # FetchAttributes + email_txt = driver.find_element(By.NAME, "email_input") + value_info = email_txt.get_attribute("value") + assert value_info == "admin@localhost" diff --git a/examples/python/tests/elements/test_interaction.py b/examples/python/tests/elements/test_interaction.py index 53b695b6fc83..eb05f7d928a1 100644 --- a/examples/python/tests/elements/test_interaction.py +++ b/examples/python/tests/elements/test_interaction.py @@ -1,2 +1,39 @@ from selenium import webdriver +from selenium.webdriver.common.by import By +import pytest + + +def test_interactions(): + # Initialize WebDriver + driver = webdriver.Chrome() + driver.implicitly_wait(0.5) + + # Navigate to URL + driver.get("https://www.selenium.dev/selenium/web/inputs.html") + + # Click on the checkbox + check_input = driver.find_element(By.NAME, "checkbox_input") + check_input.click() + + is_checked = check_input.is_selected() + assert is_checked == False + + # Handle the email input field + email_input = driver.find_element(By.NAME, "email_input") + email_input.clear() # Clear field + + email = "admin@localhost.dev" + email_input.send_keys(email) # Enter text + + # Verify input + data = email_input.get_attribute("value") + assert data == email + + # Clear the email input field again + email_input.clear() + data = email_input.get_attribute("value") + assert data == "" + + # Quit the driver + driver.quit() diff --git a/examples/python/tests/elements/test_locators.py b/examples/python/tests/elements/test_locators.py index 53b695b6fc83..3622b70d97f2 100644 --- a/examples/python/tests/elements/test_locators.py +++ b/examples/python/tests/elements/test_locators.py @@ -1,2 +1,84 @@ +import pytest from selenium import webdriver +from selenium.webdriver.common.by import By + +def test_class_name(): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.CLASS_NAME, "information") + + assert element is not None + assert element.tag_name == "input" + + driver.quit() + +def test_css_selector(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.CSS_SELECTOR, "#fname") + + assert element is not None + assert element.get_attribute("value") == "Jane" + + driver.quit() + +def test_id(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.ID, "lname") + + assert element is not None + assert element.get_attribute("value") == "Doe" + + driver.quit() + +def test_name(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.NAME, "newsletter") + + assert element is not None + assert element.tag_name == "input" + + driver.quit() + +def test_link_text(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.LINK_TEXT, "Selenium Official Page") + + assert element is not None + assert element.get_attribute("href") == "https://www.selenium.dev/" + + driver.quit() + +def test_partial_link_text(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.PARTIAL_LINK_TEXT, "Official Page") + + assert element is not None + assert element.get_attribute("href") == "https://www.selenium.dev/" + + driver.quit() + +def test_tag_name(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.TAG_NAME, "a") + + assert element is not None + assert element.get_attribute("href") == "https://www.selenium.dev/" + + driver.quit() + +def test_xpath(driver): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/locators_tests/locators.html") + element = driver.find_element(By.XPATH, "//input[@value='f']") + + assert element is not None + assert element.get_attribute("type") == "radio" + + driver.quit() diff --git a/examples/python/tests/extensions/webextensions-selenium-example.xpi b/examples/python/tests/extensions/webextensions-selenium-example.xpi index 34b0ae3913f7..dca8e2e12312 100644 Binary files a/examples/python/tests/extensions/webextensions-selenium-example.xpi and b/examples/python/tests/extensions/webextensions-selenium-example.xpi differ diff --git a/examples/python/tests/extensions/webextensions-selenium-example/manifest.json b/examples/python/tests/extensions/webextensions-selenium-example/manifest.json index e938974a20b1..a8b4fec6e60f 100644 --- a/examples/python/tests/extensions/webextensions-selenium-example/manifest.json +++ b/examples/python/tests/extensions/webextensions-selenium-example/manifest.json @@ -1,17 +1,22 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "webextensions-selenium-example", - "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium" , + "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium", "version": "0.1", "content_scripts": [ { - "matches": ["https://*/*","http://*/*"], - "js": ["inject.js"] + "matches": [ + "https://*/*", + "http://*/*" + ], + "js": [ + "inject.js" + ] } ], - "applications": { - "gecko": { - "id": "webextensions-selenium-example@example.com" - } - } -} + "browser_specific_settings": { + "gecko": { + "id": "webextensions-selenium-example-v3@example.com" + } + } +} \ No newline at end of file diff --git a/examples/python/tests/getting_started/using_selenium_tests.py b/examples/python/tests/getting_started/using_selenium_tests.py index 8af0a1deeb20..efac8c0cfc3d 100644 --- a/examples/python/tests/getting_started/using_selenium_tests.py +++ b/examples/python/tests/getting_started/using_selenium_tests.py @@ -3,9 +3,7 @@ def test_eight_components(): - driver = webdriver.Chrome() - - driver.get("https://www.selenium.dev/selenium/web/web-form.html") + driver = setup() title = driver.title assert title == "Web form" @@ -22,4 +20,12 @@ def test_eight_components(): value = message.text assert value == "Received!" + teardown(driver) + +def setup(): + driver = webdriver.Chrome() + driver.get("https://www.selenium.dev/selenium/web/web-form.html") + return driver + +def teardown(driver): driver.quit() diff --git a/examples/python/tests/interactions/test_alerts.py b/examples/python/tests/interactions/test_alerts.py index 53b695b6fc83..59b2cb15698c 100644 --- a/examples/python/tests/interactions/test_alerts.py +++ b/examples/python/tests/interactions/test_alerts.py @@ -1,2 +1,50 @@ from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +global url +url = "https://www.selenium.dev/documentation/webdriver/interactions/alerts/" + + +def test_alert_popup(): + driver = webdriver.Chrome() + driver.get(url) + element = driver.find_element(By.LINK_TEXT, "See an example alert") + element.click() + + wait = WebDriverWait(driver, timeout=2) + alert = wait.until(lambda d : d.switch_to.alert) + text = alert.text + alert.accept() + assert text == "Sample alert" + + driver.quit() + +def test_confirm_popup(): + driver = webdriver.Chrome() + driver.get(url) + element = driver.find_element(By.LINK_TEXT, "See a sample confirm") + driver.execute_script("arguments[0].click();", element) + + wait = WebDriverWait(driver, timeout=2) + alert = wait.until(lambda d : d.switch_to.alert) + text = alert.text + alert.dismiss() + assert text == "Are you sure?" + + driver.quit() + +def test_prompt_popup(): + driver = webdriver.Chrome() + driver.get(url) + element = driver.find_element(By.LINK_TEXT, "See a sample prompt") + driver.execute_script("arguments[0].click();", element) + + wait = WebDriverWait(driver, timeout=2) + alert = wait.until(lambda d : d.switch_to.alert) + alert.send_keys("Selenium") + text = alert.text + alert.accept() + assert text == "What is your tool of choice?" + + driver.quit() \ No newline at end of file diff --git a/examples/python/tests/interactions/test_cookies.py b/examples/python/tests/interactions/test_cookies.py index 53b695b6fc83..0b40e9045172 100644 --- a/examples/python/tests/interactions/test_cookies.py +++ b/examples/python/tests/interactions/test_cookies.py @@ -1,2 +1,71 @@ from selenium import webdriver + +def test_add_cookie(): + driver = webdriver.Chrome() + driver.get("http://www.example.com/") + + # Adds the cookie into current browser context + driver.add_cookie({"name": "key", "value": "value"}) + + +def test_get_named_cookie(): + driver = webdriver.Chrome() + driver.get("http://www.example.com/") + + # Adds the cookie into current browser context + driver.add_cookie({"name": "foo", "value": "bar"}) + + # Get cookie details with named cookie 'foo' + print(driver.get_cookie("foo")) + + +def test_get_all_cookies(): + driver = webdriver.Chrome() + + driver.get("http://www.example.com/") + + driver.add_cookie({"name": "test1", "value": "cookie1"}) + driver.add_cookie({"name": "test2", "value": "cookie2"}) + + # Get all available cookies + print(driver.get_cookies()) + +def test_delete_cookie(): + driver = webdriver.Chrome() + + driver.get("http://www.example.com/") + + driver.add_cookie({"name": "test1", "value": "cookie1"}) + driver.add_cookie({"name": "test2", "value": "cookie2"}) + + # Delete cookie with name 'test1' + driver.delete_cookie("test1") + + +def test_delete_all_cookies(): + driver = webdriver.Chrome() + + driver.get("http://www.example.com/") + + driver.add_cookie({"name": "test1", "value": "cookie1"}) + driver.add_cookie({"name": "test2", "value": "cookie2"}) + + # Delete all cookies + driver.delete_all_cookies() + + +def test_same_side_cookie_attr(): + driver = webdriver.Chrome() + + driver.get("http://www.example.com/") + + # Adds the cookie into current browser context with sameSite 'Strict' (or) 'Lax' + driver.add_cookie({"name": "foo", "value": "value", "sameSite": "Strict"}) + driver.add_cookie({"name": "foo1", "value": "value", "sameSite": "Lax"}) + + cookie1 = driver.get_cookie("foo") + cookie2 = driver.get_cookie("foo1") + + print(cookie1) + print(cookie2) diff --git a/examples/python/tests/interactions/test_frames.py b/examples/python/tests/interactions/test_frames.py index 53b695b6fc83..9a1b21fa16e6 100644 --- a/examples/python/tests/interactions/test_frames.py +++ b/examples/python/tests/interactions/test_frames.py @@ -1,2 +1,55 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + from selenium import webdriver +from selenium.webdriver.common.by import By + +#set chrome and launch web page +driver = webdriver.Chrome() +driver.get("https://www.selenium.dev/selenium/web/iframes.html") + +# --- Switch to iframe using WebElement --- +iframe = driver.find_element(By.ID, "iframe1") +driver.switch_to.frame(iframe) +assert "We Leave From Here" in driver.page_source + +email_element = driver.find_element(By.ID, "email") +email_element.send_keys("admin@selenium.dev") +email_element.clear() +driver.switch_to.default_content() + +# --- Switch to iframe using name or ID --- +iframe1=driver.find_element(By.NAME, "iframe1-name") # (This line doesn't switch, just locates) +driver.switch_to.frame(iframe) +assert "We Leave From Here" in driver.page_source + +email = driver.find_element(By.ID, "email") +email.send_keys("admin@selenium.dev") +email.clear() +driver.switch_to.default_content() + +# --- Switch to iframe using index --- +driver.switch_to.frame(0) +assert "We Leave From Here" in driver.page_source + +# --- Final page content check --- +driver.switch_to.default_content() +assert "This page has iframes" in driver.page_source + +#quit the driver +driver.quit() +#demo code for conference diff --git a/examples/python/tests/interactions/test_interactions.py b/examples/python/tests/interactions/test_interactions.py new file mode 100644 index 000000000000..7bf82b6b9f5a --- /dev/null +++ b/examples/python/tests/interactions/test_interactions.py @@ -0,0 +1,13 @@ +from selenium import webdriver + +driver = webdriver.Chrome() + +driver.get("https://www.selenium.dev/") + +title = driver.title +assert title == "Selenium" + +url = driver.current_url +assert url == "https://www.selenium.dev/" + +driver.quit() diff --git a/examples/python/tests/interactions/test_navigation.py b/examples/python/tests/interactions/test_navigation.py index 53b695b6fc83..819e760a598a 100644 --- a/examples/python/tests/interactions/test_navigation.py +++ b/examples/python/tests/interactions/test_navigation.py @@ -1,2 +1,23 @@ from selenium import webdriver +driver = webdriver.Chrome() + +driver.get("https://www.selenium.dev/") +driver.get("https://www.selenium.dev/selenium/web/index.html") + +title = driver.title +assert title == "Index of Available Pages" + +driver.back() +title = driver.title +assert title == "Selenium" + +driver.forward() +title = driver.title +assert title == "Index of Available Pages" + +driver.refresh() +title = driver.title +assert title == "Index of Available Pages" + +driver.quit() diff --git a/examples/python/tests/interactions/test_print_options.py b/examples/python/tests/interactions/test_print_options.py new file mode 100644 index 000000000000..5d40f4d43ecf --- /dev/null +++ b/examples/python/tests/interactions/test_print_options.py @@ -0,0 +1,58 @@ +import pytest +from selenium import webdriver +from selenium.webdriver.common.print_page_options import PrintOptions + +@pytest.fixture() +def driver(): + driver = webdriver.Chrome() + yield driver + driver.quit() + +def test_orientation(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.orientation = "landscape" ## landscape or portrait + assert print_options.orientation == "landscape" + +def test_range(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.page_ranges = ["1, 2, 3"] ## ["1", "2", "3"] or ["1-3"] + assert print_options.page_ranges == ["1, 2, 3"] + +def test_size(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.page_height = 27.94 # Use page_width to assign width + assert print_options.page_height == 27.94 + +def test_margin(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.margin_top = 10 + print_options.margin_bottom = 10 + print_options.margin_left = 10 + print_options.margin_right = 10 + assert print_options.margin_top == 10 + assert print_options.margin_bottom == 10 + assert print_options.margin_left == 10 + assert print_options.margin_right == 10 + +def test_scale(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.scale = 0.5 ## 0.1 to 2.0 + current_scale = print_options.scale + assert current_scale == 0.5 + +def test_background(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.background = True ## True or False + assert print_options.background is True + +def test_shrink_to_fit(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + print_options.shrink_to_fit = True ## True or False + assert print_options.shrink_to_fit is True diff --git a/examples/python/tests/interactions/test_prints_page.py b/examples/python/tests/interactions/test_prints_page.py new file mode 100644 index 000000000000..2735e4228fc2 --- /dev/null +++ b/examples/python/tests/interactions/test_prints_page.py @@ -0,0 +1,15 @@ +import pytest +from selenium import webdriver +from selenium.webdriver.common.print_page_options import PrintOptions + +pytest.fixture() +def driver(): + driver = webdriver.Chrome() + yield driver + driver.quit() + +def test_prints_page(driver): + driver.get("https://www.selenium.dev/") + print_options = PrintOptions() + pdf = driver.print_page(print_options) + assert len(pdf) > 0 \ No newline at end of file diff --git a/examples/python/tests/interactions/test_virtual_authenticator.py b/examples/python/tests/interactions/test_virtual_authenticator.py index 25a235635223..a40576b91bfa 100644 --- a/examples/python/tests/interactions/test_virtual_authenticator.py +++ b/examples/python/tests/interactions/test_virtual_authenticator.py @@ -48,7 +48,7 @@ def test_virtual_authenticator_options(): assert len(options.to_dict()) == 6 -def test_create_authenticator(driver): +def test_add_authenticator(driver): # Create virtual authenticator options options = VirtualAuthenticatorOptions() options.protocol = VirtualAuthenticatorOptions.Protocol.U2F @@ -93,7 +93,7 @@ def test_create_and_add_resident_key(driver): privatekey = urlsafe_b64decode(BASE64__ENCODED_PK) sign_count = 0 - # create a resident credential using above parameters + # create a resident credential using above parameters resident_credential = Credential.create_resident_credential(credential_id, rp_id, user_handle, privatekey, sign_count) # add the credential created to virtual authenticator diff --git a/examples/python/tests/resources/server.jks b/examples/python/tests/resources/server.jks new file mode 100644 index 000000000000..76579e1776c1 Binary files /dev/null and b/examples/python/tests/resources/server.jks differ diff --git a/examples/python/tests/resources/tls.crt b/examples/python/tests/resources/tls.crt new file mode 100644 index 000000000000..58a511093dde --- /dev/null +++ b/examples/python/tests/resources/tls.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIIPgWI/2ppJPowDQYJKoZIhvcNAQELBQAwgYcxEDAOBgNV +BAYTB1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24x +EzARBgNVBAoTClNlbGVuaXVtSFExJTAjBgNVBAsTHFNvZnR3YXJlIEZyZWVkb20g +Q29uc2VydmFuY3kxEzARBgNVBAMTClNlbGVuaXVtSFEwHhcNMjQxMTAzMDkwMDUz +WhcNMzQxMTAxMDkwMDUzWjCBhzEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMH +VW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjETMBEGA1UEChMKU2VsZW5pdW1IUTEl +MCMGA1UECxMcU29mdHdhcmUgRnJlZWRvbSBDb25zZXJ2YW5jeTETMBEGA1UEAxMK +U2VsZW5pdW1IUTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKVTx0e5 +6/75QgE5E6rTYPlTkIxDjZylOMT2YBNuB8vIFZkSaCtLEqto0XTVV6dQc8Ge41QV +rkt7DID1oN40rvWZdla9/2bVhCsWsRiXlvrKDbjoUi5kiLcfKJW+erUWs28xnLOw +bvGNLLAjEUitKKGpR1vsSMOuvMN9VnsSkn9smAHLT2y41CjKpvdkq+OCUdnqfYju +vV6OthRPXFMsDb1fYqZfE7fZhLc806Rg31qLssNVPwxt6VeNYi1/e5cWYeKIJQoj +sFkqIdvu7xHtR7Qu1tNdeQoiDhMS7VLdZDsnAAtQLHvyAVEBicBX95VrGnOTlKdk ++UDwyOP6djCISzUCAwEAAaNxMG8wHQYDVR0OBBYEFNrLCgZ7d2vfurWaJ4wa8O/T +PfXPME4GA1UdEQEB/wREMEKCCWxvY2FsaG9zdIITc2VsZW5pdW0tZ3JpZC5sb2Nh +bIISc2VsZW5pdW0tZ3JpZC5wcm9kggxzZWxlbml1bS5kZXYwDQYJKoZIhvcNAQEL +BQADggEBABtxoPrVrPO5ELzUuSXbvYKHQG9YEuoAisXsiOWmldXRRvT/yTr3nzJn +bC4dJywMW5unPdq1NoMxza0AF0KBFp1GzLDW5/KcA26R4IQi2xfQKVyRzb4vu0CY +BDbnzF7Bisj50sSI4WThtF4xLEHVmzJ2GWNp6SgAScIrdGeB320aTqUIDO8YHH+y +oeSu6qQfEcDiBWh3KD85vCIx0+L4AM3WKkP5nDq2FL6nwCdxqV7bo5/BZJEODMiW +xv/hw0r1OBn2T2Z6o3pRI92zu9sjj6PzPP80DUPl7+fqAaRlLFglXd8b+Qxojm9o +B0QN+gEM717L6WqmJGr1VC6HWQCvcCc= +-----END CERTIFICATE----- diff --git a/examples/python/tests/resources/tls.key b/examples/python/tests/resources/tls.key new file mode 100644 index 000000000000..d97038cd2ef8 --- /dev/null +++ b/examples/python/tests/resources/tls.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQClU8dHuev++UIB +OROq02D5U5CMQ42cpTjE9mATbgfLyBWZEmgrSxKraNF01VenUHPBnuNUFa5LewyA +9aDeNK71mXZWvf9m1YQrFrEYl5b6yg246FIuZIi3HyiVvnq1FrNvMZyzsG7xjSyw +IxFIrSihqUdb7EjDrrzDfVZ7EpJ/bJgBy09suNQoyqb3ZKvjglHZ6n2I7r1ejrYU +T1xTLA29X2KmXxO32YS3PNOkYN9ai7LDVT8MbelXjWItf3uXFmHiiCUKI7BZKiHb +7u8R7Ue0LtbTXXkKIg4TEu1S3WQ7JwALUCx78gFRAYnAV/eVaxpzk5SnZPlA8Mjj ++nYwiEs1AgMBAAECggEAA2D+tT3SGlmG9Tube2CLaRUW4shSVDBWmcSXn+teBUFv +MDwdfRMGoZJdw4eaXWz0wgaItV7QZjJbMKXfK44ZQaOTtP/4QLuzkjuKE4tXloO7 +e5BjS5eaPrSIPGU9S0cDPvjH2oP22dYi4sJYt6ry+2ODC0Mn6o3p8Dc3Ja1HvrXA +SNImimok7YemXVMbdPyaqbu2eXjPvWAA8W9/OW2L1n4U4neM0S5Nt3tVl5sMELj5 +iFC7Z+M3ZLon/54137h3xPmHYQMiPIX+PulaRLOJYSbR0dtMHhPMyWtR7GwEK4Aw +EgtDLKfa6qMv5BYsI2E0bPHRDaj39UXGeWX5/2xzyQKBgQDcMUL3sEbRmeBKhYlT +xv5ea2P4P247DDWObTDw5jLhwfmOycFcJVlaiXICpGR6hqWY8wI7kKxbQQVKFob6 +JVpIHmkkRqsV8JfXVAcaH1thlKAS4NVZsOJIVBHO3JdPaCUFq7HHbBA3436aJLtC +HiINkuiNXd2dDMuDwOsfhsRFzQKBgQDANnK1P7sZSP7dJHinA2sPSbGAK8ZTbYWD +8oe/lYlLkw6qM9i8vIKCfTpfi4vh4qfjQUczdy1w2zFbxriC2+uxhEqDN2tud3/P +0CYrO0SGQKYCROrYUh0Pl1MswBeu8yT5AdrIBK3t92wfYqTWK7VUZQaUQ7YJWfXS +usbz5qIzCQKBgH8ICHt+/gxUOtqjWYu0pPFyATWp2n1EWO13PyHrnHU0BDaFXQE9 +JuSdoOG3V6R8Y7Lul14n49etllCc2Hgd7ozmxn/AKVm5+M+oUYSXjI+qQANEJLHe +410Y60EtcDnGen1gBWtog57KpzJkeIf3fGvaUkGkYoMFa6/yL3N7u2YNAoGADH29 +WKAKpasDvRVYrenf9D9ixKSTn+pXKesB/WZXZMzqwA7cf+90P8yplXn5HjXfmTot +yV9uWY41F/TDGuX13DRvrzVTyvsDGFs7j8WrP1pGL5GQ/XvgnZnE8vyMzXbJqVEA +ic0cDIHuyd9cPPrcLt7d3ZbE5ris7APtV/5d/hkCgYAMFCYoKcCh+9/2HOgwQ1b6 +16CS71TvDBCx7+D1V3WXrIOWkNzW2SIZtnhQwglU9L7PFw6ViJAY4sB2p9hDDtcZ +e7Lotmnbrb75QQpWUyaoZMsw8l23MOGPzHKPqNiT57uOorjcFrePi9EOdERSG9+4 +lRKqCFhaNBUwQ4idzO0rWA== +-----END PRIVATE KEY----- diff --git a/examples/python/tests/selenium_manager/example_se-config.toml b/examples/python/tests/selenium_manager/example_se-config.toml new file mode 100644 index 000000000000..78eef8c2f6e7 --- /dev/null +++ b/examples/python/tests/selenium_manager/example_se-config.toml @@ -0,0 +1,21 @@ +browser = "chrome" # --browser BROWSER +driver = "chromedriver" # --driver DRIVER +browser-version = "106" # --browser-version BROWSER_VERSION +driver-version = "106.0.5249.61" # --driver-version DRIVER_VERSION +browser-path = "/usr/bin/google-chrome" # --browser-path BROWSER_PATH +driver-mirror-url = "https://custom-driver-mirror.com/" # --driver-mirror-url DRIVER_MIRROR_URL +browser-mirror-url = "https://custom-browser-mirror.com/" # --browser-mirror-url BROWSER_MIRROR_URL +output = "LOGGER" # --output OUTPUT +os = "linux" # --os OS +arch = "x64" # --arch ARCH +proxy = "myproxy:8080" # --proxy PROXY +timeout = 300 # --timeout TIMEOUT +offline = true # --offline +force-browser-download = true # --force-browser-download +avoid-browser-download = false # --avoid-browser-download +debug = true # --debug +trace = true # --trace +cache-path = "/custom/cache/path" # --cache-path CACHE_PATH +ttl = 3600 # --ttl TTL +language-binding = "Python" # --language-binding LANGUAGE +avoid-stats = true # --avoid-stats \ No newline at end of file diff --git a/examples/python/tests/selenium_manager/usage.py b/examples/python/tests/selenium_manager/usage.py new file mode 100644 index 000000000000..c9e93ca542b0 --- /dev/null +++ b/examples/python/tests/selenium_manager/usage.py @@ -0,0 +1,12 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.chrome.service import Service + +def setup_without_selenium_manager(): + chrome_service = Service(executable_path='path/to/chrome.exe') + driver = webdriver.Chrome(chrome_service) + return driver + +def setup_with_selenium_manager(): + driver = webdriver.Chrome() + return driver \ No newline at end of file diff --git a/examples/python/tests/support/test_expected_conditions.py b/examples/python/tests/support/test_expected_conditions.py index 53b695b6fc83..fb93f26b79cd 100644 --- a/examples/python/tests/support/test_expected_conditions.py +++ b/examples/python/tests/support/test_expected_conditions.py @@ -1,2 +1,18 @@ -from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.wait import WebDriverWait +# Expected Conditions API Documentation: +# https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html + + +def test_expected_condition(driver): + driver.get("https://www.selenium.dev/selenium/web/dynamic.html") + revealed = driver.find_element(By.ID, "revealed") + driver.find_element(By.ID, "reveal").click() + + wait = WebDriverWait(driver, timeout=2) + wait.until(EC.visibility_of_element_located((By.ID, "revealed"))) + + revealed.send_keys("Displayed") + assert revealed.get_property("value") == "Displayed" diff --git a/examples/python/tests/waits/test_waits.py b/examples/python/tests/waits/test_waits.py index b496a8bff6cd..aa3d773a6018 100644 --- a/examples/python/tests/waits/test_waits.py +++ b/examples/python/tests/waits/test_waits.py @@ -39,7 +39,7 @@ def test_explicit(driver): driver.find_element(By.ID, "reveal").click() wait = WebDriverWait(driver, timeout=2) - wait.until(lambda d : revealed.is_displayed()) + wait.until(lambda _ : revealed.is_displayed()) revealed.send_keys("Displayed") assert revealed.get_property("value") == "Displayed" @@ -52,6 +52,6 @@ def test_explicit_options(driver): errors = [NoSuchElementException, ElementNotInteractableException] wait = WebDriverWait(driver, timeout=2, poll_frequency=.2, ignored_exceptions=errors) - wait.until(lambda d : revealed.send_keys("Displayed") or True) + wait.until(lambda _ : revealed.send_keys("Displayed") or True) assert revealed.get_property("value") == "Displayed" diff --git a/examples/python/tox.ini b/examples/python/tox.ini new file mode 100644 index 000000000000..49c6f636bb75 --- /dev/null +++ b/examples/python/tox.ini @@ -0,0 +1,30 @@ +# Tox (https://tox.wiki/) is a tool for running tests in multiple +# virtualenvs. This configuration file will run the test suite on all +# supported python versions. To use it, run "tox" from this directory. +# +# For a specific environment, run: "tox -e " (i.e.: "tox -e py313") +# +# This tox configuration will skip any Python interpreters that can't be found. +# To manage multiple Python interpreters for covering all versions, you can use +# pyenv: https://github.com/pyenv/pyenv + + +[tox] +env_list = + py39 + py310 + py311 + py312 + py313 +skip_missing_interpreters = True + +[testenv] +description = run tests +passenv = * +deps = + -r requirements.txt +commands = + # "-vv" means extra verbose + # "-r fEsxXp" means show extra test summary info as specified by: + # (f)ailed, (E)rror, (s)kipped, (x)failed, (X)passed, (p)assed + pytest -vv -r fEsxXp {posargs:.} diff --git a/examples/ruby/.rubocop.yml b/examples/ruby/.rubocop.yml index b88b233a689b..492720f45e42 100644 --- a/examples/ruby/.rubocop.yml +++ b/examples/ruby/.rubocop.yml @@ -1,9 +1,10 @@ inherit_from: .rubocop_todo.yml -require: rubocop-rspec +plugins: + - rubocop-rspec AllCops: - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.1 NewCops: enable SuggestExtensions: rubocop-rake: false diff --git a/examples/ruby/Gemfile b/examples/ruby/Gemfile index aeb5588d936b..f4987c1f9889 100644 --- a/examples/ruby/Gemfile +++ b/examples/ruby/Gemfile @@ -6,14 +6,6 @@ gem 'ffi', '~> 1.15', '>= 1.15.5' if Gem.win_platform? # Windows only gem 'rake', '~> 13.0' gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.35' -gem 'rubocop-rspec', '~> 2.12' -gem 'selenium-devtools', '~> 0.119' - -if ENV['SE_NIGHTLY'] - source 'https://rubygems.pkg.github.com/seleniumhq' do - selenium = 'selenium-webdriver' - gem selenium, '~> 4.16.0.nightly' - end -else - gem 'selenium-webdriver', '= 4.15.0' -end +gem 'rubocop-rspec', '~> 3.0' +gem 'selenium-devtools', '= 0.136.0' +gem 'selenium-webdriver', '= 4.32.0' diff --git a/examples/ruby/Gemfile.lock b/examples/ruby/Gemfile.lock index e91804e231e4..b1fb4f3da842 100644 --- a/examples/ruby/Gemfile.lock +++ b/examples/ruby/Gemfile.lock @@ -2,67 +2,69 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - base64 (0.1.1) - diff-lcs (1.5.0) - json (2.6.3) - language_server-protocol (3.17.0.3) - parallel (1.23.0) - parser (3.2.2.3) + base64 (0.2.0) + diff-lcs (1.5.1) + json (2.10.2) + language_server-protocol (3.17.0.4) + lint_roller (1.1.0) + logger (1.7.0) + parallel (1.26.3) + parser (3.3.7.1) ast (~> 2.4.1) racc - racc (1.7.1) + racc (1.8.1) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.8.1) - rexml (3.2.6) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rake (13.2.1) + regexp_parser (2.10.0) + rexml (3.4.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-support (3.12.1) - rubocop (1.56.3) - base64 (~> 0.1.1) + rspec-support (~> 3.13.0) + rspec-support (3.13.0) + rubocop (1.72.2) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) - parser (>= 3.2.2.3) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) - parser (>= 3.2.1.0) - rubocop-capybara (2.19.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.24.0) - rubocop (~> 1.33) - rubocop-rspec (2.24.1) - rubocop (~> 1.33) - rubocop-capybara (~> 2.17) - rubocop-factory_bot (~> 2.22) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.38.0) + parser (>= 3.3.1.0) + rubocop-rspec (3.5.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) - rubyzip (2.3.2) - selenium-devtools (0.119.0) + rubyzip (2.4.1) + selenium-devtools (0.136.0) selenium-webdriver (~> 4.2) - selenium-webdriver (4.15.0) + selenium-webdriver (4.32.0) + base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - unicode-display_width (2.4.2) - websocket (1.2.10) + unicode-display_width (2.6.0) + websocket (1.2.11) PLATFORMS arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 + arm64-darwin-24 + ruby x86_64-darwin-19 x86_64-darwin-20 x86_64-darwin-22 @@ -72,9 +74,9 @@ DEPENDENCIES rake (~> 13.0) rspec (~> 3.0) rubocop (~> 1.35) - rubocop-rspec (~> 2.12) - selenium-devtools (~> 0.119) - selenium-webdriver (= 4.15.0) + rubocop-rspec (~> 3.0) + selenium-devtools (= 0.136.0) + selenium-webdriver (= 4.32.0) BUNDLED WITH - 2.3.11 + 2.5.6 diff --git a/examples/ruby/README.md b/examples/ruby/README.md index 7f46d8a2511c..ad44bc2fdee8 100755 --- a/examples/ruby/README.md +++ b/examples/ruby/README.md @@ -20,10 +20,18 @@ cd seleniumhq.github.io/examples/ruby bundler install ``` -4. Run all all tests +4. Run all tests ``` bundle exec rspec ``` -> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers \ No newline at end of file +> Please keep some patience - If you are doing it for the first time, it will take a little while to verify and download the browser drivers + +# Execute a ruby script + +Use this command to run a ruby script and follow the first script example + +``` +ruby example_script.rb +``` \ No newline at end of file diff --git a/examples/ruby/spec/actions_api/keys_spec.rb b/examples/ruby/spec/actions_api/keys_spec.rb index 62acfb00e650..c6b97e41a4f1 100644 --- a/examples/ruby/spec/actions_api/keys_spec.rb +++ b/examples/ruby/spec/actions_api/keys_spec.rb @@ -56,8 +56,7 @@ expect(text_field.attribute('value')).to eq 'Selenium!' end - it 'copy and paste', except: {browser: :chrome, - reason: 'https://bugs.chromium.org/p/chromedriver/issues/detail?id=4264'} do + it 'copy and paste' do driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' wait.until { driver.find_element(id: 'textInput').attribute('autofocus') } diff --git a/examples/ruby/spec/bidi/cdp/cdp_spec.rb b/examples/ruby/spec/bidi/cdp/cdp_spec.rb new file mode 100644 index 000000000000..fa2d82f14767 --- /dev/null +++ b/examples/ruby/spec/bidi/cdp/cdp_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Logging' do + let(:driver) { start_session } + + it 'sets cookie' do + driver.execute_cdp('Network.setCookie', + name: 'cheese', + value: 'gouda', + domain: 'www.selenium.dev', + secure: true) + + driver.get('https://www.selenium.dev/') + cheese = driver.manage.cookie_named('cheese') + + expect(cheese[:value]).to eq 'gouda' + end +end diff --git a/examples/ruby/spec/bidi/cdp/logging_spec.rb b/examples/ruby/spec/bidi/cdp/logging_spec.rb new file mode 100644 index 000000000000..c92f924faccf --- /dev/null +++ b/examples/ruby/spec/bidi/cdp/logging_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Logging' do + let(:driver) { start_session } + + it 'listens for console logs' do + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + logs = [] + driver.on_log_event(:console) { |log| logs << log.args.first } + + driver.find_element(id: 'consoleLog').click + driver.find_element(id: 'consoleError').click + + Selenium::WebDriver::Wait.new.until { logs.size > 1 } + expect(logs).to include 'Hello, world!' + expect(logs).to include 'I am console error' + end + + it 'listens for js exception' do + driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') + + exceptions = [] + driver.on_log_event(:exception) { |exception| exceptions << exception } + + driver.find_element(id: 'jsException').click + + Selenium::WebDriver::Wait.new.until { exceptions.any? } + expect(exceptions.first&.description).to include 'Error: Not working' + end +end diff --git a/examples/ruby/spec/bidirectional/chrome_devtools/cdp_api_spec.rb b/examples/ruby/spec/bidi/cdp/network_spec.rb similarity index 57% rename from examples/ruby/spec/bidirectional/chrome_devtools/cdp_api_spec.rb rename to examples/ruby/spec/bidi/cdp/network_spec.rb index 3974cd0ae0fa..c9a818917702 100644 --- a/examples/ruby/spec/bidirectional/chrome_devtools/cdp_api_spec.rb +++ b/examples/ruby/spec/bidi/cdp/network_spec.rb @@ -2,26 +2,58 @@ require 'spec_helper' -RSpec.describe 'Chrome DevTools' do +RSpec.describe 'Network' do let(:driver) { start_session } - it 'sets cookie' do - driver.devtools.network.set_cookie(name: 'cheese', - value: 'gouda', - domain: 'www.selenium.dev', - secure: true) + it 'does basic authentication' do + driver.register(username: 'admin', + password: 'admin', + uri: /herokuapp/) - driver.get('https://www.selenium.dev/') - cheese = driver.manage.cookie_named('cheese') + driver.get('https://the-internet.herokuapp.com/basic_auth') - expect(cheese[:value]).to eq 'gouda' + expect(driver.find_element(tag_name: 'p').text).to eq('Congratulations! You must have the proper credentials.') + end + + it 'records network response' do + content_type = [] + driver.intercept do |request, &continue| + continue.call(request) do |response| + content_type << response.headers['content-type'] + end + end + + driver.get('https://www.selenium.dev/selenium/web/blank.html') + expect(content_type.first).to eq('text/html; charset=utf-8') + end + + it 'transforms network response' do + driver.intercept do |request, &continue| + continue.call(request) do |response| + response.body = 'Creamy, delicious cheese!' if request.url.include?('blank') + end + end + + driver.get('https://www.selenium.dev/selenium/web/blank.html') + expect(driver.find_element(tag_name: 'body').text).to eq('Creamy, delicious cheese!') + end + + it 'intercepts network request' do + driver.intercept do |request, &continue| + uri = URI(request.url) + request.url = uri.to_s.gsub('one', 'two') if uri.path&.end_with?('one.js') + continue.call(request) + end + + driver.get('https://www.selenium.dev/selenium/web/devToolsRequestInterceptionTest.html') + driver.find_element(tag_name: 'button').click + expect(driver.find_element(id: 'result').text).to eq('two') end it 'gets performance metrics' do driver.get('https://www.selenium.dev/selenium/web/frameset.html') driver.devtools.performance.enable - metric_list = driver.devtools.performance.get_metrics.dig('result', 'metrics') metrics = metric_list.each_with_object({}) do |metric, hash| @@ -32,32 +64,16 @@ expect(metrics['Frames']).to eq 12 end - it 'does basic authentication' do - driver.devtools.network.enable - - credentials = Base64.strict_encode64('admin:admin') - - driver.devtools.network.set_extra_http_headers(headers: {authorization: "Basic #{credentials}"}) - - driver.get('https://the-internet.herokuapp.com/basic_auth') - - expect(driver.find_element(tag_name: 'p').text).to eq('Congratulations! You must have the proper credentials.') - end - - it 'listens for console logs' do - driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - - driver.devtools.runtime.enable - - logs = [] - driver.devtools.runtime.on(:console_api_called) do |params| - logs << params['args'].first['value'] - end + it 'sets cookie' do + driver.devtools.network.set_cookie(name: 'cheese', + value: 'gouda', + domain: 'www.selenium.dev', + secure: true) - driver.find_element(id: 'consoleLog').click + driver.get('https://www.selenium.dev/') + cheese = driver.manage.cookie_named('cheese') - Selenium::WebDriver::Wait.new.until { logs.any? } - expect(logs).to include 'Hello, world!' + expect(cheese[:value]).to eq 'gouda' end it 'waits for downloads', except: {platform: :windows} do diff --git a/examples/ruby/spec/bidi/cdp/script_spec.rb b/examples/ruby/spec/bidi/cdp/script_spec.rb new file mode 100644 index 000000000000..77575404cd6e --- /dev/null +++ b/examples/ruby/spec/bidi/cdp/script_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Script' do + let(:driver) { start_session } + + it 'pins script' do + driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') + element = driver.find_element(id: 'id1') + + key = driver.pin_script('return arguments;') + arguments = driver.execute_script(key, 1, true, element) + + expect(arguments).to eq([1, true, element]) + end + + it 'gets mutated elements' do + driver.get 'https://www.selenium.dev/selenium/web/dynamic.html' + + mutations = [] + driver.on_log_event(:mutation) { |mutation| mutations << mutation.element } + + driver.find_element(id: 'reveal').click + Selenium::WebDriver::Wait.new(timeout: 30).until { mutations.any? } + + expect(mutations).to include(driver.find_element(id: 'revealed')) + end +end diff --git a/examples/ruby/spec/bidi/logging_spec.rb b/examples/ruby/spec/bidi/logging_spec.rb new file mode 100644 index 000000000000..df010daae8e3 --- /dev/null +++ b/examples/ruby/spec/bidi/logging_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Logging' do + let(:driver) { start_bidi_session } + let(:wait) { Selenium::WebDriver::Wait.new(timeout: 2) } + + it 'adds console message handler' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html' + log_entries = [] + + driver.script.add_console_message_handler { |log| log_entries << log } + + driver.find_element(id: 'consoleLog').click + wait.until { log_entries.any? } + expect(log_entries.first&.text).to eq 'Hello, world!' + end + + it 'removes console message handler' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html' + log_entries = [] + + id = driver.script.add_console_message_handler { |log| log_entries << log } + driver.script.remove_console_message_handler(id) + + driver.find_element(id: 'consoleLog').click + expect(log_entries).to be_empty + end + + it 'adds JavaScript error handler' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html' + log_entries = [] + + driver.script.add_javascript_error_handler { |error| log_entries << error } + + driver.find_element(id: 'jsException').click + wait.until { log_entries.any? } + expect(log_entries.first&.text).to eq 'Error: Not working' + end + + it 'removes JavaScript error handler' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html' + log_entries = [] + + id = driver.script.add_javascript_error_handler { |error| log_entries << error } + driver.script.remove_javascript_error_handler(id) + + driver.find_element(id: 'jsException').click + expect(log_entries).to be_empty + end +end diff --git a/examples/ruby/spec/bidirectional/chrome_devtools/bidi_api_spec.rb b/examples/ruby/spec/bidirectional/chrome_devtools/bidi_api_spec.rb deleted file mode 100644 index 68bc6b29ad8e..000000000000 --- a/examples/ruby/spec/bidirectional/chrome_devtools/bidi_api_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'BiDi API' do - let(:driver) { start_session } - - it 'does basic authentication' do - driver.register(username: 'admin', - password: 'admin', - uri: /herokuapp/) - - driver.get('https://the-internet.herokuapp.com/basic_auth') - - expect(driver.find_element(tag_name: 'p').text).to eq('Congratulations! You must have the proper credentials.') - end - - it 'pins script' do - driver.get('https://www.selenium.dev/selenium/web/xhtmlTest.html') - element = driver.find_element(id: 'id1') - - key = driver.pin_script('return arguments;') - arguments = driver.execute_script(key, 1, true, element) - - expect(arguments).to eq([1, true, element]) - end - - it 'gets mutated elements' do - driver.get 'https://www.selenium.dev/selenium/web/dynamic.html' - - mutations = [] - driver.on_log_event(:mutation) { |mutation| mutations << mutation.element } - - driver.find_element(id: 'reveal').click - Selenium::WebDriver::Wait.new(timeout: 30).until { mutations.any? } - - expect(mutations).to include(driver.find_element(id: 'revealed')) - end - - it 'listens for console logs' do - driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - - logs = [] - driver.on_log_event(:console) { |log| logs << log.args.first } - - driver.find_element(id: 'consoleLog').click - driver.find_element(id: 'consoleError').click - - Selenium::WebDriver::Wait.new.until { logs.size > 1 } - expect(logs).to include 'Hello, world!' - expect(logs).to include 'I am console error' - end - - it 'listens for js exception' do - driver.get('https://www.selenium.dev/selenium/web/bidi/logEntryAdded.html') - - exceptions = [] - driver.on_log_event(:exception) { |exception| exceptions << exception } - - driver.find_element(id: 'jsException').click - - Selenium::WebDriver::Wait.new.until { exceptions.any? } - expect(exceptions.first&.description).to include 'Error: Not working' - end - - it 'records network response' do - content_type = [] - driver.intercept do |request, &continue| - continue.call(request) do |response| - content_type << response.headers['content-type'] - end - end - - driver.get('https://www.selenium.dev/selenium/web/blank.html') - expect(content_type.first).to eq('text/html; charset=utf-8') - end - - it 'transforms network response' do - driver.intercept do |request, &continue| - continue.call(request) do |response| - response.body = 'Creamy, delicious cheese!' if request.url.include?('blank') - end - end - - driver.get('https://www.selenium.dev/selenium/web/blank.html') - expect(driver.find_element(tag_name: 'body').text).to eq('Creamy, delicious cheese!') - end - - it 'intercepts network request' do - driver.intercept do |request, &continue| - uri = URI(request.url) - request.url = uri.to_s.gsub('one', 'two') if uri.path&.end_with?('one.js') - continue.call(request) - end - - driver.get('https://www.selenium.dev/selenium/web/devToolsRequestInterceptionTest.html') - driver.find_element(tag_name: 'button').click - expect(driver.find_element(id: 'result').text).to eq('two') - end -end diff --git a/examples/ruby/spec/bidirectional/chrome_devtools/cdp_endpoint_spec.rb b/examples/ruby/spec/bidirectional/chrome_devtools/cdp_endpoint_spec.rb deleted file mode 100644 index 543f8c81fe43..000000000000 --- a/examples/ruby/spec/bidirectional/chrome_devtools/cdp_endpoint_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Execute CDP' do - let(:driver) { start_session } - - it 'sets cookie' do - cookie = {name: 'cheese', - value: 'gouda', - domain: 'www.selenium.dev', - secure: true} - - driver.execute_cdp('Network.setCookie', **cookie) - - driver.get('https://www.selenium.dev/') - cheese = driver.manage.cookie_named(cookie[:name]) - - expect(cheese[:value]).to eq 'gouda' - end - - it 'gets performance metrics' do - driver.get('https://www.selenium.dev/selenium/web/frameset.html') - - driver.execute_cdp('Performance.enable') - - metric_list = driver.execute_cdp('Performance.getMetrics')['metrics'] - - metrics = metric_list.each_with_object({}) do |metric, hash| - hash[metric['name']] = metric['value'] - end - - expect(metrics['DevToolsCommandDuration']).to be > 0 - expect(metrics['Frames']).to eq 12 - end - - it 'sets basic authentication' do - driver.execute_cdp('Network.enable') - - credentials = Base64.strict_encode64('admin:admin') - headers = {authorization: "Basic #{credentials}"} - - driver.execute_cdp('Network.setExtraHTTPHeaders', headers: headers) - - driver.get('https://the-internet.herokuapp.com/basic_auth') - - expect(driver.find_element(tag_name: 'p').text).to eq('Congratulations! You must have the proper credentials.') - end -end diff --git a/examples/ruby/spec/bidirectional/webdriver_bidi/browsing_context_spec.rb b/examples/ruby/spec/bidirectional/webdriver_bidi/browsing_context_spec.rb deleted file mode 100644 index 17500df33484..000000000000 --- a/examples/ruby/spec/bidirectional/webdriver_bidi/browsing_context_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Browsing Context' do - it 'opens a window without browsing context' - it 'opens a window with browsing context' - it 'opens a tab without browsing context' - it 'opens a tab with browsing context' - it 'uses existing window' - it 'navigates to url with readiness state' - it 'navigates to url without readiness state' - it 'gets browsing context tree with depth' - it 'gets browsing context tree without depth' - it 'gets all browsing contexts' - it 'closes a tab' - it 'closes a window' -end diff --git a/examples/ruby/spec/bidirectional/webdriver_bidi/log_spec.rb b/examples/ruby/spec/bidirectional/webdriver_bidi/log_spec.rb deleted file mode 100644 index cde9854b1e4e..000000000000 --- a/examples/ruby/spec/bidirectional/webdriver_bidi/log_spec.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Log Inspector' do - it 'listens to console log' - it 'gets JS exceptions' - it 'gets JS logs' -end diff --git a/examples/ruby/spec/browsers/chrome_spec.rb b/examples/ruby/spec/browsers/chrome_spec.rb index 36f2639f3fa4..81fe78c6ed08 100644 --- a/examples/ruby/spec/browsers/chrome_spec.rb +++ b/examples/ruby/spec/browsers/chrome_spec.rb @@ -113,9 +113,58 @@ end end + describe 'Special Features' do + it 'casts' do + @driver = Selenium::WebDriver.for :chrome + sinks = @driver.cast_sinks + unless sinks.empty? + device_name = sinks.first['name'] + @driver.start_cast_tab_mirroring(device_name) + expect { @driver.stop_casting(device_name) }.not_to raise_exception + end + end + + it 'gets and sets network conditions' do + @driver = Selenium::WebDriver.for :chrome + @driver.network_conditions = {offline: false, latency: 100, throughput: 200} + expect(@driver.network_conditions).to eq( + 'offline' => false, + 'latency' => 100, + 'download_throughput' => 200, + 'upload_throughput' => 200 + ) + end + + it 'gets the browser logs' do + @driver = Selenium::WebDriver.for :chrome + @driver.navigate.to 'https://www.selenium.dev/selenium/web/' + sleep 1 + logs = @driver.logs.get(:browser) + + expect(logs.first.message).to include 'Failed to load resource' + end + + it 'sets permissions' do + @driver = Selenium::WebDriver.for :chrome + @driver.navigate.to 'https://www.selenium.dev/selenium/web/' + @driver.add_permission('camera', 'denied') + @driver.add_permissions('clipboard-read' => 'denied', 'clipboard-write' => 'prompt') + expect(permission('camera')).to eq('denied') + expect(permission('clipboard-read')).to eq('denied') + expect(permission('clipboard-write')).to eq('prompt') + end + end + def driver_finder options = Selenium::WebDriver::Options.chrome(browser_version: 'stable') - ENV['CHROMEDRIVER_BIN'] = Selenium::WebDriver::DriverFinder.path(options, Selenium::WebDriver::Chrome::Service) - ENV['CHROME_BIN'] = options.binary + service = Selenium::WebDriver::Service.chrome + finder = Selenium::WebDriver::DriverFinder.new(options, service) + ENV['CHROMEDRIVER_BIN'] = finder.driver_path + ENV['CHROME_BIN'] = finder.browser_path + end + + def permission(name) + @driver.execute_async_script('callback = arguments[arguments.length - 1];' \ + 'callback(navigator.permissions.query({name: arguments[0]}));', name)['state'] end end diff --git a/examples/ruby/spec/browsers/edge_spec.rb b/examples/ruby/spec/browsers/edge_spec.rb index aeb8bcd5184a..5820fab3e799 100644 --- a/examples/ruby/spec/browsers/edge_spec.rb +++ b/examples/ruby/spec/browsers/edge_spec.rb @@ -29,11 +29,11 @@ it 'add extensions' do extension_file_path = File.expand_path('../spec_support/extensions/webextensions-selenium-example.crx', __dir__) - options = Selenium::WebDriver::Options.chrome + options = Selenium::WebDriver::Options.edge options.add_extension(extension_file_path) - @driver = Selenium::WebDriver.for :chrome, options: options + @driver = Selenium::WebDriver.for :edge, options: options @driver.get('https://www.selenium.dev/selenium/web/blank.html') injected = @driver.find_element(:id, 'webextensions-selenium-example') expect(injected.text).to eq 'Content injected by webextensions-selenium-example' @@ -113,9 +113,58 @@ end end + describe 'Special Features' do + it 'casts' do + @driver = Selenium::WebDriver.for :edge + sinks = @driver.cast_sinks + unless sinks.empty? + device_name = sinks.first['name'] + @driver.start_cast_tab_mirroring(device_name) + expect { @driver.stop_casting(device_name) }.not_to raise_exception + end + end + + it 'gets and sets network conditions' do + @driver = Selenium::WebDriver.for :edge + @driver.network_conditions = {offline: false, latency: 100, throughput: 200} + expect(@driver.network_conditions).to eq( + 'offline' => false, + 'latency' => 100, + 'download_throughput' => 200, + 'upload_throughput' => 200 + ) + end + + it 'gets the browser logs' do + @driver = Selenium::WebDriver.for :edge + @driver.navigate.to 'https://www.selenium.dev/selenium/web/' + sleep 1 + logs = @driver.logs.get(:browser) + + expect(logs.first.message).to include 'Failed to load resource' + end + + it 'sets permissions' do + @driver = Selenium::WebDriver.for :edge + @driver.navigate.to 'https://www.selenium.dev/selenium/web/' + @driver.add_permission('camera', 'denied') + @driver.add_permissions('clipboard-read' => 'denied', 'clipboard-write' => 'prompt') + expect(permission('camera')).to eq('denied') + expect(permission('clipboard-read')).to eq('denied') + expect(permission('clipboard-write')).to eq('prompt') + end + end + def driver_finder options = Selenium::WebDriver::Options.edge(browser_version: 'stable') - ENV['EDGEDRIVER_BIN'] = Selenium::WebDriver::DriverFinder.path(options, Selenium::WebDriver::Edge::Service) - ENV['EDGE_BIN'] = options.binary + service = Selenium::WebDriver::Service.edge + finder = Selenium::WebDriver::DriverFinder.new(options, service) + ENV['EDGEDRIVER_BIN'] = finder.driver_path + ENV['EDGE_BIN'] = finder.browser_path + end + + def permission(name) + @driver.execute_async_script('callback = arguments[arguments.length - 1];' \ + 'callback(navigator.permissions.query({name: arguments[0]}));', name)['state'] end end diff --git a/examples/ruby/spec/browsers/firefox_spec.rb b/examples/ruby/spec/browsers/firefox_spec.rb index b67d1dee72f4..7a56053e4838 100644 --- a/examples/ruby/spec/browsers/firefox_spec.rb +++ b/examples/ruby/spec/browsers/firefox_spec.rb @@ -118,11 +118,36 @@ injected = driver.find_element(id: 'webextensions-selenium-example') expect(injected.text).to eq 'Content injected by webextensions-selenium-example' end + + it 'takes full page screenshot' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + Dir.mktmpdir('screenshot_test') do |dir| + screenshot = driver.save_full_page_screenshot(File.join(dir, 'screenshot.png')) + + expect(screenshot).to be_a File + end + end + + it 'sets the context' do + driver.context = 'content' + expect(driver.context).to eq 'content' + end + end + + describe 'Profile' do + it 'creates a new profile' do + profile = Selenium::WebDriver::Firefox::Profile.new + profile['browser.download.dir'] = '/tmp/webdriver-downloads' + options = Selenium::WebDriver::Firefox::Options.new(profile: profile) + expect(options.profile).to eq(profile) + end end def driver_finder options = Selenium::WebDriver::Options.firefox(browser_version: 'stable') - ENV['GECKODRIVER_BIN'] = Selenium::WebDriver::DriverFinder.path(options, Selenium::WebDriver::Firefox::Service) - ENV['FIREFOX_BIN'] = options.binary + service = Selenium::WebDriver::Service.firefox + finder = Selenium::WebDriver::DriverFinder.new(options, service) + ENV['GECKODRIVER_BIN'] = finder.driver_path + ENV['FIREFOX_BIN'] = finder.browser_path end end diff --git a/examples/ruby/spec/browsers/internet_explorer_spec.rb b/examples/ruby/spec/browsers/internet_explorer_spec.rb index f3c2d32a25e9..63aa20096c4b 100644 --- a/examples/ruby/spec/browsers/internet_explorer_spec.rb +++ b/examples/ruby/spec/browsers/internet_explorer_spec.rb @@ -5,9 +5,16 @@ RSpec.describe 'Internet Explorer', exclusive: {platform: :windows} do describe 'Options' do let(:edge_location) { ENV.fetch('http://github.com/EDGE_BIN', nil) } + let(:url) { 'https://www.selenium.dev/selenium/web/' } + + before do + @options = Selenium::WebDriver::IE::Options.new + @options.attach_to_edge_chrome = true + @options.edge_executable_path = edge_location + end it 'basic options Win10' do - options = Selenium::WebDriver::Options.ie + options = Selenium::WebDriver::IE::Options.new options.attach_to_edge_chrome = true options.edge_executable_path = edge_location @driver = Selenium::WebDriver.for :ie, options: options @@ -17,6 +24,47 @@ options = Selenium::WebDriver::Options.ie @driver = Selenium::WebDriver.for :ie, options: options end + + it 'sets the file upload dialog timeout' do + @options.file_upload_dialog_timeout = 2000 + driver = Selenium::WebDriver.for(:ie, options: @options) + driver.quit + end + + it 'ensures a clean session' do + @options.ensure_clean_session = true + driver = Selenium::WebDriver.for(:ie, options: @options) + driver.quit + end + + it 'ignores the zoom setting' do + @options.ignore_zoom_level = true + driver = Selenium::WebDriver.for(:ie, options: @options) + driver.quit + end + + it 'ignores the protected mode settings' do + @options.ignore_protected_mode_settings = true + driver = Selenium::WebDriver.for(:ie, options: @options) + driver.quit + end + + it 'adds the silent option', skip: 'This capability will be added on the release 4.22.0' do + @options.silent = true + expect(@options.silent).to be_truthy + end + + it 'sets the command line options' do + @options.add_argument('-k') + Selenium::WebDriver.for(:ie, options: @options) + end + + it 'launches ie with the create process api', skip: 'When using with IE 8 or higher, it needs a registry value' do + @options.force_create_process_api = true + Selenium::WebDriver.for(:ie, options: @options) + expect(@options.instance_variable_get(:@options)['force_create_process_api']) + .to eq({force_create_process_api: true}) + end end describe 'Service' do diff --git a/examples/ruby/spec/browsers/safari_spec.rb b/examples/ruby/spec/browsers/safari_spec.rb index 09a8bb8d8181..64e89ee22926 100644 --- a/examples/ruby/spec/browsers/safari_spec.rb +++ b/examples/ruby/spec/browsers/safari_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' +# rubocop:disable RSpec/MultipleDescribes RSpec.describe 'Safari', exclusive: {platform: :macosx} do describe 'Options' do it 'basic options' do @@ -13,7 +14,7 @@ describe 'Service' do let(:directory) { "#{Dir.home}/Library/Logs/com.apple.WebDriver/*" } - it 'enable logs' do + it 'enables logs' do original_count = Dir[directory].length service = Selenium::WebDriver::Service.safari @@ -32,3 +33,13 @@ end end end + +RSpec.describe 'Safari Technology Preview', skip: 'This test is being skipped as GitHub Actions ' \ + 'have no support for Safari Technology Preview' do + it 'sets the technology preview' do + Selenium::WebDriver::Safari.technology_preview! + local_driver = Selenium::WebDriver.for :safari + expect(local_driver.capabilities.browser_name).to eq 'Safari Technology Preview' + end +end +# rubocop:enable RSpec/MultipleDescribes diff --git a/examples/ruby/spec/drivers/http_client_spec.rb b/examples/ruby/spec/drivers/http_client_spec.rb index fd0c169763d3..b8fdc6e9a675 100644 --- a/examples/ruby/spec/drivers/http_client_spec.rb +++ b/examples/ruby/spec/drivers/http_client_spec.rb @@ -3,4 +3,17 @@ require 'spec_helper' RSpec.describe 'HTTP Client' do + let(:url) { 'https://www.selenium.dev/selenium/web/' } + + it 'sets client configuration' do + client = Selenium::WebDriver::Remote::Http::Default.new(open_timeout: 30, read_timeout: 30) + expect(client.open_timeout).to eq 30 + end + + it 'uses the custom http client' do + client = Selenium::WebDriver::Remote::Http::Default.new + driver = Selenium::WebDriver.for :chrome, http_client: client + driver.get(url) + driver.quit + end end diff --git a/examples/ruby/spec/drivers/options_spec.rb b/examples/ruby/spec/drivers/options_spec.rb index dcf6194c9cd6..0f3171050c53 100644 --- a/examples/ruby/spec/drivers/options_spec.rb +++ b/examples/ruby/spec/drivers/options_spec.rb @@ -2,5 +2,132 @@ require 'spec_helper' -RSpec.describe 'Options' do +RSpec.describe 'Chrome' do + describe 'Driver Options' do + let(:chrome_location) { driver_finder && ENV.fetch('http://github.com/CHROME_BIN', nil) } + let(:url) { 'https://www.selenium.dev/selenium/web/' } + + it 'page load strategy normal' do + options = Selenium::WebDriver::Options.chrome + options.page_load_strategy = :normal + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'page load strategy eager' do + options = Selenium::WebDriver::Options.chrome + options.page_load_strategy = :eager + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'page load strategy none' do + options = Selenium::WebDriver::Options.chrome + options.page_load_strategy = :none + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets remote capabilities', skip: 'this is example code that will not execute' do + options = Selenium::WebDriver::Options.firefox + options.platform_name = 'Windows 10' + options.browser_version = 'latest' + cloud_options = {} + cloud_options[:build] = my_test_build + cloud_options[:name] = my_test_name + options.add_option('cloud:options', cloud_options) + driver = Selenium::WebDriver.for :remote, capabilities: options + driver.get(url) + driver.quit + end + + it 'accepts untrusted certificates' do + options = Selenium::WebDriver::Options.chrome + options.accept_insecure_certs = true + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets unhandled prompt behavior' do + options = Selenium::WebDriver::Options.chrome + options.unhandled_prompt_behavior = :accept + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets window rect' do + options = Selenium::WebDriver::Options.firefox + options.set_window_rect = true + + driver = Selenium::WebDriver.for :firefox, options: options + driver.get(url) + driver.quit + end + + it 'sets strict file interactability' do + options = Selenium::WebDriver::Options.chrome + options.strict_file_interactability = true + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets the proxy' do + options = Selenium::WebDriver::Options.chrome + options.proxy = Selenium::WebDriver::Proxy.new(http: 'myproxy.com:8080') + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets the implicit timeout' do + options = Selenium::WebDriver::Options.chrome + options.timeouts = {implicit: 1} + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets the page load timeout' do + options = Selenium::WebDriver::Options.chrome + options.timeouts = {page_load: 400_000} + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets the script timeout' do + options = Selenium::WebDriver::Options.chrome + options.timeouts = {script: 40_000} + + driver = Selenium::WebDriver.for :chrome, options: options + driver.get(url) + driver.quit + end + + it 'sets capabilities in the pre-selenium 4 way', skip: 'this is example code that will not execute' do + caps = Selenium::WebDriver::Remote::Capabilities.firefox + caps[:platform] = 'Windows 10' + caps[:version] = '92' + caps[:build] = my_test_build + caps[:name] = my_test_name + driver = Selenium::WebDriver.for :remote, url: cloud_url, desired_capabilities: caps + driver.get(url) + driver.quit + end + end end diff --git a/examples/ruby/spec/drivers/remote_webdriver_spec.rb b/examples/ruby/spec/drivers/remote_webdriver_spec.rb index e2673c89e349..cc517c421330 100644 --- a/examples/ruby/spec/drivers/remote_webdriver_spec.rb +++ b/examples/ruby/spec/drivers/remote_webdriver_spec.rb @@ -47,12 +47,12 @@ driver.get('https://www.selenium.dev/selenium/web/downloads/download.html') driver.find_element(id: 'file-1').click driver.find_element(id: 'file-2').click - wait.until { driver.downloadable_files.include? 'file_2.jpg' } + wait.until { driver.downloadable_files.include?('file_2.jpg') && driver.downloadable_files.include?('file_1.txt') } files = driver.downloadable_files - expect(files).to eq file_names - downloadable_file = files.first + expect(files.sort).to eq file_names.sort + downloadable_file = 'file_1.txt' driver.download_file(downloadable_file, target_directory) diff --git a/examples/ruby/spec/drivers/service_spec.rb b/examples/ruby/spec/drivers/service_spec.rb index c08b27157584..c9d45a05a83f 100644 --- a/examples/ruby/spec/drivers/service_spec.rb +++ b/examples/ruby/spec/drivers/service_spec.rb @@ -33,7 +33,9 @@ def driver_finder options = Selenium::WebDriver::Options.chrome(browser_version: 'stable') - ENV['CHROMEDRIVER_BIN'] = Selenium::WebDriver::DriverFinder.path(options, Selenium::WebDriver::Chrome::Service) - ENV['CHROME_BIN'] = options.binary + service = Selenium::WebDriver::Service.chrome + finder = Selenium::WebDriver::DriverFinder.new(options, service) + ENV['CHROMEDRIVER_BIN'] = finder.driver_path + ENV['CHROME_BIN'] = finder.browser_path end end diff --git a/examples/ruby/spec/elements/finders_spec.rb b/examples/ruby/spec/elements/finders_spec.rb index 9918bb1d4b1b..5c6aa67dc841 100644 --- a/examples/ruby/spec/elements/finders_spec.rb +++ b/examples/ruby/spec/elements/finders_spec.rb @@ -4,4 +4,39 @@ RSpec.describe 'Element Finders' do let(:driver) { start_session } + + context 'without executing finders', skip: 'these are just examples, not actual tests' do + it 'finds the first matching element' do + driver.find_element(class: 'tomatoes') + end + + it 'uses a subset of the dom to find an element' do + fruits = driver.find_element(id: 'fruits') + fruits.find_element(class: 'tomatoes') + end + + it 'uses an optimized locator' do + driver.find_element(css: '#fruits .tomatoes') + end + + it 'finds all matching elements' do + driver.find_elements(tag_name: 'li') + end + + it 'gets an element from a collection' do + elements = driver.find_elements(:tag_name, 'p') + elements.each { |e| puts e.text } + end + + it 'finds element from element' do + element = driver.find_element(:tag_name, 'div') + elements = element.find_elements(:tag_name, 'p') + elements.each { |e| puts e.text } + end + + it 'find active element' do + driver.find_element(css: '[name="q"]').send_keys('webElement') + driver.switch_to.active_element.attribute('title') + end + end end diff --git a/examples/ruby/spec/elements/information_spec.rb b/examples/ruby/spec/elements/information_spec.rb index 69b06716a2b0..b3e159d3e06e 100644 --- a/examples/ruby/spec/elements/information_spec.rb +++ b/examples/ruby/spec/elements/information_spec.rb @@ -4,4 +4,48 @@ RSpec.describe 'Element Information' do let(:driver) { start_session } + let(:url) { 'https://www.selenium.dev/selenium/web/inputs.html' } + + before { driver.get(url) } + + it 'checks if an element is displayed' do + displayed_value = driver.find_element(name: 'email_input').displayed? + expect(displayed_value).to be_truthy + end + + it 'checks if an element is enabled' do + enabled_value = driver.find_element(name: 'email_input').enabled? + expect(enabled_value).to be_truthy + end + + it 'checks if an element is selected' do + selected_value = driver.find_element(name: 'email_input').selected? + expect(selected_value).to be_falsey + end + + it 'gets the tag name of an element' do + tag_name = driver.find_element(name: 'email_input').tag_name + expect(tag_name).not_to be_empty + end + + it 'gets the size and position of an element' do + size = driver.find_element(name: 'email_input').size + expect(size.width).to be_positive + expect(size.height).to be_positive + end + + it 'gets the css value of an element' do + css_value = driver.find_element(name: 'email_input').css_value('background-color') + expect(css_value).not_to be_empty + end + + it 'gets the text of an element' do + text = driver.find_element(xpath: '//h1').text + expect(text).to eq('Testing Inputs') + end + + it 'gets the attribute value of an element' do + attribute_value = driver.find_element(name: 'number_input').attribute('value') + expect(attribute_value).not_to be_empty + end end diff --git a/examples/ruby/spec/elements/interaction_spec.rb b/examples/ruby/spec/elements/interaction_spec.rb index 31711d6cee9a..b0355a77ae69 100644 --- a/examples/ruby/spec/elements/interaction_spec.rb +++ b/examples/ruby/spec/elements/interaction_spec.rb @@ -4,4 +4,15 @@ RSpec.describe 'Element Interaction' do let(:driver) { start_session } + + before { driver.get 'https://www.selenium.dev/selenium/web/inputs.html' } + + it 'clicks an element' do + driver.find_element(name: 'color_input').click + end + + it 'clears and send keys to an element' do + driver.find_element(name: 'email_input').clear + driver.find_element(name: 'email_input').send_keys 'admin@localhost.dev' + end end diff --git a/examples/ruby/spec/elements/locators_spec.rb b/examples/ruby/spec/elements/locators_spec.rb index 2882d196f988..99e1544d1a60 100644 --- a/examples/ruby/spec/elements/locators_spec.rb +++ b/examples/ruby/spec/elements/locators_spec.rb @@ -2,6 +2,62 @@ require 'spec_helper' -RSpec.describe 'Element Locators' do - let(:driver) { start_session } +RSpec.describe 'Element Locators', skip: 'These are reference following the documentation example' do + it 'finds element by class name' do + driver.find_element(class: 'information') + end + + it 'finds element by css selector' do + driver.find_element(css: '#fname') + end + + it 'finds element by id' do + driver.find_element(id: 'lname') + end + + it 'find element by name' do + driver.find_element(name: 'newsletter') + end + + it 'finds element by link text' do + driver.find_element(link_text: 'Selenium Official Page') + end + + it 'finds element by partial link text' do + driver.find_element(partial_link_text: 'Official Page') + end + + it 'finds element by tag name' do + driver.find_element(tag_name: 'a') + end + + it 'finds element by xpath' do + driver.find_element(xpath: "//input[@value='f']") + end + + context 'with relative locators' do + it 'finds element above' do + driver.find_element({relative: {tag_name: 'input', above: {id: 'password'}}}) + end + + it 'finds element below' do + driver.find_element({relative: {tag_name: 'input', below: {id: 'email'}}}) + end + + it 'finds element to the left' do + driver.find_element({relative: {tag_name: 'button', left: {id: 'submit'}}}) + end + + it 'finds element to the right' do + driver.find_element({relative: {tag_name: 'button', right: {id: 'cancel'}}}) + end + + it 'finds near element' do + driver.find_element({relative: {tag_name: 'input', near: {id: 'lbl-email'}}}) + end + + it 'chains relative locators' do + driver.find_element({relative: {tag_name: 'button', below: {id: 'email'}, right: {id: 'cancel'}}}) + end + end end diff --git a/examples/ruby/spec/getting_started/using_selenium_spec.rb b/examples/ruby/spec/getting_started/using_selenium_spec.rb index e371cf3699bc..f972389fca0b 100644 --- a/examples/ruby/spec/getting_started/using_selenium_spec.rb +++ b/examples/ruby/spec/getting_started/using_selenium_spec.rb @@ -1,28 +1,29 @@ # frozen_string_literal: true require 'spec_helper' +require 'selenium-webdriver' RSpec.describe 'Using Selenium' do - it 'uses eight components' do - driver = Selenium::WebDriver.for :chrome + before do + @driver = Selenium::WebDriver.for :chrome + end - driver.get('https://www.selenium.dev/selenium/web/web-form.html') + it 'uses eight components' do + @driver.get('https://www.selenium.dev/selenium/web/web-form.html') - title = driver.title + title = @driver.title expect(title).to eq('Web form') - driver.manage.timeouts.implicit_wait = 500 + @driver.manage.timeouts.implicit_wait = 500 - text_box = driver.find_element(name: 'my-text') - submit_button = driver.find_element(tag_name: 'button') + text_box = @driver.find_element(name: 'my-text') + submit_button = @driver.find_element(tag_name: 'button') text_box.send_keys('Selenium') submit_button.click - message = driver.find_element(id: 'message') + message = @driver.find_element(id: 'message') value = message.text expect(value).to eq('Received!') - - driver.quit end end diff --git a/examples/ruby/spec/hello/hello_selenium_spec.rb b/examples/ruby/spec/hello/hello_selenium.rb similarity index 79% rename from examples/ruby/spec/hello/hello_selenium_spec.rb rename to examples/ruby/spec/hello/hello_selenium.rb index c8078fe08a64..52b2d69d81f8 100644 --- a/examples/ruby/spec/hello/hello_selenium_spec.rb +++ b/examples/ruby/spec/hello/hello_selenium.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome diff --git a/examples/ruby/spec/interactions/alerts_spec.rb b/examples/ruby/spec/interactions/alerts_spec.rb index b2dd8ca218e0..b8337db2fcdc 100644 --- a/examples/ruby/spec/interactions/alerts_spec.rb +++ b/examples/ruby/spec/interactions/alerts_spec.rb @@ -4,4 +4,47 @@ RSpec.describe 'Alerts' do let(:driver) { start_session } + + before do + driver.navigate.to 'https://selenium.dev/' + end + + it 'interacts with an alert' do + driver.execute_script 'alert("Hello, World!")' + + # Store the alert reference in a variable + alert = driver.switch_to.alert + + # Get the text of the alert + alert.text + + # Press on Cancel button + alert.dismiss + end + + it 'interacts with a confirm' do + driver.execute_script 'confirm("Are you sure?")' + + # Store the alert reference in a variable + alert = driver.switch_to.alert + + # Get the text of the alert + alert.text + + # Press on Cancel button + alert.dismiss + end + + it 'interacts with a prompt' do + driver.execute_script 'prompt("What is your name?")' + + # Store the alert reference in a variable + alert = driver.switch_to.alert + + # Type a message + alert.send_keys('selenium') + + # Press on Ok button + alert.accept + end end diff --git a/examples/ruby/spec/interactions/browser_spec.rb b/examples/ruby/spec/interactions/browser_spec.rb new file mode 100644 index 000000000000..f404a6d9eee4 --- /dev/null +++ b/examples/ruby/spec/interactions/browser_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Browser' do + let(:driver) { start_session } + + it 'gets the current title' do + driver.navigate.to 'https://www.selenium.dev/' + current_title = driver.title + expect(current_title).to eq 'Selenium' + end + + it 'gets the current url' do + driver.navigate.to 'https://www.selenium.dev/' + current_url = driver.current_url + expect(current_url).to eq 'https://www.selenium.dev/' + end +end diff --git a/examples/ruby/spec/interactions/cookies_spec.rb b/examples/ruby/spec/interactions/cookies_spec.rb index fe5b3dfc4235..b679a720ba20 100644 --- a/examples/ruby/spec/interactions/cookies_spec.rb +++ b/examples/ruby/spec/interactions/cookies_spec.rb @@ -4,4 +4,55 @@ RSpec.describe 'Cookies' do let(:driver) { start_session } + + it 'adds a cookie' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + # Add cookie into current browser context + driver.manage.add_cookie(name: 'key', value: 'value') + # Verify cookie was added + expect(driver.manage.cookie_named('key')[:value]).to eq('value') + end + + it 'gets a named cookie' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + # Add cookie into current browser context + driver.manage.add_cookie(name: 'foo', value: 'bar') + # Get cookie details with named cookie 'foo' + cookie = driver.manage.cookie_named('foo') + expect(cookie[:value]).to eq('bar') + end + + it 'gets all cookies' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + # Add cookies into current browser context + driver.manage.add_cookie(name: 'test1', value: 'cookie1') + driver.manage.add_cookie(name: 'test2', value: 'cookie2') + # Get cookies + cookies = driver.manage.all_cookies + # Verify both cookies exist with correct values + test1_cookie = cookies.find { |c| c[:name] == 'test1' } + test2_cookie = cookies.find { |c| c[:name] == 'test2' } + expect(test1_cookie[:value]).to eq('cookie1') + expect(test2_cookie[:value]).to eq('cookie2') + end + + it 'deletes a cookie by name' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + driver.manage.add_cookie(name: 'test1', value: 'cookie1') + # Delete cookie named + driver.manage.delete_cookie('test1') + # Verify cookie is deleted + expect { driver.manage.cookie_named('test1') }.to raise_error(Selenium::WebDriver::Error::NoSuchCookieError) + end + + it 'deletes all cookies' do + driver.navigate.to 'https://www.selenium.dev/selenium/web/blank.html' + # Add cookies into current browser context + driver.manage.add_cookie(name: 'test1', value: 'cookie1') + driver.manage.add_cookie(name: 'test2', value: 'cookie2') + # Delete All cookies + driver.manage.delete_all_cookies + # Verify all cookies are deleted + expect(driver.manage.all_cookies.size).to eq(0) + end end diff --git a/examples/ruby/spec/interactions/navigation_spec.rb b/examples/ruby/spec/interactions/navigation_spec.rb index d4a16a415b52..badebb5bc0b6 100644 --- a/examples/ruby/spec/interactions/navigation_spec.rb +++ b/examples/ruby/spec/interactions/navigation_spec.rb @@ -2,6 +2,33 @@ require 'spec_helper' -RSpec.describe 'Navigation' do +RSpec.describe 'Browser' do let(:driver) { start_session } + + it 'navigates to a page' do + driver.navigate.to 'https://www.selenium.dev/' + driver.get 'https://www.selenium.dev/' + expect(driver.current_url).to eq 'https://www.selenium.dev/' + end + + it 'navigates back' do + driver.navigate.to 'https://www.selenium.dev/' + driver.navigate.to 'https://www.selenium.dev/selenium/web/inputs.html' + driver.navigate.back + expect(driver.current_url).to eq 'https://www.selenium.dev/' + end + + it 'navigates forward' do + driver.navigate.to 'https://www.selenium.dev/' + driver.navigate.to 'https://www.selenium.dev/selenium/web/inputs.html' + driver.navigate.back + driver.navigate.forward + expect(driver.current_url).to eq 'https://www.selenium.dev/selenium/web/inputs.html' + end + + it 'refreshes the page' do + driver.navigate.to 'https://www.selenium.dev/' + driver.navigate.refresh + expect(driver.current_url).to eq 'https://www.selenium.dev/' + end end diff --git a/examples/ruby/spec/interactions/windows_spec.rb b/examples/ruby/spec/interactions/windows_spec.rb index e7dec85fe82a..a20c7a77c507 100644 --- a/examples/ruby/spec/interactions/windows_spec.rb +++ b/examples/ruby/spec/interactions/windows_spec.rb @@ -4,4 +4,16 @@ RSpec.describe 'Windows' do let(:driver) { start_session } + + it 'opens new tab' do + driver.switch_to.new_window(:tab) + + expect(driver.window_handles.size).to eq 2 + end + + it 'opens new window' do + driver.switch_to.new_window(:window) + + expect(driver.window_handles.size).to eq 2 + end end diff --git a/examples/ruby/spec/selenium_manager/usage.rb b/examples/ruby/spec/selenium_manager/usage.rb new file mode 100644 index 000000000000..bff0161ef79f --- /dev/null +++ b/examples/ruby/spec/selenium_manager/usage.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'selenium-webdriver' + +def setup_without_selenium_manager + service = Selenium::WebDriver::Chrome::Service.new(path: '/path/to/chromedriver') + driver = Selenium::WebDriver.for(:chrome, service: service) + driver.get('https://www.selenium.dev/documentation/selenium_manager/') + driver.quit +end + +def setup_with_selenium_manager + driver = Selenium::WebDriver.for(:chrome) # Selenium Manager handles the driver automatically + driver.get('https://www.selenium.dev/documentation/selenium_manager/') + driver.quit +end diff --git a/examples/ruby/spec/spec_helper.rb b/examples/ruby/spec/spec_helper.rb index 902e708d8c6c..a68a4526f0be 100644 --- a/examples/ruby/spec/spec_helper.rb +++ b/examples/ruby/spec/spec_helper.rb @@ -9,13 +9,15 @@ # Disable RSpec exposing methods globally on `Module` and `main` config.disable_monkey_patching! + Dir.mktmpdir('tmp') + config.example_status_persistence_file_path = 'tmp/examples.txt' config.expect_with :rspec do |c| c.syntax = :expect end config.before do |example| - bug_tracker = 'https://gigithub.com/SeleniumHQ/seleniumhq.github.io/issues' + bug_tracker = 'https://github.com/SeleniumHQ/seleniumhq.github.io/issues' guards = Selenium::WebDriver::Support::Guards.new(example, bug_tracker: bug_tracker) guards.add_condition(:platform, Selenium::WebDriver::Platform.os) @@ -28,7 +30,15 @@ config.after { @driver&.quit } def start_session - @driver = Selenium::WebDriver.for :chrome + options = Selenium::WebDriver::Chrome::Options.new + options.add_argument('disable-search-engine-choice-screen') + options.add_argument('--no-sandbox') + @driver = Selenium::WebDriver.for(:chrome, options: options) + end + + def start_bidi_session + options = Selenium::WebDriver::Chrome::Options.new(web_socket_url: true) + @driver = Selenium::WebDriver.for :chrome, options: options end def start_firefox diff --git a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.crx b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.crx index 38b38003b7ec..941114eb446e 100644 Binary files a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.crx and b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.crx differ diff --git a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.xpi b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.xpi index 34b0ae3913f7..dca8e2e12312 100644 Binary files a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.xpi and b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example.xpi differ diff --git a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example/manifest.json b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example/manifest.json index e938974a20b1..69e480dc60dd 100644 --- a/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example/manifest.json +++ b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example/manifest.json @@ -1,17 +1,30 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "webextensions-selenium-example", - "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium" , + "description": "Inject a div with id webextensions-selenium-example to verify that WebExtensions work in Firefox/Selenium", "version": "0.1", "content_scripts": [ { - "matches": ["https://*/*","http://*/*"], - "js": ["inject.js"] + "matches": [ + "https://*/*", + "http://*/*" + ], + "js": [ + "inject.js" + ] } ], - "applications": { - "gecko": { - "id": "webextensions-selenium-example@example.com" - } - } + "permissions": [ + "storage", + "scripting" + ], + "host_permissions": [ + "https://*/*", + "http://*/*" + ], + "browser_specific_settings": { + "gecko": { + "id": "webextensions-selenium-example-v3@example.com" + } + } } diff --git a/examples/selenium-server-4.15.0.jar b/examples/selenium-server-4.32.0.jar similarity index 75% rename from examples/selenium-server-4.15.0.jar rename to examples/selenium-server-4.32.0.jar index b555dceaa6d4..1faf9005d530 100644 Binary files a/examples/selenium-server-4.15.0.jar and b/examples/selenium-server-4.32.0.jar differ diff --git a/favicon.ico b/favicon.ico index cf9d9dda1ffe..80bf7ae9a9ac 100755 Binary files a/favicon.ico and b/favicon.ico differ diff --git a/netlify.toml b/netlify.toml index 2c3646f185d9..9134e4bc7067 100644 --- a/netlify.toml +++ b/netlify.toml @@ -3,8 +3,8 @@ publish = "website_and_docs/public" command = "chmod +x build-site.sh && ./build-site.sh" [context.production.environment] -NODE_VERSION = "18.14.1" -HUGO_VERSION = "0.110.0" +NODE_VERSION = "22.13.0" +HUGO_VERSION = "0.125.4" GO_VERSION = "1.20.1" HUGO_ENV = "production" @@ -12,14 +12,14 @@ HUGO_ENV = "production" command = "chmod +x build-site.sh && ./build-site.sh" [context.deploy-preview.environment] -NODE_VERSION = "18.14.1" -HUGO_VERSION = "0.110.0" +NODE_VERSION = "22.13.0" +HUGO_VERSION = "0.125.4" GO_VERSION = "1.20.1" [context.branch-deploy] command = "chmod +x build-site.sh && ./build-site.sh" [context.branch-deploy.environment] -NODE_VERSION = "18.14.1" -HUGO_VERSION = "0.110.0" +NODE_VERSION = "22.13.0" +HUGO_VERSION = "0.125.4" GO_VERSION = "1.20.1" diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000000..17ef8d85f042 --- /dev/null +++ b/renovate.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "ignorePaths": [ + "**/node_modules/**", + "**/bower_components/**", + "**/vendor/**" + ] +} diff --git a/scripts/latest-nightly-version.py b/scripts/latest-nightly-version.py new file mode 100755 index 000000000000..cd51ba10cebf --- /dev/null +++ b/scripts/latest-nightly-version.py @@ -0,0 +1,31 @@ +import subprocess +import json +import argparse + +def get_latest_nightly_version(package_type, package_name): + path_packages_api = f"orgs/seleniumhq/packages/{package_type}/{package_name}/versions" + accept_header = "Accept: application/vnd.github+json" + version_header = "X-GitHub-Api-Version: 2022-11-28" + + gh_api_command = [ + "gh", "api", "-H", accept_header, "-H", version_header, path_packages_api + ] + + result = subprocess.run(gh_api_command, capture_output=True, text=True) + if result.returncode != 0: + raise Exception(f"Error executing gh api command: {result.stderr}") + + versions = json.loads(result.stdout) + latest_version = versions[0]['name'] + return latest_version + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Get the latest nightly version of a package.') + parser.add_argument('package_type', type=str, help='The type of the package') + parser.add_argument('package_name', type=str, help='The name of the package') + + args = parser.parse_args() + package_type = args.package_type + package_name = args.package_name + + print(get_latest_nightly_version(package_type, package_name)) \ No newline at end of file diff --git a/scripts/latest-python-nightly-version.py b/scripts/latest-python-nightly-version.py new file mode 100755 index 000000000000..fb1f18e812fa --- /dev/null +++ b/scripts/latest-python-nightly-version.py @@ -0,0 +1,12 @@ +import requests +import json + +response = requests.get("https://test.pypi.org/pypi/selenium/json") +data = response.json() + +# Extract versions and their upload times +versions = data['releases'] +sorted_versions = sorted(versions.items(), key=lambda item: item[1][0]['upload_time'], reverse=True) +latest_version = sorted_versions[0][0] + +print(latest_version) \ No newline at end of file diff --git a/scripts/release-updates.sh b/scripts/release-updates.sh new file mode 100755 index 000000000000..aa6fd79079e1 --- /dev/null +++ b/scripts/release-updates.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +VERSION_STR="$1" +NEW_VERSION=$(echo "$VERSION_STR" | cut -d. -f2) +OLD_VERSION=$((NEW_VERSION - 1)) + +FILES=( + "website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html" + "website_and_docs/layouts/downloads/list.html" +) + +for FILE_PATH in "${FILES[@]}"; do + sed -i '' -E "s/4\.$OLD_VERSION\.[0-9]+/4.$NEW_VERSION.0/g" "$FILE_PATH" +done + +OLD_BLOG="website_and_docs/content/blog/2025/selenium-4-$OLD_VERSION-released.md" +NEW_BLOG="website_and_docs/content/blog/2025/selenium-4-$NEW_VERSION-released.md" +cp "$OLD_BLOG" "$NEW_BLOG" +git add "$NEW_BLOG" + +sed -i '' "s/4\.$OLD_VERSION/4\.$NEW_VERSION/g" "$NEW_BLOG" + +SINCE_COMMIT_DATE=$(gh api repos/seleniumhq/selenium/commits/selenium-4.${OLD_VERSION}.0 --jq '.commit.committer.date') +UNTIL_COMMIT_DATE=$(gh api repos/seleniumhq/selenium/commits/selenium-4.${NEW_VERSION}.0 --jq '.commit.committer.date') + +echo "Selenium Contributors" +gh api --method GET /repos/seleniumhq/selenium/commits -f since="$SINCE_COMMIT_DATE" -f until="$UNTIL_COMMIT_DATE" -f per_page=1000 \ +--jq 'map(.author.login) | unique | sort | map("{{< gh-user \"https://api.github.com/users/" + . + "\" >}}") | .[]' + +echo +echo "Docs Contributors" +gh api --method GET /repos/seleniumhq/seleniumhq.github.io/commits -f since="$SINCE_COMMIT_DATE" -f until="$UNTIL_COMMIT_DATE" -f per_page=1000 \ +--jq 'map(.author.login) | unique | sort | map("{{< gh-user \"https://api.github.com/users/" + . + "\" >}}") | .[]' + +echo +echo "Docker Contributors" +gh api --method GET /repos/seleniumhq/docker-selenium/commits -f since="$SINCE_COMMIT_DATE" -f until="$UNTIL_COMMIT_DATE" -f per_page=1000 \ +--jq 'map(.author.login) | unique | sort | map("{{< gh-user \"https://api.github.com/users/" + . + "\" >}}") | .[]' diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 000000000000..663bd1f6a2ae --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file diff --git a/scripts/update-versions.sh b/scripts/update-versions.sh index ad94cdde9924..735d053c2252 100755 --- a/scripts/update-versions.sh +++ b/scripts/update-versions.sh @@ -6,8 +6,6 @@ OLD_VERSION=$((NEW_VERSION - 1)) NEXT_VERSION=$((NEW_VERSION + 1)) FILES=( - "website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html" - "website_and_docs/layouts/downloads/list.html" "examples/java/build.gradle" "examples/dotnet/SeleniumDocs/SeleniumDocs.csproj" "examples/python/requirements.txt" diff --git a/website_and_docs/assets/scss/_links.scss b/website_and_docs/assets/scss/_links.scss index 522402ab7947..c0555d8f57f1 100644 --- a/website_and_docs/assets/scss/_links.scss +++ b/website_and_docs/assets/scss/_links.scss @@ -1,6 +1,5 @@ .selenium-link { color: $primary; - border-bottom: 2px solid; transition: 0.3s; } @@ -8,13 +7,12 @@ color: $selenium-cyan-color; } -p > a, main a { +p > a, main a, div > a { color: $primary !important; - border-bottom: 2px solid; transition: 0.3s; } -p > a:hover, main a:hover { +p > a:hover, main a:hover, div > a:hover { color: $selenium-cyan-color !important; } diff --git a/website_and_docs/assets/scss/_styles_project.scss b/website_and_docs/assets/scss/_styles_project.scss index 884e084ab1c4..9f0878022493 100644 --- a/website_and_docs/assets/scss/_styles_project.scss +++ b/website_and_docs/assets/scss/_styles_project.scss @@ -10,3 +10,5 @@ @import "http://github.com/logo"; @import "http://github.com/screen"; @import "http://github.com/tabs"; + +.td-page-meta--project-issue { display: none !important; } diff --git a/website_and_docs/content/about/_index.html b/website_and_docs/content/about/_index.html index 0c2ad4659d1b..fb4918c700e7 100644 --- a/website_and_docs/content/about/_index.html +++ b/website_and_docs/content/about/_index.html @@ -104,7 +104,7 @@

Get involved