Repository: robotframework/SeleniumLibrary
Pull request #968 (2017-10-23 11:27:51)

Cleanup selecting windows.
Code and documentation cleanup to Select Window. Also add Get Window Handles to replace List Windows. See individual commit messages for more details. Ready for review.


Patch:
diff --git a/.travis.yml b/.travis.yml
index 7e1299ec..5ea9c99c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,7 +33,7 @@ matrix:
     - python: "3.6"
       env:
         - BROWSER=chrome
-        - SELENIUM=3.5.0
+        - SELENIUM=3.6.0
         - ROBOTFRAMEWORK=3.0.2
         - ROBOT_OPTIONS=--dotted
     - python: "2.7"
@@ -45,7 +45,7 @@ matrix:
     - python: "2.7"
       env:
         - BROWSER=chrome
-        - SELENIUM=3.5.0
+        - SELENIUM=3.6.0
         - ROBOTFRAMEWORK=2.8.7
         - ROBOT_OPTIONS=
     - python: "3.3"
@@ -57,7 +57,7 @@ matrix:
     - python: "3.3"
       env:
         - BROWSER=firefox
-        - SELENIUM=3.5.0
+        - SELENIUM=3.6.0
         - ROBOTFRAMEWORK=3.0.2
         - ROBOT_OPTIONS="--exclude Known_Issue_Firefox --dotted"
 before_script:
diff --git a/src/SeleniumLibrary/__init__.py b/src/SeleniumLibrary/__init__.py
index 8e3767c5..96aab4e7 100644
--- a/src/SeleniumLibrary/__init__.py
+++ b/src/SeleniumLibrary/__init__.py
@@ -31,7 +31,8 @@ from SeleniumLibrary.keywords import (AlertKeywords,
                                       ScreenshotKeywords,
                                       SelectElementKeywords,
                                       TableElementKeywords,
-                                      WaitingKeywords)
+                                      WaitingKeywords,
+                                      WindowKeywords)
 from SeleniumLibrary.locators import ElementFinder, TableElementFinder
 from SeleniumLibrary.utils import (BrowserCache, Deprecated, LibraryListener,
                                    timestr_to_secs)
@@ -339,7 +340,8 @@ class SeleniumLibrary(DynamicCore):
             ScreenshotKeywords(self),
             SelectElementKeywords(self),
             TableElementKeywords(self),
-            WaitingKeywords(self)
+            WaitingKeywords(self),
+            WindowKeywords(self)
         ]
         self._browsers = BrowserCache()
         DynamicCore.__init__(self, libraries)
diff --git a/src/SeleniumLibrary/errors.py b/src/SeleniumLibrary/errors.py
index 8217f34e..3f905852 100644
--- a/src/SeleniumLibrary/errors.py
+++ b/src/SeleniumLibrary/errors.py
@@ -21,3 +21,7 @@ class SeleniumLibraryException(Exception):
 
 class ElementNotFound(SeleniumLibraryException):
     pass
+
+
+class WindowNotFound(SeleniumLibraryException):
+    pass
diff --git a/src/SeleniumLibrary/keywords/__init__.py b/src/SeleniumLibrary/keywords/__init__.py
index 5776bc2a..a3236bcf 100644
--- a/src/SeleniumLibrary/keywords/__init__.py
+++ b/src/SeleniumLibrary/keywords/__init__.py
@@ -26,3 +26,4 @@ from .screenshot import ScreenshotKeywords
 from .selectelement import SelectElementKeywords
 from .tableelement import TableElementKeywords
 from .waiting import WaitingKeywords
+from .window import WindowKeywords
diff --git a/src/SeleniumLibrary/keywords/browsermanagement.py b/src/SeleniumLibrary/keywords/browsermanagement.py
index 1050b797..cc7ccba2 100644
--- a/src/SeleniumLibrary/keywords/browsermanagement.py
+++ b/src/SeleniumLibrary/keywords/browsermanagement.py
@@ -20,7 +20,6 @@ import types
 
 from robot.utils import NormalizedDict
 from selenium import webdriver
-from selenium.common.exceptions import NoSuchWindowException
 
 from SeleniumLibrary.base import keyword, LibraryComponent
 from SeleniumLibrary.locators import WindowManager
@@ -238,150 +237,6 @@ class BrowserManagementKeywords(LibraryComponent):
         self.debug('Switched to browser with Selenium session id %s.'
                    % self.browser.session_id)
 
