diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index ebba025a718e..243c75765a84 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Node 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 diff --git a/.github/workflows/ruby-examples.yml b/.github/workflows/ruby-examples.yml index eee3b46a192d..b0717816e088 100644 --- a/.github/workflows/ruby-examples.yml +++ b/.github/workflows/ruby-examples.yml @@ -41,13 +41,17 @@ jobs: if: matrix.os != 'windows' run: | sudo rm -rf $CHROMEWEBDRIVER $EDGEWEBDRIVER $GECKOWEBDRIVER - - name: Start Xvfb + - name: Setup Fluxbox and Xvfb if: matrix.os == 'ubuntu' - run: Xvfb :99 & + 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 Nightly non-Windows if: matrix.release == 'nightly' && matrix.os != 'windows' @@ -83,11 +87,43 @@ jobs: with: distribution: 'temurin' java-version: 11 - - name: Run tests + - 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/README.md b/README.md index 42fcd27a138a..b58633891dfa 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ 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` diff --git a/examples/dotnet/SeleniumDocs/BaseTest.cs b/examples/dotnet/SeleniumDocs/BaseTest.cs index 18c17c3bb3f0..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.29.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)); diff --git a/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj b/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj index 264303d3d127..bb82466b3d91 100644 --- a/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj +++ b/examples/dotnet/SeleniumDocs/SeleniumDocs.csproj @@ -10,8 +10,8 @@ - - + + 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/java/build.gradle b/examples/java/build.gradle index cd41e4b42842..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.29.0' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.12.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/pom.xml b/examples/java/pom.xml index ca1ac5f7358c..6b66cc9c188b 100644 --- a/examples/java/pom.xml +++ b/examples/java/pom.xml @@ -13,7 +13,7 @@ 17 17 UTF-8 - 4.29.0 + 4.31.0 @@ -40,7 +40,7 @@ org.junit.jupiter junit-jupiter-engine - 5.12.0 + 5.12.1 test @@ -55,7 +55,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.3 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 index 79b94137deaf..b7505c31f58b 100644 --- a/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpApiTest.java +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/CdpApiTest.java @@ -16,9 +16,9 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.HasDevTools; -import org.openqa.selenium.devtools.v131.browser.Browser; -import org.openqa.selenium.devtools.v131.network.Network; -import org.openqa.selenium.devtools.v131.network.model.Headers; +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 { @@ -27,7 +27,7 @@ public class CdpApiTest extends BaseTest { @BeforeEach public void createSession() { ChromeOptions options = getDefaultChromeOptions(); - options.setBrowserVersion("131"); + options.setBrowserVersion("134"); driver = new ChromeDriver(options); wait = new WebDriverWait(driver, Duration.ofSeconds(10)); } diff --git a/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java b/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java index d9fb6f454866..96d0c1ad5ab0 100644 --- a/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java +++ b/examples/java/src/test/java/dev/selenium/bidi/cdp/NetworkTest.java @@ -21,10 +21,10 @@ import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.HasDevTools; import org.openqa.selenium.devtools.NetworkInterceptor; -import org.openqa.selenium.devtools.v131.browser.Browser; -import org.openqa.selenium.devtools.v131.network.Network; -import org.openqa.selenium.devtools.v131.performance.Performance; -import org.openqa.selenium.devtools.v131.performance.model.Metric; +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; 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/javascript/package-lock.json b/examples/javascript/package-lock.json index 282993925e2a..b068602c0bfd 100644 --- a/examples/javascript/package-lock.json +++ b/examples/javascript/package-lock.json @@ -10,10 +10,10 @@ "license": "Apache-2.0", "dependencies": { "assert": "2.1.0", - "selenium-webdriver": "4.29.0" + "selenium-webdriver": "4.32.0" }, "devDependencies": { - "mocha": "11.1.0" + "mocha": "11.2.2" } }, "node_modules/@bazel/runfiles": { @@ -128,15 +128,6 @@ "node": ">=14" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -161,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", @@ -209,15 +187,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "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" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -227,19 +196,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.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", @@ -300,30 +256,19 @@ } }, "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": { @@ -479,19 +424,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "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", @@ -541,20 +473,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -607,18 +525,6 @@ "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==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/glob/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -746,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", @@ -769,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", @@ -801,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", @@ -828,16 +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, - "license": "MIT", - "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", @@ -993,15 +856,14 @@ } }, "node_modules/mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "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": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", @@ -1012,6 +874,7 @@ "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", @@ -1040,15 +903,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "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", @@ -1164,17 +1018,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "ISC" }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -1205,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": { @@ -1232,9 +1083,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/selenium-webdriver": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.29.0.tgz", - "integrity": "sha512-8XPGtDoji5xk7ZUCzFT1rqHmCp67DCzESsttId7DzmrJmlTRmRLF6X918rbwclcH89amcBNM4zB3lVPj404I0g==", + "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", @@ -1245,6 +1096,7 @@ "url": "https://opencollective.com/selenium" } ], + "license": "Apache-2.0", "dependencies": { "@bazel/runfiles": "^6.3.1", "jszip": "^3.10.1", @@ -1413,19 +1265,6 @@ "node": ">=14.14" } }, - "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, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -1684,12 +1523,6 @@ "dev": true, "optional": true }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1705,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", @@ -1744,12 +1567,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "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 - }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1759,15 +1576,6 @@ "balanced-match": "^1.0.0" } }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "requires": { - "fill-range": "^7.1.1" - } - }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -1812,19 +1620,12 @@ } }, "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": { @@ -1934,15 +1735,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "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", @@ -1977,13 +1769,6 @@ "signal-exit": "^4.0.1" } }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2031,15 +1816,6 @@ } } }, - "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", @@ -2113,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", @@ -2147,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", @@ -2165,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", @@ -2285,14 +2031,13 @@ "dev": true }, "mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "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.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", @@ -2303,6 +2048,7 @@ "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", @@ -2326,12 +2072,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "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", @@ -2408,10 +2148,10 @@ "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": { @@ -2443,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", @@ -2463,9 +2200,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "selenium-webdriver": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.29.0.tgz", - "integrity": "sha512-8XPGtDoji5xk7ZUCzFT1rqHmCp67DCzESsttId7DzmrJmlTRmRLF6X918rbwclcH89amcBNM4zB3lVPj404I0g==", + "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", @@ -2587,15 +2324,6 @@ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==" }, - "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" - } - }, "util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", diff --git a/examples/javascript/package.json b/examples/javascript/package.json index 0713af445189..dc76bb8d3cd5 100644 --- a/examples/javascript/package.json +++ b/examples/javascript/package.json @@ -8,9 +8,9 @@ "license": "Apache-2.0", "dependencies": { "assert": "2.1.0", - "selenium-webdriver": "4.29.0" + "selenium-webdriver": "4.32.0" }, "devDependencies": { - "mocha": "11.1.0" + "mocha": "11.2.2" } } diff --git a/examples/javascript/test/bidirectional/network_events.spec.js b/examples/javascript/test/bidirectional/network_events.spec.js index bade8719a2ef..931a5f2e7087 100644 --- a/examples/javascript/test/bidirectional/network_events.spec.js +++ b/examples/javascript/test/bidirectional/network_events.spec.js @@ -1,6 +1,6 @@ const assert = require("assert"); const firefox = require('selenium-webdriver/firefox'); -const Network = require("selenium-webdriver/bidi/network"); +const { Network } = require("selenium-webdriver/bidi/network"); const {until, Builder} = require("selenium-webdriver"); @@ -76,7 +76,7 @@ describe('Network events', function () { 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[2].request.url.includes('redirected.html')) + assert(beforeRequestEvent[3].request.url.includes('redirected.html')) }) it('can subscribe to response started', async function () { 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/kotlin/pom.xml b/examples/kotlin/pom.xml index b93b1150eebe..bc714bc589cb 100644 --- a/examples/kotlin/pom.xml +++ b/examples/kotlin/pom.xml @@ -9,18 +9,17 @@ 1.0.0 - 2.1.0 + 2.1.20 2.0.17 - 1.5.17 + 1.5.18 - 5.12.0 - 5.2.3 + 5.12.1 - 3.5.2 + 3.5.3 - 1.8 - 4.29.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 2db5f25cb9a5..9650c07cbb5e 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -1,38 +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 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: -## Execute a specific example -To run a specific Selenium Python example, use the following command: -```bash +``` +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. \ No newline at end of file +> 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 f1afcbd854a6..b8b5db656ee3 100644 --- a/examples/python/requirements.txt +++ b/examples/python/requirements.txt @@ -1,7 +1,8 @@ -selenium==4.29.0 +selenium==4.31.0 pytest==8.3.5 -trio==0.29.0 +trio==0.30.0 pytest-trio==0.8.0 -pytest-rerunfailures==14.0 -flake8==7.1.2 +pytest-rerunfailures==15.0 +flake8==7.2.0 requests==2.32.3 +tox==4.25.0 diff --git a/examples/python/tests/bidi/cdp/test_network.py b/examples/python/tests/bidi/cdp/test_network.py index 191d7a4a95ad..c8fe2d4011c9 100644 --- a/examples/python/tests/bidi/cdp/test_network.py +++ b/examples/python/tests/bidi/cdp/test_network.py @@ -2,7 +2,7 @@ import pytest from selenium.webdriver.common.by import By -from selenium.webdriver.common.devtools.v131.network import Headers +from selenium.webdriver.common.devtools.v134.network import Headers @pytest.mark.trio diff --git a/examples/python/tests/conftest.py b/examples/python/tests/conftest.py index 660ce87e396a..4bcfb23090c4 100644 --- a/examples/python/tests/conftest.py +++ b/examples/python/tests/conftest.py @@ -14,6 +14,12 @@ 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(request): marker = request.node.get_closest_marker("driver_type") @@ -142,7 +148,7 @@ def server_old(request): os.path.abspath(__file__) ) ), - "selenium-server-4.29.0.jar", + "selenium-server-4.32.0.jar", ) def wait_for_server(url, timeout): @@ -200,7 +206,7 @@ def server(): ) ) ), - "selenium-server-4.29.0.jar", + "selenium-server-4.32.0.jar", ) args = [ @@ -273,7 +279,7 @@ def grid_server(): ) ) ), - "selenium-server-4.29.0.jar", + "selenium-server-4.32.0.jar", ) args = [ 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/interactions/test_cookies.py b/examples/python/tests/interactions/test_cookies.py index 779965a8a332..0b40e9045172 100644 --- a/examples/python/tests/interactions/test_cookies.py +++ b/examples/python/tests/interactions/test_cookies.py @@ -1,7 +1,7 @@ from selenium import webdriver -def add_cookie(): +def test_add_cookie(): driver = webdriver.Chrome() driver.get("http://www.example.com/") @@ -9,7 +9,7 @@ def add_cookie(): driver.add_cookie({"name": "key", "value": "value"}) -def get_named_cookie(): +def test_get_named_cookie(): driver = webdriver.Chrome() driver.get("http://www.example.com/") @@ -20,7 +20,7 @@ def get_named_cookie(): print(driver.get_cookie("foo")) -def get_all_cookies(): +def test_get_all_cookies(): driver = webdriver.Chrome() driver.get("http://www.example.com/") @@ -31,7 +31,7 @@ def get_all_cookies(): # Get all available cookies print(driver.get_cookies()) -def delete_cookie(): +def test_delete_cookie(): driver = webdriver.Chrome() driver.get("http://www.example.com/") @@ -43,7 +43,7 @@ def delete_cookie(): driver.delete_cookie("test1") -def delete_all_cookies(): +def test_delete_all_cookies(): driver = webdriver.Chrome() driver.get("http://www.example.com/") @@ -55,7 +55,7 @@ def delete_all_cookies(): driver.delete_all_cookies() -def same_side_cookie_attr(): +def test_same_side_cookie_attr(): driver = webdriver.Chrome() driver.get("http://www.example.com/") 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/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/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 17d408f72607..f4987c1f9889 100644 --- a/examples/ruby/Gemfile +++ b/examples/ruby/Gemfile @@ -7,5 +7,5 @@ gem 'rake', '~> 13.0' gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.35' gem 'rubocop-rspec', '~> 3.0' -gem 'selenium-devtools', '= 0.133.0' -gem 'selenium-webdriver', '= 4.29.1' +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 5c54339b78de..b1fb4f3da842 100644 --- a/examples/ruby/Gemfile.lock +++ b/examples/ruby/Gemfile.lock @@ -4,10 +4,10 @@ GEM ast (2.4.2) base64 (0.2.0) diff-lcs (1.5.1) - json (2.10.1) + json (2.10.2) language_server-protocol (3.17.0.4) lint_roller (1.1.0) - logger (1.6.6) + logger (1.7.0) parallel (1.26.3) parser (3.3.7.1) ast (~> 2.4.1) @@ -48,9 +48,9 @@ GEM rubocop (~> 1.72, >= 1.72.1) ruby-progressbar (1.13.0) rubyzip (2.4.1) - selenium-devtools (0.133.0) + selenium-devtools (0.136.0) selenium-webdriver (~> 4.2) - selenium-webdriver (4.29.1) + selenium-webdriver (4.32.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -63,6 +63,8 @@ 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 @@ -73,8 +75,8 @@ DEPENDENCIES rspec (~> 3.0) rubocop (~> 1.35) rubocop-rspec (~> 3.0) - selenium-devtools (= 0.133.0) - selenium-webdriver (= 4.29.1) + selenium-devtools (= 0.136.0) + selenium-webdriver (= 4.32.0) BUNDLED WITH 2.5.6 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/logging_spec.rb b/examples/ruby/spec/bidi/logging_spec.rb index b07b93342628..df010daae8e3 100644 --- a/examples/ruby/spec/bidi/logging_spec.rb +++ b/examples/ruby/spec/bidi/logging_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe 'Logging' do diff --git a/examples/ruby/spec/browsers/chrome_spec.rb b/examples/ruby/spec/browsers/chrome_spec.rb index c15f4d88a978..81fe78c6ed08 100644 --- a/examples/ruby/spec/browsers/chrome_spec.rb +++ b/examples/ruby/spec/browsers/chrome_spec.rb @@ -131,7 +131,8 @@ 'offline' => false, 'latency' => 100, 'download_throughput' => 200, - 'upload_throughput' => 200) + 'upload_throughput' => 200 + ) end it 'gets the browser logs' do diff --git a/examples/ruby/spec/browsers/edge_spec.rb b/examples/ruby/spec/browsers/edge_spec.rb index 225f8c601c40..5820fab3e799 100644 --- a/examples/ruby/spec/browsers/edge_spec.rb +++ b/examples/ruby/spec/browsers/edge_spec.rb @@ -131,7 +131,8 @@ 'offline' => false, 'latency' => 100, 'download_throughput' => 200, - 'upload_throughput' => 200) + 'upload_throughput' => 200 + ) end it 'gets the browser logs' do @@ -164,6 +165,6 @@ def driver_finder def permission(name) @driver.execute_async_script('callback = arguments[arguments.length - 1];' \ - 'callback(navigator.permissions.query({name: arguments[0]}));', name)['state'] + '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 16928c9aa5fc..7a56053e4838 100644 --- a/examples/ruby/spec/browsers/firefox_spec.rb +++ b/examples/ruby/spec/browsers/firefox_spec.rb @@ -89,7 +89,7 @@ describe 'Features' do let(:driver) { start_firefox } - it 'installs addon', :skip => "Skipping tests until Firefox 127 is released" do + it 'installs addon' do extension_file_path = File.expand_path('../spec_support/extensions/webextensions-selenium-example.xpi', __dir__) driver.install_addon(extension_file_path) @@ -99,7 +99,7 @@ expect(injected.text).to eq 'Content injected by webextensions-selenium-example' end - it 'uninstalls addon', :skip => "Skipping tests until Firefox 127 is released" do + it 'uninstalls addon' do extension_file_path = File.expand_path('../spec_support/extensions/webextensions-selenium-example.xpi', __dir__) extension_id = driver.install_addon(extension_file_path) @@ -109,7 +109,7 @@ expect(driver.find_elements(id: 'webextensions-selenium-example')).to be_empty end - it 'installs unsigned addon', :skip => "Skipping tests until Firefox 127 is released" do + it 'installs unsigned addon' do extension_dir_path = File.expand_path('../spec_support/extensions/webextensions-selenium-example/', __dir__) driver.install_addon(extension_dir_path, true) diff --git a/examples/ruby/spec/browsers/safari_spec.rb b/examples/ruby/spec/browsers/safari_spec.rb index a26006070148..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 @@ -33,10 +34,12 @@ end end -RSpec.describe 'Safari Technology Preview', skip: "This test is being skipped as GitHub Actions have no support for Safari Technology Preview" do +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 \ No newline at end of file +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 4a6b05d3b086..b8fdc6e9a675 100644 --- a/examples/ruby/spec/drivers/http_client_spec.rb +++ b/examples/ruby/spec/drivers/http_client_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe 'HTTP Client' do diff --git a/examples/ruby/spec/elements/finders_spec.rb b/examples/ruby/spec/elements/finders_spec.rb index f6523ea029fd..5c6aa67dc841 100644 --- a/examples/ruby/spec/elements/finders_spec.rb +++ b/examples/ruby/spec/elements/finders_spec.rb @@ -12,31 +12,31 @@ it 'uses a subset of the dom to find an element' do fruits = driver.find_element(id: 'fruits') - fruit = fruits.find_element(class: 'tomatoes') + fruits.find_element(class: 'tomatoes') end it 'uses an optimized locator' do - fruit = driver.find_element(css: '#fruits .tomatoes') + driver.find_element(css: '#fruits .tomatoes') end it 'finds all matching elements' do - plants = driver.find_elements(tag_name: 'li') + driver.find_elements(tag_name: 'li') end it 'gets an element from a collection' do - elements = driver.find_elements(:tag_name,'p') + 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') + 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') - attr = driver.switch_to.active_element.attribute('title') + driver.switch_to.active_element.attribute('title') 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 8e50e32f5f1c..f972389fca0b 100644 --- a/examples/ruby/spec/getting_started/using_selenium_spec.rb +++ b/examples/ruby/spec/getting_started/using_selenium_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true + require 'spec_helper' require 'selenium-webdriver' - RSpec.describe 'Using Selenium' do before do @driver = Selenium::WebDriver.for :chrome 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/browser_spec.rb b/examples/ruby/spec/interactions/browser_spec.rb index 8bc281c163aa..f404a6d9eee4 100644 --- a/examples/ruby/spec/interactions/browser_spec.rb +++ b/examples/ruby/spec/interactions/browser_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe 'Browser' do 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 36c60e4ace26..badebb5bc0b6 100644 --- a/examples/ruby/spec/interactions/navigation_spec.rb +++ b/examples/ruby/spec/interactions/navigation_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.describe 'Browser' do 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 a79f81232d01..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) @@ -30,6 +32,7 @@ def start_session 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 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/manifest.json b/examples/ruby/spec/spec_support/extensions/webextensions-selenium-example/manifest.json index a8b4fec6e60f..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 @@ -14,9 +14,17 @@ ] } ], + "permissions": [ + "storage", + "scripting" + ], + "host_permissions": [ + "https://*/*", + "http://*/*" + ], "browser_specific_settings": { "gecko": { "id": "webextensions-selenium-example-v3@example.com" } } -} \ No newline at end of file +} diff --git a/examples/selenium-server-4.29.0.jar b/examples/selenium-server-4.32.0.jar similarity index 84% rename from examples/selenium-server-4.29.0.jar rename to examples/selenium-server-4.32.0.jar index 92c0d43e7b27..1faf9005d530 100644 Binary files a/examples/selenium-server-4.29.0.jar and b/examples/selenium-server-4.32.0.jar differ diff --git a/scripts/release-updates.sh b/scripts/release-updates.sh index 927406e3af79..aa6fd79079e1 100755 --- a/scripts/release-updates.sh +++ b/scripts/release-updates.sh @@ -20,18 +20,19 @@ 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=selenium-4.${OLD_VERSION}.0 -f per_page=1000 \ +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/" + . + "\" >}}") | .[]' -COMMIT_DATE=$(gh api repos/seleniumhq/selenium/commits/selenium-4.${OLD_VERSION}.0 --jq '.commit.committer.date') - echo echo "Docs Contributors" -gh api --method GET /repos/seleniumhq/seleniumhq.github.io/commits -f since="$COMMIT_DATE" -f per_page=1000 \ +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="$COMMIT_DATE" -f per_page=1000 \ +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/website_and_docs/content/blog/2024/selenium-community-live-episode1.md b/website_and_docs/content/blog/2024/selenium-community-live-episode1.md index 6e0069410d94..093a11ac81d6 100644 --- a/website_and_docs/content/blog/2024/selenium-community-live-episode1.md +++ b/website_and_docs/content/blog/2024/selenium-community-live-episode1.md @@ -4,7 +4,7 @@ linkTitle: "Selenium Community Live - Episode 1" date: 2024-12-25 tags: ["webinar", "meetup", "talks","community"] categories: ["webinar"] -author: Pallavi Sharma +author: Pallavi Sharma images: description: > Selenium Community Live - Episode 1 diff --git a/website_and_docs/content/blog/2024/selenium-milestone-20yrs-blog.md b/website_and_docs/content/blog/2024/selenium-milestone-20yrs-blog.md index f6f524595991..7a17c5ce33a0 100644 --- a/website_and_docs/content/blog/2024/selenium-milestone-20yrs-blog.md +++ b/website_and_docs/content/blog/2024/selenium-milestone-20yrs-blog.md @@ -4,7 +4,7 @@ linkTitle: "Celebrating 20 Years of Selenium" date: 2024-10-18 tags: ["webinar", "meetup", "talks"] categories: ["webinar"] -author: Pallavi Sharma +author: Pallavi Sharma images: - "/images/blog/2024/20-selenium" description: > @@ -40,7 +40,7 @@ This webinar is more than just a celebration—it’s a chance to learn from the Join Us in Celebrating Selenium’s Incredible Journey! For 20 years, Selenium has helped shape how we test, automate, and innovate on the web. This is your opportunity to celebrate that legacy and learn what the future holds for browser automation. We can’t wait to see you there! -Event Organizers - **Maaret**, **Diego**, and **Pallavi**. +Event Organizers - **Maaret**, **Diego**, and **Pallavi**. ## Watch the Recording of Our 20th Anniversary Event diff --git a/website_and_docs/content/blog/2025/lambdatest-selenium-partnership.md b/website_and_docs/content/blog/2025/lambdatest-selenium-partnership.md new file mode 100644 index 000000000000..82cd175c3244 --- /dev/null +++ b/website_and_docs/content/blog/2025/lambdatest-selenium-partnership.md @@ -0,0 +1,66 @@ +--- +title: "LambdaTest Becomes a Selenium Development Partner" +linkTitle: "LambdaTest Becomes a Selenium Development Partner" +date: 2025-04-10 +tags: ["selenium"] +categories: ["general"] +author: Sri Harsha [@harsha509](https://www.linkedin.com/in/sriharsha509/) +images: + - "/images/blog/2025/lambdatest-selenium-development-partner.png" +description: > + The Selenium Project is pleased to welcome LambdaTest as an official Development Partner. +--- + +We are excited to share that [LambdaTest](https://www.lambdatest.com) has joined as a development +partner for Selenium. This partnership highlights our shared commitment +to innovation, community engagement, and collaborative progress in the +test automation space. LambdaTest’s dedicated Open Source Program Office (OSPO) +is contributing valuable expertise and resources that will further enrich the Selenium ecosystem. + +## A Partnership Rooted in Open Source Values + +At the heart of Selenium lies a vibrant community dedicated to open +standards and continuous improvement. Our collaboration with LambdaTest +is designed to strengthen the community by channeling focused contributions +from their OSPO—improving tools, integrations, and documentation for +Selenium users around the world. + +## About LambdaTest + +[LambdaTest](https://www.lambdatest.com) is a platform that empowers businesses to +accelerate time to market through intelligent, cloud-based test authoring, +orchestration, and execution. With over 15,000 customers and 2.3 million+ +users across 130+ countries, LambdaTest is the trusted choice for modern software testing. + +## Key Points of the Partnership + +- **Open Source Integrity:** Selenium continues to flourish as a community-led project, embodying the true spirit of open source. +- **Dedicated OSPO Contributions:** The team at LambdaTest is actively enhancing Selenium by improving documentation, bolstering community support, and enabling better integrations. Their efforts are aimed at empowering the global Selenium user base. +- **Community-Focused Innovation:** By pooling our collective expertise, we are well-positioned to drive new solutions and elevate test automation practices to new heights. + +## What This Partnership Means for the Community + +The contributions from LambdaTest’s OSPO are set to bring +notable benefits to the broader testing landscape: + +- **Faster Development:** The infusion of dedicated engineering resources will accelerate the development of new features and improvements. +- **Improved Stability:** Increased efforts in testing and quality assurance will help quickly identify and resolve issues. +- **Better Documentation:** Enhanced documentation will make it easier for users to dive into Selenium and harness its advanced capabilities. +- **Enhanced Community Support:** LambdaTest will play an active role in organizing and sponsoring community events, fostering rich collaboration and knowledge sharing. + +## Looking Ahead + +This partnership is more than a technical alliance—it reaffirms our +belief in the power of community-led development. The proactive contributions +from LambdaTest’s OSPO will help create a more connected and empowered test +automation community. We invite you to join our upcoming workshops, webinars, +and discussion forums to see firsthand how these collaborative projects will +shape the future of open source test automation. + +Formalizing LambdaTest as a Selenium development partner is a milestone that +celebrates our commitment to innovation and community spirit. With the dedicated +efforts from LambdaTest’s OSPO, we continue our mission to enhance the +Selenium ecosystem for every developer and tester worldwide. + +Join us on this journey toward a future that is more resilient, +supportive, and inventive in the realm of test automation. diff --git a/website_and_docs/content/blog/2025/selenium-4-29-released.md b/website_and_docs/content/blog/2025/selenium-4-29-released.md index 278f97aeb2e0..92a32986dc91 100644 --- a/website_and_docs/content/blog/2025/selenium-4-29-released.md +++ b/website_and_docs/content/blog/2025/selenium-4-29-released.md @@ -62,6 +62,7 @@ Links to everything can be found on our [downloads page][downloads]. - Improved logging stability. ([#15257](https://github.com/SeleniumHQ/selenium/pull/15257)) ### **Docker Selenium** + - Publish Node/Standalone images with the latest Grid core version and browser backward versions - Update container environment to JDK21 ([#2642](https://github.com/SeleniumHQ/docker-selenium/pull/2642)) - Node base with share system certificate support ([#2653](https://github.com/SeleniumHQ/docker-selenium/pull/2653)) @@ -109,7 +110,6 @@ For a detailed look at all changes, check out the [release notes](https://github {{< gh-user "https://api.github.com/users/AndreyJVM" >}} {{< gh-user "https://api.github.com/users/Delta456" >}} {{< gh-user "https://api.github.com/users/alaahong" >}} -{{< gh-user "https://api.github.com/users/automatealchemist" >}} {{< gh-user "https://api.github.com/users/b2m" >}} {{< gh-user "https://api.github.com/users/pallavigitwork" >}} diff --git a/website_and_docs/content/blog/2025/selenium-4-30-released.md b/website_and_docs/content/blog/2025/selenium-4-30-released.md new file mode 100644 index 000000000000..8c9c41880c27 --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-4-30-released.md @@ -0,0 +1,197 @@ +--- +title: "Selenium 4.30 Released!" +linkTitle: "Selenium 4.30 Released!" +date: 2025-03-21 +tags: [ "selenium" ] +categories: [ "releases" ] +author: Diego Molina [@diemol](https://www.diemol.com) +images: + - "/images/blog/2025/selenium_4.30.jpg" +description: > + Today we're happy to announce that Selenium 4.30 has been released! +--- + +We're very happy to announce the release of Selenium 4.30 for Javascript, Ruby, Python, .NET, Java +and the Grid! +This version brings key updates across the project, with improvements to the BiDi protocol, +extensive nullability work in .NET, better error handling, and various bug fixes. It’s a great +step forward as we continue strengthening Selenium’s stability, consistency, and support across +all supported languages. + +Links to all assets can be found on our [downloads page][downloads]. + + +--- + +## 🚀 Major Highlights + +- Continued enhancements to **BiDi (Bi-Directional Protocol)** support across Java, Ruby, .NET, JavaScript, and Python. +- Extensive **nullability annotations** added throughout the .NET bindings. +- Selenium Manager (Rust) now supports **nightly Grid builds**. +- Improvements to testing infrastructure and developer experience, including better packaging, linting, and platform support. +- Numerous bug fixes and refactors across the Grid, bindings, and devtools. + +--- + +## 🔹 Language-Specific Changes + +### **Java** + +- Implemented BiDi commands: `getBidiSessionStatus` and `Permissions`. +- Refined logger initialization. +- Removed deprecated, non-W3C compliant `NetworkConnection` interface. +- Added support for setting viewport and handling CDP warnings gracefully. + +### **Python** + +- Improved devtools test handling and documentation. +- Fixed packaging issues and test discovery for `pytest`. +- Added docstring updates for clarity and modernization. +- Replaced strings with `By` class attributes. +- Improved socket resource management and error handling. +- Updated `expected_conditions` type annotations. + +### **JavaScript** + +- Fixed BiDi tests for Chrome and Firefox on CI. +- Implemented BiDi `permissions` module commands. + +### **Ruby** + +- Fixed a compatibility issue with Ruby 3.1 ("no anonymous block parameter"). +- Added BiDi support for: + - Setting viewport + - Activating browser context + - Providing responses +- Added a `target_type` parameter to devtools. + +### **.NET** + +- Enabled **nullable reference types** across many components. +- Trimmed away CDP for **AOT** applications. +- Enhanced BiDi support including: + - `SetFiles` command + - Support for `UnhandledPromptBehavior` + - Event support like `OnNavigationCommitted` + - Encapsulation of the transport layer +- Improved `WebDriver`, `WebElement`, and capabilities types with nullability. +- Introduced `SystemClock` singleton. +- Revisited and fixed test execution on Windows/macOS. +- Removed obsoleted members for 4.30. + +### **Grid & Selenium Manager** + +- Added trace logging for session stop events in Grid. +- Improved configuration options for server timeouts and session handling. +- Added support in Selenium Manager (Rust) for **nightly Grid builds**. +- Enhanced ability to trace and view live sessions. + +### **Docker Selenium** + +- Helm config: Node Relay to extend autoscaling Grid with test cloud resources ([#2703](https://github.com/SeleniumHQ/docker-selenium/pull/2703)). +- Docker: Disable HeapDumpOnOutOfMemoryError by default ([#2708](https://github.com/SeleniumHQ/docker-selenium/pull/2708)) +- [See all changes](https://github.com/SeleniumHQ/docker-selenium/releases) + + +
+ +We thank all our contributors for their incredible efforts in making Selenium better with every +release. ❤️ + +For a detailed look at all changes, check out +the [release notes](https://github.com/SeleniumHQ/selenium/releases/tag/4.30). + +
+ +## Contributors + +**Special shout-out to everyone who helped the Selenium Team get this release out!** + +### [Selenium](https://github.com/SeleniumHQ/selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/Delta456" >}} +{{< gh-user "https://api.github.com/users/FloKNetcare" >}} +{{< gh-user "https://api.github.com/users/ahalbrock" >}} +{{< gh-user "https://api.github.com/users/allrob23" >}} +{{< gh-user "https://api.github.com/users/jpawlyn" >}} +{{< gh-user "https://api.github.com/users/navin772" >}} +{{< gh-user "https://api.github.com/users/smortex" >}} +{{< gh-user "https://api.github.com/users/pallavigitwork" >}} +{{< gh-user "https://api.github.com/users/ahalbrock" >}} +
+
+
+ +### [Selenium Docs & Website](https://github.com/SeleniumHQ/seleniumhq.github.io) + +
+
+
+{{< gh-user "https://api.github.com/users/Delta456" >}} +{{< gh-user "https://api.github.com/users/WasiqB" >}} +{{< gh-user "https://api.github.com/users/alaahong" >}} +{{< gh-user "https://api.github.com/users/beinghumantester" >}} +{{< gh-user "https://api.github.com/users/franciscotrenco" >}} +{{< gh-user "https://api.github.com/users/pallavigitwork" >}} +
+
+
+ +### [Docker Selenium](https://github.com/SeleniumHQ/docker-selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/VietND96" >}} +
+
+
+ +### [Selenium Team Members][team] + +**Thanks as well to all the team members who contributed to this release:** + +
+
+
+{{< gh-user "https://api.github.com/users/aguspe" >}} +{{< gh-user "https://api.github.com/users/AutomatedTester" >}} +{{< gh-user "https://api.github.com/users/bonigarcia" >}} +{{< gh-user "https://api.github.com/users/cgoldberg" >}} +{{< gh-user "https://api.github.com/users/diemol" >}} +{{< gh-user "https://api.github.com/users/harsha509" >}} +{{< gh-user "https://api.github.com/users/joerg1985" >}} +{{< gh-user "https://api.github.com/users/nvborisenko" >}} +{{< gh-user "https://api.github.com/users/p0deje" >}} +{{< gh-user "https://api.github.com/users/pujagani" >}} +{{< gh-user "https://api.github.com/users/RenderMichael" >}} +{{< gh-user "https://api.github.com/users/shbenzer" >}} +{{< gh-user "https://api.github.com/users/shs96c" >}} +{{< gh-user "https://api.github.com/users/titusfortner" >}} +{{< gh-user "https://api.github.com/users/VietND96" >}} +
+
+
+ + + +Stay tuned for updates by following SeleniumHQ on: + +- [Mastodon](https://mastodon.social/@seleniumHQ@fosstodon.org) +- [BlueSky](https://bsky.app/profile/seleniumconf.bsky.social) +- [LinkedIn](https://www.linkedin.com/company/selenium/) +- [Selenium Community YouTube Channel](https://www.youtube.com/@SeleniumHQProject/streams) +- [X (Formerly Twitter)](https://twitter.com/seleniumhq) + +Happy automating! + +[downloads]: /downloads + +[bindings]: /downloads#bindings + +[team]: /project/structure + +[BiDi]: https://github.com/w3c/webdriver-bidi diff --git a/website_and_docs/content/blog/2025/selenium-4-31-released.md b/website_and_docs/content/blog/2025/selenium-4-31-released.md new file mode 100644 index 000000000000..c4c1fa14db18 --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-4-31-released.md @@ -0,0 +1,175 @@ +--- +title: "Selenium 4.31 Released!" +linkTitle: "Selenium 4.31 Released!" +date: 2025-04-05 +tags: [ "selenium" ] +categories: [ "releases" ] +author: Diego Molina [@diemol](https://www.diemol.com) +images: + - "/images/blog/2025/selenium_4.31.jpg" +description: > + Today we're happy to announce that Selenium 4.31 has been released! +--- + +We’re excited to announce the release of **Selenium 4.31** for Javascript, Ruby, Python, .NET, Java +and the Grid! 🎉 +This release focuses on improvements across the board, including better BiDi protocol support, test +reliability, nullability enhancements, and cleanup of legacy code across languages. + +Links to all assets can be found on our [downloads page][downloads]. + + +--- + +## 🚀 Major Highlights + +- Continued work towards full BiDi support in all bindings +- Cleanup of unused legacy components (like `wgxpath`) +- Expanded test coverage and fixes for various environments (CI, RBE, MacOS) +- Improvements in documentation and development tooling + +--- + +## 🔹 Language-Specific Changes + +### **Java** + +- [Handle `getNamedCookie` and `deleteNamedCookie` for empty strings](https://github.com/SeleniumHQ/selenium/pull/15092) +- [Add nullness for AppCacheStatus, Credential, and Either](https://github.com/SeleniumHQ/selenium/pull/15119) +- [Add nullness for interactions](https://github.com/SeleniumHQ/selenium/pull/15118) +- [Enable Safari for CookieImplementationTest](https://github.com/SeleniumHQ/selenium/pull/15544) +- [Add test to add a cookie in a user context (BiDi)](https://github.com/SeleniumHQ/selenium/pull/15312) + +### **Python** + +- [Fix docstring issues that sphinx complains about](https://github.com/SeleniumHQ/selenium/pull/15466) +- [Only shutdown service if process not already terminated](https://github.com/SeleniumHQ/selenium/pull/15183) +- [Remove unused mocker arg in chrome options test](https://github.com/SeleniumHQ/selenium/pull/15540) +- [Fix driver class name in test fixtures](https://github.com/SeleniumHQ/selenium/pull/15550) + +### **JavaScript** + +- Fixed BiDi tests for Chrome and Firefox on CI. +- Implemented BiDi `permissions` module commands. + +### **Ruby** + +- [Fix BiDi test errors](https://github.com/SeleniumHQ/selenium/pull/15482) +- [Allow symbols again to be passed on `delete_cookie`](https://github.com/SeleniumHQ/selenium/pull/15519) + +### **.NET** + +- [Decouple nested BiDi types across multiple modules](https://github.com/SeleniumHQ/selenium/pulls?q=is%3Apr+author%3Anvborisenko+label%3Adotnet) +- [Fix null warnings in `RelativeBy` by sealing the type](https://github.com/SeleniumHQ/selenium/pull/15379) +- [Simplify conversion to `LocalValue`](https://github.com/SeleniumHQ/selenium/pull/15441) +- [Unify protected and internal Execute methods](https://github.com/SeleniumHQ/selenium/pull/15233) +- [Make `ContinueWithAuthCommand` closer to spec (breaking change)](https://github.com/SeleniumHQ/selenium/pull/15545) +- [Avoid intermediate JsonDocument allocation to improve performance](https://github.com/SeleniumHQ/selenium/pull/15555) + +### **Grid** + +- [Expose register status via Node status response](https://github.com/SeleniumHQ/selenium/pull/15448) +- [Add traces for event stop session in Node](https://github.com/SeleniumHQ/selenium/pull/15348) + +### **Docker Selenium** + +- Helm config: Add template for file browser video records service ([#2763](https://github.com/SeleniumHQ/docker-selenium/pull/2763)) +- Helm config: Strictly handle `basicAuth.enabled` in template ([#2760](https://github.com/SeleniumHQ/docker-selenium/pull/2760)) +- Selenium Grid Autoscaling in Kubernetes is expected working well with KEDA core v2.17.0. +- [See all changes](https://github.com/SeleniumHQ/docker-selenium/releases) + + +
+ +We thank all our contributors for their incredible efforts in making Selenium better with every +release. ❤️ + +For a detailed look at all changes, check out +the [release notes](https://github.com/SeleniumHQ/selenium/releases/tag/4.31). + +
+ +## Contributors + +**Special shout-out to everyone who helped the Selenium Team get this release out!** + +### [Selenium](https://github.com/SeleniumHQ/selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/Delta456" >}} +{{< gh-user "https://api.github.com/users/PSandro" >}} +{{< gh-user "https://api.github.com/users/mk868" >}} +{{< gh-user "https://api.github.com/users/navin772" >}} +
+
+
+ +### [Selenium Docs & Website](https://github.com/SeleniumHQ/seleniumhq.github.io) + +
+
+
+{{< gh-user "https://api.github.com/users/alaahong" >}} +{{< gh-user "https://api.github.com/users/pallavigitwork" >}} +
+
+
+ +### [Docker Selenium](https://github.com/SeleniumHQ/docker-selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/KenHuPricer" >}} +{{< gh-user "https://api.github.com/users/KyriosGN0" >}} +
+
+
+ +### [Selenium Team Members][team] + +**Thanks as well to all the team members who contributed to this release:** + +
+
+
+{{< gh-user "https://api.github.com/users/aguspe" >}} +{{< gh-user "https://api.github.com/users/AutomatedTester" >}} +{{< gh-user "https://api.github.com/users/bonigarcia" >}} +{{< gh-user "https://api.github.com/users/cgoldberg" >}} +{{< gh-user "https://api.github.com/users/diemol" >}} +{{< gh-user "https://api.github.com/users/harsha509" >}} +{{< gh-user "https://api.github.com/users/joerg1985" >}} +{{< gh-user "https://api.github.com/users/nvborisenko" >}} +{{< gh-user "https://api.github.com/users/p0deje" >}} +{{< gh-user "https://api.github.com/users/pujagani" >}} +{{< gh-user "https://api.github.com/users/RenderMichael" >}} +{{< gh-user "https://api.github.com/users/shbenzer" >}} +{{< gh-user "https://api.github.com/users/shs96c" >}} +{{< gh-user "https://api.github.com/users/titusfortner" >}} +{{< gh-user "https://api.github.com/users/VietND96" >}} +
+
+
+ + + +Stay tuned for updates by following SeleniumHQ on: + +- [Mastodon](https://mastodon.social/@seleniumHQ@fosstodon.org) +- [BlueSky](https://bsky.app/profile/seleniumconf.bsky.social) +- [LinkedIn](https://www.linkedin.com/company/selenium/) +- [Selenium Community YouTube Channel](https://www.youtube.com/@SeleniumHQProject/streams) +- [X (Formerly Twitter)](https://twitter.com/seleniumhq) + +Happy automating! + +[downloads]: /downloads + +[bindings]: /downloads#bindings + +[team]: /project/structure + +[BiDi]: https://github.com/w3c/webdriver-bidi diff --git a/website_and_docs/content/blog/2025/selenium-4-32-released.md b/website_and_docs/content/blog/2025/selenium-4-32-released.md new file mode 100644 index 000000000000..a02c31e01cad --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-4-32-released.md @@ -0,0 +1,180 @@ +--- +title: "Selenium 4.32 Released!" +linkTitle: "Selenium 4.32 Released!" +date: 2025-05-05 +tags: [ "selenium" ] +categories: [ "releases" ] +author: Diego Molina [@diemol](https://www.diemol.com) +images: + - "/images/blog/2025/selenium_4.32.jpg" +description: > + Today we're happy to announce that Selenium 4.32 has been released! +--- + +We’re excited to announce the release of **Selenium 4.32** for Javascript, Ruby, Python, .NET, Java +and the Grid! 🎉 +This release continues the focus on strengthening BiDi support across multiple bindings, improving +stability in tests, and refining documentation and developer experience. + +Links to all assets can be found on our [downloads page][downloads]. + + +--- + +## 🚀 Major Highlights + +- Enhanced **BiDi (Bi-Directional)** protocol support for Python, Java, Ruby, and .NET bindings +- Dozens of **bug fixes and stability improvements** in tests and documentation +- Selenium Grid now better handles **capabilities for mobile testing with Relay Nodes** +- New utility class in Python to manage a local Grid server +- Additional updates to support AOT compatibility and memory optimizations in .NET + +--- + +## 🔹 Language-Specific Changes + +### **Java** + +- BiDi improvements: `onNavigationCommitted`, `getClientWindows`, and Edge support [#15560](https://github.com/SeleniumHQ/selenium/pull/15560), [#15661](https://github.com/SeleniumHQ/selenium/pull/15661) +- BiDi tests enabled for Edge network module [#15654](https://github.com/SeleniumHQ/selenium/pull/15654) +- Set BiDi as active protocol for Remote Firefox [#15224](https://github.com/SeleniumHQ/selenium/pull/15224) +- Dependency versioning improvements via BOM [#15689](https://github.com/SeleniumHQ/selenium/pull/15689) + +### **Python** + +- Fixes to test args for `--headless` and `--bidi` [#15567](https://github.com/SeleniumHQ/selenium/pull/15567) +- Improvements in test coverage and cleanup [#15579](https://github.com/SeleniumHQ/selenium/pull/15579), [#15580](https://github.com/SeleniumHQ/selenium/pull/15580) +- FedCM state leak fix [#15583](https://github.com/SeleniumHQ/selenium/pull/15583) +- BiDi Network: intercepts and authentication implemented [#14592](https://github.com/SeleniumHQ/selenium/pull/14592) +- Implemented BiDi `browser`, `browsing_context`, and `log` modules [#15616](https://github.com/SeleniumHQ/selenium/pull/15616), [#15631](https://github.com/SeleniumHQ/selenium/pull/15631), [#15668](https://github.com/SeleniumHQ/selenium/pull/15668) +- Added `Server` utility class to manage Grid [#15666](https://github.com/SeleniumHQ/selenium/pull/15666) +- Modernized linting setup and doc publishing [#15614](https://github.com/SeleniumHQ/selenium/pull/15614) + +### **JavaScript** + +- [Set remote active protocol in Firefox to BiDi only](https://github.com/SeleniumHQ/selenium/commit/a1ff120a9fd69daeea6a51d41aee6beb83748895) + +### **Ruby** + +- Added `PrintOptions` support [#15158](https://github.com/SeleniumHQ/selenium/pull/15158) +- WebSocket port handling for Firefox [#15458](https://github.com/SeleniumHQ/selenium/pull/15458) +- BiDi `setViewport`, `activate`, and log support enhanced [#15290](https://github.com/SeleniumHQ/selenium/pull/15290), [#15365](https://github.com/SeleniumHQ/selenium/pull/15365) + + +### **.NET** + +- Extensive BiDi refactoring for better spec alignment and AOT compatibility [#15575](https://github.com/SeleniumHQ/selenium/pull/15575), [#15591](https://github.com/SeleniumHQ/selenium/pull/15591) +- Introduced strong typing for LocalValue conversions [#15532](https://github.com/SeleniumHQ/selenium/pull/15532) +- Refined network interception and error handling [#15603](https://github.com/SeleniumHQ/selenium/pull/15603), [#15521](https://github.com/SeleniumHQ/selenium/pull/15521) +- Websocket memory and platform detection improvements [#15640](https://github.com/SeleniumHQ/selenium/pull/15640), [#15649](https://github.com/SeleniumHQ/selenium/pull/15649) + +### **Grid** + +- Fixed Safari-specific capability prefix handling [#15574](https://github.com/SeleniumHQ/selenium/pull/15574) +- Improved handling of `browserName` for Relay Nodes in mobile [#15537](https://github.com/SeleniumHQ/selenium/pull/15537) + +### **Docker Selenium** + +- Docker: Init python venv with non-root user ([#2769](https://github.com/SeleniumHQ/docker-selenium/pull/2769)) +- Docker: Remove Hub GraphQL dependency from video recorder ([#2813](https://github.com/SeleniumHQ/docker-selenium/pull/2813)) +- Docker: Fluxbox not rendering Chinese characters via VNC view ([#2817](https://github.com/SeleniumHQ/docker-selenium/pull/2817)) +- [See all changes](https://github.com/SeleniumHQ/docker-selenium/releases) + + +
+ +We thank all our contributors for their incredible efforts in making Selenium better with every +release. ❤️ + +For a detailed look at all changes, check out +the [release notes](https://github.com/SeleniumHQ/selenium/releases/tag/4.32). + +
+ +## Contributors + +**Special shout-out to everyone who helped the Selenium Team get this release out!** + +### [Selenium](https://github.com/SeleniumHQ/selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/Delta456" >}} +{{< gh-user "https://api.github.com/users/FFederi" >}} +{{< gh-user "https://api.github.com/users/navin772" >}} +{{< gh-user "https://api.github.com/users/yvsvarma" >}} +
+
+
+ +### [Selenium Docs & Website](https://github.com/SeleniumHQ/seleniumhq.github.io) + +
+
+
+{{< gh-user "https://api.github.com/users/HandyCC" >}} +{{< gh-user "https://api.github.com/users/Ozoniuss" >}} +{{< gh-user "https://api.github.com/users/alaahong" >}} +{{< gh-user "https://api.github.com/users/manoj9788" >}} +{{< gh-user "https://api.github.com/users/pallavigitwork" >}} +
+
+
+ +### [Docker Selenium](https://github.com/SeleniumHQ/docker-selenium) + +
+
+
+{{< gh-user "https://api.github.com/users/Trigtrig" >}} +{{< gh-user "https://api.github.com/users/lermit" >}} +
+
+
+ +### [Selenium Team Members][team] + +**Thanks as well to all the team members who contributed to this release:** + +
+
+
+{{< gh-user "https://api.github.com/users/aguspe" >}} +{{< gh-user "https://api.github.com/users/AutomatedTester" >}} +{{< gh-user "https://api.github.com/users/bonigarcia" >}} +{{< gh-user "https://api.github.com/users/cgoldberg" >}} +{{< gh-user "https://api.github.com/users/diemol" >}} +{{< gh-user "https://api.github.com/users/harsha509" >}} +{{< gh-user "https://api.github.com/users/joerg1985" >}} +{{< gh-user "https://api.github.com/users/nvborisenko" >}} +{{< gh-user "https://api.github.com/users/p0deje" >}} +{{< gh-user "https://api.github.com/users/pujagani" >}} +{{< gh-user "https://api.github.com/users/RenderMichael" >}} +{{< gh-user "https://api.github.com/users/shbenzer" >}} +{{< gh-user "https://api.github.com/users/shs96c" >}} +{{< gh-user "https://api.github.com/users/titusfortner" >}} +{{< gh-user "https://api.github.com/users/VietND96" >}} +
+
+
+ + + +Stay tuned for updates by following SeleniumHQ on: + +- [Mastodon](https://mastodon.social/@seleniumHQ@fosstodon.org) +- [BlueSky](https://bsky.app/profile/seleniumconf.bsky.social) +- [LinkedIn](https://www.linkedin.com/company/selenium/) +- [Selenium Community YouTube Channel](https://www.youtube.com/@SeleniumHQProject/streams) +- [X (Formerly Twitter)](https://twitter.com/seleniumhq) + +Happy automating! + +[downloads]: /downloads + +[bindings]: /downloads#bindings + +[team]: /project/structure + +[BiDi]: https://github.com/w3c/webdriver-bidi diff --git a/website_and_docs/content/blog/2025/selenium-appium-conference-2025.md b/website_and_docs/content/blog/2025/selenium-appium-conference-2025.md new file mode 100644 index 000000000000..6edaaba66946 --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-appium-conference-2025.md @@ -0,0 +1,39 @@ +--- +title: "Selenium Conference and Appium Conference 2025, Valencia Spain" +linkTitle: "Selenium Conference Appium Conference 2025 Valencia Spain" +date: 2025-04-21 +tags: ["conference", "selenium","appium", "web driver ecosystem", "valencia", "spain"] +categories: ["conference"] +author: Pallavi Sharma +images: +description: > + Selenium Conference and Appium Conference 2025, Valencia Spain +--- + +Selenium and Appium projects joined hands together for the 2025 annual conference of both, which was held from March 26th - March 28th in Valencia, Spain. The official web page of the conference can be found **here** + +The event took place at the beautiful venue of **Veles e Vents**. + +On March 26th, there were **Workshops**, which were enthusiastically attended by participants from across the globe. On the 26th March evening, the conference organised Speaker's Dinner, which provided a fun space to sit, talk and know other better. + +We are thankful to our esteemed speaker group, who joined us from all over the world and helped make the event a success. Details about the speakers for the event is available here - **Speakers of the Conference** + +The main event started from 27th of March and ran through 28th March evening. The event was attended by close to 400 global participants. We are thankful to each of them, for their presence which made the event worthwhile. + +Conference also provided scholarship to 4 people who were chosen after a tough selection process to attend the conference. We thank all our **Sponsors** who collaborated and helped make the event possible. + +The video recording, presentations and photographs from the main event can be found here - **Videos, Photos and More..** + +Conference, also ran Pre Conference webinars which helped showcase high rated talks which couldn't make it to the end program to the audience. The details of the same are available here - **Pre Conference Webinars** + +The conference program chair was **Diego Molina**. Diego helmed all the activities of the conference with great leadership and meticulous supervision. + +The conference was supported by a wide group of professionals who participated in volunteer capacity as reviewers and organizers of the event. +More details about them can be found here - **Organizers & Program Review Committee **. + +The entire event was professionally managed by the event organiser company **OneStic**. They ensured smooth flow of the event. Special mention to **Jesus Sanchez** for going out of the way to ensure everyone was well taken care of. + + +## Subscribe to Official Selenium Conference YouTube Channel +To explore more about our previous conferences and the next ones don't forget to subscribe to our official You Tube Channel **Selenium Conference Official YouTube Channel.** + diff --git a/website_and_docs/content/blog/2025/selenium-community-live-episode2.md b/website_and_docs/content/blog/2025/selenium-community-live-episode2.md index 773345c2e834..11fb566e2871 100644 --- a/website_and_docs/content/blog/2025/selenium-community-live-episode2.md +++ b/website_and_docs/content/blog/2025/selenium-community-live-episode2.md @@ -4,13 +4,13 @@ linkTitle: "Selenium Community Live - Episode 2" date: 2025-01-21 tags: ["webinar", "meetup", "talks","community"] categories: ["webinar"] -author: Pallavi Sharma +author: Pallavi Sharma images: description: > Selenium Community Live - Episode 2 --- -The second episode of Selenium Community Live happened on Jan 21st, 2025, with speaker **David Burns**, event hosted by **Pallavi Sharma** +The second episode of Selenium Community Live happened on Jan 21st, 2025, with speaker **David Burns**, event hosted by **Pallavi Sharma** You can watch the episode here- **Selenium Community Live - Episode 2** diff --git a/website_and_docs/content/blog/2025/selenium-community-live-episode4.md b/website_and_docs/content/blog/2025/selenium-community-live-episode4.md new file mode 100644 index 000000000000..9a5cb4bb6c3e --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-community-live-episode4.md @@ -0,0 +1,40 @@ +--- +title: "Selenium Community Live - Episode 4" +linkTitle: "Selenium Community Live - Episode 4" +date: 2025-03-19 +tags: ["webinar", "meetup", "talks","community"] +categories: ["webinar"] +author: Pallavi Sharma +images: +description: > + Selenium Community Live - Episode 4 +--- + +The fourth episode of Selenium Community Live happened on March 19th, 2025, with speaker **Michael Mintz**, event hosted by **Pallavi Sharma** + +You can watch the episode on YouTube here- **Episode 4 on YouTube** +or +You can watch the episode on LinkedIn here- **Episode 4 on LinkedIn** + +**Selenium Community Live - Episode 4** + +Michael Mintz is creator of Selenium Base, an all in one Browser Automation Framework, built over Selenium WebDriver Python bindings. The framework is well known and used +in the WebDriver Ecosystem community for testing, web scraping, web crawling and stealth purposes. +You can find out more about Selenium Base here - **Selenium Base** + + +**Meet the Speakers:** + +1. **Michael Mintz** + + +## Watch the Recording + +Couldn’t join us live? Watch the entire episode here - +📹 Recording Link: [Watch the Event Recording on YouTube](https://youtube.com/live/FSH712hhHvo?feature=share) + +To know more about Selenium Base, please follow the link +**Explore more on Selenium Base** + +In case you were wondering what happened to episode 3, it was cancelled but will be scheduled in coming weeks. Thank you! +Stay tuned as we bring the next! **Subscribe here to the Selenium HQ Official YouTube Channel.** diff --git a/website_and_docs/content/blog/2025/selenium-community-live-episode5.md b/website_and_docs/content/blog/2025/selenium-community-live-episode5.md new file mode 100644 index 000000000000..76aeb320a2ee --- /dev/null +++ b/website_and_docs/content/blog/2025/selenium-community-live-episode5.md @@ -0,0 +1,97 @@ +--- +title: "Selenium Community Live - Episode 5" +linkTitle: "Selenium Community Live - Episode 5" +date: 2025-05-05 +tags: ["webinar", "meetup", "talks","community"] +categories: ["webinar"] +author: Puja Jagani +images: +description: > + Selenium Community Live - Episode 5 +--- + +The fifth episode of Selenium Community Live happened on April 25th 2025. + +The event featured speakers **Ashley Hunsberger **, Director at NBCUniversal with close to 25 years of industry experience and a long-time friend of the Selenium, alongside **Puja Jagani**, Open Source Engineer & Developer Advocate at BrowserStack and member of the Selenium leadership(TLC and PLC). The event was hosted by +**Pallavi Sharma**, Founder 5 Elements Learning and a long-time Selenium Committer. + +The theme of the community event was "Beyond Code: Understanding Developer Satisfaction in Open Source Contributions". + +While many discussions around open source have happened that focus on code contributions and technical aspects, there is a vital human element involved, something that keeps the contributions rolling for decades, i.e. developer satisfaction. This community event was dedicated to discussing the human factor in open source contributions. The speakers shared their insights and experience on developer satisfaction in open source. + +### What motivates Open Source contributors? + +Ashley’s LinkedIn states that "My driving principle is simple: people first" and building on that, Ashley and Puja both highlight that open source is "by the community, for the community," where collaboration and human connections are foundational motivating factors for them. + +Ashley shares her journey with Selenium, highlighting how the warm, caring community has helped her build genuine friendships and good memories. + +"In the end, do people really remember what we build? They're going to remember how we made them feel." - Ashley Hunsberger + +She states that for her, a main motivational factor is community, and what she thinks drives people is the altruistic purpose of giving back to the community beyond their organisation and serving a great purpose. According to her, motivation drives behaviour, and if you have clear motivation, that will drive your place in the community in the long run. + +Beyond altruism, Puja thinks there is a diversity of motivators, emphasising that contributions extend far beyond code. Contributions might include: +- Helping with documentation +- Managing continuous integration (CI) pipelines +- Handling legal, and financial aspects, and other administrative aspects +- Organising conferences, community events, and meetups. + +These roles are often in the spotlight but critical to the health and growth of open source projects. A huge spectrum of motivators drives people’s behaviour and keeps the open source project breathing and growing. + +### No single factor that contributes to developer satisfaction + +Ashley brings a unique perspective to the idea that developer satisfaction can be understood through the lens of the Job Characteristics Model. This model outlines key aspects of work that lead to positive outcomes such as retention, motivation, and job satisfaction. + +Key factors include: +- Skill Variety: Open source contributors engage in a wide range of skills, from coding to release engineering, documentation, and advocacy. +- Task Identity: Contributors often see their work through from start to finish, building and shipping features that users directly benefit from. +- Task Significance: Understanding the impact and value of their contributions motivates developers to continue their work. +- Autonomy: Contributors enjoy flexibility in how, when, and where they contribute, within the project's guidelines. +- Feedback: Constructive feedback loops help contributors improve and feel connected to the community. + +These elements combine to foster long-term satisfaction. + +### The Evolution of Motivation in Open Source + +Puja shared her own journey with Selenium, from initially feeling nervous about contributing to becoming a part of the technical leadership. Initially, simple contributions like fixing a bug brought immense satisfaction. Over time, the motivation evolved to include community appreciation and the visible impact of her work on the end users of Selenium. She recounts a meaningful interaction at a recent conference where an attendee thanked her for contributing to Selenium, highlighting how such moments validate and inspire ongoing commitment. + +### Handling Conflict in Open Source + +Ashley and Puja acknowledged that interactions on platforms like GitHub or chat channels can include harsh or unexpected comments or the project itself can have some differences of opinion. And this could be largely due to the diverse background of people, any open source project experiences. This difference of opinion and thought diversity is what makes the group awesome, but certain situations need to be resolved with care. + +Ashley shares her first experience receiving a non-constructive code review and emphasises the importance of kindness and clarity in feedback: +"Be kind, but clear. Clear is kind. You don't have to be nice, but be clear about what happened, why, and how to improve." - Ashley Hunsberger + +Effective conflict resolution involves open questions, understanding the intent, and focusing on shared goals. It’s important to remember that conflicts are natural in any group, but they can be handled constructively with the key focus being on what is important for the situation. It is also essential to make sure an open source project has a code of conduct that is implemented in such situations and that the community is aware they have a safe space to report their issue and that they will be heard. + +### Inclusivity + +Ashley distinguishes between mentorship and sponsorship as two pillars of inclusion: +- Mentorship: Providing advice, guidance, and support to help someone grow and navigate the community. +- Sponsorship: Actively advocating for someone, opening doors, and recommending them for opportunities + +Ashley further discussed that inclusivity needs to be beyond code. Such as ensuring inclusive language and removing any barriers of entry for new contributors. The key focus should be on building an inclusive environment and creating a welcoming space for new contributors and the community. + +### Overcoming Impostor Syndrome + +Impostor syndrome is a common challenge for developers, especially when engaging in large, visible open-source projects. Ashley shares candidly about her struggles and offers practical advice when Pallavi asked her to share her insights on how to enable people to overcome impostor syndrome. Ashley shares the following +- Be kind to yourself and reframe negative thoughts. Add "yet" to statements like "I don’t know how to do this... yet." +- Recognise that many others share the same fears and questions. +- Build a support network of trusted friends, mentors, and peers who understand your journey. +- Use tools like worksheets to identify negative thinking patterns and consciously reframe them. +- Focus on facts about your skills and contributions rather than self-doubt. + +These strategies can help contributors maintain confidence in their open-source journey. + +Next they discussed how open source projects can help people, and here the importance of visibility and recognition in sustaining open source motivation was emphasised. Seeing the direct impact of one’s work, whether through download statistics, user feedback, or conference stories, reinforces the value of contributions. Whether you are a seasoned contributor or a newcomer, reflecting on the above areas can hopefully help you foster a more satisfying developer experience. + +Selenium is now entering its 21st year of existence, has had contributors spanning across various time zones, geographies and areas of expertise. With nearly 800 contributors over two decades, we take this moment to express gratitude to each of them. Through continuous feedback and meaningful interaction with the community, Selenium remains dedicated to work towards a healthier developer satisfaction. + +## Watch the Recording + +Couldn’t join us live? Watch the entire episode! + +You can watch the episode on YouTube here- **Episode 5 on YouTube** +or +you can watch the episode on LinkedIn here- **Episode 5 on LinkedIn**. + +Thank you! Stay tuned as we bring the next! Subscribe here to the Selenium HQ Official YouTube Channel. \ No newline at end of file diff --git a/website_and_docs/content/documentation/_index.en.md b/website_and_docs/content/documentation/_index.en.md index 0313bb1f586b..136db40dc513 100755 --- a/website_and_docs/content/documentation/_index.en.md +++ b/website_and_docs/content/documentation/_index.en.md @@ -42,7 +42,7 @@ a browser. You can find a more comprehensive example in [Writing your first Sele {{< gh-codeblock path="/examples/dotnet/HelloSelenium.cs" >}} {{< /tab >}} {{< tab header="Ruby" >}} -{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium_spec.rb" >}} +{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium.rb" >}} {{< /tab >}} {{< tab header="JavaScript" >}} {{< gh-codeblock path="/examples/javascript/test/hello/helloSelenium.js" >}} diff --git a/website_and_docs/content/documentation/_index.ja.md b/website_and_docs/content/documentation/_index.ja.md index 794dacbe3419..1512d796ef18 100755 --- a/website_and_docs/content/documentation/_index.ja.md +++ b/website_and_docs/content/documentation/_index.ja.md @@ -29,7 +29,7 @@ Seleniumの中核は[WebDriver]({{< ref "webdriver.md" >}})であり、様々な {{< gh-codeblock path="/examples/dotnet/HelloSelenium.cs" >}} {{< /tab >}} {{< tab header="Ruby" >}} -{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium_spec.rb" >}} +{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium.rb" >}} {{< /tab >}} {{< tab header="JavaScript" >}} {{< gh-codeblock path="/examples/javascript/test/hello/helloSelenium.js" >}} diff --git a/website_and_docs/content/documentation/_index.pt-br.md b/website_and_docs/content/documentation/_index.pt-br.md index 9fdaeeb151d1..7d92d0f5eb8b 100755 --- a/website_and_docs/content/documentation/_index.pt-br.md +++ b/website_and_docs/content/documentation/_index.pt-br.md @@ -40,7 +40,7 @@ navegadores. Aqui está uma das instruções mais simples que você pode fazer: {{< gh-codeblock path="/examples/dotnet/HelloSelenium.cs" >}} {{< /tab >}} {{< tab header="Ruby" >}} -{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium_spec.rb" >}} +{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium.rb" >}} {{< /tab >}} {{< tab header="JavaScript" >}} {{< gh-codeblock path="/examples/javascript/test/hello/helloSelenium.js" >}} diff --git a/website_and_docs/content/documentation/_index.zh-cn.md b/website_and_docs/content/documentation/_index.zh-cn.md index a68a2a5cbb70..8049e83ad16d 100755 --- a/website_and_docs/content/documentation/_index.zh-cn.md +++ b/website_and_docs/content/documentation/_index.zh-cn.md @@ -34,7 +34,7 @@ Selenium 的核心是 [WebDriver]({{< ref "webdriver.md" >}}),这是一个编 {{< gh-codeblock path="/examples/dotnet/HelloSelenium.cs" >}} {{< /tab >}} {{< tab header="Ruby" >}} -{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium_spec.rb" >}} +{{< gh-codeblock path="/examples/ruby/spec/hello/hello_selenium.rb" >}} {{< /tab >}} {{< tab header="JavaScript" >}} {{< gh-codeblock path="/examples/javascript/test/hello/helloSelenium.js" >}} diff --git a/website_and_docs/content/documentation/grid/advanced_features/endpoints.zh-cn.md b/website_and_docs/content/documentation/grid/advanced_features/endpoints.zh-cn.md index 226edffd2e4b..a11399844ff8 100644 --- a/website_and_docs/content/documentation/grid/advanced_features/endpoints.zh-cn.md +++ b/website_and_docs/content/documentation/grid/advanced_features/endpoints.zh-cn.md @@ -21,19 +21,19 @@ Grid状态提供Grid的当前状态. cURL GET 'http://localhost:4444/status' ``` -### 检查会话所有者 +### 删除会话 -要检查会话是否属于某一节点, 请使用下面列出的cURL命令. +删除会话会终止 WebDriver 会话、退出驱动程序并将其从活动会话映射中删除。任何使用删除的会话标识或重新使用驱动程序实例的请求都会出错。 ```shell cURL --request DELETE 'http://localhost:4444/session/%3Csession-id%3E' ``` -### Which URL should I use? +### 我应该使用哪一个URL? -在独立模式下, Grid URL是独立服务器的地址. +在 Standalone 模式下, Grid URL是独立服务器的地址. -在集线器节点模式下, Grid URL是集线器服务器的地址. +在 Hub-Node 模式下, Grid URL是集线器服务器的地址. 在完全分布式模式下, Grid URL是路由服务器的地址. @@ -43,90 +43,66 @@ cURL --request DELETE 'http://localhost:4444/session/%3Csession-id%3E' ### 删除节点 -要从Grid中删除节点, -请使用下面列出的cURL命令. -它不会停止在该节点上运行的任何持续中的会话. -除非显式终止, 否则节点将继续按原样运行. -分发器不再知晓该节点, -因此任何匹配的新会话请求 -也不会转发到该节点. +要从网格中删除节点,请使用下面列出的 cURL 命令。该命令不会停止正在该节点上运行的任何会话。除非显式终止, 否则节点将继续运行。分发器不再知道该节点,因此任何匹配的新会话请求都不会转发到该节点。 -在独立模式下, 分发器URL是独立服务器的地址. +在 Standalone 模式下,分发器 URL 是独立服务器地址。 -在集线器节点模式下, 分发器URL是集线器服务器的地址. +在 Hub-Node 模式下, 分发器 URL 是 Hub 服务器的地址。 ```shell cURL --request DELETE 'http://localhost:4444/se/grid/distributor/node/%3Cnode-id%3E' --header 'X-REGISTRATION-SECRET: ' ``` -在完全分布式模式下, URL是分发器的地址. +在完全分布式模式下, URL是分发器的地址。 ```shell cURL --request DELETE 'http://localhost:4444/se/grid/distributor/node/%3Cnode-id%3E' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码, 则使用 ```shell cURL --request DELETE 'http:///se/grid/distributor/node/' --header 'X-REGISTRATION-SECRET;' ``` -### 放空节点 +### 释放节点 -节点放空命令用于优雅地关闭节点. -放空节点将在所有持续中的会话完成后停止节点. -但是, 它不接受任何新的会话请求. +节点释放命令用于优雅地关闭节点。在所有正在进行的会话结束后,会停止该节点。并且,它不会接受任何新的会话请求。 -在独立模式下, 分发器URL是独立服务器的地址. +在 Standalone 模式下,分发器 URL 是独立服务器地址。 -在集线器节点模式下, 分发器URL是集线器服务器的地址. +在 Hub-Node 模式下, 分发器 URL 是 Hub 服务器的地址。 ```shell cURL --request POST 'http://localhost:4444/se/grid/distributor/node/%3Cnode-id%3E/drain' --header 'X-REGISTRATION-SECRET: ' ``` -在完全分布式模式下, URL是分发服务器的地址. +在完全分布式模式下, URL是分发服务器的地址。 ```shell cURL --request POST 'http://localhost:4444/se/grid/distributor/node/%3Cnode-id%3E/drain' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码, 则使用 ```shell cURL --request POST 'http:///se/grid/distributor/node//drain' --header 'X-REGISTRATION-SECRET;' ``` ## 节点 -本节中的端点适用于 -集线器节点模式和 -节点独立运行的完全分布式网格模式. -在一个节点的情况下, 默认节点的URL为 http://localhost:5555 . -如果有多个节点, -请使用 [Grid 状态](#grid-状态) 获取所有节点详细信息 -以及定位地址. +本节中的端点适用于 Hub-Node 模式和节点独立运行的完全分布式网格模式。在一个节点的情况下, 默认节点的URL为 http://localhost:5555 。 +如果有多个节点,请使用 [Grid 状态](#grid-状态) 获取所有节点的详细信息并查找节点地址。 ### 状态 -节点状态本质上是节点的运行状况检查. -分发器定期ping节点状态, -并相应地更新Grid模型. -状态包括相关的可用性, 会话和插槽的信息. +节点状态本质上是节点的健康检查。分发程序会定期 ping 节点状态,并相应地更新 Grid 模型。状态包括有关可用性、会话和插槽的信息。 ```shell cURL --request GET 'http://localhost:5555/status' ``` -### 放空 +### 释放 -分发器将 [放空](#放空节点) 命令传递给 -由node-id标识的相应节点. -要直接放空节点, -请使用下面列出的cuRL命令. -两个端点都有效并产生相同的结果. -放空会等待持续中的会话完成后 -才停止节点. +分发器将 [释放](#释放节点) 命令传递给由node-id标识的相应节点。要直接释放节点,请使用下面列出的cuRL命令。 +两个端点都有效并产生相同的结果。释放会等待持续中的会话完成后才停止节点。 ```shell cURL --request POST 'http://localhost:5555/se/grid/node/drain' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码,则使用 ```shell cURL --request POST 'http:///se/grid/node/drain' --header 'X-REGISTRATION-SECRET;' ``` @@ -138,30 +114,23 @@ cURL --request POST 'http:///se/grid/node/drain' --header 'X-REGISTRAT ```shell cURL --request GET 'http://localhost:5555/se/grid/node/owner/%3Csession-id%3E' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码, 则使用 ```shell cURL --request GET 'http:///se/grid/node/owner/' --header 'X-REGISTRATION-SECRET;' ``` 如果会话属于该节点, 则返回true, -否则返回false. +否则返回false。 ### 删除会话 -删除会话将终止WebDriver会话, -退出驱动程序 -并将其从活动会话集合中删除. -任何使用删除的会话id -或重用驱动程序实例的请求 -都将抛出错误. +删除会话会终止 WebDriver 会话、退出驱动程序并将其从活动会话映射中删除。任何使用删除的会话标识或重新使用驱动程序实例的请求都会出错。 ```shell cURL --request DELETE 'http://localhost:5555/se/grid/node/session/%3Csession-id%3E' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码, 则使用 ```shell cURL --request DELETE 'http:///se/grid/node/session/' --header 'X-REGISTRATION-SECRET;' ``` @@ -170,52 +139,41 @@ cURL --request DELETE 'http:///se/grid/node/session/' --he ### 清除新会话队列 -新会话请求队列保存新会话请求. -要清除队列, -请使用下面列出的cURL命令. -清除队列将拒绝队列中的所有请求. -对于每个这样的请求, -服务器都会向相应的客户端返回一个错误响应. -清除命令的返回结果是 -已删除请求的总数. +新会话请求队列保存新会话请求。要清除队列,请使用下面列出的 cURL 命令。清除队列会拒绝队列中的所有请求。对于每个此类请求,服务器都会向相应的客户端返回错误响应。清除命令的结果是被删除请求的总数。 -在独立模式下, 队列URL是独立服务器的地址. -在集线器节点模式下, 队列URL是集线器服务器的地址. +在 Standalone 模式下, 队列URL是独立服务器的地址。 +在 Hub-Node 模式下, 队列URL是集线器服务器的地址。 ```shell cURL --request DELETE 'http://localhost:4444/se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET: ' ``` 在完全分布式模式下, -队列URL是新会话队列服务器的地址. +队列URL是新会话队列服务器的地址。 ```shell cURL --request DELETE 'http://localhost:4444/se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET: ' ``` -如果在设置Grid时未配置注册密码, -则使用 +如果在设置Grid时未配置注册密码, 则使用 ```shell cURL --request DELETE 'http:///se/grid/newsessionqueue/queue' --header 'X-REGISTRATION-SECRET;' ``` ### 获取新会话队列请求 -New Session Request Queue holds the new session requests. -To get the current requests in the queue, use the cURL command enlisted below. -The response returns the total number of requests in the queue and the request payloads. -新会话请求队列保存新会话请求. +新会话请求队列保存新会话请求。 要获取队列中的当前请求, -请使用下面列出的cURL命令. -响应会返回队列中的请求总数以及请求内容. +请使用下面列出的cURL命令。 +响应会返回队列中的请求总数以及请求内容。 -在独立模式下, 队列URL是独立服务器的地址. -在集线器节点模式下, 队列URL是集线器服务器的地址. +在 Standalone 模式下, 队列URL是独立服务器的地址。 +在 Hub-Node 模式下, 队列URL是集线器服务器的地址。 ```shell cURL --request GET 'http://localhost:4444/se/grid/newsessionqueue/queue' ``` 在完全分布式模式下, -队列URL是新会话队列服务器的地址. +队列URL是新会话队列服务器的地址。 ```shell cURL --request GET 'http://localhost:4444/se/grid/newsessionqueue/queue' diff --git a/website_and_docs/content/documentation/grid/components.ja.md b/website_and_docs/content/documentation/grid/components.ja.md index 46cd256d235f..881803b51fb5 100644 --- a/website_and_docs/content/documentation/grid/components.ja.md +++ b/website_and_docs/content/documentation/grid/components.ja.md @@ -1,5 +1,5 @@ --- -title: "Serenium Grid のコンポーネント" +title: "Selenium Grid のコンポーネント" linkTitle: "コンポーネント" weight: 6 description: > diff --git a/website_and_docs/content/documentation/legacy/selenium_ide/_index.ja.md b/website_and_docs/content/documentation/legacy/selenium_ide/_index.ja.md index 387fad87d05f..dcef8026d4dd 100644 --- a/website_and_docs/content/documentation/legacy/selenium_ide/_index.ja.md +++ b/website_and_docs/content/documentation/legacy/selenium_ide/_index.ja.md @@ -664,7 +664,7 @@ id 属性に一致する要素がない場合には、name 属性を持つ要素 ### Nameによる特定 -name ロケータタイプは、nama 属性に一致する最初の要素を特定します。 +name ロケータタイプは、name 属性に一致する最初の要素を特定します。 1つの name 属性に対して、複数の要素が同じ値を持っている場合には、フィルタを使ってロケーションストラテジーの精度を高めることができます。 デフォルトのフィルタタイプは value です (value 属性に一致)。 diff --git a/website_and_docs/content/documentation/selenium_manager.en.md b/website_and_docs/content/documentation/selenium_manager.en.md index 0ae0bd890b8b..2960e3ec2e2b 100644 --- a/website_and_docs/content/documentation/selenium_manager.en.md +++ b/website_and_docs/content/documentation/selenium_manager.en.md @@ -231,14 +231,20 @@ INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\c **Selenium Manager** {{< gh-codeblock path="examples/python/tests/selenium_manager/usage.py#L10-L12" >}} {{< /tab >}} -{{< tab header="CSharp" >}} -{{< badge-code >}} +{{% tab header="CSharp" %}} +{{< gh-codeblock path="examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs#L10-L18" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -{{< badge-code >}} +{{% tab header="Ruby" %}} +**Previously** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L5-L10" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L12-L16" >}} {{< /tab >}} -{{< tab header="JavaScript" >}} -{{< badge-code >}} +{{% tab header="JavaScript" %}} +**Previously** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L16-L31" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L6-L14" >}} {{< /tab >}} {{< tab header="Kotlin" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/selenium_manager.ja.md b/website_and_docs/content/documentation/selenium_manager.ja.md index f52274f03364..226fde81e46e 100644 --- a/website_and_docs/content/documentation/selenium_manager.ja.md +++ b/website_and_docs/content/documentation/selenium_manager.ja.md @@ -231,14 +231,20 @@ INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\c **Selenium Manager** {{< gh-codeblock path="examples/python/tests/selenium_manager/usage.py#L10-L12" >}} {{< /tab >}} -{{< tab header="CSharp" >}} -{{< badge-code >}} +{{% tab header="CSharp" %}} +{{< gh-codeblock path="examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs#L10-L18" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -{{< badge-code >}} +{{% tab header="Ruby" %}} +**Previously** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L5-L10" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L12-L16" >}} {{< /tab >}} -{{< tab header="JavaScript" >}} -{{< badge-code >}} +{{% tab header="JavaScript" %}} +**Previously** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L16-L31" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L6-L14" >}} {{< /tab >}} {{< tab header="Kotlin" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/selenium_manager.pt-br.md b/website_and_docs/content/documentation/selenium_manager.pt-br.md index f52274f03364..226fde81e46e 100644 --- a/website_and_docs/content/documentation/selenium_manager.pt-br.md +++ b/website_and_docs/content/documentation/selenium_manager.pt-br.md @@ -231,14 +231,20 @@ INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\c **Selenium Manager** {{< gh-codeblock path="examples/python/tests/selenium_manager/usage.py#L10-L12" >}} {{< /tab >}} -{{< tab header="CSharp" >}} -{{< badge-code >}} +{{% tab header="CSharp" %}} +{{< gh-codeblock path="examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs#L10-L18" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -{{< badge-code >}} +{{% tab header="Ruby" %}} +**Previously** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L5-L10" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L12-L16" >}} {{< /tab >}} -{{< tab header="JavaScript" >}} -{{< badge-code >}} +{{% tab header="JavaScript" %}} +**Previously** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L16-L31" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L6-L14" >}} {{< /tab >}} {{< tab header="Kotlin" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/selenium_manager.zh-cn.md b/website_and_docs/content/documentation/selenium_manager.zh-cn.md index f52274f03364..7e163b482453 100644 --- a/website_and_docs/content/documentation/selenium_manager.zh-cn.md +++ b/website_and_docs/content/documentation/selenium_manager.zh-cn.md @@ -1,113 +1,188 @@ --- -title: "Selenium Manager (Beta)" +title: "Selenium Manager (测试版)" linkTitle: "Selenium Manager" weight: 3 description: > - Selenium Manager is a command-line tool implemented in Rust that provides automated driver and browser management for Selenium. Selenium bindings use this tool by default, so you do not need to download it or add anything to your code or do anything else to use it. + Selenium Manager 是一个用 Rust 语言实现的命令行工具, 为 Selenium 提供了自动化的驱动程序和浏览器管理功能. Selenium 默认绑定使用此工具, 因此您无需下载它, 也不需要在代码中添加任何内容或执行其他操作即可使用它. --- -## Motivation -***TL;DR:*** *Selenium Manager is the official driver manager of the Selenium project, and it is shipped out of the box with every Selenium release.* - -Selenium uses the native support implemented by each browser to carry out the automation process. For this reason, Selenium users need to place a component called _driver_ (chromedriver, geckodriver, msedgedriver, etc.) between the script using the Selenium API and the browser. For many years, managing these drivers was a manual process for Selenium users. This way, they had to download the required driver for a browser (chromedriver for Chrome, geckodriver for Firefox, etc.) and place it in the `PATH` or export the driver path as a system property (Java, JavaScript, etc.). But this process was cumbersome and led to maintainability issues. - -Let's consider an example. Imagine you manually downloaded the required chromedriver for driving your Chrome with Selenium. When you did this process, the stable version of Chrome was 113, so you downloaded chromedriver 113 and put it in your `PATH`. At that moment, your Selenium script executed correctly. But the *problem* is that Chrome is *evergreen*. This name refers to Chrome's ability to upgrade automatically and silently to the next stable version when available. This feature is excellent for end-users but potentially dangerous for browser automation. Let's go back to the example to discover it. Your local Chrome eventually updates to version 115. And that moment, your Selenium script is broken due to the incompatibility between the manually downloaded driver (113) and the Chrome version (115). Thus, your Selenium script fails with the following error message: *"session not created: This version of ChromeDriver only supports Chrome version 113"*. - -This problem is the primary reason for the existence of the so-called *driver managers* (such as [WebDriverManager](https://bonigarcia.dev/webdrivermanager/) for Java, -[webdriver-manager](https://pypi.org/project/webdriver-manager/) for Python, [webdriver-manager](https://www.npmjs.com/package/webdriver-manager) for JavaScript, [WebDriverManager.Net](https://github.com/rosolko/WebDriverManager.Net) for C#, and [webdrivers](https://github.com/titusfortner/webdrivers) for Ruby). All these projects were an inspiration and a clear sign that the community needed this feature to be built in Selenium. Thus, the Selenium project has created *Selenium Manager*, the official driver manager for Selenium, shipped out of the box with each Selenium release as of version 4.6. - -## Usage -***TL;DR:*** *Selenium Manager is used by the Selenium bindings when the drivers (chromedriver, geckodriver, etc.) are unavailable.* - -Driver management through Selenium Manager is *opt-in* for the Selenium bindings. Thus, users can continue managing their drivers manually (putting the driver in the `PATH` or using system properties) or rely on a third-party *driver manager* to do it automatically. Selenium Manager only operates as a fallback: if no driver is provided, Selenium Manager will come to the rescue. - -Selenium Manager is a CLI (command line interface) tool implemented in Rust to allow cross-platform execution and compiled for Windows, Linux, and macOS. The Selenium Manager binaries are shipped with each Selenium release. This way, each Selenium binding language invokes Selenium Manager to carry out the automated driver and browser management explained in the following sections. - -## Automated driver management -***TL;DR:*** *Selenium Manager automatically discovers, downloads, and caches the drivers required by Selenium when these drivers are unavailable.* - -The primary feature of Selenium Manager is called *automated driver management*. Let's consider an example to understand it. Suppose we want to driver Chrome with Selenium (see the doc about how to [start a session with Selenium](https://www.selenium.dev/documentation/webdriver/getting_started/first_script/#1-start-the-session)). Before the session begins, and when the driver is unavailable, Selenium Manager manages chromedriver for us. We use the term *management* for this feature (and not just *download*) since this process is broader and implies different steps: - -1. Browser version discovery. Selenium Manager discovers the browser version (e.g., Chrome, Firefox, Edge) installed in the machine that executes Selenium. This step uses shell commands (e.g., `google-chrome --version`). -2. Driver version discovery. With the discovered browser version, the proper driver version is resolved. For this step, the online metadata/endpoints maintained by the browser vendors (e.g., [chromedriver](https://chromedriver.chromium.org/downloads), [geckodriver](https://github.com/mozilla/geckodriver/releases), or [msedgedriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)) are used. -3. Driver download. The driver URL is obtained with the resolved driver version; with that URL, the driver artifact is downloaded, uncompressed, and stored locally. -4. Driver cache. Uncompressed driver binaries are stored in a local cache folder (`~/.cache/selenium`). The next time the same driver is required, it will be used from there if the driver is already in the cache. - -## Automated browser management -***TL;DR:*** *Selenium Manager automatically discovers, downloads, and caches the browsers driven with Selenium (Chrome, Firefox, and Edge) when these browsers are not installed in the local system.* - -As of Selenium 4.11.0, Selenium Manager also implements *automated browser management*. With this feature, Selenium Manager allows us to discover, download, and cache the different browser releases, making them seamlessly available for Selenium. Internally, Selenium Manager uses an equivalent management procedure explained in the section before, but this time, for browser releases. - -The browser automatically managed by Selenium Manager are: - -- Chrome. Based on [Chrome for Testing (CfT)](https://googlechromelabs.github.io/chrome-for-testing/), as of Selenium 4.11.0. -- Firefox. Based on [public Firefox releases](https://ftp.mozilla.org/pub/firefox/releases/), as of Selenium 4.12.0. -- Edge. Based on [Edge downloads](https://www.microsoft.com/en-us/edge/download), as of Selenium 4.14.0. - -Let's consider again the typical example of driving Chrome with Selenium. And this time, suppose Chrome is not installed on the local machine when [starting a new session](https://www.selenium.dev/documentation/webdriver/getting_started/first_script/#1-start-the-session)). In that case, the current stable CfT release will be discovered, downloaded, and cached (in `~/.cache/selenium/chrome`) by Selenium Manager. - -But there is more. In addition to the stable browser version, Selenium Manager also allows downloading older browser versions (in the case of CfT, starting in version 113, the first version published as CfT). To set a browser version with Selenium, we use a browser option called [browserVersion](https://www.selenium.dev/documentation/webdriver/drivers/options/#browserversion). - -Let's consider another simple example. Suppose we set `browserVersion` to `114` using [Chrome options](https://www.selenium.dev/documentation/webdriver/browsers/chrome/). In this case, Selenium Manager will check if Chrome 114 is already installed. If it is, it will be used. If not, Selenium Manager will manage (i.e., discover, download, and cache) CfT 114. And in either case, the chromedriver is also managed. Finally, Selenium will start Chrome to be driven programmatically, as usual. - -But there is even more. In addition to fixed browser versions (e.g., `113`, `114`, `115`, etc.), we can use the following labels for `browserVersion`: - -- `stable`: Current CfT version. -- `beta`: Next version to stable. -- `dev`: Version in development at this moment. -- `canary`: Nightly build for developers. -- `esr`: Extended Support Release (only for Firefox). - -When these labels are specified, Selenium Manager first checks if a given browser is already installed (`beta`, `dev`, etc.), and when it is not detected, the browser is automatically managed. - -### Edge in Windows -Automated Edge management by Selenium Manager in Windows is different from other browsers. Both Chrome and Firefox (and Edge in macOS and Linux) are downloaded automatically to the local cache (`~/.cache/selenium`) by Selenium Manager. Nevertheless, the same cannot be done for Edge in Windows. The reason is that the Edge installer for Windows is distributed as a Microsoft Installer (MSI) file, designed to be executed with administrator rights. This way, when Edge is attempted to be installed with Selenium Manager in Windows with a non-administrator session, a warning message will be displayed by Selenium Manager as follows: +## 动机 +***简而言之:*** +*Selenium Manager 是 Selenium 项目的官方驱动程序管理器, 并且在每次 Selenium 发布时都会随附提供.* + +Selenium 利用每个浏览器实现的原生支持来执行自动化流程. +因此, Selenium 用户需要在使用 Selenium API 的脚本和浏览器之间放置一个名为 _驱动程序_(如 chromedriver、geckodriver、msedgedriver 等)的组件. +多年来, 管理这些驱动程序, 对Selenium 用户来说, 一直是个繁琐手动过程. +他们必须下载浏览器所需的驱动程序(如 Chrome 的 chromedriver、Firefox 的 geckodriver 等), +并将其放置在 `PATH` 中或以系统属性的形式导出驱动程序路径(如 Java、JavaScript 等). +但这种过程很麻烦, 导致了可维护性问题. + +让我们来看一个例子. +假设您手动下载了用于通过 Selenium 驱动 Chrome 的所需 chromedriver. +在执行此操作时, Chrome 的稳定版本是 113, +所以您下载了 chromedriver 113 并将其放在您的 `PATH` 中. +此时, 您的 Selenium 脚本执行正确. 但 *问题* 在于 Chrome 是 *保持更新* 的. +这指的是 Chrome 能够在有新版本可用时自动且静默地升级到下一个稳定版本. +此功能对终端用户来说很棒, 但对浏览器自动化来说可能很危险. +让我们回到这个例子来明确这一点. +当您本地的 Chrome 最终更新到了 115 版本. +此时, 由于手动下载的驱动程序(113 版)与 Chrome 版本(115 版)不兼容, +您的 Selenium 脚本出错了. +因此, 您的 Selenium 脚本会因以下错误消息而失败: +*“会话无法创建: 此版本的 ChromeDriver 仅支持 Chrome 版本 113”*. + + +这个问题是所谓的驱动管理器(例如 Java 的 [WebDriverManager](https://bonigarcia.dev/webdrivermanager/) 、 +Python 的 [webdriver-manager](https://pypi.org/project/webdriver-manager/) 、 +JavaScript 的 [webdriver-manager](https://www.npmjs.com/package/webdriver-manager) 、 +C# 的 [WebDriverManager.Net](https://github.com/rosolko/WebDriverManager.Net) 以及 Ruby 的 [webdrivers](https://github.com/titusfortner/webdrivers) +存在的主要原因. +所有这些项目都是一种启示, +也清楚地表明社区需要将此功能内置到 Selenium 中. +因此, Selenium 项目创建了 *Selenium Manager*, +这是 Selenium 的官方驱动管理器, 从 4.6 版本开始, 它随每个 Selenium 发行版一起提供. + + +## 用法 +***简而言之:*** +*当驱动程序(如 ChromeDriver、GeckoDriver 等)不可用时, Selenium 绑定会使用 Selenium Manager.* + +通过 Selenium Manager 进行驱动程序管理是 Selenium 绑定的 *可选功能* . +因此, 用户可以继续手动管理其驱动程序(将驱动程序放在 `PATH` 中或使用系统属性), +也可以依靠第三方 *驱动程序管理器* 自动完成. +Selenium Manager 仅作为备用方案: +如果未提供驱动程序, Selenium Manager 将会介入. + +Selenium Manager 是一个用 Rust 语言实现的命令行界面(CLI)工具, +可于 Windows、Linux 和 macOS 等多种操作系统上跨平台运行. +Selenium Manager 的二进制文件随每个 Selenium 版本一起发布. +这样, 每个 Selenium 绑定语言都会调用 Selenium Manager 来执行以下各节中所述的自动化驱动程序和浏览器管理. + +## Automated driver management 自动驱动管理 +***简而言之:*** +*当所需驱动程序不可用时, Selenium Manager 会自动寻找、下载并缓存 Selenium 所需的驱动程序.* + +Selenium Manager 的主要特性被称为 *自动驱动管理* . +让我们通过一个例子来理解它. 假设我们想用 Selenium 驱动 Chrome(请参阅关于如何[使用Selenium启动会话](https://www.selenium.dev/documentation/webdriver/getting_started/first_script/#1-start-the-session)的文档). +在会话开始之前, 如果驱动程序不可用, Selenium Manager 会为我们管理 chromedriver. +我们用 *管理* 这个词来描述此功能(而不仅仅是 *下载* ), 因为这个过程更广泛, 包含不同的步骤: + +1. 探索浏览器版本. Selenium Manager 会发现执行 Selenium 的机器上安装的浏览器版本(例如, Chrome、Firefox、Edge). 此步骤使用 shell 命令(例如, `google-chrome --version` ). +2. 寻找驱动程序版本. 通过探索过的浏览器版本, 确定合适的驱动程序版本. 在此步骤中, 使用由浏览器供应商维护的在线元数据/端点(例如, [chromedriver](https://chromedriver.chromium.org/downloads), [geckodriver](https://github.com/mozilla/geckodriver/releases), 或 [msedgedriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)). +3. 驱动下载. 通过解析出的驱动程序版本获取驱动程序的 URL;利用该 URL 下载驱动程序文件, 解压后将其存储在本地. +4. 驱动程序缓存. 未压缩的驱动程序二进制文件存储在本地缓存文件夹( `~/.cache/selenium` )中. 下次需要相同的驱动程序时, 如果该驱动程序已在缓存中, 则将从那里使用. + + +## 自动化浏览器管理 +***简而言之:*** +*当本地系统未安装 Selenium 驱动的浏览器(Chrome、Firefox 和 Edge)时, Selenium Manager 会自动发现、下载并缓存这些浏览器.* + +从 Selenium 4.11.0 版本开始, Selenium Manager 还实现了 *自动浏览器管理*. +借助此功能, Selenium Manager 能够发现、下载并缓存不同浏览器的版本, +使其能够无缝地供 Selenium 使用. +在内部, Selenium Manager 使用了与前一节所述类似的管理流程, 但这次是针对浏览器版本的. + +Selenium Manager 自动管理的浏览器有: + +- Chrome浏览器, 基于 [Chrome for Testing (CfT)](https://googlechromelabs.github.io/chrome-for-testing/), 自 Selenium 4.11.0 版本起. +- 火狐浏览器. 基于[public Firefox releases](https://ftp.mozilla.org/pub/firefox/releases/), 自 Selenium 4.12.0 版本. +- Edge浏览器, 基于 [Edge downloads](https://www.microsoft.com/en-us/edge/download), 自 Selenium 4.14.0 版本. + +让我们再次考虑用 Selenium 驱动 Chrome 的典型示例. +这一次, 假设在[启动新会话](https://www.selenium.dev/documentation/webdriver/getting_started/first_script/#1-start-the-session)时本地机器上未安装 Chrome. +在这种情况下, Selenium Manager会发现、下载并缓存当前的稳定版 Chrome 浏览器(在 `~/.cache/selenium/chrome` 中). + +但不仅如此. 除了稳定的浏览器版本, +Selenium Manager 还允许下载旧版浏览器(对于 Chrome for Testing 而言, +从 113 版本开始, 这是作为 Chrome for Testing 发布的第一个版本). +要使用 Selenium 设置浏览器版本, 我们使用一个名为 [browserVersion](https://www.selenium.dev/documentation/webdriver/drivers/options/#browserversion) 的浏览器选项. + +让我们考虑另一个简单的例子. +假设我们使用 [Chrome options](https://www.selenium.dev/documentation/webdriver/browsers/chrome/) 将 `browserVersion` 设置为 `114` . +在这种情况下, Selenium Manager会检查是否已安装 Chrome 114 版本. +如果已安装, 就会使用它. 如果没有安装, Selenium Manager会进行管理(即发现、下载并缓存)Chrome 114 版本. +无论哪种情况, chromedriver 也会被管理. 最后, Selenium 会像往常一样启动 Chrome 以实现程序化驱动. + +但还有更多. 除了固定的浏览器版本(例如, `113`, `114`, `115` 等), +我们还可以为 `browserVersion` 使用以下标签: + +- `stable`: 当前 CfT 版本. +- `beta`: 下一个稳定版. +- `dev`: 当前正在开发的版本. +- `canary`: 面向开发者的夜间构建版本. +- `esr`: 扩展支持版本(仅适用于 Firefox 浏览器). + +当指定了这些标签时, Selenium Manager首先会检查给定的浏览器是否已安装( `beta`, `dev` 等), +如果未检测到, 则会自动管理该浏览器. + +### Windows中的Edge + +在 Windows 系统中, Selenium Manager 对 Edge 的自动化边缘管理与其他浏览器有所不同. +对于 Chrome 和 Firefox(以及 macOS 和 Linux 系统中的 Edge), +Selenium Manager 会自动将其下载到本地缓存( `~/.cache/selenium` ). +然而, 在 Windows 系统中, Edge 却无法实现同样的操作. +原因在于 Windows 版本的 Edge 安装程序是以微软安装程序(MSI)文件的形式分发的, +需要以管理员权限执行. +因此, 当在 Windows 系统中使用非管理员权限会话通过 Selenium Manager 安装 Edge 时, +Selenium Manager 会显示如下警告信息: ``` edge can only be installed in Windows with administrator permissions ``` -Therefore, administrator permissions are required to install Edge in Windows automatically through Selenium Manager, and Edge is eventually installed in the usual program files folder (e.g., `C:\Program Files (x86)\Microsoft\Edge`). +因此, 通过 Selenium Manager 在 Windows 系统中自动安装 Edge 浏览器需要管理员权限, +并且 Edge 最终会被安装在通常的程序文件夹中(例如 `C:\Program Files (x86)\Microsoft\Edge` ). -## Data collection -Selenium Manager will report anonymised usage [statistics](https://plausible.io/privacy-focused-web-analytics) to [Plausible](https://plausible.io/manager.selenium.dev). This allows the Selenium team to understand more about how Selenium is being used so that we can better focus our development efforts. The data being collected is: +## 数据收集 +Selenium Manager会向 [Plausible](https://plausible.io/manager.selenium.dev) 报送匿名使用[statistics](https://plausible.io/privacy-focused-web-analytics) 数据. +这能让 Selenium 团队更深入地了解 Selenium 的使用情况, +从而更好地集中我们的开发精力. 所收集的数据包括: -| Data | Purpose | -| -----|---------| -| Selenium version | This allows the Selenium developers to safely deprecate and remove features, as well as determine which new features may be available to you | -| Language binding | Programming language used to execute Selenium scripts (Java, JavaScript, Python, .Net, Ruby) | -| OS and architecture Selenium Manager is running on | The Selenium developers can use this information to help prioritise bug reports, and to identify if there are systemic OS-related issues | -| Browser and browser version | Helping for prioritising bug reports | -| Rough geolocation | Derived from the IP address you connect from. This is useful for determining where we need to focus our documentation efforts | +| Data | Purpose | +|-------------------------------|---------| +| Selenium 版本 | 这使得 Selenium 开发人员能够安全地弃用和移除功能, 并确定哪些新功能可能对您可用. | +| 语言绑定 | 用于执行 Selenium 脚本的编程语言(Java、JavaScript、Python、.Net、Ruby)| +| Selenium Manager 正在运行的操作系统和架构 | Selenium 开发人员可以利用这些信息来帮助确定错误报告的优先级, 并识别是否存在系统性的与操作系统相关的故障. | +| 浏览器及浏览器版本 | 协助确定错误报告的优先级 | +| 大致地理位置 | 根据您连接的 IP 地址得出. 这有助于我们确定需要在哪些地区集中文档编写工作. | -Selenium Manager sends these data to Plausible once a day. This period is based on the TTL value (see [configuration](https://www.selenium.dev/documentation/selenium_manager/#configuration)). +Selenium Manager 每天会将这些数据发送给 Plausible 一次. +此周期基于 TTL 值(请参阅[configuration](https://www.selenium.dev/documentation/selenium_manager/#configuration)). -### Opting out of data collection -**Data collection is on by default.** To disable it, set the `SE_AVOID_STATS` environment variable to `true`. You may also disable data collection in the configuration file (see below) by setting `avoid-stats = true`. +### 选择退出数据收集 +**默认情况下会收集数据.** 若要禁用数据收集, 请将 `SE_AVOID_STATS` 环境变量设置 `true`. +您也可以在配置文件中(见下文)通过设置 `avoid-stats = true` 来禁用数据收集. -## Configuration -***TL;DR:*** *Selenium Manager should work silently and transparently for most users. Nevertheless, there are scenarios (e.g., to specify a custom cache path or setup globally a proxy) where custom configuration can be required.* +## 配置 +***简而言之:*** +*对于大多数用户而言, Selenium Manager 应该能静默且透明地运行. 不过, 在某些场景下(例如指定自定义缓存路径或全局设置代理), 可能需要自定义配置.* -Selenium Manager is a CLI tool. Therefore, under the hood, the Selenium bindings call Selenium Manager by invoking shell commands. Like any other CLI tool, arguments can be used to specify specific capabilities in Selenium Manager. The different arguments supported by Selenium Manager can be checked by running the following command: +Selenium Manager 是一个命令行界面(CLI)工具. +因此, 在底层, Selenium 绑定通过调用 shell 命令来调用 Selenium Manager. +和任何其他 CLI 工具一样, 可以使用参数来指定 Selenium Manager 中的特定功能. +要查看 Selenium Manager 支持的不同参数, 可以运行以下命令: ``` $ ./selenium-manager --help ``` -In addition to CLI arguments, Selenium Manager allows two additional mechanisms for configuration: +除了命令行参数之外, Selenium Manager 还支持两种额外的配置机制: -- Configuration file. Selenium Manager uses a file called `se-config.toml` located in the Selenium cache (by default, at `~/.cache/selenium`) for custom configuration values. This TOML file contains a key-value collection used for custom configuration. -- Environmental variables. Each configuration key has its equivalence in environmental variables by converting each key name to uppercase, replacing the dash symbol (`-`) with an underscore (`_`), and adding the prefix `SE_`. +- 配置文件. Selenium Manager使用位于 Selenium 缓存中的一个名为 `se-config.toml` 的文件(默认情况下位于 `~/.cache/selenium` )来存储自定义配置值. 此 TOML 文件包含用于自定义配置的键值集合. +- 环境变量. 每个配置键在环境变量中都有对应的等效项, 方法是将每个键名转换为大写, 将破折号(`-`)替换为下划线(`_`), 并在前面加上前缀`SE_`. -The configuration file is honored by Selenium Manager when it is present, and the corresponding CLI parameter is not specified. Besides, the environmental variables are used when neither of the previous options (CLI arguments and configuration file) is specified. In other words, the order of preference for Selenium Manager custom configuration is as follows: +当存在配置文件且未指定相应的命令行参数时, Selenium Manager 会遵循该配置文件. +此外, 如果既未指定命令行参数也未提供配置文件, 则会使用环境变量. 换句话说, Selenium Manager 自定义配置的优先级顺序如下: -1. CLI arguments. -2. Configuration file. -3. Environment variables. +1. CLI参数. +2. 配置文件. +3. 环境变量. -Notice that the Selenium bindings use the CLI arguments to specify configuration values, which in turn, are defined in each binding using [browser options](https://www.selenium.dev/documentation/webdriver/drivers/options/). +请注意, Selenium 绑定使用命令行参数来指定配置值, +而这些配置值又在每个绑定中通过[browser options](https://www.selenium.dev/documentation/webdriver/drivers/options/)来定义. + +下表总结了 Selenium Manager 支持的所有参数及其在配置文件和环境变量中的对应键. -The following table summarizes all the supported arguments supported by Selenium Manager and their correspondence key in the configuration file and environment variables. | CLI argument| Configuration file | Env variable | Description | |-------------|--------------------|--------------|-------------| @@ -133,54 +208,80 @@ The following table summarizes all the supported arguments supported by Selenium |`--language-binding `|`language-binding = "LANGUAGE"`|`SE_LANGUAGE_BINDING=LANGUAGE`|Language that invokes Selenium Manager (e.g., Java, JavaScript, Python, DotNet, Ruby)| |`--avoid-stats`|`avoid-stats = true`|`SE_AVOID_STATS=true`|Avoid sends usage statistics to plausible.io. Default: `false`| -In addition to the configuration keys specified in the table before, there are some special cases, namely: +除了前面表格中指定的配置键之外, 还有一些特殊情况, 即: -- Browser version. In addition to `browser-version`, we can use the specific configuration keys to specify custom versions per supported browser. This way, the keys `chrome-version`, `firefox-version`, `edge-version`, etc., are supported. The same applies to environment variables (i.e., `SE_CHROME_VERSION`, `SE_FIREFOX_VERSION`, `SE_EDGE_VERSION`, etc.). -- Driver version. Following the same pattern, we can use `chromedriver-version`, `geckodriver-version`, `msedgedriver-version`, etc. (in the configuration file), and `SE_CHROMEDRIVER_VERSION`, `SE_GECKODRIVER_VERSION`, `SE_MSEDGEDRIVER_VERSION`, etc. (as environment variables). -- Browser path. Following the same pattern, we can use `chrome-path`, `firefox-path`, `edge-path`, etc. (in the configuration file), and `SE_CHROME_PATH`, `SE_FIREFOX_PATH`, `SE_EDGE_PATH`, etc. (as environment variables). The Selenium bindings also allow to specify a custom location of the browser path using options, namely: [Chrome](https://www.selenium.dev/documentation/webdriver/browsers/chrome/#start-browser-in-a-specified-location)), [Edge](https://www.selenium.dev/documentation/webdriver/browsers/edge/#start-browser-in-a-specified-location), or [Firefox](https://www.selenium.dev/documentation/webdriver/browsers/firefox/#start-browser-in-a-specified-location). -- Driver mirror. Following the same pattern, we can use `chromedriver-mirror-url`, `geckodriver-mirror-url`, `msedgedriver-mirror-url`, etc. (in the configuration file), and `SE_CHROMEDRIVER_MIRROR_URL`, `SE_GECKODRIVER_MIRROR_URL`, `SE_MSEDGEDRIVER_MIRROR_URL`, etc. (as environment variables). -- Browser mirror. Following the same pattern, we can use `chrome-mirror-url`, `firefox-mirror-url`, `edge-mirror-url`, etc. (in the configuration file), and `SE_CHROME_MIRROR_URL`, `SE_FIREFOX_MIRROR_URL`, `SE_EDGE_MIRROR_URL`, etc. (as environment variables). +- 浏览器版本. 除了 `browser-version` 之外, 我们还可以使用特定的配置键为每个受支持的浏览器指定自定义版本. 这样, 键 `chrome-version`, `firefox-version`, `edge-version` 等就得到了支持. 环境变量(即 `SE_CHROME_VERSION`, `SE_FIREFOX_VERSION`, `SE_EDGE_VERSION` 等)也是如此. +- 驱动程序版本. 遵循相同的模式, 我们可以在配置文件中使用 `chromedriver-version`, `geckodriver-version`, `msedgedriver-version`等, 在环境变量中使用 `SE_CHROMEDRIVER_VERSION`, `SE_GECKODRIVER_VERSION`, `SE_MSEDGEDRIVER_VERSION` 等. +- 浏览器路径. 遵循相同的模式, 我们可以在配置文件中使用 `chrome-path`, `firefox-path`, `edge-path` 等, 在环境变量中使用 `SE_CHROME_PATH`, `SE_FIREFOX_PATH`, `SE_EDGE_PATH`等. Selenium 绑定还允许使用选项指定浏览器路径的自定义位置, 例如: Chrome、Edge 或 Firefox. +- 驱动镜像. 遵循同样的模式, 我们可以在配置文件中使用 `chromedriver-mirror-url`, `geckodriver-mirror-url`, `msedgedriver-mirror-url` 等, 在环境变量中使用 `SE_CHROMEDRIVER_MIRROR_URL`, `SE_GECKODRIVER_MIRROR_URL`, `SE_MSEDGEDRIVER_MIRROR_URL` 等. +- 浏览器镜像. 遵循同样的模式, 我们可以在配置文件中使用 `chrome-mirror-url`, `firefox-mirror-url`, `edge-mirror-url` 等, 在环境变量中使用 `SE_CHROME_MIRROR_URL`, `SE_FIREFOX_MIRROR_URL`, `SE_EDGE_MIRROR_URL` 等. -### se-config.toml Example +### se-config.toml 示例 {{< tabpane text=true >}} {{< tab header="se-config.toml" >}} {{< gh-codeblock path="examples/python/tests/selenium_manager/example_se-config.toml#L1-L21" >}} {{< /tab >}} {{< /tabpane >}} -## Caching -***TL;DR:*** *The drivers and browsers managed by Selenium Manager are stored in a local folder (`~/.cache/selenium`).* +## 缓存 +***简而言之:*** +*由 Selenium Manager 管理的驱动程序和浏览器会存储在本地文件夹(`~/.cache/selenium` )中.* -The cache in Selenium Manager is a local folder (`~/.cache/selenium` by default) in which the downloaded assets (drivers and browsers) are stored. For the sake of performance, when a driver or browser is already in the cache (i.e., there is a *cache hint*), Selenium Manager uses it from there. +Selenium Manager中的缓存是一个本地文件夹(默认为 `~/.cache/selenium` ), 用于存储下载的资源(驱动程序和浏览器). +为了提高性能, 当驱动程序或浏览器已存在于缓存中(即存在 *缓存* 提示)时, Selenium Manager会从那里使用它们. -In addition to the downloaded drivers and browsers, two additional files live in the cache's root: +除了已下载的驱动程序和浏览器之外, 缓存根目录中还有两个额外的文件: -- Configuration file (`se-config.toml`). This file is optional and, as explained in the previous section, allows to store custom configuration values for Selenium Manager. This file is maintained by the end-user and read by Selenium Manager. -- Metadata file (`se-metadata.json`). This file contains versions discovered by Selenium Manger making network requests (e.g., using the [CfT JSON endpoints](https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints)) and the time-to-live (TTL) in which they are valid. Selenium Manager automatically maintains this file. +- 配置文件( `se-config.toml` ). 此文件是可选的, 正如前一节所述, 它允许为 Selenium Manager 存储自定义配置值. 此文件由最终用户维护, 并由 Selenium Manager 读取. +- 元数据文件( `se-metadata.json` ). 此文件包含由 Selenium Manager通过网络请求(例如, 使用 [CfT JSON endpoints](https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints))发现的版本以及它们有效的生存时间(TTL). Selenium Manager会自动维护此文件. -The TTL in Selenium Manager is inspired by the TTL for DNS, a well-known mechanism that refers to how long some values are cached before they are automatically refreshed. In the case of Selenium Manager, these values are the versions found by making network requests for driver and browser version discovery. By default, the TTL is `3600` seconds (i.e., 1 hour) and can be tuned using configuration values or disabled by setting this configuration value to `0`. +Selenium Manager 中的生存时间(TTL)借鉴了 DNS 的 TTL 机制, 这是一种广为人知的机制, +指的是某些值在被自动刷新之前被缓存的时长. +对于 Selenium Manager 而言, 这些值是通过网络请求获取的驱动程序和浏览器版本发现的结果. +默认情况下, TTL 为 3600 秒(即 1 小时), 并且可以通过配置值进行调整, 或者将其设置为 0 来禁用此功能. -The TTL mechanism is a way to improve the overall performance of Selenium. It is based on the fact that the discovered driver and browser versions (e.g., the proper chromedriver version for Chrome 115 is 115.0.5790.170) will likely remain the same in the short term. Therefore, the discovered versions are written in the metadata file and read from there instead of making the same consecutive network request. This way, during the driver version discovery (step 2 of the automated driver management process previously introduced), Selenium Manager first reads the file metadata. When a *fresh* resolution (i.e., a driver/browser version valid during a TTL) is found, that version is used (saving some time in making a new network request). If not found or the TTL has expired, a network request is made, and the result is stored in the metadata file. +TTL 机制是一种提升 Selenium 总体性能的方法. +它基于这样一个事实: 在短期内, 发现的驱动程序和浏览器版本(例如, 适用于 Chrome 115 的正确 chromedriver 版本是 115.0.5790.170)很可能保持不变. +因此, 发现的版本会被写入元数据文件, 并从那里读取, 而不是连续进行相同的网络请求. +这样, 在驱动程序版本发现过程中(之前介绍的自动化驱动程序管理流程的第 2 步), +Selenium Manager 首先读取元数据文件. 如果找到新的解决方案(即在 TTL 期间有效的驱动程序/浏览器版本), +则使用该版本(节省了进行新网络请求的时间). +如果未找到或 TTL 已过期, 则会进行网络请求, 并将结果存储在元数据文件中. -Let's consider an example. A Selenium binding asks Selenium Manager to resolve chromedriver. Selenium Manager detects that Chrome 115 is installed, so it makes a network request to the CfT endpoints to discover the proper chromedriver version (115.0.5790.170, at that moment). This version is stored in the metadata file and considered valid during the next hour (TTL). If Selenium Manager is asked to resolve chromedriver during that time (which is likely to happen in the execution of a test suite), the chromedriver version is discovered by reading the metadata file instead of making a new request to the CfT endpoints. After one hour, the chromedriver version stored in the cache will be considered as *stale*, and Selenium Manager will refresh it by making a new network request to the corresponding endpoint. +我们来看一个例子. +一个 Selenium 绑定请求 Selenium Manager解析 chromedriver. +Selenium Manager检测到已安装 Chrome 115 版本, +于是向 CfT 端点发出网络请求以确定合适的 chromedriver 版本(当时为 115.0.5790.170). +此版本会被存储在元数据文件中, 并在接下来的一小时内(TTL)被视为有效. +如果在此期间(在执行测试套件时很可能发生)再次请求 Selenium Manager解析 chromedriver, +那么将通过读取元数据文件来获取 chromedriver 版本, 而无需向 CfT 端点发出新的请求. 一小时后, +缓存中存储的 chromedriver 版本将被视为过期, Selenium Manager会通过向相应端点发出新的网络请求来刷新它. Selenium Manager includes two additional arguments two handle the cache, namely: +Selenium Manager包含两个用于处理缓存的附加参数, 分别是: -- `--clear-cache`: To remove the cache folder (equivalent to the environment variable `SE_CLEAR_CACHE=true`). -- `--clear-metadata`: To remove the metadata file (equivalent to the environment variable `SE_CLEAR_METADATA=true`). +- `--clear-cache`: 要删除缓存文件夹(相当于环境变量 `SE_CLEAR_CACHE=true` ). +- `--clear-metadata` : 删除元数据文件(相当于环境变量 `SE_CLEAR_METADATA=true` ). -## Versioning -Selenium Manager follows the same versioning schema as Selenium. Nevertheless, we use the major version 0 for Selenium Manager releases because it is still in beta. For example, the Selenium Manager binaries shipped with Selenium 4.12.0 corresponds to version 0.4.12. +## 版本控制 +Selenium Manager 采用与 Selenium 相同的版本命名规则. +不过, 由于 Selenium Manager 仍处于测试阶段, 因此其主版本号为 0. +例如, 与 Selenium 4.12.0 一同发布的 Selenium Manager 二进制文件对应的是 0.4.12 版本. -## Getting Selenium Manager -For most users, direct interaction with Selenium Manager is not required since the Selenium bindings use it internally. Nevertheless, if you want to *play* with Selenium Manager or use it for your use case involving driver or browser management, you can get the Selenium Manager binaries in different ways: +## 获取 Selenium Manager +对于大多数用户而言, 由于 Selenium 绑定会在内部使用 Selenium Manager, +所以无需直接与之交互. +不过, 如果您想试用 Selenium Manager 或将其用于涉及驱动程序或浏览器管理的用例, +可以通过多种方式获取 Selenium Manager 的二进制文件 -- From the Selenium repository. The Selenium Manager source code is stored in the main Selenium repo under the folder [rust](https://github.com/SeleniumHQ/selenium/tree/trunk/rust). Moreover, you can find the compiled versions for Windows, Linux, and macOS in the [Selenium Manager Artifacts](https://github.com/SeleniumHQ/selenium_manager_artifacts) repo. The stable Selenium Manager binaries (i.e., those distributed in the latest stable Selenium version) are linked in this [file](https://github.com/SeleniumHQ/selenium/blob/trunk/common/selenium_manager.bzl). -- From the build workflow. Selenium Manager is compiled using a [GitHub Actions workflow](https://github.com/SeleniumHQ/selenium/actions/workflows/ci-rust.yml). This workflow creates binaries for Windows, Linux, and macOS. You can download these binaries from these workflow executions. -- From the cache. As of version 4.15.0 of the Selenium Java bindings, the Selenium Manager binary is extracted and copied to the cache folder. For instance, the Selenium Manager binary shipped with Selenium 4.15.0 is stored in the folder `~/.cache/selenium/manager/0.4.15`). +- Selenium Manager的源代码存储在 Selenium 主仓库的 [rust](https://github.com/SeleniumHQ/selenium/tree/trunk/rust) 文件夹中. 此外, 您可以在 [Selenium Manager Artifacts](https://github.com/SeleniumHQ/selenium_manager_artifacts) 仓库中找到适用于 Windows、Linux 和 macOS 的编译版本. 此文件中链接了稳定版的 Selenium Manager二进制[file](https://github.com/SeleniumHQ/selenium/blob/trunk/common/selenium_manager.bzl)(即在最新稳定版 Selenium 中分发的那些). +- 在构建工作流中, Selenium Manager 是通过 [GitHub Actions workflow](https://github.com/SeleniumHQ/selenium/actions/workflows/ci-rust.yml)进行编译的. 此工作流会为 Windows、Linux 和 macOS 创建二进制文件. 您可以从这些工作流执行中下载这些二进制文件. +- 在缓存中. 自 Selenium Java 绑定的 4.15.0 版本起, Selenium Manager 二进制文件会被提取并复制到缓存文件夹中. 例如, 与 Selenium 4.15.0 一同提供的 Selenium Manager 二进制文件会存储在文件夹 `~/.cache/selenium/manager/0.4.15` 中. -## Examples -Let's consider a typical example: we want to manage chromedriver automatically. For that, we invoke Selenium Manager as follows (notice that the flag `--debug` is optional, but it helps us to understand what Selenium Manager is doing): +## 例子 +让我们来看一个典型的例子: 我们想要自动管理 chromedriver. +为此, 我们像下面这样调用 Selenium Manager +(请注意, 标志 `--debug` 是可选的, 但它有助于我们理解 Selenium Manager 正在做什么): ``` $ ./selenium-manager --browser chrome --debug @@ -196,9 +297,14 @@ INFO Driver path: C:\Users\boni\.cache\selenium\chromedriver\win64\116.0.5845 INFO Browser path: C:\Program Files\Google\Chrome\Application\chrome.exe ``` -In this case, the local Chrome (in Windows) is detected by Selenium Manager. Then, using its version and the CfT endpoints, the proper chromedriver version (115, in this example) is downloaded to the local cache. Finally, Selenium Manager provides two results: i) the driver path (downloaded) and ii) the browser path (local). +在这种情况下, Selenium Manager会检测到本地的 Chrome(在 Windows 系统中). +然后, 根据其版本和 CfT 端点, 会将合适的 chromedriver 版本(在此示例中为 115 版)下载到本地缓存. +最后, Selenium Manager提供两个结果: +i)驱动程序路径(已下载)和 ii)浏览器路径(本地). -Let's consider another example. Now we want to use Chrome beta. Therefore, we invoke Selenium Manager specifying that version label as follows (notice that the CfT beta is discovered, downloaded, and stored in the local cache): +让我们再来看一个例子. 现在我们想要使用 Chrome 测试版. +因此, 我们调用 Selenium Manager并指定该版本标签, +如下所示(请注意, CfT 测试版会被发现、下载并存储在本地缓存中): ``` $ ./selenium-manager --browser chrome --browser-version beta --debug @@ -216,7 +322,7 @@ INFO Driver path: C:\Users\boni\.cache\selenium\chromedriver\win64\117.0.5938 INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\chrome.exe ``` -### Implementing Selenium Manager in Your Scripts +### 在您的脚本中实现 Selenium Manager {{< tabpane text=true >}} {{% tab header="Java" %}} @@ -231,14 +337,20 @@ INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\c **Selenium Manager** {{< gh-codeblock path="examples/python/tests/selenium_manager/usage.py#L10-L12" >}} {{< /tab >}} -{{< tab header="CSharp" >}} -{{< badge-code >}} +{{% tab header="CSharp" %}} +{{< gh-codeblock path="examples/dotnet/SeleniumDocs/SeleniumManager/UsageTest.cs#L10-L18" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -{{< badge-code >}} +{{% tab header="Ruby" %}} +**Previously** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L5-L10" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/ruby/spec/selenium_manager/usage.rb#L12-L16" >}} {{< /tab >}} -{{< tab header="JavaScript" >}} -{{< badge-code >}} +{{% tab header="JavaScript" %}} +**Previously** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L16-L31" >}} +**Selenium Manager** +{{< gh-codeblock path="examples/javascript/test/selenium_manager/usage.spec.js#L6-L14" >}} {{< /tab >}} {{< tab header="Kotlin" >}} {{< badge-code >}} @@ -246,22 +358,27 @@ INFO Browser path: C:\Users\boni\.cache\selenium\chrome\win64\117.0.5938.22\c {{< /tabpane >}} ## Selenium Grid -Selenium Manager allows you to configure the drivers automatically when setting up Selenium Grid. To that aim, you need to include the argument `--selenium-manager true` in the command to start Selenium Grid. For more details, visit the [Selenium Grid starting page](https://www.selenium.dev/documentation/grid/getting_started/). +Selenium Manager 可让您在设置 Selenium Grid 时自动配置驱动程序. +为此, 您需要在启动 Selenium Grid 的命令中包含 `--selenium-manager true` 参数. +更多详情, 请访问 [Selenium Grid starting page](https://www.selenium.dev/documentation/grid/getting_started/). -Moreover, Selenium Manager also allows managing Selenium Grid releases automatically. For that, the argument `--grid` is used as follows: +此外, Selenium Manager 还允许自动管理 Selenium Grid 的版本. 为此, 使用如下参数 `--grid` : ``` $ ./selenium-manager --grid ``` -After this command, Selenium Manager discovers the latest version of Selenium Grid, storing the `selenium-server.jar` in the local cache. +执行此命令后, Selenium Manager会发现 Selenium Grid 的最新版本, +并将 `selenium-server.jar` 存储在本地缓存中. -Optionally, the argument `--grid` allows to specify a Selenium Grid version (`--grid `). +可选地, 参数 `--grid` 允许指定 Selenium Grid 版本( `--grid ` ). -## Known Limitations +## 已知的限制 -### Connectivity issues -Selenium Manager requests remote endpoints (like Chrome for Testing (CfT), among others) to discover and download drivers and browsers from online repositories. When this operation is done in a corporate environment with a proxy or firewall, it might lead to connectivity problems like the following: +### 连接问题 +Selenium Manager会请求远程端点(例如 Chrome 测试版(CfT)等) +从在线存储库中发现并下载驱动程序和浏览器. +当在具有代理或防火墙的企业环境中执行此操作时, 可能会导致以下连接问题: ``` error sending request for url (https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json) @@ -275,70 +392,79 @@ error trying to connect: dns error: failed to lookup address information error trying to connect: An existing connection was forcibly closed by the remote host. (os error 10054) ``` -When that happens, consider the following solutions: +当这种情况发生时, 请考虑以下解决方案: + +- 使用 Selenium 的代理功能(请参阅[documentation](https://www.selenium.dev/documentation/webdriver/drivers/options/#proxy)). 或者, 使用环境变量 `SE_PROXY` 来设置代理 URL, 或者使用配置文件(请参阅 [configuration](https://www.selenium.dev/documentation/selenium_manager/#configuration) ). +- 检查您的网络设置, 以启用 Selenium Manager所需的远程请求和下载. + +### 自定义包管理器 +如果您正在使用需要特定驱动程序的 Linux 软件包管理器(如 Anaconda、snap 等)来运行您的浏览器, +您可能需要指定[driver location](https://www.selenium.dev/documentation/webdriver/drivers/service/#driver-location)、[browser location](https://www.selenium.dev/documentation/webdriver/browsers/chrome/#start-browser-in-a-specified-location), +或者两者都需要, 具体取决于管理器的要求. + + -- Use the proxy capabilities of Selenium (see [documentation](https://www.selenium.dev/documentation/webdriver/drivers/options/#proxy)). Alternatively, use the environment variable `SE_PROXY` to set the proxy URL or use the configuration file (see [configuration](https://www.selenium.dev/documentation/selenium_manager/#configuration)). -- Review your network setup to enable the remote requests and downloads required by Selenium Manager. +### 备选架构 +Selenium 支持由谷歌 Chrome for Testing 管理的所有五种架构, +以及为微软 Edge 提供的所有六种驱动程序. -### Custom package managers -If you are using a Linux package manager (Anaconda, snap, etc) that requires a specific driver be used for your browsers, -you'll need to either specify the -[driver location](https://www.selenium.dev/documentation/webdriver/drivers/service/#driver-location), -the [browser location](https://www.selenium.dev/documentation/webdriver/browsers/chrome/#start-browser-in-a-specified-location), -or both, depending on the requirements. +Selenium 绑定的每次发布都包含三个独立的 Selenium Manager 二进制文件, +分别适用于 Linux、Windows 和 Mac 系统. -### Alternative architectures -Selenium supports all five architectures managed by Google's Chrome for Testing, and all six drivers provided for Microsoft Edge. +* Mac 版本支持 x64 和 aarch64(英特尔和苹果)架构. +* Windows 版本应适用于 x86 和 x64(32 位和 64 位操作系统). +* Linux 版本仅经过验证可在 x64 系统上运行 -Each release of the Selenium bindings comes with three separate Selenium Manager binaries — one for Linux, Windows, and Mac. -* The Mac version supports both x64 and aarch64 (Intel and Apple). -* The Windows version should work for both x86 and x64 (32-bit and 64-bit OS). -* The Linux version has only been verified to work for x64. +不支持更多架构的原因: -Reasons for not supporting more architectures: -1. Neither Chrome for Testing nor Microsoft Edge supports additional architectures, so Selenium Manager would need to -manage something unofficial for it to work. -2. We currently build the binaries from existing GitHub actions runners, which do not support these architectures -3. Any additional architectures would get distributed with all Selenium releases, increasing the total build size +1. 无论是 Chrome for Testing 还是 Microsoft Edge 都不支持其他架构, 因此 Selenium Manager 需要管理一些非官方的东西才能使其正常工作. +2. 我们目前从现有的 GitHub 操作运行器构建二进制文件, 这些运行器不支持这些架构. +3. 任何额外的架构都会随所有 Selenium 版本一起分发, 从而增加总的构建大小. -If you are running Linux on arm64/aarch64, 32-bit architecture, or a Raspberry Pi, Selenium Manager will not work for you. -The biggest issue for people is that they used to get custom-built drivers and put them on PATH and have them work. -Now that Selenium Manager is responsible for locating drivers on PATH, this approach no longer works, and users -need to use a `Service` class and [set the location directly](https://www.selenium.dev/documentation/webdriver/drivers/service/#driver-location). -There are a number of advantages to having Selenium Manager look for drivers on PATH instead of managing that logic -in each of the bindings, so that's currently a trade-off we are comfortable with. +如果您在 arm64/aarch64、32 位架构或树莓派上运行 Linux, Selenium Manager 将无法为您服务. +对于用户来说, 最大的问题在于他们过去常常获取自定义构建的驱动程序并将其放在 PATH 上, 然后就能正常工作. +现在由于 Selenium Manager 负责在 PATH 上查找驱动程序, +这种方法不再奏效, 用户需要使用 `Service` 类并[直接设置位置](https://www.selenium.dev/documentation/webdriver/drivers/service/#driver-location) . +让 Selenium Manager 在 PATH 上查找驱动程序而不是在每个绑定中管理该逻辑, 有诸多优势, 所以目前这是我们愿意接受的权衡. -However, as of Selenium 4.13.0, the Selenium bindings allow locating the Selenium Manager binary using an environment variable called `SE_MANAGER_PATH`. If this variable is set, the bindings will use its value as the Selenium Manager path in the local filesystem. This feature will allow users to provide a custom compilation of Selenium Manager, for instance, if the default binaries (compiled for Windows, Linux, and macOS) are incompatible with a given system (e.g., ARM64 in Linux). +然而, 从 Selenium 4.13.0 版本开始, +Selenium 绑定允许通过一个名为 `SE_MANAGER_PATH` 的环境变量来定位 Selenium Manager 二进制文件. +如果设置了此变量, 绑定将使用其值作为本地文件系统中的 Selenium Manager 路径. +此功能将允许用户提供自定义编译的 Selenium Manager, +例如, 如果默认的二进制文件(针对 Windows、Linux 和 macOS 编译)与给定系统(例如 Linux 中的 ARM64)不兼容. -### Browser dependencies -When automatically managing browsers in Linux, Selenium Manager relies on the releases published by the browser vendors (i.e., Chrome, Firefox, and Edge). These releases are portable in most cases. Nevertheless, there might be cases in which existing libraries are required. In Linux, this problem might be experienced when trying to run Firefox, e.g., as follows: +### 浏览器依赖 +在 Linux 系统中自动管理浏览器时, Selenium Manager 依赖于浏览器供应商(例如 Chrome、Firefox 和 Edge)发布的版本. +这些版本在大多数情况下都是可移植的. +然而, 在某些情况下可能需要现有的库. +在 Linux 中, 尝试运行 Firefox 时可能会遇到此问题, 例如: ``` libdbus-glib-1.so.2: cannot open shared object file: No such file or directory Couldn't load XPCOM. ``` -If that happens, the solution is to install that library, for instance, as follows: +如果出现这种情况, 解决办法是安装相应的库, 例如, 可以按如下方式操作: ``` sudo apt-get install libdbus-glib-1-2 ``` -A similar issue might happen when trying to execute Chrome for Testing in Linux: +在 Linux 系统中尝试执行 Chrome for Testing 时可能会出现类似的问题: ``` error while loading shared libraries: libatk-1.0.so.0: cannot open shared object file: No such file or directory ``` -In this case, the library to be installed is the following: +在这种情况下, 要安装的库是以下这个: ``` sudo apt-get install libatk-bridge2.0-0 ``` -### Using an environment variable for the driver path -It's possible to use an environment variable to specify the driver path without using Selenium Manager. -The following environment variables are supported: +### 使用环境变量来指定驱动程序路径 +可以使用环境变量来指定驱动程序路径, 而无需使用 Selenium Manager. +支持以下环境变量: * `SE_CHROMEDRIVER` * `SE_EDGEDRIVER` @@ -346,27 +472,28 @@ The following environment variables are supported: * `SE_IEDRIVER` * `SE_SAFARIDRIVER` -For example, to specify the path to the chromedriver, -you can set the `SE_CHROMEDRIVER` environment variable to the path of the chromedriver executable. -The following bindings allow you to specify the driver path using an environment variable: +例如, 要指定 chromedriver 的路径, +您可以将 `SE_CHROMEDRIVER` 环境变量设置为 chromedriver 可执行文件的路径. +以下绑定允许您使用环境变量指定驱动程序路径: * Ruby * Java * Python -This feature is available in the Selenium Ruby binding starting from version 4.25.0 and in the Python binding from version 4.26.0. +此功能从 Selenium Ruby 绑定的 4.25.0 版本以及 Python 绑定的 4.26.0 版本开始可用. -## Building a Custom Selenium Manager -In order to build your own custom Selenium Manager that works in an architecture we don't currently support, you can -utilize the following steps: +## 构建自定义 Selenium Manager +若要构建适用于我们当前不支持的架构的自定义 Selenium Manager, +您可以按照以下步骤操作: -1. Install Rust Dev Environment -2. clone Selenium onto your local machine `git clone https://github.com/SeleniumHQ/selenium.git --depth 1` -3. Navigate into your clone `cd selenium/rust` -4. Build selenium `cargo build --release` -5. Set the following environment variable for the driver path `SE_MANAGER_PATH=~/selenium/rust/target/release/selenium-manager` -6. Put the driver you want in a location on your system PATH -7. Selenium will now use the built Selenium Manager to locate the manually downloaded driver on PATH +2. 安装 Rust 开发环境 +3. 将 Selenium 克隆到您的本地机器上 `git clone https://github.com/SeleniumHQ/selenium.git --depth 1` +4. 进入您的下载目录 `cd selenium/rust` +5. 构建 Selenium `cargo build --release` +6. 设置以下环境变量以指定驱动程序路径 `SE_MANAGER_PATH=~/selenium/rust/target/release/selenium-manager` +7. 将您想要的驱动程序放在系统路径中的某个位置. +8. Selenium 现在将使用内置的 Selenium Manager在 PATH 中定位手动下载的驱动程序. -## Roadmap -You can trace the work in progress in the [Selenium Manager project dashboard](https://github.com/orgs/SeleniumHQ/projects/5). Moreover, you can check the new features shipped with each Selenium Manager release in its [changelog file](https://github.com/SeleniumHQ/selenium/blob/trunk/rust/CHANGELOG.md). +## 路线图 +您可以在 [Selenium Manager project dashboard](https://github.com/orgs/SeleniumHQ/projects/5) 中追踪正在进行的工作. +此外, 您还可以在每个 Selenium Manager 版本的[changelog file](https://github.com/SeleniumHQ/selenium/blob/trunk/rust/CHANGELOG.md) 中查看随版本发布的新增功能. diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.en.md b/website_and_docs/content/documentation/test_practices/design_strategies.en.md index 6db4fe03c094..517b7f0e20a3 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.en.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.en.md @@ -442,3 +442,128 @@ public class ActionBot { ``` Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. + +## Example + +{{< tabpane text=true >}} +{{< tab header="Python" >}} + +An example of `python + pytest + selenium` which implemented "**Action Bot**, **Loadable Component** and **Page Object**". + +A `pytest` fixture `chrome_driver`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Action Bot**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L28-L65" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** definition. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L67-L80" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** and **Page Object**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L82-L172" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + +Test cases implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +Test cases implementation with `pytest`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L174-L240" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md index 34dfec87e6bf..c351cc2a5cfe 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md @@ -434,3 +434,129 @@ public class ActionBot { ``` これらの抽象化が構築され、テストでの重複が特定されると、ボットの上にPageObjectsを階層化することができます。 + +## Example + +{{< tabpane text=true >}} +{{< tab header="Python" >}} + +**Action Bot**、**Loadable Component**、および **Page Object** を実装した `python + pytest + selenium` の例です。 + +A `pytest` fixture `chrome_driver`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Action Bot**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L28-L65" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** definition. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L67-L80" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** and **Page Object**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L82-L172" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + +Test cases implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} + +Test cases implementation with `pytest`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L174-L240" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md index ff0c94c48c00..d45d25e41114 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md @@ -441,3 +441,128 @@ public class ActionBot { ``` Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. + + +## Example + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +An example of `python + pytest + selenium` which implemented "**Action Bot**, **Loadable Component** and **Page Object**". + +A `pytest` fixture `chrome_driver`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Action Bot**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L28-L65" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** definition. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L67-L80" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** and **Page Object**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L82-L172" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + +Test cases implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +Test cases implementation with `pytest`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L174-L240" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md index 9326b0fa8f55..0d379b4b4a36 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md @@ -456,3 +456,128 @@ public class ActionBot { Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. + + +## Example + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +一个用例 使用 `python + pytest + selenium` 实现了设计策略 "**Action Bot**, **Loadable Component** 和 **Page Object**". + +A `pytest` fixture `chrome_driver`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Action Bot**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L28-L65" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** definition. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L67-L80" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + + +"**Loadable Component** and **Page Object**" implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L82-L172" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} + +Test cases implementation. + +{{< tabpane text=true >}} +{{< tab header="Python" >}} +Test cases implementation with `pytest`. + +{{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L174-L240" >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} diff --git a/website_and_docs/content/documentation/webdriver/bidi/logging.zh-cn.md b/website_and_docs/content/documentation/webdriver/bidi/logging.zh-cn.md index 8109898135fd..682b8dc2f866 100644 --- a/website_and_docs/content/documentation/webdriver/bidi/logging.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/bidi/logging.zh-cn.md @@ -1,24 +1,26 @@ --- -title: "WebDriver BiDi Logging Features" -linkTitle: "Logging" +title: "WebDriver BiDi 日志功能" +linkTitle: "日志" weight: 1 description: > - These features are related to logging. Because "logging" can refer to so many - different things, these methods are made available via a "script" namespace. + 这些功能与日志记录有关。 + 由于"logging"可以指代许多不同的事物, + 因此这些方法通过"script"命名空间提供. aliases: [ "/documentation/zh-cn/webdriver/bidirectional/bidirectional_w3c/log", "/documentation/webdriver/bidirectional/webdriver_bidi/log" ] --- -Remember that to use WebDriver BiDi, you must enable it in Options. -For more details, see [Enabling BiDi]({{< ref "BiDi" >}}) +请记住, 要使用 WebDriver BiDi, +您必须在选项中启用它. +更多详情, 请参阅 [启用 BiDi]({{< ref "BiDi" >}}) . -## Console Message Handlers +## 控制台消息处理程序 -Record or take actions on `console.log` events. +记录或对 `console.log` 事件采取行动. -### Add Handler +### 添加处理程序 {{< tabpane text=true >}} {{< tab header="Java" >}} @@ -41,9 +43,9 @@ Record or take actions on `console.log` events. {{< /tab >}} {{< /tabpane >}} -### Remove Handler +### 删除处理程序 -You need to store the ID returned when adding the handler to delete it. +您需要存储添加处理程序时返回的 ID 以便将其删除. {{< tabpane text=true >}} {{< tab header="Java" >}} @@ -66,11 +68,11 @@ You need to store the ID returned when adding the handler to delete it. {{< /tab >}} {{< /tabpane >}} -## JavaScript Exception Handlers +## JavaScript 异常处理程序 -Record or take actions on JavaScript exception events. +记录或对 JavaScript 异常事件采取行动. -### Add Handler +### 添加处理程序 {{< tabpane text=true >}} {{< tab header="Java" >}} @@ -93,9 +95,9 @@ Record or take actions on JavaScript exception events. {{< /tab >}} {{< /tabpane >}} -### Remove Handler +### 删除处理程序 -You need to store the ID returned when adding the handler to delete it. +您需要存储添加处理程序时返回的 ID 以便将其删除. {{< tabpane text=true >}} {{< tab header="Java" >}} diff --git a/website_and_docs/content/documentation/webdriver/browsers/firefox.en.md b/website_and_docs/content/documentation/webdriver/browsers/firefox.en.md index e38174e824e5..8d91e16fb4c3 100644 --- a/website_and_docs/content/documentation/webdriver/browsers/firefox.en.md +++ b/website_and_docs/content/documentation/webdriver/browsers/firefox.en.md @@ -135,7 +135,16 @@ options.profile = FirefoxProfile() driver = FirefoxDriver(options) {{< /tab >}} {{< /tabpane >}} +**Note**: Whether you create an empty `FirefoxProfile` or point it to the directory of your own profile, Selenium +will create a temporary directory to store either the data of the new profile or a copy of your existing one. Every +time you run your program, a different temporary directory will be created. These directories are not cleaned up +explicitly by Selenium, they should eventually get removed by the operating system. However, if you want to remove +the copy manually (e.g. if your profile is large in size), the path of the copy is exposed by the `FirefoxProfile` +object. Check the language specific implementation to see how to retrieve that location. +If you want to use an existing Firefox profile, you can pass in the path to that profile. Please refer to the official +[Firefox documentation](https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_how-do-i-find-my-profile) +for instructions on how to find the directory of your profile. ## Service @@ -451,3 +460,5 @@ please refer to the {{< badge-code >}} {{< /tab >}} {{< /tabpane >}} + +**Note**: As of Firefox 138, geckodriver needs to be started with the argument `--allow-system-access` to switch the context to `CHROME`. diff --git a/website_and_docs/content/documentation/webdriver/browsers/firefox.ja.md b/website_and_docs/content/documentation/webdriver/browsers/firefox.ja.md index 664ab91d6bf6..78e2b4d33bac 100644 --- a/website_and_docs/content/documentation/webdriver/browsers/firefox.ja.md +++ b/website_and_docs/content/documentation/webdriver/browsers/firefox.ja.md @@ -141,6 +141,16 @@ driver = RemoteWebDriver(options) {{< /tabpane >}} +**Note**: Whether you create an empty `FirefoxProfile` or point it to the directory of your own profile, Selenium +will create a temporary directory to store either the data of the new profile or a copy of your existing one. Every +time you run your program, a different temporary directory will be created. These directories are not cleaned up +explicitly by Selenium, they should eventually get removed by the operating system. However, if you want to remove +the copy manually (e.g. if your profile is large in size), the path of the copy is exposed by the `FirefoxProfile` +object. Check the language specific implementation to see how to retrieve that location. + +If you want to use an existing Firefox profile, you can pass in the path to that profile. Please refer to the official +[Firefox documentation](https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_how-do-i-find-my-profile) +for instructions on how to find the directory of your profile. ## サービス @@ -447,3 +457,5 @@ IDはアドオンインストール時の戻り値から取得できます。 {{< badge-code >}} {{< /tab >}} {{< /tabpane >}} + +**Note**: As of Firefox 138, geckodriver needs to be started with the argument `--allow-system-access` to switch the context to `CHROME`. diff --git a/website_and_docs/content/documentation/webdriver/browsers/firefox.pt-br.md b/website_and_docs/content/documentation/webdriver/browsers/firefox.pt-br.md index 0603016ae1d2..0bd6c937a292 100644 --- a/website_and_docs/content/documentation/webdriver/browsers/firefox.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/browsers/firefox.pt-br.md @@ -140,6 +140,16 @@ driver = RemoteWebDriver(options) {{< /tabpane >}} +**Note**: Whether you create an empty `FirefoxProfile` or point it to the directory of your own profile, Selenium +will create a temporary directory to store either the data of the new profile or a copy of your existing one. Every +time you run your program, a different temporary directory will be created. These directories are not cleaned up +explicitly by Selenium, they should eventually get removed by the operating system. However, if you want to remove +the copy manually (e.g. if your profile is large in size), the path of the copy is exposed by the `FirefoxProfile` +object. Check the language specific implementation to see how to retrieve that location. + +If you want to use an existing Firefox profile, you can pass in the path to that profile. Please refer to the official +[Firefox documentation](https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_how-do-i-find-my-profile) +for instructions on how to find the directory of your profile. ## Service @@ -456,3 +466,5 @@ please refer to the {{< badge-code >}} {{< /tab >}} {{< /tabpane >}} + +**Note**: As of Firefox 138, geckodriver needs to be started with the argument `--allow-system-access` to switch the context to `CHROME`. diff --git a/website_and_docs/content/documentation/webdriver/browsers/firefox.zh-cn.md b/website_and_docs/content/documentation/webdriver/browsers/firefox.zh-cn.md index d1384cebc591..4f10968e9b1e 100644 --- a/website_and_docs/content/documentation/webdriver/browsers/firefox.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/browsers/firefox.zh-cn.md @@ -139,6 +139,16 @@ driver = RemoteWebDriver(options) {{< /tabpane >}} +**Note**: Whether you create an empty `FirefoxProfile` or point it to the directory of your own profile, Selenium +will create a temporary directory to store either the data of the new profile or a copy of your existing one. Every +time you run your program, a different temporary directory will be created. These directories are not cleaned up +explicitly by Selenium, they should eventually get removed by the operating system. However, if you want to remove +the copy manually (e.g. if your profile is large in size), the path of the copy is exposed by the `FirefoxProfile` +object. Check the language specific implementation to see how to retrieve that location. + +If you want to use an existing Firefox profile, you can pass in the path to that profile. Please refer to the official +[Firefox documentation](https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_how-do-i-find-my-profile) +for instructions on how to find the directory of your profile. ## Service @@ -453,3 +463,5 @@ please refer to the {{< badge-code >}} {{< /tab >}} {{< /tabpane >}} + +**Note**: As of Firefox 138, geckodriver needs to be started with the argument `--allow-system-access` to switch the context to `CHROME`. diff --git a/website_and_docs/content/documentation/webdriver/getting_started/install_library.en.md b/website_and_docs/content/documentation/webdriver/getting_started/install_library.en.md index c1fda4c5d1a4..8584b09a26ca 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/install_library.en.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/install_library.en.md @@ -37,7 +37,7 @@ Specify the dependency in the project `build.gradle` file as `testImplementation {{% /tab %}} {{% tab header="Python" %}} The minimum supported Python version for each Selenium version can be found -in `Supported Python Versions` on [PyPi](https://pypi.org/project/selenium/) +in "Supported Python Versions" on [PyPi](https://pypi.org/project/selenium/). There are a couple different ways to install Selenium. @@ -50,11 +50,11 @@ pip install selenium ### Download -Alternatively you can download the [PyPI source archive](https://pypi.org/project/selenium/#files) -(selenium-x.x.x.tar.gz) and install it using _setup.py_: +Alternatively you can download the [PyPI Built Distribution](https://pypi.org/project/selenium/#files) +(selenium-x.x.x.-py3-none-any.whl) and install it using _pip_: ```shell -python setup.py install +pip install selenium-x.x.x.-py3-none-any.whl ```
diff --git a/website_and_docs/content/documentation/webdriver/getting_started/install_library.ja.md b/website_and_docs/content/documentation/webdriver/getting_started/install_library.ja.md index cc266706a996..0f2c95dc2fd1 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/install_library.ja.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/install_library.ja.md @@ -48,10 +48,10 @@ pip install selenium ### ダウンロード または、ダウンロードすることもできます[PyPI ソースアーカイブ](https://pypi.org/project/selenium/#files) -(selenium-x.x.x.tar.gz) を使用してインストールします _setup.py_ +(selenium-x.x.x.-py3-none-any.whl) を使用してインストールします _pip_: ```shell -python setup.py install +pip install selenium-x.x.x.-py3-none-any.whl ```
diff --git a/website_and_docs/content/documentation/webdriver/getting_started/install_library.pt-br.md b/website_and_docs/content/documentation/webdriver/getting_started/install_library.pt-br.md index 02456646fb26..3e0a116d3fd5 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/install_library.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/install_library.pt-br.md @@ -37,7 +37,7 @@ Especifique a dependência no `build.gradle` do seu projeto como `testImplementa {{% /tab %}} {{% tab header="Python" %}} A mínima versão suportada do Python para cada versão do Selenium pode ser encontrada -em `Supported Python Versions` no [PyPi](https://pypi.org/project/selenium/) +em "Supported Python Versions" no [PyPi](https://pypi.org/project/selenium/). Existe muitas formas diferentes de instalar Selenium. @@ -51,10 +51,10 @@ pip install selenium ### Download Como uma alternativa você pode baixar o [código fonte PyPI](https://pypi.org/project/selenium/#files) -(selenium-x.x.x.tar.gz) e instalar usando _setup.py_: +(selenium-x.x.x.-py3-none-any.whl) e instalar usando _pip_: ```shell -python setup.py install +pip install selenium-x.x.x.-py3-none-any.whl ```
diff --git a/website_and_docs/content/documentation/webdriver/getting_started/install_library.zh-cn.md b/website_and_docs/content/documentation/webdriver/getting_started/install_library.zh-cn.md index 3a3932a94a3a..2b3335bafc75 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/install_library.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/install_library.zh-cn.md @@ -48,11 +48,11 @@ pip install selenium ### 下载 -此外你可以从这里下载 [PyPI source archive](https://pypi.org/project/selenium/#files) -(selenium-x.x.x.tar.gz) 并通过: _setup.py_ 文件安装: +此外你可以从这里下载 [PyPI Built Distribution](https://pypi.org/project/selenium/#files) +(selenium-x.x.x.-py3-none-any.whl) 并通过: _pip_ 文件安装: ```shell -python setup.py install +pip install selenium-x.x.x.-py3-none-any.whl ```
diff --git a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.en.md b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.en.md index 00dabca65ca1..f2641c0136b7 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.en.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.en.md @@ -198,7 +198,7 @@ In your project's `package.json`, add requirement to `dependencies`: ### Tear Down -{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L28" >}} +{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L30" >}} {{% /tab %}} {{< tab header="JavaScript" >}} diff --git a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.ja.md b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.ja.md index 5f8c05ad8ef9..e69f82b69732 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.ja.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.ja.md @@ -188,7 +188,7 @@ Seleniumコードの使用方法に関係なく、優れた統合開発環境が ### 取り壊す -{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L28" >}} +{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L30" >}} {{% /tab %}} {{< tab header="JavaScript" >}} diff --git a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.pt-br.md b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.pt-br.md index e4bc9a7ac093..8281f5211e32 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.pt-br.md @@ -192,7 +192,7 @@ In your project's `package.json`, adicionar requisito às `dependências`: ### Tear Down -{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L28" >}} +{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L30" >}} {{% /tab %}} {{< tab header="JavaScript" >}} diff --git a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.zh-cn.md b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.zh-cn.md index e3da22cd5f85..f7f1c09a37fb 100644 --- a/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/getting_started/using_selenium.zh-cn.md @@ -6,7 +6,8 @@ description: > 使用IDE和Test Runner库组织Selenium的执行 --- -如果你不仅仅只是想执行一小撮的一次性脚本,你需要能组织和安排好你的代码。这一页会启发你如何真正地使用 Selenium 代码做高效的事情。 +如果你不仅仅只是想执行一小撮的一次性脚本,你需要能组织并编排好你的代码。 +本章会启发你如何真正地使用 Selenium 代码做高效的事情。 ## 常见用法 @@ -14,15 +15,19 @@ description: > ### 重复性任务 -有时候你需要往网站记录日志或者下载一些东西,或者提交一个表单,你可以在预设的时间创建一个 Selenium 脚本去执行一个服务。 +有时候你需要往网站记录日志或者下载一些东西,或者提交一个表单, +你可以在预设的时间创建一个 Selenium 脚本去执行一个服务。 ### 网页爬虫 -你是否期望从一个不提供 API 的网站收集数据?Selenium 可以满足你,但是请确保你了解该网站的服务条例,因为有些网站不允许你这样做,甚至有些网站会屏蔽 Selenium。 +你是否期望从一个不提供 API 的网站收集数据?Selenium 可以满足你, +但是请确保你了解该网站的服务条例, +因为有些网站不允许你这样做,甚至有些网站会屏蔽 Selenium。 ### 测试 -使用 Selenium 做测试需要在 Selenium 执行操作后进行断言,所以一个好的断言类库是很有必要的。至于组织测试用例结构的一些额外特性则需要[Test Runner](#test-runner)来完成。 +使用 Selenium 做测试需要在 Selenium 执行操作后进行断言,所以一个好的断言类库是很有必要的。 +至于组织测试用例结构的一些额外特性则需要[Test Runner](#test-runner)来完成。 ## IDEs @@ -38,18 +43,21 @@ description: > ## Test Runner -即使不使用 Selenium 做测试,如果你有高级用例,使用一个 test runner 去更好地组织你的代码是很有意义的。学会使用 before/after hooks 和分组执行或者并行执行将会非常有用。 +即使不使用 Selenium 做测试,如果你有高级用例,使用一个 test runner 去更好地组织你的代码是很有意义的。 +学会使用 before/after hooks 和分组执行或者并行执行将会非常有用。 -### 待选 +### 候选 有非常多不同的 test runner 可供选择。 -这个教程中所有使用到 test runner 的代码示例都可以在我们的示例目录中找到(或者正在被迁移过去),而且这些示例在每一次发版都会被执行,以确保代码是正确的和最新的。下面是一份包含对应链接的 test runner 清单,其中第一项是被这个仓库和本页所有用例所使用的。 +这个教程中所有使用到 test runner 的代码示例都可以在我们的示例目录中找到(或者正在被迁移过去), +而且这些示例在每一次发版都会被执行,以确保代码是正确的和最新的。 +下面是一份包含对应链接的 test runner 清单,其中第一项是被这个仓库和本页所有用例所使用的。 {{< tabpane text=true >}} {{% tab header="Java" %}} -- [JUnit](https://junit.org/junit5/) - 个广泛使用的用于基于 Java 的 Selenium 测试的测试框架。 +- [JUnit](https://junit.org/junit5/) - 一个广泛使用的用于基于 Java 的 Selenium 测试的测试框架。 - [TestNG](https://testng.org/) - 提供诸如并行测试执行和参数化测试等额外功能。 {{% /tab %}} @@ -78,14 +86,15 @@ description: > {{% /tab %}} {{% tab header="Kotlin" %}} -- [Kotest](https://kotest.io/) - 个灵活且全面的测试框架,专为 Kotlin 设计。 -- [JUnit5](https://junit.org/junit5/) -标准的 Java 测试框架,完全兼容 Kotlin。 +- [Kotest](https://kotest.io/) - 一个灵活且全面的测试框架,专为 Kotlin 设计。 +- [JUnit5](https://junit.org/junit5/) - 标准的 Java 测试框架,完全兼容 Kotlin。 {{% /tab %}} {{< /tabpane >}} ### 安装 -在[安装 Selenium 类库]({{< ref "install_library.md" >}})一节中详细说明了需要哪些东西。这里的代码只展示在我们的文档示例项目中用到的示例。 +在[安装 Selenium 类库]({{< ref "install_library.md" >}})一节中详细说明了需要哪些东西。 +这里的代码只展示在我们的文档示例项目中用到的示例。 {{< tabpane text=true >}} {{% tab header="Java" %}} @@ -176,7 +185,7 @@ In your project's `package.json`, add requirement to `dependencies`: ### Tear Down -{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L28" >}} +{{< gh-codeblock path="examples/ruby/spec/spec_helper.rb#L30" >}} {{% /tab %}} {{< tab header="JavaScript" >}} @@ -242,7 +251,8 @@ npx mocha runningTests.spec.js ### 示例 -在[第一个脚本]({{< ref "first_script.md" >}})一节中,我们了解了 Selenium 脚本的每一个组件。这里是使用 test runner 重新组织那个脚本的一个示例: +在[第一个脚本]({{< ref "first_script.md" >}})一节中,我们了解了 Selenium 脚本的每一个组件。 +这里是使用 test runner 重新组织那个脚本的一个示例: {{< tabpane text=true >}} {{< tab header="Java" >}} @@ -267,6 +277,7 @@ npx mocha runningTests.spec.js ## 下一步 -使用你目前所学到的知识建立你自己的 Selenium 代码吧! +使用你目前所学到的知识构建你自己的 Selenium 代码吧! -想要了解更多的功能特性,请继续阅读我们接下来的[WebDriver 教程]({{< ref "/documentation/webdriver/" >}}) +想要了解更多的功能特性, +请继续阅读我们接下来的[WebDriver 教程]({{< ref "/documentation/webdriver/" >}}) diff --git a/website_and_docs/content/documentation/webdriver/interactions/cookies.en.md b/website_and_docs/content/documentation/webdriver/interactions/cookies.en.md index 2565a731e51e..ba057000f599 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/cookies.en.md +++ b/website_and_docs/content/documentation/webdriver/interactions/cookies.en.md @@ -36,18 +36,8 @@ e.g. http://example.com/some404page) {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L32-L34" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - - # Adds the cookie into current browser context - driver.manage.add_cookie(name: "key", value: "value") -ensure - driver.quit -end +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L9-L11" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L18">}} @@ -85,19 +75,8 @@ It returns the serialized cookie data matching with the cookie name among all as {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L40-L44" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "foo", value: "bar") - - # Get cookie details with named cookie 'foo' - puts driver.manage.cookie_named('foo') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L17-L21" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L35-L38">}} @@ -138,20 +117,8 @@ If browser is no longer available it returns error. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L51-L64" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # Get all available cookies - puts driver.manage.all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L26-L31" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L49-L51">}} @@ -193,20 +160,8 @@ It deletes the cookie data matching with the provided cookie name. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L70-L73" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # delete a cookie with name 'test1' - driver.manage.delete_cookie('test1') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L40-L43" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L61-L62">}} @@ -251,20 +206,8 @@ It deletes all the cookies of the current browsing context. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L92-L97" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # deletes all cookies - driver.manage.delete_all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L49-L54" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L77-L78">}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/cookies.ja.md b/website_and_docs/content/documentation/webdriver/interactions/cookies.ja.md index 9a8be028f39f..74dca2e6b13d 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/cookies.ja.md +++ b/website_and_docs/content/documentation/webdriver/interactions/cookies.ja.md @@ -33,18 +33,8 @@ Cookieの追加では、一連の定義済みのシリアル化可能なJSONオ {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L32-L34" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - - # Adds the cookie into current browser context - driver.manage.add_cookie(name: "key", value: "value") -ensure - driver.quit -end +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L9-L11" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L18">}} @@ -81,19 +71,8 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L40-L44" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "foo", value: "bar") - - # Get cookie details with named cookie 'foo' - puts driver.manage.cookie_named('foo') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L17-L21" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L35-L38">}} @@ -133,20 +112,8 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L51-L64" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # Get all available cookies - puts driver.manage.all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L26-L31" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L49-L51">}} @@ -187,20 +154,8 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L70-L73" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # delete a cookie with name 'test1' - driver.manage.delete_cookie('test1') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L40-L43" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L61-L62">}} @@ -244,20 +199,8 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L92-L97" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # deletes all cookies - driver.manage.delete_all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L49-L54" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L77-L78">}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/cookies.pt-br.md b/website_and_docs/content/documentation/webdriver/interactions/cookies.pt-br.md index 63d69e63ea17..b080164a06d9 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/cookies.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/interactions/cookies.pt-br.md @@ -35,18 +35,8 @@ por exemplo http://example.com/some404page) {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L32-L34" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - - # Adds the cookie into current browser context - driver.manage.add_cookie(name: "key", value: "value") -ensure - driver.quit -end +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L9-L11" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L18">}} @@ -83,19 +73,8 @@ Retorna os dados do cookie serializado correspondentes ao nome do cookie entre t {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L40-L44" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "foo", value: "bar") - - # Get cookie details with named cookie 'foo' - puts driver.manage.cookie_named('foo') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L17-L21" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L35-L38">}} @@ -135,20 +114,8 @@ Se o navegador não estiver mais disponível, ele retornará um erro. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L51-L64" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # Get all available cookies - puts driver.manage.all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L26-L31" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L49-L51">}} @@ -189,20 +156,8 @@ Exclui os dados do cookie que correspondem ao nome do cookie fornecido. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L70-L73" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # delete a cookie with name 'test1' - driver.manage.delete_cookie('test1') -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L40-L43" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L61-L62">}} @@ -246,20 +201,8 @@ Exclui todos os cookies do contexto de navegação atual. {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L92-L97" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # deletes all cookies - driver.manage.delete_all_cookies -ensure - driver.quit -end + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L49-L54" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L77-L78">}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/cookies.zh-cn.md b/website_and_docs/content/documentation/webdriver/interactions/cookies.zh-cn.md index 2a60ccdd93a9..1f3515009759 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/cookies.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/interactions/cookies.zh-cn.md @@ -31,18 +31,8 @@ WebDriver API提供了一种使用内置的方法与Cookie进行交互: {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L32-L34" >}} {{< /tab >}} -{{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - - # Adds the cookie into current browser context - driver.manage.add_cookie(name: "key", value: "value") -ensure - driver.quit -end +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L9-L11" >}} {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L18">}} @@ -79,20 +69,11 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L40-L44" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "foo", value: "bar") - - # Get cookie details with named cookie 'foo' - puts driver.manage.cookie_named('foo') -ensure - driver.quit -end + + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L17-L21" >}} {{< /tab >}} + {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L35-L38">}} {{< /tab >}} @@ -130,21 +111,11 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L51-L64" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L26-L31" >}} +{{< /tab >}} - # Get all available cookies - puts driver.manage.all_cookies -ensure - driver.quit -end - {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L49-L51">}} {{< /tab >}} @@ -184,21 +155,11 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L70-L73" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") +{{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L40-L43" >}} +{{< /tab >}} - # delete a cookie with name 'test1' - driver.manage.delete_cookie('test1') -ensure - driver.quit -end - {{< /tab >}} {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L61-L62">}} {{< /tab >}} @@ -241,21 +202,11 @@ fun main() { {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/CookiesTest.cs#L92-L97" >}} {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - driver.get 'https://www.example.com/' - driver.manage.add_cookie(name: "test1", value: "cookie1") - driver.manage.add_cookie(name: "test2", value: "cookie2") - - # deletes all cookies - driver.manage.delete_all_cookies -ensure - driver.quit -end + + {{< tab header="Ruby" text=true >}} +{{< gh-codeblock path="examples/ruby/spec/interactions/cookies_spec.rb#L49-L54" >}} {{< /tab >}} + {{< tab header="JavaScript" text=true >}} {{< gh-codeblock path="/examples/javascript/test/interactions/cookies.spec.js#L77-L78">}} {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/frames.en.md b/website_and_docs/content/documentation/webdriver/interactions/frames.en.md index 4c02033c24c7..fa53e01d43c4 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/frames.en.md +++ b/website_and_docs/content/documentation/webdriver/interactions/frames.en.md @@ -75,16 +75,12 @@ find the frame using your preferred selector and switch to it. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L38-L46" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Store iframe web element -iframe = driver.find_element(By.CSS_SELECTOR, "#modal > iframe") - - # switch to selected iframe -driver.switch_to.frame(iframe) - - # Now click on button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} + + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L24-L32" >}} +{{< /tab >}} + + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L38-L46" >}} {{< /tab >}} @@ -130,13 +126,11 @@ one found will be switched to. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L50-L58" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Switch frame by id -driver.switch_to.frame('buttonframe') - - # Now, Click on the button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} + +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L34-L42" >}} +{{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L50-L58" >}} {{< /tab >}} @@ -180,7 +174,11 @@ queried using _window.frames_ in JavaScript. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L62-L63" >}} {{< /tab >}} - + +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L45-L46" >}} +{{< /tab >}} + {{< tab header="Ruby" >}} # Switch to the second frame driver.switch_to.frame(1) @@ -210,10 +208,11 @@ like so: {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L66-L67" >}} {{< /tab >}} - {{< tab header="Python" >}} - # switch back to default content -driver.switch_to.default_content() - {{< /tab >}} + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L49-L50" >}} +{{< /tab >}} + + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L66-L67" >}} {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/frames.ja.md b/website_and_docs/content/documentation/webdriver/interactions/frames.ja.md index 97ae27619657..57281cdbd6a5 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/frames.ja.md +++ b/website_and_docs/content/documentation/webdriver/interactions/frames.ja.md @@ -64,16 +64,10 @@ WebElementを使用した切り替えは、最も柔軟なオプションです {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L38-L46" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Store iframe web element -iframe = driver.find_element(By.CSS_SELECTOR, "#modal > iframe") - - # switch to selected iframe -driver.switch_to.frame(iframe) - - # Now click on button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L24-L32" >}} +{{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L38-L46" >}} {{< /tab >}} @@ -118,13 +112,11 @@ FrameまたはiFrameにidまたはname属性がある場合、代わりにこれ {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L50-L58" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Switch frame by id -driver.switch_to.frame('buttonframe') - - # Now, Click on the button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} + +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L34-L42" >}} +{{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L50-L58" >}} {{< /tab >}} @@ -155,31 +147,27 @@ driver.findElement(By.tagName("button")).click() JavaScriptの _window.frames_ を使用して照会できるように、Frameのインデックスを使用することもできます。 {{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" text=true >}} +{{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L62-L63" >}} - {{< /tab >}} - {{< tab header="Ruby" >}} - # Switch to the second frame +{{< /tab >}} +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L45-L46" >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +# Switch to the second frame driver.switch_to.frame(1) - {{< /tab >}} - {{< tab header="CSharp" text=true >}} +{{< /tab >}} +{{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L62-L63" >}} {{< /tab >}} - {{< tab header="Python" >}} - # switching to second iframe based on index -iframe = driver.find_elements(By.TAG_NAME,'iframe')[1] - - # switch to selected iframe -driver.switch_to.frame(iframe) - {{< /tab >}} - {{< tab header="JavaScript" >}} +{{< tab header="JavaScript" >}} // Switches to the second frame await driver.switchTo().frame(1); - {{< /tab >}} - {{< tab header="Kotlin" >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} // Switches to the second frame driver.switchTo().frame(1) - {{< /tab >}} +{{< /tab >}} {{< /tabpane >}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/frames.pt-br.md b/website_and_docs/content/documentation/webdriver/interactions/frames.pt-br.md index 0fd61db35931..eda4f364ffb4 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/frames.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/interactions/frames.pt-br.md @@ -72,16 +72,9 @@ encontrar o quadro usando seu seletor preferido e mudar para ele. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L38-L46" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Store iframe web element -iframe = driver.find_element(By.CSS_SELECTOR, "#modal > iframe") - - # switch to selected iframe -driver.switch_to.frame(iframe) - - # Now click on button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L24-L32" >}} +{{< /tab >}} {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L38-L46" >}} {{< /tab >}} @@ -126,13 +119,11 @@ primeiro encontrado será utilizado. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L50-L58" >}} {{< /tab >}} - {{< tab header="Python" >}} - # Switch frame by id -driver.switch_to.frame('buttonframe') - # Now, Click on the button -driver.find_element(By.TAG_NAME, 'button').click() - {{< /tab >}} +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L34-L42" >}} +{{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L50-L58" >}} {{< /tab >}} @@ -171,31 +162,27 @@ Também é possível usar o índice do frame, podendo ser consultado usando _window.frames_ em JavaScript. {{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" text=true >}} +{{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L62-L63" >}} - {{< /tab >}} - {{< tab header="Ruby" >}} - # Switch to the second frame +{{< /tab >}} +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L45-L46" >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +# Switch to the second frame driver.switch_to.frame(1) - {{< /tab >}} - {{< tab header="CSharp" text=true >}} +{{< /tab >}} +{{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L62-L63" >}} {{< /tab >}} - {{< tab header="Python" >}} - # switching to second iframe based on index -iframe = driver.find_elements(By.TAG_NAME,'iframe')[1] - - # switch to selected iframe -driver.switch_to.frame(iframe) - {{< /tab >}} - {{< tab header="JavaScript" >}} +{{< tab header="JavaScript" >}} // Switches to the second frame await driver.switchTo().frame(1); - {{< /tab >}} - {{< tab header="Kotlin" >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} // Switches to the second frame driver.switchTo().frame(1) - {{< /tab >}} +{{< /tab >}} {{< /tabpane >}} @@ -205,26 +192,25 @@ Para deixar um iframe ou frameset, volte para o conteúdo padrão como a seguir: {{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" text=true >}} +{{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L66-L67" >}} - {{< /tab >}} - {{< tab header="Python" >}} - # switch back to default content -driver.switch_to.default_content() - {{< /tab >}} - {{< tab header="CSharp" text=true >}} +{{< /tab >}} +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L49-L50" >}} +{{< /tab >}} +{{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L66-L67" >}} {{< /tab >}} - {{< tab header="Ruby" >}} - # Return to the top level +{{< tab header="Ruby" >}} +# Return to the top level driver.switch_to.default_content - {{< /tab >}} - {{< tab header="JavaScript" >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} // Return to the top level await driver.switchTo().defaultContent(); - {{< /tab >}} - {{< tab header="Kotlin" >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} // Return to the top level driver.switchTo().defaultContent() - {{< /tab >}} +{{< /tab >}} {{< /tabpane >}} diff --git a/website_and_docs/content/documentation/webdriver/interactions/frames.zh-cn.md b/website_and_docs/content/documentation/webdriver/interactions/frames.zh-cn.md index af55faf58226..96dba4e38f4a 100644 --- a/website_and_docs/content/documentation/webdriver/interactions/frames.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/interactions/frames.zh-cn.md @@ -65,16 +65,10 @@ driver.findElement(By.tagName("button")).click() {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L38-L46" >}} {{< /tab >}} -{{< tab header="Python" >}} - # 存储网页元素 -iframe = driver.find_element(By.CSS_SELECTOR, "#modal > iframe") - - # 切换到选择的 iframe -driver.switch_to.frame(iframe) - - # 单击按钮 -driver.find_element(By.TAG_NAME, 'button').click() + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L24-L32" >}} {{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L38-L46" >}} {{< /tab >}} @@ -119,14 +113,12 @@ driver.findElement(By.tagName("button")).click() {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L50-L58" >}} {{< /tab >}} -{{< tab header="Python" >}} - # 通过 id 切换框架 -driver.switch_to.frame('buttonframe') - - # 单击按钮 -driver.find_element(By.TAG_NAME, 'button').click() + +{{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L34-L42" >}} {{< /tab >}} - {{< tab header="CSharp" text=true >}} + + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L50-L58" >}} {{< /tab >}} {{< tab header="Ruby" >}} @@ -168,20 +160,20 @@ _window.frames_ 进行查询. {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L62-L63" >}} {{< /tab >}} + + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L45-L46" >}} +{{< /tab >}} + {{< tab header="Ruby" >}} # 切换到第 2 个框架 driver.switch_to.frame(1) {{< /tab >}} + {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L62-L63" >}} {{< /tab >}} -{{< tab header="Python" >}} - # 基于索引切换到第 2 个 iframe -iframe = driver.find_elements(By.TAG_NAME,'iframe')[1] - # 切换到选择的 iframe -driver.switch_to.frame(iframe) -{{< /tab >}} {{< tab header="JavaScript" >}} // 切换到第 2 个框架 await driver.switchTo().frame(1); @@ -201,9 +193,8 @@ driver.switchTo().frame(1) {{< tab header="Java" text=true >}} {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/interactions/FramesTest.java#L66-L67" >}} {{< /tab >}} -{{< tab header="Python" >}} - # 切回到默认内容 -driver.switch_to.default_content() + {{< tab header="Python" text=true >}} +{{< gh-codeblock path="examples/python/tests/interactions/test_frames.py#L49-L50" >}} {{< /tab >}} {{< tab header="CSharp" text=true >}} {{< gh-codeblock path="examples/dotnet/SeleniumDocs/Interactions/FramesTest.cs#L66-L67" >}} diff --git a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.en.md b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.en.md index 729ccb3908a1..ae8d70bcab8e 100644 --- a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.en.md +++ b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.en.md @@ -24,10 +24,9 @@ These methods can include conditions such as: [Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html) {{< badge-code >}} {{% /tab %}} -{{< tab header="Python" >}} -[Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html) -{{< badge-code >}} -{{< /tab >}} +{{% tab header="Python" %}} +{{< gh-codeblock path="examples/python/tests/support/test_expected_conditions.py#L14-L15" >}} +{{% /tab %}} {{< tab header="CSharp" >}} .NET stopped supporting Expected Conditions in Selenium 4 to minimize maintenance hassle and redundancy. {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.ja.md b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.ja.md index 729ccb3908a1..ae8d70bcab8e 100644 --- a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.ja.md +++ b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.ja.md @@ -24,10 +24,9 @@ These methods can include conditions such as: [Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html) {{< badge-code >}} {{% /tab %}} -{{< tab header="Python" >}} -[Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html) -{{< badge-code >}} -{{< /tab >}} +{{% tab header="Python" %}} +{{< gh-codeblock path="examples/python/tests/support/test_expected_conditions.py#L14-L15" >}} +{{% /tab %}} {{< tab header="CSharp" >}} .NET stopped supporting Expected Conditions in Selenium 4 to minimize maintenance hassle and redundancy. {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.pt-br.md b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.pt-br.md index 298cbe838688..2c2a3f75d80b 100644 --- a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.pt-br.md @@ -24,10 +24,9 @@ These methods can include conditions such as: [Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html) {{< badge-code >}} {{% /tab %}} -{{< tab header="Python" >}} -[Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html) -{{< badge-code >}} -{{< /tab >}} +{{% tab header="Python" %}} +{{< gh-codeblock path="examples/python/tests/support/test_expected_conditions.py#L14-L15" >}} +{{% /tab %}} {{< tab header="CSharp" >}} .NET stopped supporting Expected Conditions in Selenium 4 to minimize maintenance hassle and redundancy. {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.zh-cn.md b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.zh-cn.md index f9ce68fb0c46..f25b9d5e5215 100644 --- a/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/support_features/expected_conditions.zh-cn.md @@ -24,10 +24,9 @@ description: > [Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html) {{< badge-code >}} {{% /tab %}} -{{< tab header="Python" >}} -[Expected Conditions Documentation](https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html) -{{< badge-code >}} -{{< /tab >}} +{{% tab header="Python" %}} +{{< gh-codeblock path="examples/python/tests/support/test_expected_conditions.py#L14-L15" >}} +{{% /tab %}} {{< tab header="CSharp" >}} .NET stopped supporting Expected Conditions in Selenium 4 to minimize maintenance hassle and redundancy. {{< /tab >}} diff --git a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.en.md b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.en.md index c29b50e2ea8b..d2bb6821ae27 100644 --- a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.en.md +++ b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.en.md @@ -179,3 +179,19 @@ This exception occurs when Selenium tries to interact with an element that is no 2. Ensure locators uniquely identify the intended element to avoid incorrect matches. 3. Check if the element is visible on the page before interacting with it. Use scrolling to bring the element into view, if required. 4. Use explicit waits to ensure the element is interactable before performing actions. + +## ElementNotVisibleException + +This exception is thrown when the element you are trying to interact with _is_ present in the DOM, but is not visible. + +### Likely Cause + +This can occur in several situations: +* Another element is blocking your intended element +* The element is disabled/invisible to the user + +### Possible Solutions + +This issue cannot always be resolved on the user's end, however when it can it is usually solved by the following: +using an explicit wait, or interacting with the page in such a way to make the element visible +(scrolling, clicking a button, etc.) diff --git a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.ja.md b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.ja.md index e07e2ee97d2c..3a61c2ce3147 100644 --- a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.ja.md +++ b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.ja.md @@ -175,3 +175,20 @@ This exception occurs when Selenium tries to interact with an element that is no 2. Ensure locators uniquely identify the intended element to avoid incorrect matches. 3. Check if the element is visible on the page before interacting with it. Use scrolling to bring the element into view, if required. 4. Use explicit waits to ensure the element is interactable before performing actions. + +## ElementNotVisibleException + +This exception is thrown when the element you are trying to interact with _is_ present in the DOM, but is not visible. + +### Likely Cause + +This can occur in several situations: +* Another element is blocking your intended element +* The element is disabled/invisible to the user + +### Possible Solutions + +This issue cannot always be resolved on the user's end, however when it can it is usually solved by the following: +using an explicit wait, or interacting with the page in such a way to make the element visible +(scrolling, clicking a button, etc.) + diff --git a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.pt-br.md b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.pt-br.md index c589a269edd0..31109672a38b 100644 --- a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.pt-br.md +++ b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.pt-br.md @@ -175,3 +175,19 @@ This exception occurs when Selenium tries to interact with an element that is no 2. Ensure locators uniquely identify the intended element to avoid incorrect matches. 3. Check if the element is visible on the page before interacting with it. Use scrolling to bring the element into view, if required. 4. Use explicit waits to ensure the element is interactable before performing actions. + +## ElementNotVisibleException + +This exception is thrown when the element you are trying to interact with _is_ present in the DOM, but is not visible. + +### Likely Cause + +This can occur in several situations: +* Another element is blocking your intended element +* The element is disabled/invisible to the user + +### Possible Solutions + +This issue cannot always be resolved on the user's end, however when it can it is usually solved by the following: +using an explicit wait, or interacting with the page in such a way to make the element visible +(scrolling, clicking a button, etc.) diff --git a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.zh-cn.md b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.zh-cn.md index 5818b46d6a08..a2704257f470 100644 --- a/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.zh-cn.md +++ b/website_and_docs/content/documentation/webdriver/troubleshooting/errors/_index.zh-cn.md @@ -178,3 +178,19 @@ Actions class with `Actions.moveToElement(element)`. 2. 确保定位器唯一标识目标元素,以避免错误匹配。 3. 在与元素交互之前,检查其是否在页面上可见。如果需要,将元素滚动到视图中。 4. 使用显式等待以确保元素在执行操作前可交互。 + +## ElementNotVisibleException + +This exception is thrown when the element you are trying to interact with _is_ present in the DOM, but is not visible. + +### Likely Cause + +This can occur in several situations: +* Another element is blocking your intended element +* The element is disabled/invisible to the user + +### Possible Solutions + +This issue cannot always be resolved on the user's end, however when it can it is usually solved by the following: +using an explicit wait, or interacting with the page in such a way to make the element visible +(scrolling, clicking a button, etc.) diff --git a/website_and_docs/content/sponsor/_index.html b/website_and_docs/content/sponsor/_index.html index 544ebac3a017..f920ad3e0f75 100644 --- a/website_and_docs/content/sponsor/_index.html +++ b/website_and_docs/content/sponsor/_index.html @@ -52,7 +52,8 @@

General Benefits

  • Our website is the first place people learn about Selenium. By having a link to your - website, you will be seen as a supporter of the Selenium project that our users adore.
  • + website, you will be seen as a supporter of the Selenium project that our users adore. +
  • Contributions are tax-deductible.
  • @@ -64,7 +65,6 @@

    General Benefits

    Check the Sponsorship Levels for more benefits. -

    @@ -115,7 +115,7 @@

    Sponsorship Levels

    needs to be actively contributing to the project.
  • - Currently limited to 2 companies but reviewed yearly. + Currently limited to 3 companies but reviewed yearly.
  • Special mention at Selenium events
  • diff --git a/website_and_docs/content/support/_index.html b/website_and_docs/content/support/_index.html index f9c8e619a3d1..faf929c73b8f 100644 --- a/website_and_docs/content/support/_index.html +++ b/website_and_docs/content/support/_index.html @@ -61,7 +61,7 @@

    Chat Room

    Another option, if you prefer, is to use the - + Selenium Slack channel .

    diff --git a/website_and_docs/data/sponsors.yml b/website_and_docs/data/sponsors.yml index c6d90da9c270..913f94cb1752 100644 --- a/website_and_docs/data/sponsors.yml +++ b/website_and_docs/data/sponsors.yml @@ -9,6 +9,10 @@ development: - logo: "/images/sponsors/saucelabs.png" url: "https://saucelabs.com/resources/topic-hub/selenium?utm_source=selenium&utm_medium=website&utm_campaign=selenium-sponsorship-fy25" name: "Sauce Labs" + # Sponsorship renewed October 16, 2024. Renewed again on April 8, 2025, under the "Development" level + - logo: "/images/sponsors/lambda-test.png" + url: "https://www.lambdatest.com/selenium-automation?utm_source=selenium&utm_medium=open-source-sponsor&utm_campaign=nov_14&utm_term=sk&utm_content=web" + name: "LambdaTest" selenium: # Set to false if no items are present @@ -22,10 +26,6 @@ selenium: - logo: "/images/sponsors/applitools.png" url: "https://applitools.com/" name: "Applitools" - # Sponsorship renewed October 16, 2024. - - logo: "/images/sponsors/lambda-test.png" - url: "https://www.lambdatest.com/selenium-automation?utm_source=selenium&utm_medium=open-source-sponsor&utm_campaign=nov_14&utm_term=sk&utm_content=web" - name: "LambdaTest" # Sponsorship start date: June 24, 2015, updated July 2023 (only logo, not the agreement) # - logo: "/images/sponsors/Digital.ai.jpg" # url: "http://bit.ly/36uZ7ad" @@ -87,6 +87,10 @@ bronze: # Set to false if no items are present enable: true item: + # Sponsorship renewal date: Apr 12, 2026 + - logo: "/images/sponsors/kualtee.png" + url: "http://www.kualitee.com/?utm_source=selenium&utm_medium=website&utm_campaign=seleniumpartnership" + name: "Kualitee" # Sponsorship renewal date: Feb 4, 2025 - logo: "/images/sponsors/alpi.jpg" url: "http://www.alpi.com/courses-opensource/index.cfm" diff --git a/website_and_docs/hugo.toml b/website_and_docs/hugo.toml index fa46c474e1e2..bcfe43ff4b29 100644 --- a/website_and_docs/hugo.toml +++ b/website_and_docs/hugo.toml @@ -164,7 +164,7 @@ plausible_analytics = true prism_syntax_highlighting = true # Enable announcement banner below navbar (Go to announcement-banner.html to change the message) -banner_flag = true +banner_flag = false announcement_banner = true # Enable Algolia DocSearch @@ -258,7 +258,7 @@ enable = false desc = "Development takes place here!" [[params.links.developer]] name = "Slack" - url = "https://join.slack.com/t/seleniumhq/shared_invite/zt-2stlcmc6b-Hww~3r3yAzquhSq4riOFxA" + url = "https://inviter.co/seleniumhq" icon = "fab fa-slack" desc = "Chat with other project developers and users in Slack" [[params.links.developer]] diff --git a/website_and_docs/layouts/downloads/list.html b/website_and_docs/layouts/downloads/list.html index 119d236986c4..ab285b64d790 100644 --- a/website_and_docs/layouts/downloads/list.html +++ b/website_and_docs/layouts/downloads/list.html @@ -25,7 +25,7 @@

    Latest stable version - 4.29.0 + 4.32.0

    To use the Selenium Server in a Grid configuration see the @@ -115,7 +115,7 @@

    C# NuGet

    - Nuget latest release is 4.29.0 Released on February 20, 2025. + Nuget latest release is 4.32.0 Released on May 02, 2025.

    • diff --git a/website_and_docs/layouts/partials/announcement-banner.html b/website_and_docs/layouts/partials/announcement-banner.html index 9bcd5cadfdbd..93521caa3f31 100644 --- a/website_and_docs/layouts/partials/announcement-banner.html +++ b/website_and_docs/layouts/partials/announcement-banner.html @@ -4,16 +4,12 @@
      - +
      diff --git a/website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html b/website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html index 110ea411129d..8d389816865e 100644 --- a/website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html +++ b/website_and_docs/layouts/partials/selenium-clients-and-webdriver-bindings.html @@ -27,7 +27,7 @@

      Selenium Clients and WebDriver Language Bin

      Stable: - 4.29.0 (February 20, 2025) + 4.32.0 (May 02, 2025)

      @@ -54,8 +54,8 @@

      Selenium Clients and WebDriver Language Bin

      Stable: - - 4.29.0 (February 20, 2025) + + 4.32.0 (May 02, 2025)

      @@ -82,8 +82,8 @@

      Selenium Clients and WebDriver Language Bin

      Stable: - - 4.29.0 (February 20, 2025) + + 4.32.0 (May 02, 2025)

      @@ -111,7 +111,7 @@

      Selenium Clients and WebDriver Language Bin

      Stable: - 4.29.0 (February 20, 2024) + 4.32.0 (May 02, 2024)

      @@ -139,11 +139,11 @@

      Selenium Clients and WebDriver Language Bin

      Stable: - 4.29.0 (February 20, 2025) + 4.32.0 (May 02, 2025)

      - + Changelog

      diff --git a/website_and_docs/package-lock.json b/website_and_docs/package-lock.json index 74955c2f9e78..2e2f78b8f4cf 100644 --- a/website_and_docs/package-lock.json +++ b/website_and_docs/package-lock.json @@ -11,50 +11,7 @@ "dependencies": { "autoprefixer": "^10.4.21", "postcss": "^8.5.3", - "postcss-cli": "^11.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "postcss-cli": "^11.0.1" } }, "node_modules/ansi-regex": { @@ -256,11 +213,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "license": "MIT", "engines": { - "node": ">= 0.6.0" + "node": ">=4" } }, "node_modules/electron-to-chromium": { @@ -283,29 +241,6 @@ "node": ">=6" } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -364,17 +299,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -386,38 +310,11 @@ "node": ">= 6" } }, - "node_modules/globby": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", - "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -487,26 +384,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -546,17 +423,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -610,21 +476,21 @@ } }, "node_modules/postcss-cli": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.0.tgz", - "integrity": "sha512-xMITAI7M0u1yolVcXJ9XTZiO9aO49mcoKQy6pCDFdMh9kGqhzLVpWxeD/32M/QBmkhcGypZFFOLNLmIW4Pg4RA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz", + "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", + "license": "MIT", "dependencies": { "chokidar": "^3.3.0", - "dependency-graph": "^0.11.0", + "dependency-graph": "^1.0.0", "fs-extra": "^11.0.0", - "get-stdin": "^9.0.0", - "globby": "^14.0.0", "picocolors": "^1.0.0", "postcss-load-config": "^5.0.0", "postcss-reporter": "^7.0.0", "pretty-hrtime": "^1.0.3", "read-cache": "^1.0.0", "slash": "^5.0.0", + "tinyglobby": "^0.2.12", "yargs": "^17.0.0" }, "bin": { @@ -703,25 +569,6 @@ "node": ">= 0.8" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -749,41 +596,11 @@ "node": ">=0.10.0" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -828,6 +645,48 @@ "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==" }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -840,17 +699,6 @@ "node": ">=8.0" } }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -951,34 +799,6 @@ } }, "dependencies": { - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==" - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1082,9 +902,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==" }, "electron-to-chromium": { "version": "1.5.113", @@ -1101,26 +921,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "requires": { - "reusify": "^1.0.4" - } - }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1155,11 +955,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==" - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1168,29 +963,11 @@ "is-glob": "^4.0.1" } }, - "globby": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", - "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", - "requires": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - } - }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1236,20 +1013,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==" }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, "nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -1270,11 +1033,6 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, - "path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==" - }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1301,21 +1059,20 @@ } }, "postcss-cli": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.0.tgz", - "integrity": "sha512-xMITAI7M0u1yolVcXJ9XTZiO9aO49mcoKQy6pCDFdMh9kGqhzLVpWxeD/32M/QBmkhcGypZFFOLNLmIW4Pg4RA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz", + "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", "requires": { "chokidar": "^3.3.0", - "dependency-graph": "^0.11.0", + "dependency-graph": "^1.0.0", "fs-extra": "^11.0.0", - "get-stdin": "^9.0.0", - "globby": "^14.0.0", "picocolors": "^1.0.0", "postcss-load-config": "^5.0.0", "postcss-reporter": "^7.0.0", "pretty-hrtime": "^1.0.3", "read-cache": "^1.0.0", "slash": "^5.0.0", + "tinyglobby": "^0.2.12", "yargs": "^17.0.0" } }, @@ -1347,11 +1104,6 @@ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==" }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -1373,19 +1125,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, "slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -1419,6 +1158,28 @@ "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==" }, + "tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "requires": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "dependencies": { + "fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "requires": {} + }, + "picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==" + } + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1427,11 +1188,6 @@ "is-number": "^7.0.0" } }, - "unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==" - }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/website_and_docs/package.json b/website_and_docs/package.json index f089c34c9a40..81bde38ea5b8 100644 --- a/website_and_docs/package.json +++ b/website_and_docs/package.json @@ -11,6 +11,6 @@ "dependencies": { "autoprefixer": "^10.4.21", "postcss": "^8.5.3", - "postcss-cli": "^11.0.0" + "postcss-cli": "^11.0.1" } } diff --git a/website_and_docs/static/images/blog/2025/lambdatest-selenium-development-partner.png b/website_and_docs/static/images/blog/2025/lambdatest-selenium-development-partner.png new file mode 100644 index 000000000000..712d0245cef7 Binary files /dev/null and b/website_and_docs/static/images/blog/2025/lambdatest-selenium-development-partner.png differ diff --git a/website_and_docs/static/images/blog/2025/selenium_4.30.jpg b/website_and_docs/static/images/blog/2025/selenium_4.30.jpg new file mode 100644 index 000000000000..e4a948f087d3 Binary files /dev/null and b/website_and_docs/static/images/blog/2025/selenium_4.30.jpg differ diff --git a/website_and_docs/static/images/blog/2025/selenium_4.31.jpg b/website_and_docs/static/images/blog/2025/selenium_4.31.jpg new file mode 100644 index 000000000000..a8374b70e789 Binary files /dev/null and b/website_and_docs/static/images/blog/2025/selenium_4.31.jpg differ diff --git a/website_and_docs/static/images/blog/2025/selenium_4.32.jpg b/website_and_docs/static/images/blog/2025/selenium_4.32.jpg new file mode 100644 index 000000000000..1f24f76ff216 Binary files /dev/null and b/website_and_docs/static/images/blog/2025/selenium_4.32.jpg differ diff --git a/website_and_docs/static/images/sponsors/kualtee.png b/website_and_docs/static/images/sponsors/kualtee.png new file mode 100644 index 000000000000..e38fc42c8bc3 Binary files /dev/null and b/website_and_docs/static/images/sponsors/kualtee.png differ