Repository: saucelabs/sauce_bindings (language: python)

Issue:
Add tunnelOwner option
I want to know if it's possible to add a tunnelOwner options that allows to use a tunnel from a different user ? 

Documentation : 
https://docs.saucelabs.com/dev/test-configuration-options/#tunnelowner


Discussion / hints:
@brice thank you, we'll look at this. Which programming language are you using?
I use Python and I created this PR #300 
Currently, the following is available in [python/saucebindings/configs.py](https://github.com/saucelabs/sauce_bindings/blob/main/python/saucebindings/configs.py#L9-L10):
    ```
        'tunnel_identifier': 'tunnelIdentifier',
        'parent_tunnel': 'parentTunnel'
    ```

it could be replaced with:

    ```
        'tunnel_name': 'tunnelName',
        'tunnel_owner: 'tunnelOwner'
    ```

`tunnel_owner` is equivalent to `parent_tunnel` and `tunnel_identifier` is equivalent to `tunnel_name`. [Sauce Labs docs](https://docs.saucelabs.com/dev/test-configuration-options/#parenttunnel) contain a deprecation notice In the future, only `tunnelName`/`tunnelOwner` will be supported

Fix patch:
diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml
index 39373d74..02824881 100644
--- a/.github/workflows/java.yml
+++ b/.github/workflows/java.yml
@@ -6,8 +6,12 @@ name: Java
 on:
   push:
     branches: [ main ]
+    paths:
+      - 'java/**'
   pull_request:
     branches: [ main ]
+    paths:
+      - 'java/**'
   workflow_dispatch:
 
 jobs:
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index 2b88b645..4fb8634a 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -5,23 +5,43 @@ name: Python
 
 on:
   push:
-    branches: [ main ]
+    branches:
+      - main
+    paths:
+      - 'python/**'
   pull_request:
-    branches: [ main ]
+    branches:
+      - main
+    paths:
+      - 'python/**'
 
 jobs:
-  build:
+  tests:
     runs-on: ubuntu-latest
 
     steps:
       - uses: actions/checkout@v4
-      - name: Set up Python 3.8
-        uses: actions/setup-python@v2
+      - name: Set up Sauce Connect
+        uses: saucelabs/sauce-connect-action@v2
         with:
-          python-version: 3.8
+            username: ${{ secrets.SAUCE_USERNAME }}
+            accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
+            tunnelName: "sauce-bindings-${{ github.sha }}"
+            region: us-west
+      - name: Set up Python 3.12
+        uses: actions/setup-python@v5
+        with:
+          python-version: 3.12
       - name: Test with pytest
         env:
           SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
           SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
         run: |
           make python_tests
+
+      - name: Sauce Connect cleanup
+        uses: actions/upload-artifact@v3
+        if: ${{ failure() }}
+        with:
+          name: sauce-connect-log
+          path: ${{ env.SAUCE_CONNECT_DIR_IN_HOST }}/sauce-connect.log
diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml
index ab565786..ba08f7bd 100644
--- a/.github/workflows/ruby.yml
+++ b/.github/workflows/ruby.yml
@@ -1,6 +1,3 @@
-# This workflow will install Python dependencies, run tests and lint with a single version of Python
-# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
-
 name: Ruby
 
 on:
diff --git a/python/saucebindings/configs.py b/python/saucebindings/configs.py
index c9b8a702..00a04753 100644
--- a/python/saucebindings/configs.py
+++ b/python/saucebindings/configs.py
@@ -7,6 +7,8 @@ class Configs:
         'custom_data': 'customData',
         'public': 'public',
         'tunnel_identifier': 'tunnelIdentifier',
+        'tunnel_name': 'tunnelName',
+        'tunnel_owner': 'tunnelOwner',
         'parent_tunnel': 'parentTunnel'
     }
 
diff --git a/python/saucebindings/options.py b/python/saucebindings/options.py
index a06dbec9..f3f38951 100644
--- a/python/saucebindings/options.py
+++ b/python/saucebindings/options.py
@@ -31,6 +31,7 @@
     'iedriver_version': 'iedriverVersion',
     'max_duration': 'maxDuration',
     'name': 'name',
+    # parent_tunnel is deprecated and being replaced with tunnel_owner.
     'parent_tunnel': 'parentTunnel',
     'prerun': 'prerun',
     'priority': 'priority',
@@ -42,7 +43,10 @@
     'selenium_version': 'seleniumVersion',
     'tags': 'tags',
     'time_zone': 'timeZone',
+    # tunnel_identifier is deprecated and being replaced with tunnel_name.
     'tunnel_identifier': 'tunnelIdentifier',
+    'tunnel_name': 'tunnelName',
+    'tunnel_owner': 'tunnelOwner',
     'video_upload_on_pass': 'videoUploadOnPass',
     'capture_performance': 'capturePerformance'
 }


Test patch:
diff --git a/python/tests/examples/test_common_options.py b/python/tests/examples/test_common_options.py
index 4f30f503..f4b5d98c 100644
--- a/python/tests/examples/test_common_options.py
+++ b/python/tests/examples/test_common_options.py
@@ -6,8 +6,8 @@ class TestCommonOptions(object):
 
     def test_creates_session(self):
         # 1. Create SauceOptions instance with common w3c options