-    @keyword
-    def close_window(self):
-        """Closes currently opened pop-up window."""
-        self.browser.close()
-
-    @keyword
-    def get_window_identifiers(self):
-        """Returns and logs id attributes of all known browser windows."""
-        ids = [info.id for info in self._window_manager.get_window_infos()]
-        return self._log_list(ids)
-
-    @keyword
-    def get_window_names(self):
-        """Returns and logs names of all known browser windows."""
-        names = [info.name for info in self._window_manager.get_window_infos()]
-        return self._log_list(names)
-
-    @keyword
-    def get_window_titles(self):
-        """Returns and logs titles of all known browser windows."""
-        titles = [info.title for info in self._window_manager.get_window_infos()]
-        return self._log_list(titles)
-
-    @keyword
-    def maximize_browser_window(self):
-        """Maximizes current browser window."""
-        self.browser.maximize_window()
-
-    @keyword
-    def get_window_size(self):
-        """Returns current window width and height as integers.
-
-        See also `Set Window Size`.
-
-        Example:
-        | ${width} | ${height}= | `Get Window Size` |
-        """
-        size = self.browser.get_window_size()
-        return size['width'], size['height']
-
-    @keyword
-    def set_window_size(self, width, height):
-        """Sets current windows size to given ``width`` and ``height``.
-
-        Values can be given using strings containing numbers or by using
-        actual numbers. See also `Get Window Size`.
-
-        Browsers have a limit how small they can be set. Trying to set them
-        smaller will cause the actual size to be bigger than the requested
-        size.
-
-        Example:
-        | `Set Window Size` | 800 | 600 |
-        """
-        return self.browser.set_window_size(int(width), int(height))
-
-    @keyword
-    def get_window_position(self):
-        """Returns current window position.
-
-        Position is relative to the top left corner of the screen. Returned
-        values are integers. See also `Set Window Position`.
-
-        Example:
-        | ${x} | ${y}= | `Get Window Position` |
-        """
-        position = self.browser.get_window_position()
-        return position['x'], position['y']
-
-    @keyword
-    def set_window_position(self, x, y):
-        """Sets window position using ``x`` and ``y`` coordinates.
-
-        The position is relative to the top left corner of the screen,
-        but some browsers exclude possible task bar set by the operating
-        system from the calculation. The actual position may thus be
-        different with different browsers.
-
-        Values can be given using strings containing numbers or by using
-        actual numbers. See also `Get Window Position`.
-
-        Example:
-        | `Set Window Position` | 100 | 200 |
-        """
-        self.browser.set_window_position(int(x), int(y))
-
-    @keyword
-    def select_window(self, locator=None):
-        """Selects the window matching locator and return previous window handle.
-
-        locator: any of name, title, url, window handle, excluded handle's list, or special words.
-        return: either current window handle before selecting, or None if no current window.
-
-        If the window is found, all subsequent commands use that window, until
-        this keyword is used again. If the window is not found, this keyword fails.
-
-        By default, when a locator value is provided,
-        it is matched against the title of the window and the
-        javascript name of the window. If multiple windows with
-        same identifier are found, the first one is selected.
-
-        There are some special locators for searching target window:
-        string 'main' (default): select the main window;
-        string 'self': only return current window handle;
-        string 'new': select the last-indexed window assuming it is the newest opened window
-        window list: select the first window not in given list (See 'List Windows' to get the list)
-
-        It is also possible to specify the approach SeleniumLibrary should take
-        to find a window by specifying a locator strategy:
-
-        | *Strategy* | *Example*                               | *Description*                        |
-        | title      | Select Window `|` title=My Document     | Matches by window title              |
-        | name       | Select Window `|` name=${name}          | Matches by window javascript name    |
-        | url        | Select Window `|` url=http://google.com | Matches by window's current URL      |
-
-        Example:
-        | Click Link | popup_link | # opens new window |
-        | Select Window | popupName |
-        | Title Should Be | Popup Title |
-        | Select Window |  | | # Chooses the main window again |
-        """
-        try:
-            return self.browser.current_window_handle
-        except NoSuchWindowException:
-            pass
-        finally:
-            self._window_manager.select(locator)
-
-    @keyword
-    def list_windows(self):
-        """Return all current window handles as a list."""
-        return self.browser.window_handles
-
-    @keyword
-    def get_location(self):
-        """Returns the current browser URL."""
-        return self.browser.current_url
-
-    @keyword
-    def get_locations(self):
-        """Returns and logs URLs of all known browser windows."""
-        urls = [info.url for info in self._window_manager.get_window_infos()]
-        return self._log_list(urls)
-
     @keyword
     def get_source(self):
         """Returns the entire HTML source of the current page or frame."""
@@ -392,6 +247,12 @@ class BrowserManagementKeywords(LibraryComponent):
         """Returns the title of current page."""
         return self.browser.title
 
+    @keyword
+    def get_location(self):
+        """Returns the current browser URL."""
+        return self.browser.current_url
+
+
     @keyword
     def location_should_be(self, url):
         """Verifies that current URL is exactly ``url``."""
@@ -696,16 +557,6 @@ class BrowserManagementKeywords(LibraryComponent):
             browser.execute = types.MethodType(execute, browser)
         browser._speed = self.ctx.speed
 
-    def _log_list(self, items, what='item'):
-        msg = [
-            'Altogether {} {}.'.format(
-                len(items), what if len(items) == 1 else '{}s'.format(what))
-        ]
-        for index, item in enumerate(items):
-            msg.append('{}: {}'.format(index + 1, item))
-        self.info('\n'.join(msg))
-        return items
-
     @property
     def _geckodriver_log_config(self):
         if SELENIUM_VERSION.major == '3':
diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py
index 9048285a..bb41d5f8 100644
--- a/src/SeleniumLibrary/keywords/element.py
+++ b/src/SeleniumLibrary/keywords/element.py
@@ -103,7 +103,8 @@ class ElementKeywords(LibraryComponent):
         self.info("Current page contains text '%s'." % text)
 
     @keyword
-    def page_should_contain_element(self, locator, message=None, loglevel='INFO'):
+    def page_should_contain_element(self, locator, message=None,
+                                    loglevel='INFO', limit=None):
         """Verifies that element ``locator`` is found on the current page.
 
         See the `Locating elements` section for details about the locator
@@ -112,21 +113,40 @@ class ElementKeywords(LibraryComponent):
         The ``message`` argument can be used to override the default error
         message.
 
+        The ``limit`` argument can used to define how many elements the
+        page should contain. When ``limit`` is ``None`` (default) page can
+        contain one or more elements. When limit is a number, page must
+        contain same number of elements.
+
         See `Page Should Contain` for explanation about the ``loglevel``
         argument.
+
+        Examples assumes that locator matches to two elements.
+        | `Page Should Contain Element` | div_name | limit=1    | # Keyword fails.                  |
+        | `Page Should Contain Element` | div_name | limit=2    | # Keyword passes.                 |
+        | `Page Should Contain Element` | div_name | limit=none | # None is considered one or more. |
+        | `Page Should Contain Element` | div_name |            | # Same as above.                  |
+
+        The ``limit`` argument is new in SeleniumLibrary 3.0.
         """
-        self.assert_page_contains(locator, message=message, loglevel=loglevel)
+        if is_noney(limit):
+            return self.assert_page_contains(locator, message=message,
+                                             loglevel=loglevel)
+        limit = int(limit)
+        count = len(self.find_elements(locator))
+        if count == limit:
+            self.info('Current page contains {} element(s).'.format(count))
+        else:
+            if is_noney(message):
+                message = ('Page should have contained "{}" element(s), '
+                           'but it did contain "{}" element(s).'
+                           .format(limit, count))
+            self.ctx.log_source(loglevel)
+            raise AssertionError(message)
 
     @keyword
     def locator_should_match_x_times(self, locator, x, message=None, loglevel='INFO'):
