testing/web-platform/harness/wptrunner/executors/pytestrunner/fixtures.py
author Andreas Tolfsen <ato@mozilla.com>
Mon, 21 Nov 2016 23:39:42 +0100
changeset 442949 a6a1af5d6cdb38b1a91c875b0abc95ae57826513
parent 441262 1ee751ec56eba735291b9ec0e3d451db2cca41e1
child 492579 448d84ab7edad36ca80a510cce7abdb9d788845b
permissions -rw-r--r--
Bug 1319237 - Make session fixture module scoped; r?jgraham I recently changed it to function scoped, which was a mistake. pytest complains if it is not module scoped. MozReview-Commit-ID: GYP9Ky1avks

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import pytest
import webdriver

import contextlib
import httplib


"""pytest fixtures for use in Python-based WPT tests.

The purpose of test fixtures is to provide a fixed baseline upon which
tests can reliably and repeatedly execute.
"""


class Session(object):
    """Fixture to allow access to wptrunner's existing WebDriver session
    in tests.

    The session is not created by default to enable testing of session
    creation.  However, a function-scoped session will be implicitly created
    at the first call to a WebDriver command.  This means methods such as
    `session.send_command` and `session.session_id` are possible to use
    without having a session.

    To illustrate implicit session creation::

        def test_session_scope(session):
            # at this point there is no session
            assert session.session_id is None

            # window_id is a WebDriver command,
            # and implicitly creates the session for us
            assert session.window_id is not None

            # we now have a session
            assert session.session_id is not None

    You can also access the session in custom fixtures defined in the
    tests, such as a setup function::

        @pytest.fixture(scope="function")
        def setup(request, session):
            session.url = "https://example.org"

        def test_something(setup, session):
            assert session.url == "https://example.org"

    When the test function goes out of scope, any remaining user prompts
    and opened windows are closed, and the current browsing context is
    switched back to the top-level browsing context.
    """

    def __init__(self, client):
        self.client = client

    @pytest.fixture(scope="module")
    def session(self, request):
        # finalisers are popped off a stack,
        # making their ordering reverse
        request.addfinalizer(self.switch_to_top_level_browsing_context)
        request.addfinalizer(self.restore_windows)
        request.addfinalizer(self.dismiss_user_prompts)

        return self.client

    def dismiss_user_prompts(self):
        """Dismisses any open user prompts in windows."""
        current_window = self.client.window_handle

        for window in self.windows():
            self.client.window_handle = window
            try:
                self.client.alert.dismiss()
            except webdriver.NoSuchAlertException:
                pass

        self.client.window_handle = current_window

    def restore_windows(self):
        """Closes superfluous windows opened by the test without ending
        the session implicitly by closing the last window.
        """
        current_window = self.client.window_handle

        for window in self.windows(exclude=[current_window]):
            self.client.window_handle = window
            if len(self.client.window_handles) > 1:
                self.client.close()

        self.client.window_handle = current_window

    def switch_to_top_level_browsing_context(self):
        """If the current browsing context selected by WebDriver is a
        `<frame>` or an `<iframe>`, switch it back to the top-level
        browsing context.
        """
        self.client.switch_frame(None)

    def windows(self, exclude=None):
        """Set of window handles, filtered by an `exclude` list if
        provided.
        """
        if exclude is None:
            exclude = []
        wins = [w for w in self.client.handles if w not in exclude]
        return set(wins)


class HTTPRequest(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port

    def head(self, path):
        return self._request("HEAD", path)

    def get(self, path):
        return self._request("GET", path)

    @contextlib.contextmanager
    def _request(self, method, path):
        conn = httplib.HTTPConnection(self.host, self.port)
        try:
            conn.request(method, path)
            yield conn.getresponse()
        finally:
            conn.close()


@pytest.fixture(scope="module")
def http(session):
    return HTTPRequest(session.transport.host, session.transport.port)