-        sauceOptions = SauceOptions.firefox(browserVersion='73.0',
-                                            platformName='Windows 8',
+        sauceOptions = SauceOptions.firefox(browserVersion='128',
+                                            platformName='Windows 11',
                                             unhandledPromptBehavior="ignore")
 
         # 2. Create Session object with SauceOptions object instance
diff --git a/python/tests/examples/test_sauce_tunnel.py b/python/tests/examples/test_sauce_tunnel.py
new file mode 100644
index 00000000..2133f4ec
--- /dev/null
+++ b/python/tests/examples/test_sauce_tunnel.py
@@ -0,0 +1,26 @@
+import os
+
+from saucebindings.options import SauceOptions
+from saucebindings.session import SauceSession
+
+
+class TestSauceOptions(object):
+
+    def test_creates_session(self):
+        # 1. Create a SauceOptions instance using tunnelName of a tunnel started earlier
+        tunnel_name = "sauce-bindings-{}".format(os.environ.get('GITHUB_SHA') or "test")
+        sauceOptions = SauceOptions.chrome(extendedDebugging=True,
+                                           idleTimeout=45,
+                                           tunnelName=tunnel_name)
+
+        # 2. Create Session object with SauceOptions object instance
+        session = SauceSession(sauceOptions)
+
+        # 3. Start Session to get the Driver
+        driver = session.start()
+
+        # 4. Use the driver in your tests just like normal
+        driver.get('https://www.saucedemo.com/')
+
+        # 5. Stop the Session with whether the test passed or failed
+        session.stop(True)
diff --git a/python/tests/unit/test_chrome.py b/python/tests/unit/test_chrome.py
index 6d58f181..6c3b0494 100644
--- a/python/tests/unit/test_chrome.py
+++ b/python/tests/unit/test_chrome.py
@@ -117,6 +117,20 @@ def test_accepts_sauce_values_with_dict(self):
         assert sauce.tunnel_identifier == 'foobar'
         assert sauce.video_upload_on_pass is False
 
+    def test_accepts_tunnel(self):
+        options = {'build': 'bar',
+                   'idleTimeout': 3,
+                   'name': 'foo',
+                   'tunnelOwner': 'bar',
+                   'tunnelName': 'foobar'}
+
+        sauce = SauceOptions.chrome(**options)
+
+        assert sauce.build == 'bar'
+        assert sauce.name == 'foo'
+        assert sauce.tunnel_owner == 'bar'
+        assert sauce.tunnel_name == 'foobar'
+
     def test_accepts_sauce_values_as_params(self):
         custom_data = {'foo': 'foo',
                        'bar': 'bar'}
diff --git a/python/tests/unit/test_edge.py b/python/tests/unit/test_edge.py
index a98f8c11..9be56de4 100644
--- a/python/tests/unit/test_edge.py
+++ b/python/tests/unit/test_edge.py
@@ -293,6 +293,20 @@ def test_sauce_options(self):
         assert options.tunnel_identifier == 'tunnelname'
         assert options.video_upload_on_pass is False
 
+    def test_accepts_tunnel(self):
+        options = {'build': 'bar',
+                   'idleTimeout': 3,
+                   'name': 'foo',
+                   'tunnelOwner': 'bar',
+                   'tunnelName': 'foobar'}
+
+        sauce = SauceOptions.edge(**options)
+
+        assert sauce.build == 'bar'
+        assert sauce.name == 'foo'
+        assert sauce.tunnel_owner == 'bar'
+        assert sauce.tunnel_name == 'foobar'
+
     def test_setting_browser_name(self):
         options = SauceOptions.edge()
 
diff --git a/python/tests/unit/test_firefox.py b/python/tests/unit/test_firefox.py
index 5371f6de..7d6c8006 100644
--- a/python/tests/unit/test_firefox.py
+++ b/python/tests/unit/test_firefox.py
@@ -352,7 +352,7 @@ def test_setting_capabilities(self):
         assert options.build == 'Sample Build Name'
         assert options.command_timeout == 2
         assert options.custom_data == custom_data
-        assert options.extended_debugging == True
+        assert options.extended_debugging is True
         assert options.idle_timeout == 3
         assert options.geckodriver_version == '0.23'
         assert options.max_duration == 300
@@ -487,3 +487,17 @@ def test_capabilities_for_selenium(self):
                                  }
 
         assert options.to_capabilities() == expected_capabilities
+
+    def test_accepts_tunnel(self):
+        options = {'build': 'bar',
+                   'idleTimeout': 3,
+                   'name': 'foo',
+                   'tunnelOwner': 'bar',
+                   'tunnelName': 'foobar'}
+
+        sauce = SauceOptions.firefox(**options)
+
+        assert sauce.build == 'bar'
+        assert sauce.name == 'foo'
+        assert sauce.tunnel_owner == 'bar'
+        assert sauce.tunnel_name == 'foobar'