-        """Verifies that ``locator`` matches ``x`` number of elements.
-
-        See the `Locating elements` section for details about the locator
-        syntax.
-
-        See `Page Should Contain Element` for explanation about ``message``
-        and ``loglevel`` arguments.
-        """
+        """Deprecated, use `Page Should Contain Element` with ``limit`` argument instead."""
         count = len(self.find_elements(locator))
         x = int(x)
         if count != x:
@@ -693,24 +713,31 @@ return !element.dispatchEvent(evt);
 
     @keyword
     def get_matching_xpath_count(self, xpath, return_str=True):
-        """Returns number of elements matching ``xpath``.
-
-        The default return type is string, but it can changed to an integer
-        by setting the ``return_str`` argument to a false value.
-
-        The ``xpath`` should not contain ``xpath:`` prefix.
-
-        Example:
-        | count = | `Get Matching Xpath Count` | //div[@id='sales-pop'] |
-        """
-        count = len(self.find_elements('xpath:' + xpath))
+        """Deprecated. Use `Get Element Count` instead."""
+        count = self.get_element_count('xpath:' + xpath)
         return str(count) if is_truthy(return_str) else count
 
     @keyword
     def xpath_should_match_x_times(self, xpath, x, message=None, loglevel='INFO'):
-        """Deprecated. Use `Locator Should Match X Times` instead."""
+        """Deprecated, use `Page Should Contain Element` with ``limit`` argument instead."""
         self.locator_should_match_x_times('xpath:'+xpath, x, message, loglevel)
 
+    @keyword
+    def get_element_count(self, locator):
+        """Returns number of elements matching ``locator``.
+
+        If you wish to assert the number of matching elements, use
+        `Page Should Contain Element` with ``limit`` argument. Keyword will
+        always return an integer.
+
+        Example:
+        | ${count} =      | Get Matching Locator Count | name:div_name  |
+        | Should Be True  | ${count} > 2               |                |
+
+        New in SeleniumLibrary 3.0.
+        """
+        return len(self.find_elements(locator))
+
     @keyword
     def add_location_strategy(self, strategy_name, strategy_keyword, persist=False):
         """Adds a custom location strategy.
diff --git a/src/SeleniumLibrary/keywords/frames.py b/src/SeleniumLibrary/keywords/frames.py
index 0587e540..de62e1b2 100644
--- a/src/SeleniumLibrary/keywords/frames.py
+++ b/src/SeleniumLibrary/keywords/frames.py
@@ -23,18 +23,17 @@ class FrameKeywords(LibraryComponent):
     def select_frame(self, locator):
         """Sets frame identified by ``locator`` as the current frame.
 
-        Key attributes for frames are `id` and `name.` See `introduction` for
-        details about locating elements.
-
-        See `Unselect Frame` to cancel the frame selection and return to the Main frame.
+        See the `Locating elements` section for details about the locator
+        syntax.
 
-        Please note that the frame search always start from the document root or main frame.
+        Works both with frames and iframes. Use `Unselect Frame` to cancel
+        the frame selection and return to the main frame.
 
         Example:
-        | Select Frame   | xpath: //frame[@name='top]/iframe[@name='left'] | # Selects the 'left' iframe |
-        | Click Link     | foo                                             | # Clicks link 'foo' in 'left' iframe |
-        | Unselect Frame |                                                 | # Returns to main frame |
-        | Select Frame   | left                                            | # Selects the 'top' frame |
+        | `Select Frame`   | top-frame | # Select frame with id or name 'top-frame'   |
+        | `Click Link`     | example   | # Click link 'example' in the selected frame |
+        | `Unselect Frame` |           | # Back to main frame.                        |
+        | `Select Frame`   | //iframe[@name='xxx'] | # Select frame using xpath       |
         """
         self.info("Selecting frame '%s'." % locator)
         element = self.find_element(locator)
@@ -42,9 +41,9 @@ class FrameKeywords(LibraryComponent):
 
     @keyword
     def unselect_frame(self):
-        """Sets the top frame as the current frame.
+        """Sets the main frame as the current frame.
 
-        In practice cancels a previous `Select Frame` call.
+        In practice cancels the previous `Select Frame` call.
         """
         self.browser.switch_to.default_content()
 
diff --git a/src/SeleniumLibrary/keywords/window.py b/src/SeleniumLibrary/keywords/window.py
new file mode 100644
index 00000000..654d3732
--- /dev/null
+++ b/src/SeleniumLibrary/keywords/window.py
@@ -0,0 +1,221 @@
+# Copyright 2008-2011 Nokia Networks
+# Copyright 2011-2016 Ryan Tomac, Ed Manlove and contributors
+# Copyright 2016-     Robot Framework Foundation
+#
+# Licensed 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.common.exceptions import NoSuchWindowException
+
+from SeleniumLibrary.base import keyword, LibraryComponent
+from SeleniumLibrary.locators import WindowManager
+
+
+class WindowKeywords(LibraryComponent):
+
+    def __init__(self, ctx):
+        LibraryComponent.__init__(self, ctx)
+        self._window_manager = WindowManager(ctx)
+
+    @keyword
+    def select_window(self, locator='MAIN'):
+        """Selects browser window matching ``locator``.
+
+        If the window is found, all subsequent commands use the selected
+        window, until this keyword is used again. If the window is not
+        found, this keyword fails. The previous window handle is returned,
+        and can be used to return back to it later.
+
+        Notice that in this context _window_ means a pop-up window opened
+        when doing something on an existing window. It is not possible to
+        select windows opened with `Open Browser`, `Switch Browser` must
+        be used instead. Notice also that alerts should be handled with
+        `Handle Alert` or other alert related keywords.
+
+        The ``locator`` can be specified using different strategies somewhat
+        similarly as when `locating elements` on pages.
+
+        - By default the ``locator`` is matched against window handle, name,
+          title, and URL. Matching is done in that order and the the first
+          matching window is selected.
+
+        - The ``locator`` can specify an explicit strategy by using format
+          ``strategy:value`` (recommended) or ``strategy=value``. Supported
+          strategies are ``name``, ``title`` and ``url``, which match windows
+          using name, title, and URL, respectively. Additionally, ``default``
+          can be used to explicitly use the default strategy explained above.
+
+        - If the ``locator`` is ``NEW`` (case-insensitive), the latest
+          opened window is selected. It is an error if this is the same
+          as the current window.
+
+        - If the ``locator`` is ``MAIN`` (default, case-insensitive),
+          the main window is selected.
+
+        - If the ``locator`` is ``CURRENT`` (case-insensitive), nothing is
+          done. This effectively just returns the current window handle.
+
+        - If the ``locator`` is not a string, it is expected to be a list
+          of window handles _to exclude_. Such a list of excluded windows
+          can be get from `Get Window Handles` prior to doing an action that
+          opens a new window.
+
+        Example:
+        | `Click Link`      | popup1      |      | # Open new window |
+        | `Select Window`   | example     |      | # Select window using default strategy |
+        | `Title Should Be` | Pop-up 1    |      |
+        | `Click Button`    | popup2      |      | # Open another window |
+        | ${handle} = | `Select Window`   | NEW  | # Select latest opened window |
+        | `Title Should Be` | Pop-up 2    |      |
+        | `Select Window`   | ${handle}   |      | # Select window using handle |
+        | `Title Should Be` | Pop-up 1    |      |
+        | `Select Window`   | MAIN        |      | # Select the main window |
+        | `Title Should Be` | Main        |      |
+        | ${excludes} = | `Get Window Handles` | | # Get list of current windows |
+        | `Click Link`      | popup3      |      | # Open one more window |
+        | `Select Window`   | ${excludes} |      | # Select window using excludes |
+        | `Title Should Be` | Pop-up 3    |      |
+
+        *NOTE:*
+
+        - The ``strategy:value`` syntax is only supported by SeleniumLibrary
+          3.0 and newer.
+        - Earlier versions supported aliases ``None``, ``null`` and the
+          empty string for selecting the main window, and alias ``self``
+          for selecting the current window. These aliases were deprecated
+          in SeleniumLibrary 3.0.
+        - Prior to SeleniumLibrary 3.0 matching windows by name, title
+          and URL was case-insensitive.
+        """
+        try:
+            return self.browser.current_window_handle
+        except NoSuchWindowException:
+            pass
+        finally:
+            self._window_manager.select(locator)
+
+    @keyword
+    def close_window(self):
+        """Closes currently opened pop-up window."""
+        self.browser.close()
+
+    @keyword
+    def get_window_handles(self):
+        """Return all current window handles as a list.
+
+        Can be used as a list of windows to exclude with `Select Window`.
+
+        Prior to SeleniumLibrary 3.0, this keyword was named `List Windows`.
+        """
+        return self.browser.window_handles
+
+    @keyword
+    def list_windows(self):
+        """Deprecated. Use `Get Window Handles` instead."""
+        return self.get_window_handles()
+
+    @keyword
+    def get_window_identifiers(self):
+        """Returns and logs id attributes of all known browser windows."""
+        ids = [info.id for info in self._window_manager.get_window_infos()]
+        return self._log_list(ids)
+
+    @keyword
+    def get_window_names(self):
+        """Returns and logs names of all known browser windows."""
+        names = [info.name for info in self._window_manager.get_window_infos()]
+        return self._log_list(names)
+
+    @keyword
+    def get_window_titles(self):
+        """Returns and logs titles of all known browser windows."""
+        titles = [info.title for info in self._window_manager.get_window_infos()]
+        return self._log_list(titles)
+
+    @keyword
+    def get_locations(self):
+        """Returns and logs URLs of all known browser windows."""
+        urls = [info.url for info in self._window_manager.get_window_infos()]
+        return self._log_list(urls)
+
+    @keyword
+    def maximize_browser_window(self):
+        """Maximizes current browser window."""
+        self.browser.maximize_window()
+
+    @keyword
+    def get_window_size(self):
+        """Returns current window width and height as integers.
+
+        See also `Set Window Size`.
+
+        Example:
+        | ${width} | ${height}= | `Get Window Size` |
+        """
+        size = self.browser.get_window_size()
+        return size['width'], size['height']
+
+    @keyword
+    def set_window_size(self, width, height):
+        """Sets current windows size to given ``width`` and ``height``.
+
+        Values can be given using strings containing numbers or by using
+        actual numbers. See also `Get Window Size`.
+
+        Browsers have a limit how small they can be set. Trying to set them
+        smaller will cause the actual size to be bigger than the requested
+        size.
+
+        Example:
+        | `Set Window Size` | 800 | 600 |
+        """
+        return self.browser.set_window_size(int(width), int(height))
+
+    @keyword
+    def get_window_position(self):
+        """Returns current window position.
+
+        Position is relative to the top left corner of the screen. Returned
+        values are integers. See also `Set Window Position`.
+
+        Example:
+        | ${x} | ${y}= | `Get Window Position` |
+        """
+        position = self.browser.get_window_position()
+        return position['x'], position['y']
+
+    @keyword
+    def set_window_position(self, x, y):
+        """Sets window position using ``x`` and ``y`` coordinates.
+
+        The position is relative to the top left corner of the screen,
+        but some browsers exclude possible task bar set by the operating
+        system from the calculation. The actual position may thus be
+        different with different browsers.
+
+        Values can be given using strings containing numbers or by using
+        actual numbers. See also `Get Window Position`.
+
+        Example:
+        | `Set Window Position` | 100 | 200 |
+        """
+        self.browser.set_window_position(int(x), int(y))
+
+    def _log_list(self, items, what='item'):
+        msg = [
+            'Altogether {} {}.'.format(
+                len(items), what if len(items) == 1 else '{}s'.format(what))
+        ]
+        for index, item in enumerate(items):
+            msg.append('{}: {}'.format(index + 1, item))
+        self.info('\n'.join(msg))
+        return items
diff --git a/src/SeleniumLibrary/locators/windowmanager.py b/src/SeleniumLibrary/locators/windowmanager.py
index b23f2965..0e9d6caf 100644
--- a/src/SeleniumLibrary/locators/windowmanager.py
+++ b/src/SeleniumLibrary/locators/windowmanager.py
@@ -16,25 +16,35 @@
 
 from collections import namedtuple
 
+from robot.api import logger
 from selenium.common.exceptions import (NoSuchWindowException,
                                         WebDriverException)
 
 from SeleniumLibrary.base import ContextAware
+from SeleniumLibrary.errors import WindowNotFound
+from SeleniumLibrary.utils import is_string
 
 
 WindowInfo = namedtuple('WindowInfo', 'handle, id, name, title, url')
 
 
 class WindowManager(ContextAware):
+    _deprecated_locators = {
+        None: 'main',
+        'null': 'main',
+        '': 'main',
+        'popup': 'new',
+        'self': 'current'
+    }
 
     def __init__(self, ctx):
+        ContextAware.__init__(self, ctx)
         self._strategies = {
             'title': self._select_by_title,
             'name': self._select_by_name,
             'url': self._select_by_url,
-            None: self._select_by_default
+            'default': self._select_by_default
         }
-        ContextAware.__init__(self, ctx)
 
     def get_window_infos(self):
         infos = []
@@ -52,44 +62,71 @@ class WindowManager(ContextAware):
         return infos
 
     def select(self, locator):
-        if locator is not None:
-            if isinstance(locator, list):
-                self._select_by_excludes(self.browser, locator)
-                return
-            if locator.lower() == "self" or locator.lower() == "current":
-                return
-            if locator.lower() == "new" or locator.lower() == "popup":
-                self._select_by_last_index(self.browser)
-                return
-        (prefix, criteria) = self._parse_locator(locator)
-        strategy = self._strategies.get(prefix)
-        if strategy is None:
-            raise ValueError("Window locator with prefix '" + prefix + "' is not supported")
-        return strategy(self.browser, criteria)
+        locator = self._handle_deprecated_locators(locator)
+        if not is_string(locator):
+            self._select_by_excludes(self.browser, locator)
+        elif locator.upper() == 'CURRENT':
+            pass
+        elif locator.upper() == 'MAIN':
+            self._select_main_window(self.browser)
+        elif locator.upper() == 'NEW':
+            self._select_by_last_index(self.browser)
+        else:
+            strategy, locator = self._parse_locator(locator)
+            self._strategies[strategy](self.browser, locator)
+
+    def _handle_deprecated_locators(self, locator):
+        if not (is_string(locator) or locator is None):
+            return locator
+        normalized = locator.lower() if is_string(locator) else locator
+        if normalized in self._deprecated_locators:
+            new = self._deprecated_locators[normalized]
+            logger.warn("Using '%s' as window locator is deprecated. "
+                        "Use '%s' instead." % (locator, new))
+            return new
+        return locator
 
-    def _select_by_title(self, browser, criteria):
+    def _parse_locator(self, locator):
+        index = self._get_locator_separator_index(locator)
+        if index != -1:
+            prefix = locator[:index].strip()
+            if prefix in self._strategies:
+                return prefix, locator[index+1:].lstrip()
+        return 'default', locator
+
+    def _get_locator_separator_index(self, locator):
+        if '=' not in locator:
+            return locator.find(':')
+        if ':' not in locator:
+            return locator.find('=')
+        return min(locator.find('='), locator.find(':'))
+
+    def _select_by_title(self, browser, title):
         self._select_matching(
             browser,
-            lambda window_info: window_info[3].strip().lower() == criteria.lower(),
-            "Unable to locate window with title '" + criteria + "'")
+            lambda window_info: window_info.title == title,
+            "Unable to locate window with title '%s'." % title
+        )
 
-    def _select_by_name(self, browser, criteria):
+    def _select_by_name(self, browser, name):
         self._select_matching(
             browser,
-            lambda window_info: window_info[2].strip().lower() == criteria.lower(),
-            "Unable to locate window with name '" + criteria + "'")
+            lambda window_info: window_info.name == name,
+            "Unable to locate window with name '%s'." % name
+        )
 
-    def _select_by_url(self, browser, criteria):
+    def _select_by_url(self, browser, url):
         self._select_matching(
             browser,
-            lambda window_info: window_info[4].strip().lower() == criteria.lower(),
-            "Unable to locate window with URL '" + criteria + "'")
+            lambda window_info: window_info.url == url,
+            "Unable to locate window with URL '%s'." % url
+        )
+
+    def _select_main_window(self, browser):
+        handles = browser.window_handles
+        browser.switch_to.window(handles[0])
 
     def _select_by_default(self, browser, criteria):
-        if criteria is None or len(criteria) == 0 or criteria.lower() == "null":
-            handles = browser.window_handles
-            browser.switch_to.window(handles[0])
-            return
         try:
             starting_handle = browser.current_window_handle
         except NoSuchWindowException:
@@ -99,21 +136,18 @@ class WindowManager(ContextAware):
             if criteria == handle:
                 return
             for item in self._get_current_window_info(browser)[2:4]:
-                if item.strip().lower() == criteria.lower():
+                if item == criteria:
                     return
         if starting_handle:
             browser.switch_to.window(starting_handle)
-        raise ValueError("Unable to locate window with handle or name or title or URL '" + criteria + "'")
+        raise WindowNotFound("No window matching handle, name, title or URL "
+                             "'%s' found." % criteria)
 
     def _select_by_last_index(self, browser):
         handles = browser.window_handles
-        try:
-            if handles[-1] == browser.current_window_handle:
-                raise AssertionError("No new window at last index. Please use '@{ex}= | List Windows' + new window trigger + 'Select Window | ${ex}' to find it.")
-        except IndexError:
-            raise AssertionError("No window found")
-        except NoSuchWindowException:
-            raise AssertionError("Currently no focus window. where are you making a popup window?")
+        if handles[-1] == browser.current_window_handle:
+            raise WindowNotFound('Window with last index is same as '
+                                 'the current window.')
         browser.switch_to.window(handles[-1])
 
     def _select_by_excludes(self, browser, excludes):
@@ -121,20 +155,8 @@ class WindowManager(ContextAware):
             if handle not in excludes:
                 browser.switch_to.window(handle)
                 return
-        raise ValueError("Unable to locate new window")
-
-    def _parse_locator(self, locator):
-        prefix = None
-        criteria = locator
-        if locator is not None and len(locator) > 0:
-            locator_parts = locator.partition('=')
-            if len(locator_parts[1]) > 0:
-                prefix = locator_parts[0].strip().lower()
-                criteria = locator_parts[2].strip()
-        if prefix is None or prefix == 'name':
-            if criteria is None or criteria.lower() == 'main':
-                criteria = ''
-        return (prefix, criteria)
+        raise WindowNotFound('No window not matching excludes %s found.'
+                             % excludes)
 
     def _select_matching(self, browser, matcher, error):
         try:
@@ -147,7 +169,7 @@ class WindowManager(ContextAware):
                 return
         if starting_handle:
             browser.switch_to.window(starting_handle)
-        raise ValueError(error)
+        raise WindowNotFound(error)
 
     def _get_current_window_info(self, browser):
         try:


Test patch:
diff --git a/test/acceptance/keywords/content_assertions.robot b/test/acceptance/keywords/content_assertions.robot
index 761c9a41..cc81b59d 100644
--- a/test/acceptance/keywords/content_assertions.robot
+++ b/test/acceptance/keywords/content_assertions.robot
@@ -55,7 +55,6 @@ Page Should Contain With Disabling Source Logging
     [Teardown]    Set Log Level    DEBUG
 
 Page Should Contain With Frames
-    [Documentation]    Page Should Contain With Frames
     [Setup]    Go To Page "frames/frameset.html"
     Page Should Contain    You're looking at right.
 
@@ -338,23 +337,3 @@ Get All Links
     ${links}=    Get All Links
     Length Should Be    ${links}    19
     List Should Contain Value    ${links}    bold_id
-
-Xpath Should Match X Times
-    [Setup]    Go To Page "forms/login.html"
-    Xpath Should Match X Times    //input[@type="text"]    1
-    Xpath Should Match X Times    //input[@type="text"]    ${1}
-    Run Keyword And Expect Error
-    ...    Locator 'xpath://input[@type="text"]' should have matched 2 times but matched 1 time.
-    ...    Xpath Should Match X Times    //input[@type="text"]    2
-
-Locator Should Match X Times
-    [Setup]    Go To Page "forms/login.html"
-    Locator Should Match X Times    //input[@type="text"]    1
-    Locator Should Match X Times    css:input    ${3}
-    Locator Should Match X Times    link:Missing Link    0
-    Run Keyword And Expect Error
-    ...    Locator '//input[@type="text"]' should have matched 2 times but matched 1 time.
-    ...    Locator Should Match X Times    //input[@type="text"]    2
-    Run Keyword And Expect Error
-    ...    Locator 'css:input' should have matched 1 time but matched 3 times.
-    ...    Locator Should Match X Times    css:input    1
diff --git a/test/acceptance/keywords/counting_elements.robot b/test/acceptance/keywords/counting_elements.robot
new file mode 100644
index 00000000..79f8f443
--- /dev/null
+++ b/test/acceptance/keywords/counting_elements.robot
@@ -0,0 +1,113 @@
+*** Settings ***
+Test Setup        Go To Front Page
+Default Tags      element count
+Resource          ../resource.robot
+Library           String
+
+*** Test Cases ***
+Get Matching XPath Count
+    [Documentation]    Deprecated
+    [Setup]    Go To Page "links.html"
+    ${count}=    Get Matching XPath Count    //a
+    Should Be Equal    ${count}    19
+    ${count}=    Get Matching XPath Count    //a    ${True}
+    Should Be Equal    ${count}    19
+    Should Be String    ${count}
+    ${count}=    Get Matching XPath Count    //a    ${False}
+    Should Be Equal    ${count}    ${19}
+    Should Not Be String    ${count}
+    ${count}=    Get Matching XPath Count    //div[@id="first_div"]/a
+    Should Be Equal    ${count}    2
+
+Xpath Should Match X Times
+    [Documentation]    Deprecated
+    [Setup]    Go To Page "forms/login.html"
+    Xpath Should Match X Times      //input[@type="text"]    1
+    Xpath Should Match X Times      //input[@type="text"]    ${1}
+    Run Keyword And Expect Error
+    ...    Locator 'xpath://input[@type="text"]' should have matched 2 times but matched 1 time.
+    ...    Xpath Should Match X Times    //input[@type="text"]    2
+
+Locator Should Match X Times
+    [Documentation]    Deprecated
+    [Setup]    Go To Page "links.html"
+    Locator Should Match X Times    link=Link    2
+    Locator Should Match X Times    link=Missing Link    0
+    Locator Should Match X Times    name:div_name    2
+    Locator Should Match X Times    xpath://*[@name="div_name"]    2
+
+Locator Should Match X Times Error
+    [Documentation]    Deprecated
+    [Setup]    Go To Page "links.html"
+    Run Keyword And Expect Error
+    ...    Locator 'name: div_name' should have matched 3 times but matched 2 times.
+    ...    Locator Should Match X Times    name: div_name    3
+    Run Keyword And Expect Error
+    ...    Custom error
+    ...    Locator Should Match X Times    name:div_name    3    Custom error
+
+Get Element Count With Xpath Locator
+    [Setup]    Go To Page "links.html"
+    ${count} =     Get Element Count    xpath://*[@name="div_name"]
+    Should Be Equal    ${count}    ${2}
+    ${count} =     Get Element Count    //*[@name="div_name"]
+    Should Be Equal    ${count}    ${2}
+
+Get Element Count With Default Locator
+    [Setup]    Go To Page "links.html"
+    ${count} =     Get Element Count    div_name
+    Should Be Equal    ${count}    ${2}
+
+Get Element Count With Name Locator
+    [Setup]    Go To Page "links.html"
+    ${count} =     Get Element Count    name:div_name
+    Should Be Equal    ${count}    ${2}
+
+Get Element Count Should Not Fail When Zero Elements Is Found
+    [Setup]    Go To Page "links.html"
+    ${count} =     Get Element Count    name:not_exist
+    Should Be Equal    ${count}    ${0}
+
+Page Should Contain Element When Limit Is None
+    [Setup]    Go To Page "links.html"
+    Page Should Contain Element    name: div_name
+    Page Should Contain Element    name: div_name    limit=None
+    Page Should Contain Element    name: div_name    limit=${None}
+
+Page Should Contain Element When Limit Is Number
+    [Documentation]    LOG 2:3    INFO Current page contains 2 element(s).
+    [Setup]    Go To Page "links.html"
+    Page Should Contain Element    name: div_name    limit=2
+
+Page Should Contain Element Log Level Does Not Affect When Keyword Passes
+    [Documentation]    LOG 2:3    INFO Current page contains 2 element(s).
+    [Setup]    Go To Page "links.html"
+    Page Should Contain Element    name: div_name    loglevel=debug    limit=2
+
+Page Should Contain Element When Limit Is Number And Error
+    [Setup]    Go To Page "links.html"
+    Run Keyword And Expect Error
+    ...    Page should have contained "99" element(s), but it did contain "2" element(s).
+    ...    Page Should Contain Element    name: div_name    limit=99
+    Run Keyword And Expect Error
+    ...    Custom error message.
+    ...    Page Should Contain Element    name: div_name    message=Custom error message.    limit=${99}
+
+Page Should Contain Element When Limit Is Not Number
+    [Setup]    Go To Page "links.html"
+    Run Keyword And Expect Error
+    ...    ValueError: invalid literal for int() with base 10: 'AA'
+    ...    Page Should Contain Element    name: div_name    limit=AA
+
+Page Should Contain Element When Error With Limit And Different Loglevels
+    [Documentation]
+    ...    LOG 2.1:5    INFO REGEXP: .*links\\.html.*
+    ...    LOG 3.1:5    DEBUG REGEXP: .*links\\.html.*
+    [Setup]    Go To Page "links.html"
+    Run Keyword And Ignore Error
+    ...    Page Should Contain Element    name: div_name    limit=99
+    Run Keyword And Ignore Error
+    ...    Page Should Contain Element
+    ...    name: div_name
+    ...    loglevel=debug
+    ...    limit=99
diff --git a/test/acceptance/keywords/elements.robot b/test/acceptance/keywords/elements.robot
index 4b46a0fb..95b32a5a 100644
--- a/test/acceptance/keywords/elements.robot
+++ b/test/acceptance/keywords/elements.robot
@@ -55,18 +55,6 @@ Get Element Attribute
     ${class}=    Get Element Attribute    ${second_div}    class
     Should Be Equal    ${class}    Second Class
 
-Get Matching XPath Count
-    ${count}=    Get Matching XPath Count    //a
-    Should Be Equal    ${count}    19
-    ${count}=    Get Matching XPath Count    //a    ${True}
-    Should Be Equal    ${count}    19
-    Should Be String    ${count}
-    ${count}=    Get Matching XPath Count    //a    ${False}
-    Should Be Equal    ${count}    ${19}
-    Should Not Be String    ${count}
-    ${count}=    Get Matching XPath Count    //div[@id="first_div"]/a
-    Should Be Equal    ${count}    2
-
 Get Horizontal Position
     ${pos}=    Get Horizontal Position    link=Link
     Should Be True    ${pos} > 0
diff --git a/test/acceptance/windows.robot b/test/acceptance/windows.robot
index 719e5344..ebe35eb7 100644
--- a/test/acceptance/windows.robot
+++ b/test/acceptance/windows.robot
@@ -122,7 +122,7 @@ Select Popup Window By Excluded List
 Select Window By Special Locator
     [Tags]    Known Issue Internet Explorer
     Cannot Be Executed in IE
-    ${start}=    Select Window    self
+    ${start}=    Select Window    current
     Click Link    my popup
     Wait Until New Window Is Open
     ${parent}=    Select Window    new
diff --git a/test/resources/html/links.html b/test/resources/html/links.html
index 2f163adc..84c6d1bc 100644
--- a/test/resources/html/links.html
+++ b/test/resources/html/links.html
@@ -4,7 +4,7 @@
     <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
   </head>
   <body>
-    <div id="div_id">
+    <div id="div_id" name="div_name">
       <a href="index.html">Relative</a> with text after<br/>
       <a href="sub/index.html">Relative to sub-directory</a><br/>
       <a href="http://www.google.com">Absolute external link</a><br/>
@@ -27,7 +27,7 @@
       </a><br/>
       <a href="target/first.html" id="bold_id"><b>Link with bolded text</b></a>
     </div>
-    <div id="first_div">
+    <div id="first_div" name="div_name">
       <a href="target/first.html"><img src="robot.png" alt="Image"/></a>
       <a href="target/first.html">Link</a>
     </div>
diff --git a/test/unit/keywords/test_keyword_arguments_element.py b/test/unit/keywords/test_keyword_arguments_element.py
index 19a6346b..9d460563 100644
--- a/test/unit/keywords/test_keyword_arguments_element.py
+++ b/test/unit/keywords/test_keyword_arguments_element.py
@@ -1,6 +1,7 @@
 import unittest
 
 from mockito import mock, unstub, when
+from robot.api import logger
 
 from SeleniumLibrary.keywords import ElementKeywords
 
@@ -31,7 +32,6 @@ class KeywordArgumentsElementTest(unittest.TestCase):
         when(self.element).find_element(locator).thenReturn(element)
         with self.assertRaisesRegexp(AssertionError, 'should have been'):
             self.element.element_text_should_be(locator, 'not text')
-
         with self.assertRaisesRegexp(AssertionError, 'foobar'):
             self.element.element_text_should_be(locator, 'not text', 'foobar')
 
@@ -44,8 +44,9 @@ class KeywordArgumentsElementTest(unittest.TestCase):
         value = self.element.get_element_attribute(locator, attrib)
         self.assertEqual(value, 'value')
 
-        locator = '//div@id'
-        value = self.element.get_element_attribute(locator, 'None')
+        when(logger).warn("Using 'Get Element Attribute' without explicit "
+                          "attribute is deprecated.", False).thenReturn(None)
+        value = self.element.get_element_attribute('//div@id', 'None')
         self.assertEqual(value, 'value')
 
     def test_get_matching_xpath_count(self):
diff --git a/test/unit/locators/test_windowmanager.py b/test/unit/locators/test_windowmanager.py
index 52f2f6b2..16e5c09b 100644
--- a/test/unit/locators/test_windowmanager.py
+++ b/test/unit/locators/test_windowmanager.py
@@ -1,8 +1,10 @@
 import unittest
 import uuid
 
-from mockito import mock
+from mockito import mock, when
+from robot.api import logger
 
+from SeleniumLibrary.errors import WindowNotFound
 from SeleniumLibrary.locators import WindowManager
 
 
@@ -10,12 +12,12 @@ class WindowManagerTests(unittest.TestCase):
 
     def test_select_with_invalid_prefix(self):
         manager = WindowManagerWithMockBrowser()
-        with self.assertRaises(ValueError) as err:
+        with self.assertRaises(WindowNotFound) as context:
             manager.select("something=test1")
-            self.assertEqual(
-                str(err),
-                "Window locator with prefix 'something' is not supported"
-            )
+        self.assertEqual(
+            str(context.exception),
+            "No window matching handle, name, title or URL 'something=test1' found."
+        )
 
     def test_select_by_title(self):
         manager = WindowManagerWithMockBrowser(
@@ -26,15 +28,6 @@ class WindowManagerTests(unittest.TestCase):
         manager.select("title=Title 2")
         self.assertEqual(manager.browser.current_window.name, 'win2')
 
-    def test_select_by_title_sloppy_match(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
-        )
-        manager.select("title= tItLe 2  ")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-
     def test_select_by_title_with_multiple_matches(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
@@ -50,10 +43,10 @@ class WindowManagerTests(unittest.TestCase):
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
             {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
         )
-        with self.assertRaises(ValueError) as err:
+        with self.assertRaises(WindowNotFound) as context:
             manager.select("title=Title -1")
-            self.assertEqual(str(err),
-                             "Unable to locate window with title 'Title -1'")
+        self.assertEqual(str(context.exception),
+                         "Unable to locate window with title 'Title -1'.")
 
     def test_select_by_name(self):
         manager = WindowManagerWithMockBrowser(
@@ -64,33 +57,16 @@ class WindowManagerTests(unittest.TestCase):
         manager.select("name=win2")
         self.assertEqual(manager.browser.current_window.name, 'win2')
 
-    def test_select_by_name_sloppy_match(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
-        )
-        manager.select("name= win2  ")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-
-    def test_select_by_name_with_bad_case(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
-        )
-        manager.select("name=Win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-
     def test_select_by_name_no_match(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
             {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
         )
-        with self.assertRaises(ValueError) as err:
+        with self.assertRaises(WindowNotFound) as context:
             manager.select("name=win-1")
-            self.assertEqual(str(err), "Unable to locate window with name 'win-1'")
+        self.assertEqual(str(context.exception),
+                         "Unable to locate window with name 'win-1'.")
 
     def test_select_by_url(self):
         manager = WindowManagerWithMockBrowser(
@@ -101,15 +77,6 @@ class WindowManagerTests(unittest.TestCase):
         manager.select("url=http://localhost/page2.html")
         self.assertEqual(manager.browser.current_window.name, 'win2')
 
-    def test_select_by_url_sloppy_match(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
-        )
-        manager.select("url=   http://LOCALHOST/page2.html  ")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-
     def test_select_by_url_with_multiple_matches(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
@@ -125,45 +92,27 @@ class WindowManagerTests(unittest.TestCase):
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
             {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
         )
-        with self.assertRaises(ValueError) as err:
+        with self.assertRaises(WindowNotFound) as context:
             manager.select("url=http://localhost/page-1.html")
-            self.assertEqual(str(err), "Unable to locate window with URL "
-                                       "'http://localhost/page-1.html'")
-
-    def test_select_with_null_locator(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
+        self.assertEqual(
+            str(context.exception),
+            "Unable to locate window with URL 'http://localhost/page-1.html'."
         )
-        manager.select("name=win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-        manager.select(None)
-        self.assertEqual(manager.browser.current_window.name, 'win1')
 
-    def test_select_with_null_string_locator(self):
+    def test_select_with_deprecated_locators_to_select_main_window(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
             {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
         )
-        manager.select("name=win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-        manager.select("null")
-        self.assertEqual(manager.browser.current_window.name, 'win1')
+        for deprecated in [None, 'null', 'NULL', '']:
+            manager.select("name=win2")
+            msg = "Using '%s' as window locator is deprecated. Use 'main' instead." % deprecated
+            with when(logger).warn(msg).thenReturn(None):
+                manager.select(deprecated)
+            self.assertEqual(manager.browser.current_window.name, 'win1')
 
-    def test_select_with_empty_locator(self):
-        manager = WindowManagerWithMockBrowser(
-            {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
-            {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
-            {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
-        )
-        manager.select("name=win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-        manager.select("")
-        self.assertEqual(manager.browser.current_window.name, 'win1')
-
-    def test_select_with_main_constant_locator(self):
+    def test_select_main_window(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
@@ -173,6 +122,8 @@ class WindowManagerTests(unittest.TestCase):
         self.assertEqual(manager.browser.current_window.name, 'win2')
         manager.select("main")
         self.assertEqual(manager.browser.current_window.name, 'win1')
+        manager.select("MAIN")
+        self.assertEqual(manager.browser.current_window.name, 'win1')
 
     def test_select_by_default_with_name(self):
         manager = WindowManagerWithMockBrowser(
@@ -198,9 +149,14 @@ class WindowManagerTests(unittest.TestCase):
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
             {'name': 'win3', 'title': "Title 3", 'url': 'http://localhost/page3.html'}
         )
-        self.assertRaises(ValueError, manager.select, "win-1")
+        with self.assertRaises(WindowNotFound) as context:
+            manager.select('foobar')
+        self.assertEqual(
+            str(context.exception),
+            "No window matching handle, name, title or URL 'foobar' found."
+        )
 
-    def test_select_with_sloppy_prefix(self):
+    def test_prefix_is_case_sensitive(self):
         manager = WindowManagerWithMockBrowser(
             {'name': 'win1', 'title': "Title 1", 'url': 'http://localhost/page1.html'},
             {'name': 'win2', 'title': "Title 2", 'url': 'http://localhost/page2.html'},
@@ -208,10 +164,12 @@ class WindowManagerTests(unittest.TestCase):
         )
         manager.select("name=win2")
         self.assertEqual(manager.browser.current_window.name, 'win2')
-        manager.select("nAmE=win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
-        manager.select(" name  =win2")
-        self.assertEqual(manager.browser.current_window.name, 'win2')
+        with self.assertRaises(WindowNotFound) as context:
+            manager.select("nAmE=win2")
+        self.assertEqual(
+            str(context.exception),
+            "No window matching handle, name, title or URL 'nAmE=win2' found."
+        )
 
     def test_get_window_infos(self):
         manager = WindowManagerWithMockBrowser(