Codebase list splinter / f711c88
New upstream version 0.13.0 Sophie Brun 4 years ago
168 changed file(s) with 12064 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 # Copyright 2012 splinter authors. All rights reserved.
1 # Use of this source code is governed by a BSD-style
2 # license that can be found in the LICENSE file.
3
4 [report]
5 exclude_lines =
6 raise NotImplementedError
7
8 [run]
9 omit = *tests*
10
11 [paths]
12 source = splinter
0 *.pyc
1 *.swp
2 coverage-*.egg
3 ludibrio-*.egg
4 nose-*.egg
5 selenium-*.egg
6 splinter.egg-info
7 .coverage
8 distribute-*.egg
9 docs/_build/*
10 tags
11 build/*
12 *.orig
13 .ropeproject
14 dist/*
15 *.log
16 *.swn
17 *.swo
18 *.DS_Store
19 /include
20 /lib
21 /bin
22 /.Python
23 .tox
0 # Copyright 2012-2016 splinter authors. All rights reserved.
1 # Use of this source code is governed by a BSD-style
2 # license that can be found in the LICENSE file.
3
4 sudo: required
5 services:
6 - xvfb
7 before_install:
8 - export DISPLAY=:99.0
9 - wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
10 - mkdir geckodriver
11 - tar -xzf geckodriver-v0.26.0-linux64.tar.gz -C geckodriver
12 - export PATH=$PATH:$PWD/geckodriver
13 - ./travis-install.bash
14 language: python
15 addons:
16 chrome: stable
17 firefox: "68.0"
18 python:
19 - "2.7"
20 - "3.6"
21 - "3.7"
22 - "3.8"
23 env:
24 - DRIVER='tests/test_flaskclient.py tests/test_browser.py tests/test_element_list.py tests/test_request_handler.py'
25 - DRIVER=tests/test_webdriver.py
26 - DRIVER=tests/test_webdriver_remote.py
27 - DRIVER=tests/test_webdriver_firefox.py
28 - DRIVER=tests/test_webdriver_chrome.py
29 matrix:
30 include:
31 - python: 2.7
32 env:
33 - DJANGO_VERSION=1.7.11 DRIVER=tests/test_djangoclient.py
34 - DRIVER=tests/test_zopetestbrowser.py
35 - python: 3.6
36 env: DJANGO_VERSION=2.0.6 DRIVER=tests/test_djangoclient.py
37 - python: 3.7
38 env: DJANGO_VERSION=2.0.6 DRIVER=tests/test_djangoclient.py
39 - python: 3.8
40 env: DJANGO_VERSION=2.0.6 DRIVER=tests/test_djangoclient.py
41 install:
42 - export PATH=$HOME:$PATH
43 - make dependencies
44 script:
45 - pip install tox
46 - tox -- ${DRIVER}
47 dist: xenial
0 # This is the official list of splinter authors for copyright purposes.
1
2 # Please keep the list sorted.
3
4 Adam Victor Nazareth Brandizzi
5 Álvaro Justen
6 Anatoly Bubenkov
7 Andrews Medina
8 Andrey Lebedev
9 Bernardo Barreto Marques
10 Bernardo Heynemann
11 Bitdeli Chef
12 Brian S. Corbin
13 Caige Nichols
14 David Feinberg
15 David Francisco
16 Diógenes Augusto Fernandes Herminio
17 Douglas Camata
18 Douglas Miranda
19 Douglas Soares de Andrade
20 Fábio Miranda Costa
21 Flavia Missi
22 Flávio Ribeiro
23 Francisco Souza
24 Frank Kwok
25 Gabriel Jordão
26 Gabriel Falcão
27 Gabriel Lima de Oliveira
28 Gustavo Rezende
29 Hugo Lopes Tavares
30 Igor Sobreira
31 Jared McGuire
32 Jonas Baumann
33 Juha Mustonen
34 Lorin Hochstein
35 Lucas R. Martins
36 Luiz Geron
37 Marcio Mazza
38 Mayza de Oliveira
39 Michael Edwards
40 Og B. Maciel
41 Rafael Carício
42 Rodrigo Manhães
43 Rob Golding
44 Rômulo Machado
45 Sebastian Vetter
46 Sergio Jorge
47 Steve Pulec
48 Steven Skoczen
49 Sylvain Fraïssé
50 Tarsis Azevedo
51 Tatiana Al-Chueyr
52 Terrence Brannon
53 Thomas Holloway
54 Victor de Oliveira Areas
55 Victor Hooi
56 Yiorgis Gozadinos
0 Copyright (c) 2012, splinter authors
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are
5 met:
6
7 * Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9
10 * Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation and/or
12 other materials provided with the distribution.
13
14 * Neither the name of Splinter nor the names of its contributors may be used to
15 endorse or promote products derived from this software without specific prior
16 written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 include *.rst
1 prune tests
0 # Copyright 2013 splinter authors. All rights reserved.
1 # Use of this source code is governed by a BSD-style
2 # license that can be found in the LICENSE file.
3
4 all: test
5
6 clean:
7 @find . -name "*.pyc" -delete
8
9 doc_dependencies:
10 @pip install -r doc-requirements.txt
11
12 dependencies:
13 @pip install -r test-requirements.txt
14
15 doc: doc_dependencies
16 @cd docs && make clean && make html
17
18 release:
19 @sed -ic -e s/`cat VERSION`/$(version)/ setup.py docs/conf.py splinter/__init__.py
20 @echo $(version) > VERSION
21 @git add setup.py docs/conf.py VERSION splinter/__init__.py
22 @git commit -m "setup: bump to $(version)"
23 @git tag $(version)
24 @git push --tags
25 @git push origin master
26 @rm dist/*
27 @python setup.py sdist bdist_wheel
28 @twine upload dist/*
29
30 which = 'tests'
31
32 test: dependencies clean
33 @echo "Running all tests..."
34 tox -- $(which)
35
36 format: clean dependencies
37 @flake8 --max-line-length 110 ./splinter ./tests
38
39 coverage: dependencies clean
40 @echo "Running all tests with coverage..."
41 @coverage run run_tests.py -w $(which) && coverage report
42
43 install-remote:
44 @wget http://goo.gl/PJUZfa -O selenium-server.jar
45 @java -jar selenium-server.jar > /dev/null 2>&1 &
46 @sleep 1
0 .. image:: https://secure.travis-ci.org/cobrateam/splinter.svg?branch=master
1 :target: http://travis-ci.org/cobrateam/splinter
2
3 +++++++++++++++++++++++++++++++++++++++++++++++++++
4 splinter - python tool for testing web applications
5 +++++++++++++++++++++++++++++++++++++++++++++++++++
6
7 splinter is an open source tool for testing web applications using Python.
8 It lets you automate browser actions, such as visiting URLs and interacting with their items.
9
10 Sample code
11 -----------
12
13 .. code:: python
14
15 from splinter import Browser
16
17 browser = Browser()
18 browser.visit('http://google.com')
19 browser.fill('q', 'splinter - python acceptance testing for web applications')
20 browser.find_by_name('btnG').click()
21
22 if browser.is_text_present('splinter.readthedocs.io'):
23 print("Yes, the official website was found!")
24 else:
25 print("No, it wasn't found... We need to improve our SEO techniques")
26
27 browser.quit()
28
29 **Note:** if you don't provide any driver argument to the ``Browser`` function, ``firefox`` will be used (`Browser function documentation <https://splinter.readthedocs.io/en/latest/api/driver-and-element-api.html>`_).
30
31 `What's new in splinter? <https://splinter.readthedocs.io/en/latest/news.html>`_
32
33 First steps
34 ===========
35
36 * `Installation <https://splinter.readthedocs.io/en/latest/install.html>`_
37 * `Quick tutorial <https://splinter.readthedocs.io/en/latest/tutorial.html>`_
38
39 Splinter open source project
40 ============================
41
42 * `Community <https://splinter.readthedocs.io/en/latest/community.html>`_
43 * `Contribute <https://splinter.readthedocs.io/en/latest/contribute.html>`_
44
45 Documentation
46 =============
47
48 * `Splinter documentation <https://splinter.readthedocs.io>`_
49
50 External links
51 ==============
52 * `Django Full Stack Testing and BDD with Lettuce and Splinter <https://www.cilliano.com/2011/02/07/django-bdd-with-lettuce-and-splinter.html>`_
53
54 * `Testes de aceitação com Lettuce e Splinter (pt-br) <http://www.slideshare.net/franciscosouza/testes-de-aceitao-com-lettuce-e-splinter?from=ss_embed>`_
55
56 * `salad <https://github.com/salad/salad>`_, a nice mix of great BDD ingredients (splinter + `lettuce <http://lettuce.it>`_ integration)
57
58 * `behave-django <https://github.com/behave/behave-django>`_, BDD testing in Django using `Behave <https://github.com/behave/behave/>`_. Works well with splinter.
59
60 * `pytest-splinter <http://pytest-splinter.readthedocs.io>`_, Splinter plugin for the `py.test <http://docs.pytest.org>`_ runner.
61
62 * `PyPOM <http://pypom.readthedocs.io/>`_, PyPOM, or Python Page Object Model, is a Python library that provides a base page object model for use with Selenium or Splinter functional tests.
63
64 * `pypom_form <http://pypom-form.readthedocs.io>`_, is a PyPOM based package that provides declarative schema based form interaction for page object models compatible with Splinter.
0 0.13.0
0 Sphinx>=1.7.8
1 sphinx-rtd-theme>=0.1.8
0 # Makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 PAPER =
7 BUILDDIR = _build
8
9 # Internal variables.
10 PAPEROPT_a4 = -D latex_paper_size=a4
11 PAPEROPT_letter = -D latex_paper_size=letter
12 ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
13
14 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
15
16 help:
17 @echo "Please use \`make <target>' where <target> is one of"
18 @echo " html to make standalone HTML files"
19 @echo " dirhtml to make HTML files named index.html in directories"
20 @echo " singlehtml to make a single large HTML file"
21 @echo " pickle to make pickle files"
22 @echo " json to make JSON files"
23 @echo " htmlhelp to make HTML files and a HTML help project"
24 @echo " qthelp to make HTML files and a qthelp project"
25 @echo " devhelp to make HTML files and a Devhelp project"
26 @echo " epub to make an epub"
27 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
28 @echo " latexpdf to make LaTeX files and run them through pdflatex"
29 @echo " text to make text files"
30 @echo " man to make manual pages"
31 @echo " changes to make an overview of all changed/added/deprecated items"
32 @echo " linkcheck to check all external links for integrity"
33 @echo " doctest to run all doctests embedded in the documentation (if enabled)"
34
35 clean:
36 -rm -rf $(BUILDDIR)/*
37
38 html:
39 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
40 @echo
41 @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
42
43 dirhtml:
44 $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
45 @echo
46 @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
47
48 singlehtml:
49 $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
50 @echo
51 @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
52
53 pickle:
54 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
55 @echo
56 @echo "Build finished; now you can process the pickle files."
57
58 json:
59 $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
60 @echo
61 @echo "Build finished; now you can process the JSON files."
62
63 htmlhelp:
64 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
65 @echo
66 @echo "Build finished; now you can run HTML Help Workshop with the" \
67 ".hhp project file in $(BUILDDIR)/htmlhelp."
68
69 qthelp:
70 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
71 @echo
72 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
73 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
74 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/splinter.qhcp"
75 @echo "To view the help file:"
76 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/splinter.qhc"
77
78 devhelp:
79 $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
80 @echo
81 @echo "Build finished."
82 @echo "To view the help file:"
83 @echo "# mkdir -p $$HOME/.local/share/devhelp/splinter"
84 @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/splinter"
85 @echo "# devhelp"
86
87 epub:
88 $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
89 @echo
90 @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
91
92 latex:
93 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
94 @echo
95 @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
96 @echo "Run \`make' in that directory to run these through (pdf)latex" \
97 "(use \`make latexpdf' here to do that automatically)."
98
99 latexpdf:
100 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
101 @echo "Running LaTeX files through pdflatex..."
102 make -C $(BUILDDIR)/latex all-pdf
103 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
104
105 text:
106 $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
107 @echo
108 @echo "Build finished. The text files are in $(BUILDDIR)/text."
109
110 man:
111 $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
112 @echo
113 @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
114
115 changes:
116 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
117 @echo
118 @echo "The overview file is in $(BUILDDIR)/changes."
119
120 linkcheck:
121 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
122 @echo
123 @echo "Link check complete; look for any errors in the above output " \
124 "or in $(BUILDDIR)/linkcheck/output.txt."
125
126 doctest:
127 $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
128 @echo "Testing of doctests in the sources finished, look at the " \
129 "results in $(BUILDDIR)/doctest/output.txt."
0 #main ul.simple {
1 font-size: 95%;
2 margin: 20px 0 20px 20px;
3 }
4
5 #main ul.simple li {
6 display: block;
7 margin: 5px 0;
8 }
9
10 .footer {
11 clear: both;
12 }
13
14 body {
15 background: #FFF;
16 color: #000;
17 }
18
19 .section blockquote div {
20 -webkit-border-radius: 4px;
21 -moz-border-radius: 4px
22 border-radius: 4px;
23 background-color: #EEEEEE;
24 margin-left: 40px;
25 width: 590px;
26 padding: 10px;
27 }
28
29 div.document {
30 background-color: #FFF;
31 }
32
33 div.highlight pre {
34 -khtml-border-radius: 5px;
35 -moz-border-radius: 5px;
36 -ms-border-radius: 5px;
37 -o-border-radius: 5px;
38 -webkit-border-radius: 5px;
39 border-radius: 5px;
40 padding: 5px;
41 }
42
43 div.highlight-bash {
44 margin: 20px;
45 }
46
47 div.highlight {
48 margin: 20px;
49 }
50
51 div.highlight-python pre {
52 -khtml-border-radius: 5px;
53 -moz-border-radius: 5px;
54 -ms-border-radius: 5px;
55 -o-border-radius: 5px;
56 -webkit-border-radius: 5px;
57 border-radius: 5px;
58 padding: 5px;
59 }
60
61 div.toctree-wrapper {
62 line-height: 22px;
63 margin-left: 24px;
64 }
65
66 div.toctree-wrapper ul li {
67 list-style: disc inside;
68 }
69
70 strong {
71 font-weight: bold;
72 }
73
74 em {
75 font-style: italic;
76 }
77
78 a > em {
79 font-style: normal;
80 }
0 {% extends "!genindex.html" %}
1
2 {% block bodyclass %}{% endblock %}
3 {% block sidebarwrapper %}{% endblock %}
0 {% extends "basic/layout.html" %}
1 {% block doctype %}<!DOCTYPE html>{% endblock %}
2
3 {%- block htmltitle %}
4 <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
5 {%- endblock %}
6 {%- block linktags %}
7 <link rel="stylesheet" href="http://yui.yahooapis.com/3.3.0/build/cssreset/reset-min.css" type="text/css" media="screen" charset="utf-8"/>
8 <link rel="stylesheet" href="http://splinter.readthedocs.org/media/css/splinter.css" type="text/css" media="screen" charset="utf-8"/>
9 {%- if hasdoc('about') %}
10 <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
11 {%- endif %}
12 {%- if hasdoc('genindex') %}
13 <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
14 {%- endif %}
15 {%- if hasdoc('search') %}
16 <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
17 {%- endif %}
18 {%- if hasdoc('copyright') %}
19 <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
20 {%- endif %}
21 <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
22 {%- if parents %}
23 <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
24 {%- endif %}
25 {%- if next %}
26 <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
27 {%- endif %}
28 {%- if prev %}
29 <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
30 {%- endif %}
31 {%- endblock %}
32 {%- block extrahead %}
33 <link rel="stylesheet" href="{{ pathto('_static/splinter-docs.css', 1) }}" type="text/css" media="screen" charset="utf-8"/>
34 <script type="text/javascript">
35 var _gaq = _gaq || [];
36 _gaq.push(['_setAccount', 'UA-23628203-1']);
37 _gaq.push(['_trackPageview']);
38 (function() {
39 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
40 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
41 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
42 })();
43 </script>
44 {% endblock %}
45 </head>
46 <body>
47 {%- block header %}
48 <div id="header">
49 <div class="header-container">
50 <div class="title">
51 <h1><a href="https://splinter.readthedocs.io/" title="splinter">splinter</a></h1>
52 <p>test framework for web applications</p>
53 </div>
54 <ul class="nav">
55 <li><a href="/">home</a></li>
56 <li><a href="/docs/install.html">install</a></li>
57 <li><a href="/docs/">documentation</a></li>
58 <li><a href="/docs/community.html">community</a></li>
59 <li><a href="/docs/contribute.html">contribute</a></li>
60 </ul>
61 </div>
62 </div>
63 {% endblock %}
64
65 {%- block relbar1 %}
66 {% endblock %}
67
68 {%- block content %}
69 {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
70
71 {%- block document %}
72 <div class="document">
73 <div id="page">
74 <div id="main">
75 {% block body %} {% endblock %}
76 </div>
77 {%- endblock %}
78 <div id="right">
79 <div id="release">
80 <a href="https://github.com/cobrateam/splinter/zipball/{{ version }}">current release: <strong>{{ version }}</strong></a>
81 </div>
82 </div>
83 </div>
84 {%- endblock %}
85
86 {% block footer %}
87 <div class="footer">
88 Splinter is powered by <a href="http://github.com/cobrateam/splinter" rel="nofollow" target="_blank" title="open source">open source</a>. This docs was created using <a href="http://sphinx.pocoo.org" target="_blank" rel="nofollow" title="Sphinx">Sphinx</a> {{ sphinx_version }}.
89 </div>
90 {% endblock %}
91
92 {%- block relbar2 %}{% endblock %}
0 {% extends "!modindex.html" %}
1 {% block bodyclass %}{% endblock %}
2 {% block sidebarwrapper %}{% endblock %}
0 {% extends "!search.html" %}
1 {% block bodyclass %}{% endblock %}
2 {% block sidebarwrapper %}{% endblock %}
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter cookies API documentation
6 :keywords: splinter, python, api documentation, cookies, cookies manipulation
7
8 CookieManager
9 =============
10
11 .. module:: splinter.cookie_manager
12
13 .. autoclass:: CookieManagerAPI
14 :members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter API documentation for DriverAPI and ElementAPI
6 :keywords: splinter, python, api documentation, api, driverapi, elementapi, driver creation
7
8
9 Browser
10 =======
11
12 .. module:: splinter.browser
13
14 .. autofunction:: Browser
15
16
17
18 .. module:: splinter.driver
19
20 DriverAPI
21 =========
22
23
24 .. autoclass:: DriverAPI
25 :members:
26
27 ElementAPI
28 ==========
29
30 .. autoclass:: ElementAPI
31 :members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter API documentation for ElementList
6 :keywords: splinter, python, api documentation, api, DOM manipulation, element list
7
8 ElementList
9 ===========
10
11 .. module:: splinter.element_list
12
13 .. autoclass:: ElementList
14 :show-inheritance:
15 :members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter exceptions API documentation
6 :keywords: splinter, python, api documentation, exceptions
7
8 Exceptions
9 ==========
10
11 .. module:: splinter.exceptions
12
13 .. autoclass:: DriverNotFoundError
14
15 .. autoclass:: ElementDoesNotExist
16
17 .. module:: splinter.request_handler.status_code
18
19 .. autoclass:: HttpResponseError
20 :members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter API documentation
6 :keywords: splinter, python, api documentation, api
7
8 ++++++++++++++++++
9 API Documentation
10 ++++++++++++++++++
11
12 Welcome to the Splinter API documentation! Check what's inside:
13
14 .. toctree::
15 :glob:
16
17 driver-and-element-api
18 cookie-manager
19 element-list
20 request-handling
21 exceptions
22
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter request handling API documentation
6 :keywords: splinter, python, api documentation, request handling
7
8 Request handling
9 ================
10
11 .. autoclass:: splinter.request_handler.status_code.StatusCode
12 :members:
13
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Browser
6 :keywords: splinter, python, tutorial, browser, firefox, chrome, zope.testbrowser
7
8 +++++++
9 Browser
10 +++++++
11
12 To use splinter you need to create a Browser instance:
13
14 .. highlight:: python
15
16 ::
17
18 from splinter import Browser
19 browser = Browser()
20
21 Or, you can use it by a ``context manager``, through the ``with`` statement:
22
23 .. highlight:: python
24
25 ::
26
27 from splinter import Browser
28 with Browser() as b:
29 # stuff using the browser
30
31 This last example will create a new browser window and close it when the cursor
32 reaches the code outside the ``with`` statement, automatically.
33
34 splinter supports the following drivers:
35 * :doc:`Chrome </drivers/chrome>`
36 * :doc:`Firefox </drivers/firefox>`
37 * :doc:`Browsers on remote machines </drivers/remote>`
38 * :doc:`zope.testbrowser </drivers/zope.testbrowser>`
39 * :doc:`Django client </drivers/django>`
40 * :doc:`Flask client </drivers/flask>`
41
42 The following examples create new Browser instances for specific drivers:
43
44 .. highlight:: python
45
46 ::
47
48 browser = Browser('chrome')
49 browser = Browser('firefox')
50 browser = Browser('zope.testbrowser')
51
52 =============================
53 Navigating with Browser.visit
54 =============================
55
56 You can use the ``visit`` method to navigate to other pages:
57
58 .. highlight:: python
59
60 ::
61
62 browser.visit('http://cobrateam.info')
63
64 The ``visit`` method takes only a single parameter - the ``url`` to be visited.
65
66 You can visit a site protected with basic HTTP authentication by providing the
67 username and password in the url.
68
69 ::
70
71 browser.visit('http://username:[email protected]/protected')
72
73 ================
74 Managing Windows
75 ================
76
77 You can manage multiple windows (such as popups) through the windows object:
78
79 .. highlight:: python
80
81 ::
82
83 browser.windows # all open windows
84 browser.windows[0] # the first window
85 browser.windows[window_name] # the window_name window
86 browser.windows.current # the current window
87 browser.windows.current = browser.windows[3] # set current window to window 3
88
89 window = browser.windows[0]
90 window.is_current # boolean - whether window is current active window
91 window.is_current = True # set this window to be current window
92 window.next # the next window
93 window.prev # the previous window
94 window.close() # close this window
95 window.close_others() # close all windows except this one
96
97 This window management interface is not compatible with the undocumented interface
98 exposed in v0.6.0 and earlier.
99
100 =============
101 Reload a page
102 =============
103
104 You can reload a page using the ``reload`` method:
105
106 .. highlight:: python
107
108 ::
109
110 browser.reload()
111
112 ============================
113 Navigate through the history
114 ============================
115
116 You can move back and forward through your browsing history using the ``back`` and ``forward`` methods:
117
118 .. highlight:: python
119
120 ::
121
122 browser.visit('http://cobrateam.info')
123 browser.visit('https://splinter.readthedocs.io')
124 browser.back()
125 browser.forward()
126
127 =============
128 Browser.title
129 =============
130
131 You can get the title of the visited page using the ``title`` attribute:
132
133 .. highlight:: python
134
135 ::
136
137 browser.title
138
139 ========================================
140 Verifying page content with Browser.html
141 ========================================
142
143 You can use the ``html`` attribute to get the html content of the visited page:
144
145 .. highlight:: python
146
147 ::
148
149 browser.html
150
151 ===================================
152 Verifying page url with Browser.url
153 ===================================
154
155 The visited page's url can be accessed by the ``url`` attribute:
156
157 .. highlight:: python
158
159 ::
160
161 browser.url
162
163 ===========================
164 Changing Browser User-Agent
165 ===========================
166
167 You can pass a User-Agent header on Browser instantiation.
168
169 .. highlight:: python
170
171 ::
172
173 b = Browser(user_agent="Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en)")
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Find the CobraTeam and Splinter communities.
6 :keywords: splinter, python, cobrateam, community, atdd, tests, acceptance tests, web applications
7
8 +++++++++
9 Community
10 +++++++++
11
12 mailing list
13 ============
14
15 * `splinter-users <http://groups.google.com/group/splinter-users>`_ - list for help and announcements
16 * `splinter-developers <http://groups.google.com/group/splinter-developers>`_ - where the developers of splinter itself discuss new features
17
18 irc channel
19 ===========
20
21 #cobrateam channel on irc.freenode.net - chat with other splinter users and developers
22
23 ticket system
24 =============
25
26 `ticket system <https://github.com/cobrateam/splinter/issues>`_ - report bugs and make feature requests
27
28 splinter around the world
29 =========================
30
31 Projects using splinter
32 -----------------------
33
34 * `salad <https://github.com/salad/salad>`_: splinter and lettuce integration
35
36 Blog posts
37 ----------
38
39 * `Django Full Stack Testing and BDD with Lettuce and Splinter <https://www.cilliano.com/2011/02/07/django-bdd-with-lettuce-and-splinter.html>`_
40
41 Slides and talks
42 ----------------
43
44 * `[pt-br] Os complicados testes de interface <http://www.slideshare.net/franciscosouza/os-complicados-testes-de-interface>`_
45 * `[pt-br] Testes de aceitação com Lettuce e Splinter <http://www.slideshare.net/franciscosouza/testes-de-aceitao-com-lettuce-e-splinter>`_
0 # -*- coding: utf-8 -*-
1 #
2 # splinter documentation build configuration file, created by
3 # sphinx-quickstart on Sat Jan 8 23:31:41 2011.
4 #
5 # This file is execfile()d with the current directory set to its containing dir.
6 #
7 # Note that not all possible configuration values are present in this
8 # autogenerated file.
9 #
10 # All configuration values have a default; values that are commented out
11 # serve to show the default.
12
13 # If extensions (or modules to document with autodoc) are in another directory,
14 # add these directories to sys.path here. If the directory is relative to the
15 # documentation root, use os.path.abspath to make it absolute, like shown here.
16 # sys.path.insert(0, os.path.abspath('.'))
17
18 import os
19 import sys
20 from datetime import datetime
21
22 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
23
24 # -- General configuration -----------------------------------------------------
25
26 # If your documentation needs a minimal Sphinx version, state it here.
27 # needs_sphinx = '1.0'
28
29 # Add any Sphinx extension module names here, as strings. They can be extensions
30 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
31 extensions = ["sphinx.ext.autodoc"]
32
33 # Add any paths that contain templates here, relative to this directory.
34 # templates_path = ['_templates']
35
36 # The suffix of source filenames.
37 source_suffix = ".rst"
38
39 # The encoding of source files.
40 # source_encoding = 'utf-8-sig'
41
42 # The master toctree document.
43 master_doc = "index"
44
45 # General information about the project.
46 project = u"Splinter"
47 copyright = u"{}, cobrateam".format(datetime.now().year)
48
49 # The version info for the project you're documenting, acts as replacement for
50 # |version| and |release|, also used in various other places throughout the
51 # built documents.
52 #
53 # The short X.Y version.
54 version = "0.13.0"
55 # The full version, including alpha/beta/rc tags.
56 release = "0.13.0"
57
58 # The language for content autogenerated by Sphinx. Refer to documentation
59 # for a list of supported languages.
60 # language = None
61
62 # There are two options for replacing |today|: either, you set today to some
63 # non-false value, then it is used:
64 # today = ''
65 # Else, today_fmt is used as the format for a strftime call.
66 # today_fmt = '%B %d, %Y'
67
68 # List of patterns, relative to source directory, that match files and
69 # directories to ignore when looking for source files.
70 exclude_patterns = ["_build"]
71
72 # The reST default role (used for this markup: `text`) to use for all documents.
73 # default_role = None
74
75 # If true, '()' will be appended to :func: etc. cross-reference text.
76 # add_function_parentheses = True
77
78 # If true, the current module name will be prepended to all description
79 # unit titles (such as .. function::).
80 # add_module_names = True
81
82 # If true, sectionauthor and moduleauthor directives will be shown in the
83 # output. They are ignored by default.
84 # show_authors = False
85
86 # The name of the Pygments (syntax highlighting) style to use.
87 pygments_style = "vs"
88
89 # A list of ignored prefixes for module index sorting.
90 # modindex_common_prefix = []
91
92
93 # -- Options for HTML output ---------------------------------------------------
94
95 # The theme to use for HTML and HTML Help pages. See the documentation for
96 # a list of builtin themes.
97 # html_theme = 'nature'
98 if not os.environ.get("READTHEDOCS", None):
99 import sphinx_rtd_theme
100
101 html_theme = "sphinx_rtd_theme"
102 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
103
104 # Theme options are theme-specific and customize the look and feel of a theme
105 # further. For a list of options available for each theme, see the
106 # documentation.
107 # html_theme_options = {}
108
109 # Add any paths that contain custom themes here, relative to this directory.
110 # html_theme_path = []
111
112 # The name for this set of Sphinx documents. If None, it defaults to
113 # "<project> v<release> documentation".
114 # html_title = None
115
116 # A shorter title for the navigation bar. Default is the same as html_title.
117 # html_short_title = None
118
119 # The name of an image file (relative to this directory) to place at the top
120 # of the sidebar.
121 # html_logo = None
122
123 # The name of an image file (within the static path) to use as favicon of the
124 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
125 # pixels large.
126 # html_favicon = None
127
128 # Add any paths that contain custom static files (such as style sheets) here,
129 # relative to this directory. They are copied after the builtin static files,
130 # so a file named "default.css" will overwrite the builtin "default.css".
131 html_static_path = ["_static"]
132
133 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
134 # using the given strftime format.
135 # html_last_updated_fmt = '%b %d, %Y'
136
137 # If true, SmartyPants will be used to convert quotes and dashes to
138 # typographically correct entities.
139 # html_use_smartypants = True
140
141 # Custom sidebar templates, maps document names to template names.
142 # html_sidebars = {}
143
144 # Additional templates that should be rendered to pages, maps page names to
145 # template names.
146 # html_additional_pages = {}
147
148 # If false, no module index is generated.
149 # html_domain_indices = True
150
151 # If false, no index is generated.
152 # html_use_index = True
153
154 # If true, the index is split into individual pages for each letter.
155 # html_split_index = False
156
157 # If true, links to the reST sources are added to the pages.
158 # html_show_sourcelink = True
159
160 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
161 # html_show_sphinx = True
162
163 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
164 # html_show_copyright = True
165
166 # If true, an OpenSearch description file will be output, and all pages will
167 # contain a <link> tag referring to it. The value of this option must be the
168 # base URL from which the finished HTML is served.
169 # html_use_opensearch = ''
170
171 # This is the file name suffix for HTML files (e.g. ".xhtml").
172 # html_file_suffix = None
173
174 # Output file base name for HTML help builder.
175 htmlhelp_basename = "splinterdoc"
176
177
178 # -- Options for LaTeX output --------------------------------------------------
179
180 # The paper size ('letter' or 'a4').
181 # latex_paper_size = 'letter'
182
183 # The font size ('10pt', '11pt' or '12pt').
184 # latex_font_size = '10pt'
185
186 # Grouping the document tree into LaTeX files. List of tuples
187 # (source start file, target name, title, author, documentclass [howto/manual]).
188 latex_documents = [
189 ("index", "splinter.tex", u"splinter Documentation", u"andrews medina", "manual")
190 ]
191
192 # The name of an image file (relative to this directory) to place at the top of
193 # the title page.
194 # latex_logo = None
195
196 # For "manual" documents, if this is true, then toplevel headings are parts,
197 # not chapters.
198 # latex_use_parts = False
199
200 # If true, show page references after internal links.
201 # latex_show_pagerefs = False
202
203 # If true, show URL addresses after external links.
204 # latex_show_urls = False
205
206 # Additional stuff for the LaTeX preamble.
207 # latex_preamble = ''
208
209 # Documents to append as an appendix to all manuals.
210 # latex_appendices = []
211
212 # If false, no module index is generated.
213 # latex_domain_indices = True
214
215
216 # -- Options for manual page output --------------------------------------------
217
218 # One entry per manual page. List of tuples
219 # (source start file, name, description, authors, manual section).
220 man_pages = [("index", "splinter", u"splinter Documentation", [u"andrews medina"], 1)]
0 .. Copyright © 2018 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Setting up your development environment for Splinter.
6 :keywords: splinter, open source, python, contribute, development environment
7
8 +++++++++++++++++++++++++++++++++++++++++++++++
9 Setting up your splinter development environment
10 +++++++++++++++++++++++++++++++++++++++++++++++
11
12 Setting up a splinter development environment is a really easy task. Once you have some
13 basic development tools on your machine, you can set up the entire environment with just one command.
14
15 Basic development tools
16 =======================
17
18 Let's deal with those tools first.
19
20 macOS
21 ------
22
23 If you're a macOS user, you just need to install Xcode, which can be downloaded
24 from Mac App Store (on Snow Leopard or later) or from
25 `Apple website <https://developer.apple.com/download/>`_.
26
27 Linux
28 -----
29
30 If you are running a Linux distribution, you need to install some basic development libraries
31 and headers. For example, on Ubuntu, you can easily install all of them using ``apt-get``:
32
33 .. highlight:: bash
34
35 ::
36
37 $ [sudo] apt-get install build-essential python-dev libxml2-dev libxslt1-dev
38
39 PIP and virtualenv
40 ------------------
41
42 Make sure you have pip installed. We manage all splinter development dependencies with
43 `PIP <https://pip.pypa.io/en/stable/>`_, so you should use it too.
44
45 And please, for the sake of a nice development environment, use `virtualenv <https://pypi.org/project/virtualenv/>`_.
46 If you aren't using it yet, start now. :)
47
48 Dependencies
49 ------------
50
51 Once you have all development libraries installed for your OS, just install all splinter development dependencies with
52 ``make``:
53
54 .. highlight:: bash
55
56 ::
57
58 $ [sudo] make dependencies
59
60 **Note:** You will need ``sudo`` only if you aren't using virtualenv (which means you're a really bad guy - *no donuts for you*).
61
62 Also make sure you have properly configured your :doc:`Chrome driver </drivers/chrome>`.
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Find how to write new drivers for splinter.
6 :keywords: splinter, python, contribution, open source, testing, web application, atdd, drivers
7
8 ++++++++++++++++++++++++++++
9 Writing new splinter drivers
10 ++++++++++++++++++++++++++++
11
12 The process of creating a new splinter browser is really simple: you just need to implement a
13 TestCase (extending ``tests.base. BaseBrowserTests``) and make all tests green. For example:
14
15 Imagine you're creating the ``Columbia`` driver, you would add the ``test_columbia.py`` file containing some code like...
16
17 .. highlight:: python
18
19 ::
20
21 from splinter import Browser
22 from tests.base import BaseBrowserTests
23
24 class ColumbiaTest(BaseBrowserTests):
25
26 @classmethod
27 def setUpClass(cls):
28 cls.browser = Browser('columbia')
29
30 # ...
31
32 Now, to make the test green, you need to implement methods provided by the
33 `DriverAPI <https://github.com/cobrateam/splinter/blob/master/splinter/driver/__init__.py#L10>`_ and
34 the `ElementAPI <https://github.com/cobrateam/splinter/blob/master/splinter/driver/__init__.py#L172>`_.
35
36 Use ``make test`` to run the tests:
37
38 .. highlight:: bash
39
40 ::
41
42 $ make test which=tests/test_columbia.py
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Find how to contribute with splinter.
6 :keywords: splinter, python, contribution, open source, testing, web application, atdd
7
8 ++++++++++
9 Contribute
10 ++++++++++
11
12 * Source hosted at `GitHub <http://github.com/cobrateam/splinter>`_
13 * Report issues on `GitHub Issues <http://github.com/cobrateam/splinter/issues>`_
14
15 Pull requests are very welcome! Make sure your patches are well tested and documented :)
16
17 If you want to add any new driver, check out our :doc:`docs for creating new splinter drivers </contribute/writing-new-drivers>`.
18
19 running the tests
20 =================
21
22 If you are using a virtualenv, all you need is:
23
24 .. highlight:: bash
25
26 ::
27
28 $ make test
29
30 You can also specify one or more test files to run:
31
32 .. highlight:: bash
33
34 ::
35
36 $ make test which=tests/test_webdriver_firefox.py,tests/test_request_handler.py
37
38 You can pass which test files you want to run, separated by comma, to the ``which`` variable.
39
40 some conventions we like
41 ========================
42
43 You can feel free to create and pull request new branches to Splinter project.
44 When adding support for new drivers, we usually work in a separated branch.
45
46
47 writing docs
48 ============
49
50 Splinter documentation is written using `Sphinx
51 <http://sphinx.pocoo.org/>`_, which uses `RST
52 <http://docutils.sourceforge.net/rst.html>`_. We use the `Read the Docs Sphinx Theme <https://sphinx-rtd-theme.readthedocs.io/en/latest/index.html>`_. Check these tools' docs to learn how to write docs for Splinter.
53
54 building docs
55 =============
56
57 In order to build the HTML docs, just navigate to the project folder
58 (the main folder, not the ``docs`` folder) and run the following on the terminal:
59
60 .. highlight:: bash
61
62 ::
63
64 $ make doc
65
66 The requirements for building the docs are specified in
67 ``doc-requirements.txt`` in the project folder.
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Cookie manipulation
6 :keywords: splinter, python, tutorial, documentation, cookies
7
8 ++++++++++++++++++++
9 Cookies manipulation
10 ++++++++++++++++++++
11
12 It is possible to manipulate cookies using the `cookies` attribute from a
13 `Browser` instance. The `cookies` attribute is a instance of a `CookieManager`
14 class that manipulates cookies, like adding and deleting them.
15
16 Create cookie
17 -------------
18
19 To add a cookie use the add method:
20
21 .. highlight:: python
22
23 ::
24
25 browser.cookies.add({'whatever': 'and ever'})
26
27 Retrieve all cookies
28 --------------------
29
30 To retrieve all cookies use the `all` method:
31
32 .. highlight:: python
33
34 ::
35
36 browser.cookies.all()
37
38 Delete a cookie
39 ---------------
40
41 You can delete one or more cookies with the ``delete`` method:
42
43 .. highlight:: python
44
45 ::
46
47 browser.cookies.delete('mwahahahaha') # deletes the cookie 'mwahahahaha'
48 browser.cookies.delete('whatever', 'wherever') # deletes two cookies
49
50 Delete all cookies
51 ------------------
52
53 You can also delete all cookies: just call the ``delete`` method without any
54 parameters:
55
56 .. highlight:: python
57
58 ::
59
60 browser.cookies.delete() # deletes all cookies
61
62 For more details check the API reference of the
63 :class:`CookieManager <splinter.cookie_manager.CookieManagerAPI>` class.
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with Chrome webdriver
6 :keywords: splinter, python, tutorial, how to install, installation, chrome, selenium
7
8 ++++++++++++++++
9 Chrome WebDriver
10 ++++++++++++++++
11
12 Chrome WebDriver is provided by Selenium2. To use it, you need to install Selenium2 via pip:
13
14 .. highlight:: bash
15
16 ::
17
18 $ [sudo] pip install selenium
19
20 It's important to note that you also need to have Google Chrome installed in your machine.
21
22 Chrome can also be used from a custom path. To do this pass the executable path as a dictionary to the `**kwargs` argument. The dictionary should be set up with `executable_path` as the key and the value set to the path to the executable file.
23
24 .. highlight:: python
25
26 ::
27
28 from splinter import Browser
29 executable_path = {'executable_path':'</path/to/chrome>'}
30
31 browser = Browser('chrome', **executable_path)
32
33 Setting up Chrome WebDriver
34 ---------------------------
35
36 In order to use `Google Chrome <http://google.com/chrome>`_ with Splinter, since we're using Selenium 2.3.x,
37 you need to setup Chrome webdriver properly.
38
39
40 Mac OS X
41 --------
42
43 The recommended way is by using `Homebrew <http://mxcl.github.com/homebrew/>`_:
44
45 .. highlight:: bash
46
47 ::
48
49 $ brew cask install chromedriver
50
51
52 Linux
53 -----
54
55 Go to the `download page on the Chromium project
56 <https://sites.google.com/a/chromium.org/chromedriver/downloads>`_ and choose
57 the correct version for your Linux installation. Then extract the downloaded file in a
58 directory in the ``PATH`` (e.g. ``/usr/bin``). You can also extract it to any
59 directory and add that directory to the ``PATH``:
60
61 Linux 64bits
62 ============
63
64 .. highlight:: bash
65
66 ::
67
68 $ cd $HOME/Downloads
69 $ wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip
70 $ unzip chromedriver_linux64.zip
71
72 $ mkdir -p $HOME/bin
73 $ mv chromedriver $HOME/bin
74 $ echo "export PATH=$PATH:$HOME/bin" >> $HOME/.bash_profile
75
76
77 Windows
78 -------
79
80 **Note:** We don't provide official support for Windows, but you can try it by yourself.
81
82 All you need to do is go to `download page on Selenium project <https://sites.google.com/a/chromium.org/chromedriver/downloads>`_ and choose
83 "ChromeDriver server for win". Your browser will download a zip file, extract it and add the ``.exe`` file to your PATH.
84
85 If you don't know how to add an executable to the PATH on Windows, check these link out:
86
87 * `Environment variables <http://msdn.microsoft.com/en-us/library/ms682653.aspx>`_
88 * `How to manage environment variables in Windows XP <http://support.microsoft.com/kb/310519>`_
89 * `How to manage environment variables in Windows 8 & 10 <https://www.computerhope.com/issues/ch000549.htm>`_
90
91
92 Using Chrome WebDriver
93 ----------------------
94
95 To use the Chrome driver, all you need to do is pass the string ``chrome`` when you create
96 the ``Browser`` instance:
97
98 .. highlight:: python
99
100 ::
101
102 from splinter import Browser
103 browser = Browser('chrome')
104
105 **Note:** if you don't provide any driver to the ``Browser`` function, ``firefox`` will be used.
106
107 **Note:** if you have trouble with ``$HOME/.bash_profile``, you can try ``$HOME/.bashrc``.
108
109 Using headless option for Chrome
110 --------------------------------
111
112 Starting with Chrome 59, we can run Chrome as a headless browser.
113 Make sure you read `google developers updates <https://developers.google.com/web/updates/2017/05/nic59#headless>`_
114
115 .. highlight:: python
116
117 ::
118
119 from splinter import Browser
120 browser = Browser('chrome', headless=True)
121
122 Using incognito option for Chrome
123 --------------------------------
124
125 We can run Chrome as a incognito browser.
126
127 .. highlight:: python
128
129 ::
130
131 from splinter import Browser
132 browser = Browser('chrome', incognito=True)
133
134 Using emulation mode in Chrome
135 ------------------------------
136
137 Chrome options can be passed to customize Chrome's behaviour; it is then possible to leverage the
138 experimental emulation mode.
139
140 .. highlight:: python
141
142 ::
143
144 from selenium import webdriver
145 from splinter import Browser
146
147 mobile_emulation = {"deviceName": "Google Nexus 5"}
148 chrome_options = webdriver.ChromeOptions()
149 chrome_options.add_experimental_option("mobileEmulation",
150 mobile_emulation)
151 browser = Browser('chrome', options=chrome_options)
152
153
154 refer to `chrome driver documentation <https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation>`_
155
156
157 API docs
158 --------
159
160 .. autoclass:: splinter.driver.webdriver.chrome.WebDriver
161 :members:
162 :inherited-members:
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with django.
6 :keywords: splinter, python, tutorial, how to install, installation, django
7
8 ++++++++++++++++
9 django client
10 ++++++++++++++++
11
12 .. module:: splinter.driver.djangoclient
13
14 To use the ``django`` driver, you need to install `django <http://pypi.python.org/pypi/django>`_,
15 `lxml <https://pypi.python.org/pypi/lxml>`_ and `cssselect <http://pypi.python.org/pypi/cssselect>`_.
16 You can install all of them in one step by running:
17
18 .. highlight:: bash
19
20 ::
21
22 $ pip install splinter[django]
23
24 Using django client
25 -------------------
26
27 To use the ``django`` driver, all you need to do is pass the string ``django`` when you create
28 the ``Browser`` instance:
29
30 .. highlight:: python
31
32 ::
33
34 from splinter import Browser
35 browser = Browser('django')
36
37 **Note:** if you don't provide any driver to ``Browser`` function, ``firefox`` will be used.
38
39 API docs
40 --------
41
42 .. autoclass:: DjangoClient
43 :members:
44 :inherited-members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with Firefox webdriver
6 :keywords: splinter, python, tutorial, how to install, installation, firefox, selenium
7
8 +++++++++++++++++
9 Firefox WebDriver
10 +++++++++++++++++
11
12 .. module:: splinter.driver.webdriver.firefox
13
14 Firefox WebDriver is provided by Selenium 2.0. To use it, you need to install Selenium 2.0 via pip:
15
16 .. highlight:: bash
17
18 ::
19
20 $ [sudo] pip install selenium
21
22
23 It's important to note that you also need to have `Firefox <http://firefox.com>`_ and `geckodriver <https://github.com/mozilla/geckodriver/releases>`_ installed in your machine and available on `PATH` environment variable.
24 Once you have it installed, there is nothing you have to do, just use it :)
25
26 Using Firefox WebDriver
27 -----------------------
28
29 To use the Firefox driver, all you need to do is pass the string ``firefox`` when you create
30 the ``Browser`` instance:
31
32 .. highlight:: python
33
34 ::
35
36 from splinter import Browser
37 browser = Browser('firefox')
38
39 **Note:** if you don't provide any driver to ``Browser`` function, ``firefox`` will be used.
40
41
42 Using headless option for Firefox
43 -----------------------------------
44
45 Starting with Firefox 55, we can run Firefox as a headless browser in Linux.
46
47 .. highlight:: python
48
49 ::
50
51 from splinter import Browser
52 browser = Browser('firefox', headless=True)
53
54
55 Using incognito option for Firefox
56 ------------------------------------
57
58 We can run Firefox as a private browser.
59
60 .. highlight:: python
61
62 ::
63
64 from splinter import Browser
65 browser = Browser('firefox', incognito=True)
66
67
68 How to use a specific profile for Firefox
69 -----------------------------------------
70
71 You can specify a `Firefox profile <http://support.mozilla.com/en-US/kb/Profiles>`_ for using on ``Browser`` function
72 using the ``profile`` keyword (passing the name of the profile as a ``str`` instance):
73
74 .. highlight:: python
75
76 ::
77
78 from splinter import Browser
79 browser = Browser('firefox', profile='my_profile')
80
81 If you don't specify a profile, a new temporary profile will be created (and deleted when you ``close`` the browser).
82
83 How to use specific extensions for Firefox
84 ------------------------------------------
85
86 An extension for firefox is a .xpi archive. To use an extension in Firefox webdriver profile you need to give the path of the extension, using the extensions keyword (passing the extensions as a ``list`` instance):
87
88 .. highlight:: python
89
90 ::
91
92 from splinter import Browser
93 browser = Browser('firefox', extensions=['extension1.xpi', 'extension2.xpi'])
94
95 If you give an extension, after you close the browser, the extension will be deleted from the profile, even if is not a temporary one.
96
97 How to use selenium capabilities for Firefox
98 --------------------------------------------
99
100 .. highlight:: python
101
102 ::
103
104 from splinter import Browser
105 browser = Browser('firefox', capabilities={'acceptSslCerts': True})
106
107 You can pass any selenium `read-write DesiredCapabilities parameters <https://code.google.com/p/selenium/wiki/DesiredCapabilities#Read-write_capabilities>`_ for Firefox.
108
109
110 API docs
111 --------
112
113 .. autoclass:: WebDriver
114 :members:
115 :inherited-members:
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with Flask.
6 :keywords: splinter, python, tutorial, how to install, installation, Flask
7
8 ++++++++++++
9 Flask client
10 ++++++++++++
11
12 .. module:: splinter.driver.flaskclient
13
14 To use the ``flask`` driver, you need to install `Flask <https://pypi.python.org/pypi/Flask>`_,
15 `lxml <https://pypi.python.org/pypi/lxml>`_ and `cssselect <http://pypi.python.org/pypi/cssselect>`_.
16 You can install all of them in one step by running:
17
18 .. highlight:: bash
19
20 ::
21
22 $ pip install splinter[flask]
23
24 Using Flask client
25 -------------------
26
27 To use the ``flask`` driver, you'll need to pass the string ``flask`` and an app instances via the
28 ``app`` keyword argument when you create the ``Browser`` instance:
29
30 .. highlight:: python
31
32 ::
33
34 from splinter import Browser
35 browser = Browser('flask', app=app)
36
37 **Note:** if you don't provide any driver to ``Browser`` function, ``firefox`` will be used.
38
39 When visiting pages with the Flask client, you only need to provide a path rather than a full URL.
40 For example:
41
42 .. highlight:: python
43
44 ::
45
46 browser.visit('/my-path')
47
48 API docs
49 --------
50
51 .. autoclass:: FlaskClient
52 :members:
53 :inherited-members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Learn how to install PyQt4 on your machine.
6 :keywords: splinter, python, cobrateam, pyqt, pyqt4
7
8 ++++++++++++++++
9 Installing PyQt4
10 ++++++++++++++++
11
12 In order to use Spynner driver, you need to install PyQt4. PyQt4 itself has a few dependencies;
13 follow the steps below to get PyQt4 and its dependencies running on your system.
14
15 Install Qt4
16 ===========
17
18 The biggest and most important PyQt4 dependency is the Qt4 itself.
19 You can download it from its `official website <http://qt.nokia.com/downloads/>`_ and
20 install with a nice and friendly wizard.
21
22 Install SIP
23 ===========
24
25 Another PyQt4 dependency is `SIP <http://www.riverbankcomputing.co.uk/software/sip/intro>`_, which
26 is a tool used to create Python bindings for C and C++ libraries. You can navigate to the
27 `download page on SIP website <http://www.riverbankcomputing.co.uk/software/sip/download>`_,
28 choose the version according to your operating system, extract the download package and
29 install it using three commands:
30
31 .. highlight:: bash
32
33 ::
34
35 $ tar -xvzf sip-4.xx.x.tar.gz
36 $ cd sip-4.xx.x
37 $ python configure.py
38 $ make
39 $ [sudo] make install
40
41 Install PyQt4
42 =============
43
44 Now we can finally install PyQt4. Go to the `PyQt4 download page <http://www.riverbankcomputing.co.uk/software/pyqt/download>`_
45 and download the PyQt4 version according to your platform. After these steps, all you need to do is extract the download
46 package and install PyQt4 using these commands:
47
48 .. highlight:: bash
49
50 ::
51
52 $ tar -xvzf PyQt-plat-gpl-4.x.x.tar.gz
53 $ cd PyQt-plat-gpl-4.x.x
54 $ python configure.py --no-designer-plugin --qmake=/usr/bin/qmake-4.x # important, on Mac OS X don't use /usr/bin/qmake, specify the version!
55 $ make
56 $ [sudo] make install
57
58 Now you PyQt4 installed on your system. Happy hacking :)
59
60 For more information in specific platforms, check these links out:
61
62 * `Installing PyQt on Mac OS X (Snow Leopard) <http://blog.oak-tree.us/index.php/2010/05/27/pyqt-snow-leopard>`_
63 * `Installing PyQt on Windows <http://blog.oak-tree.us/index.php/2009/05/12/pyqt-windows>`_
64 * `Installing PyQt on Linux (Ubuntu) <http://blog.oak-tree.us/index.php/2009/05/12/pyqt-linux>`_
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with Remote WebDriver
6 :keywords: splinter, python, tutorial, how to install, installation, remote, selenium
7
8
9 ++++++++++++++++
10 Remote WebDriver
11 ++++++++++++++++
12
13 Remote WebDriver is provided by Selenium. To use it, you need to install
14 Selenium via pip:
15
16 .. highlight:: bash
17
18 ::
19
20 $ [sudo] pip install selenium
21
22 Setting up the Remote WebDriver
23 -------------------------------
24
25 To use Remote WebDriver, you need to have access to a Selenium remote
26 WebDriver server. Setting up one of these servers is beyond the scope of this
27 document. However, some companies provide access to a `Selenium Grid`_ as a service.
28
29
30 Using the Remote WebDriver
31 --------------------------
32
33 To use the Remote WebDriver, use ``driver_name="remote"`` when you create the ``Browser`` instance.
34
35 The ``browser_name`` argument should then be used to specify the web browser.
36 The other arguments match Selenium's `Remote WebDriver`_ arguments.
37
38 `Desired Capabilities`_ will be set automatically based on Selenium's defaults.
39 These can be expanded and/or replaced by providing your own.
40
41 The following example uses `Sauce Labs`_ (a company that provides Selenium
42 Remote WebDriver servers as a service) to request an Internet Explorer 9
43 browser instance running on Windows 7.
44
45 .. highlight:: python
46
47 ::
48
49 # Specify the server URL
50 remote_server_url = 'http://YOUR_SAUCE_USERNAME:[email protected]:80/wd/hub'
51
52 with Browser(
53 driver_name="remote",
54 browser='internetexplorer',
55 command_executor=remote_server_url,
56 desired_capabilities = {
57 'platform': 'Windows 7',
58 'version': '9',
59 'name': 'Test of IE 9 on WINDOWS',
60 },
61 keep_alive=True,
62 ) as browser:
63 print("Link to job: https://saucelabs.com/jobs/{}".format(
64 browser.driver.session_id))
65 browser.visit("https://splinter.readthedocs.io")
66 browser.find_by_text('documentation').first.click()
67
68
69 .. _Desired Capabilities: https://selenium.dev/selenium/docs/api/py/webdriver/selenium.webdriver.common.desired_capabilities.html
70 .. _Selenium Grid: https://selenium.dev/documentation/en/grid/
71 .. _Sauce Labs: https://saucelabs.com
72 .. _Remote WebDriver: https://selenium.dev/selenium/docs/api/py/webdriver_remote/selenium.webdriver.remote.webdriver.html
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: How to use splinter with zope.testbrowser
6 :keywords: splinter, python, tutorial, how to install, installation, zope, testbrowser, zope.testbrowser
7
8 ++++++++++++++++
9 zope.testbrowser
10 ++++++++++++++++
11
12 .. module:: splinter.driver.zopetestbrowser
13
14 To use the ``zope.testbrowser`` driver, you need to install `zope.testbrowser <http://pypi.python.org/pypi/zope.testbrowser>`_, `lxml <https://pypi.python.org/pypi/lxml>`_ and `cssselect <http://pypi.python.org/pypi/cssselect>`_. You can install all of them in one step by running:
15
16 .. highlight:: bash
17
18 ::
19
20 $ pip install splinter[zope.testbrowser]
21
22 Using zope.testbrowser
23 ----------------------
24
25 To use the ``zope.testbrowser`` driver, all you need to do is pass the string ``zope.testbrowser`` when you create
26 the ``Browser`` instance:
27
28 .. highlight:: python
29
30 ::
31
32 from splinter import Browser
33 browser = Browser('zope.testbrowser')
34
35 By default ``zope.testbrowser`` respects any robots.txt preventing access to a lot of sites. If you want to circumvent
36 this you can call
37
38 .. highlight:: python
39
40 ::
41
42 browser = Browser('zope.testbrowser', ignore_robots=True)
43
44 **Note:** if you don't provide any driver to ``Browser`` function, ``firefox`` will be used.
45
46 API docs
47 --------
48
49 .. autoclass:: ZopeTestBrowser
50 :members:
51 :inherited-members:
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Dealing with elements in the page.
6 :keywords: splinter, python, tutorial, documentation, forms, click links, get value
7
8 +++++++++++++++++++++++++++++++++++++
9 Interacting with elements in the page
10 +++++++++++++++++++++++++++++++++++++
11
12 Get value of an element
13 -----------------------
14
15 In order to retrieve an element's value, use the ``value`` property:
16
17 .. highlight:: python
18
19 ::
20
21 browser.find_by_css('h1').first.value
22
23 or
24
25 .. highlight:: python
26
27 ::
28
29 element = browser.find_by_css('h1').first
30 element.value
31
32
33 Clicking links
34 --------------
35
36 You can click in links. To click in links by href, partial href, text or partial text you can use this.
37 IMPORTANT: These methods return the first element always.
38
39 .. highlight:: python
40
41 ::
42
43 browser.click_link_by_href('http://www.the_site.com/my_link')
44
45 or
46
47 .. highlight:: python
48
49 ::
50
51 browser.click_link_by_partial_href('my_link')
52
53 or
54
55 .. highlight:: python
56
57 ::
58
59 browser.click_link_by_text('my link')
60
61 or
62
63 .. highlight:: python
64
65 ::
66
67 browser.click_link_by_partial_text('part of link text')
68
69 or
70
71 .. highlight:: python
72
73 ::
74
75 browser.click_link_by_id('link_id')
76
77
78 Clicking buttons
79 ----------------
80
81 You can click in buttons. Splinter follows any redirects, and submits forms associated with buttons.
82
83 .. highlight:: python
84
85 ::
86
87 browser.find_by_name('send').first.click()
88
89 or
90
91 .. highlight:: python
92
93 ::
94
95 browser.find_link_by_text('my link').first.click()
96
97
98 Interacting with forms
99 ----------------------
100
101 .. highlight:: python
102
103 ::
104
105 browser.fill('query', 'my name')
106 browser.attach_file('file', '/path/to/file/somefile.jpg')
107 browser.choose('some-radio', 'radio-value')
108 browser.check('some-check')
109 browser.uncheck('some-check')
110 browser.select('uf', 'rj')
111
112 To trigger JavaScript events, like KeyDown or KeyUp, you can use the `type` method.
113
114 .. highlight:: python
115
116 ::
117
118 browser.type('type', 'typing text')
119
120 If you pass the argument `slowly=True` to the `type` method you can interact with the
121 page on every key pressed. Useful for testing field's autocompletion (the browser
122 will wait until next iteration to type the subsequent key).
123
124 .. highlight:: python
125
126 ::
127
128 for key in browser.type('type', 'typing slowly', slowly=True):
129 pass # make some assertion here with the key object :)
130
131 You can also use ``type`` and ``fill`` methods in an element:
132
133 .. highlight:: python
134
135 ::
136
137 browser.find_by_name('name').type('Steve Jobs', slowly=True)
138 browser.find_by_css('.city').fill('San Francisco')
139
140
141 Verifying if element is visible or invisible
142 --------------------------------------------
143
144 To check if an element is visible or invisible, use the ``visible`` property. For instance:
145
146 .. highlight:: python
147
148 ::
149
150 browser.find_by_css('h1').first.visible
151
152 will be True if the element is visible, or False if it is invisible.
153
154
155 Verifying if element has a className
156 ------------------------------------
157
158 To check if an element has a className, use the ``has_class`` method. For instance:
159
160 .. highlight:: python
161
162 ::
163
164 browser.find_by_css('.content').first.has_class('content')
165
166
167 Interacting with elements through a ElementList object
168 ------------------------------------------------------
169
170 Don't you like to always use ``first`` when selecting an element for clicking, for example:
171
172 .. highlight:: python
173
174 ::
175
176 browser.find_by_css('a.my-website').first.click()
177
178 You can invoke any ``Element`` method on ``ElementList`` and it will be proxied to the **first** element of the list. So the two lines below are equivalent:
179
180 .. highlight:: python
181
182 ::
183
184 assert browser.find_by_css('a.banner').first.visible
185 assert browser.find_by_css('a.banner').visible
186
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Finding elements
6 :keywords: splinter, python, tutorial, find, selectors
7
8 ++++++++++++++++
9 Finding elements
10 ++++++++++++++++
11
12 Splinter provides 6 methods for finding elements in the page, one for each
13 selector type: ``css``, ``xpath``, ``tag``, ``name``, ``id``, ``value``,
14 ``text``.
15 Examples:
16
17 .. highlight:: python
18
19 ::
20
21 browser.find_by_css('h1')
22 browser.find_by_xpath('//h1')
23 browser.find_by_tag('h1')
24 browser.find_by_name('name')
25 browser.find_by_text('Hello World!')
26 browser.find_by_id('firstheader')
27 browser.find_by_value('query')
28
29
30 Each of these methods returns a list with the found elements. You can get the
31 first found element with the ``first`` shortcut:
32
33 .. highlight:: python
34
35 ::
36
37 first_found = browser.find_by_name('name').first
38
39 There's also the ``last`` shortcut -- obviously, it returns the last found
40 element:
41
42 .. highlight:: python
43
44 ::
45
46 last_found = browser.find_by_name('name').last
47
48
49 Get element using index
50 =======================
51
52 You also can use an index to get the desired element in the list of found
53 elements:
54
55 .. highlight:: python
56
57 ::
58
59 second_found = browser.find_by_name('name')[1]
60
61 All elements and ``find_by_id``
62 ===============================
63
64 A web page should have only one id, so the ``find_by_id`` method returns always
65 a list with just one element.
66
67 Finding links
68 =============
69
70 If you want to target only links on a page, you can use the methods provided in the
71 links namespace. This in available at both the browser and element level.
72
73 Examples:
74
75 .. highlight:: python
76
77 ::
78
79 links_found = browser.links.find_by_text('Link for Example.com')
80 links_found = browser.links.find_by_partial_text('for Example')
81 links_found = browser.links.find_by_href('http://example.com')
82 links_found = browser.links.find_by_partial_href('example')
83
84 links_found = browser.find_by_css('.main').links.find_by_text('Link for Example.com')
85 links_found = browser.find_by_css('.main').links.find_by_partial_text('for Example.com')
86 links_found = browser.find_by_css('.main').links.find_by_href('http://example.com')
87 links_found = browser.find_by_css('.main').links.find_by_partial_href('example')
88
89
90 As the other ``find_*`` methods, these returns a list of all found elements.
91
92
93 Chaining find of elements
94 =========================
95
96 Finding methods are chainable, so you can find the descendants of a previously
97 found element.
98
99 .. highlight:: python
100
101 ::
102
103 divs = browser.find_by_tag("div")
104 within_elements = divs.first.find_by_name("name")
105
106 ``ElementDoesNotExist`` exception
107 =================================
108
109 If an element is not found, the ``find_*`` methods return an empty list. But
110 if you try to access an element in this list, the method will raise the
111 :class:`splinter.exceptions.ElementDoesNotExist` exception.
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Using HTTP proxies
6 :keywords: splinter, python, tutorial, documentation, proxy
7
8 ++++++++++++++++++++++++++++++++++++++++++++
9 Using HTTP Proxies
10 ++++++++++++++++++++++++++++++++++++++++++++
11
12 Unauthenticated proxies are simple, you need only configure
13 the browser with the hostname and port.
14
15 Authenticated proxies are rather more complicated, (see
16 `RFC2617 <http://www.ietf.org/rfc/rfc2617.txt>`_)
17
18 Using an unauthenticated HTTP proxy with Firefox
19 ------------------------------------------------
20
21 ::
22
23 profile = {
24 'network.proxy.http': YOUR_PROXY_SERVER_HOST,
25 'network.proxy.http_port': YOUR_PROXY_SERVER_PORT,
26 'network.proxy.ssl': YOUR_PROXY_SERVER_HOST,
27 'network.proxy.ssl_port': YOUR_PROXY_SERVER_PORT,
28 'network.proxy.type': 1
29 }
30 self.browser = Browser(self.browser_type, profile_preferences=profile)
31
32 Authenticated HTTP proxy with Firefox
33 -------------------------------------
34
35 If you have access to the browser window, then the same technique will
36 work for an authenticated proxy, but you will have to type the username
37 and password in manually.
38
39 If this is not possible, for example on a remote CI server, then it is
40 not currently clear how to do this. This document will be updated when
41 more information is known. If you can help, please follow up on
42 `https://github.com/cobrateam/splinter/issues/359 <https://github.com/cobrateam/splinter/issues/359>`_.
43
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Dealing with HTTP status code and HTTP exceptions with Splinter
6 :keywords: splinter, python, tutorial, documentation, exception, http error, status code
7
8 ++++++++++++++++++++++++++++++++++++++++++++
9 Dealing with HTTP status code and exceptions
10 ++++++++++++++++++++++++++++++++++++++++++++
11
12 **Note:** After 0.8 version the `webdriver` (firefox, chrome) based drivers does not support http error
13 handling.
14
15 Dealing with HTTP status code
16 -----------------------------
17
18 It's also possible to check which HTTP status code a browser.visit gets. You can use ``status_code.is_success`` to do the work
19 for you or you can compare the status code directly:
20
21 .. highlight:: python
22
23 ::
24
25 browser.visit('http://cobrateam.info')
26 browser.status_code.is_success() # True
27 # or
28 browser.status_code == 200 # True
29 # or
30 browser.status_code.code # 200
31
32 The difference between those methods is that if you get a redirect (or something that is not an HTTP error),
33 ``status_code.is_success`` will consider your response as successfully. The numeric status code can be accessed via
34 ``status_code.code``.
35
36 Handling HTTP exceptions
37 ------------------------
38
39 Whenever you use the ``visit`` method, Splinter will check if the response is success or not, and if not, it will raise an
40 HttpResponseError exception. But don't worry, you can easily catch it:
41
42 .. highlight:: python
43
44 ::
45
46 try:
47 browser.visit('http://cobrateam.info/i-want-cookies')
48 except HttpResponseError, e:
49 print "Oops, I failed with the status code %s and reason %s" % (e.status_code, e.reason)
50
51 ..
52
53 **Note:** ``status_code`` and this HTTP exception handling is available only for selenium webdriver
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Automatic interaction with alerts, prompts and iframes
6 :keywords: splinter, python, tutorial, alerts, prompts, iframes, acceptance tests
7
8 ++++++++++++++++++++++++++
9 Frames, alerts and prompts
10 ++++++++++++++++++++++++++
11
12 Using iframes
13 -------------
14
15 You can use the ``get_iframe`` method and the ``with`` statement to interact with iframes. You can pass the iframe's name, id, index, or web element to ``get_iframe``.
16
17 .. highlight:: python
18
19 ::
20
21 with browser.get_iframe('iframemodal') as iframe:
22 iframe.do_stuff()
23
24
25 Handling alerts and prompts
26 ---------------------------
27
28 Chrome support for alerts and prompts is new in Splinter 0.4.
29
30 **IMPORTANT:** Only webdriver (Firefox and Chrome) has support for alerts and prompts.
31
32 You can interact with alerts and prompts using the ``get_alert`` method.
33
34 .. highlight:: python
35
36 ::
37
38 alert = browser.get_alert()
39 alert.text
40 alert.accept()
41 alert.dismiss()
42
43
44 In case of prompts, you can answer it using the ``send_keys`` method.
45
46 .. highlight:: python
47
48 ::
49
50 prompt = browser.get_alert()
51 prompt.text
52 prompt.send_keys('text')
53 prompt.accept()
54 prompt.dismiss()
55
56
57 You can also use the ``with`` statement to interact with both alerts and prompts.
58
59 .. highlight:: python
60
61 ::
62
63 with browser.get_alert() as alert:
64 alert.do_stuff()
65
66 If there's no prompt or alert, ``get_alert`` will return ``None``.
67 Remember to always use at least one of the alert/prompt ending methods (accept and dismiss).
68 Otherwise, your browser instance will be frozen until you accept or dismiss the alert/prompt correctly.
0 .. Copyright 2012-2018 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Documentation for splinter, an open source tool for testing web applications
6 :keywords: splinter, python, tutorial, documentation, web application, tests, atdd, tdd, acceptance tests
7
8 Splinter
9 ==============
10
11 Splinter is an open source tool for testing web applications using Python.
12 It lets you automate browser actions, such as visiting URLs and interacting with their items.
13
14 Sample code
15 -----------
16
17 .. highlight:: python
18
19 ::
20
21 from splinter import Browser
22
23 with Browser() as browser:
24 # Visit URL
25 url = "http://www.google.com"
26 browser.visit(url)
27 browser.fill('q', 'splinter - python acceptance testing for web applications')
28 # Find and click the 'search' button
29 button = browser.find_by_name('btnG')
30 # Interact with elements
31 button.click()
32 if browser.is_text_present('splinter.readthedocs.io'):
33 print("Yes, the official website was found!")
34 else:
35 print("No, it wasn't found... We need to improve our SEO techniques")
36
37 **Note:** if you don't provide any driver to the ``Browser`` function, ``firefox`` will be used.
38
39 Features
40 --------
41
42 * simple api
43 * multiple webdrivers (chrome, firefox, zopetestbrowser, remote
44 webdriver, Django, Flask)
45 * css and xpath selectors
46 * support for iframes and alerts
47 * can execute javascript
48 * works with ajax and async javascript
49
50 :doc:`what's new in splinter? </news>`
51
52 Getting started
53 ---------------
54 * :doc:`Why use Splinter </why>`
55 * :doc:`Installation </install>`
56 * :doc:`Quick tutorial </tutorial>`
57
58 Basic browsing and interactions
59 -------------------------------
60
61 * :doc:`Browser and navigation </browser>`
62 * :doc:`Finding elements </finding>`
63 * :doc:`Mouse interactions </mouse-interaction>`
64 * :doc:`Interacting with elements and forms </elements-in-the-page>`
65 * :doc:`Verify the presence of texts and elements in a page, with matchers </matchers>`
66 * :doc:`Cookies manipulation </cookies>`
67 * :doc:`Take screenshot </screenshot>`
68
69 JavaScript support
70 ------------------
71
72 * :doc:`Executing JavaScript </javascript>`
73
74 Walking on...
75 -------------
76
77 * :doc:`Dealing with HTTP status code and exceptions </http-status-code-and-exception>`
78 * :doc:`Using HTTP proxies </http-proxies>`
79 * :doc:`Interacting with iframes, alerts and prompts </iframes-and-alerts>`
80 * :doc:`Full API documentation </api/index>`
81
82 Drivers
83 -------
84
85 Browser based drivers
86 +++++++++++++++++++++
87
88 The following drivers open a browser to run your actions:
89
90 * :doc:`Chrome WebDriver </drivers/chrome>`
91 * :doc:`Firefox WebDriver </drivers/firefox>`
92
93 Headless drivers
94 ++++++++++++++++
95
96 The following drivers don't open a browser to run your actions (but each has its own dependencies, check the
97 specific docs for each driver):
98
99 * :doc:`Chrome WebDriver (headless option) </drivers/chrome>`
100 * :doc:`Firefox WebDriver (headless option) </drivers/firefox>`
101 * :doc:`zope.testbrowser </drivers/zope.testbrowser>`
102 * :doc:`django client </drivers/django>`
103 * :doc:`flask client </drivers/flask>`
104
105 Remote driver
106 ++++++++++++++
107
108 The remote driver uses Selenium Remote to control a web browser on a remote
109 machine.
110
111 * :doc:`Remote WebDriver </drivers/remote>`
112
113
114 Get in touch and contribute
115 ===========================
116
117 * :doc:`Community </community>`
118 * :doc:`Contribute </contribute>`
119 * :doc:`Writing new drivers </contribute/writing-new-drivers>`
120 * :doc:`Setting up your splinter development environment </contribute/setting-up-your-development-environment>`
121
122 .. toctree::
123 :caption: Getting Started
124 :hidden:
125
126 why
127 install
128 tutorial
129
130 .. toctree::
131 :caption: Browsing and Interactions
132 :hidden:
133
134 browser
135 finding
136 mouse-interaction
137 elements-in-the-page
138 matchers
139 cookies
140 screenshot
141
142 .. toctree::
143 :caption: JavaScript
144 :hidden:
145
146 javascript
147
148 .. toctree::
149 :caption: Drivers
150 :hidden:
151
152 drivers/chrome
153 drivers/firefox
154 drivers/remote
155 drivers/zope.testbrowser
156 drivers/django
157 drivers/flask
158
159 .. toctree::
160 :caption: More
161 :hidden:
162
163 http-status-code-and-exception
164 http-proxies
165 iframes-and-alerts
166 api/index
167 selenium-keys
168
169 .. toctree::
170 :caption: Get in touch and contribute
171 :hidden:
172
173 community
174 contribute
175 contribute/writing-new-drivers
176 contribute/setting-up-your-development-environment
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Install guide for splinter
6 :keywords: splinter, python, tutorial, how to install, installation
7
8 +++++++++++++
9 Install guide
10 +++++++++++++
11
12 Install Python
13 ==============
14
15 In order to install Splinter, make sure Python is installed. Note: only Python 2.7+ is supported.
16
17 Download Python from http://www.python.org. If you’re using Linux or Mac OS X, it is probably already installed.
18
19 Install splinter
20 ================
21
22 Basically, there are two ways to install Splinter:
23
24 Install a stable release
25 ------------------------
26
27 If you're interested on an official and almost bug-free version, just run from the Terminal:
28
29
30 .. highlight:: bash
31
32 ::
33
34 $ [sudo] pip install splinter
35
36
37
38 Install under-development source-code
39 -------------------------------------
40
41 Otherwise, if you want Splinter's latest-and-greatest features and aren't afraid of running under development code, run:
42
43 .. highlight:: bash
44
45 ::
46
47 $ git clone git://github.com/cobrateam/splinter.git
48 $ cd splinter
49 $ [sudo] python setup.py install
50
51
52 **Notes:**
53
54 * - make sure you have already :doc:`set up your development environment </contribute/setting-up-your-development-environment>`.
55 * - in this second case, make sure `Git <http://git-scm.com/>`_ is installed.
56 * - in order to use Chrome webdriver, you need to :doc:`setup Google Chrome properly </drivers/chrome>`.
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Executing javascript
6 :keywords: splinter, python, tutorial, javascript
7
8 ++++++++++++++++++++
9 Executing javascript
10 ++++++++++++++++++++
11
12 You can easily execute JavaScript, in drivers which support it:
13
14 .. highlight:: python
15
16 ::
17
18 browser.execute_script("$('body').empty()")
19
20 You can return the result of the script:
21
22 .. highlight:: python
23
24 ::
25
26 browser.evaluate_script("4+4") == 8
27
28
29 Example: manipulating text fields with JavaScript
30 +++++++++++++++++++++++++++++++++++++++++++++++++
31
32 Some text input actions cannot be "typed" thru ``browser.fill()``, like new lines and tab characters. Below is en example how to work around this using ``browser.execute_script()``. This is also much faster than ``browser.fill()`` as there is no simulated key typing delay, making it suitable for longer texts.
33
34 ::
35
36 def fast_fill_by_javascript(browser: DriverAPI, elem_id: str, text: str):
37 """Fill text field with copy-paste, not by typing key by key.
38
39 Otherwise you cannot type enter or tab.
40
41 :param id: CSS id of the textarea element to fill
42 """
43 text = text.replace("\t", "\\t")
44 text = text.replace("\n", "\\n")
45
46 # Construct a JavaScript snippet that is executed on the browser sdie
47 snippet = f"""document.querySelector("#{elem_id}").value = "{text}";"""
48 browser.execute_script(snippet)
0 @ECHO OFF
1
2 REM Command file for Sphinx documentation
3
4 if "%SPHINXBUILD%" == "" (
5 set SPHINXBUILD=sphinx-build
6 )
7 set BUILDDIR=_build
8 set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
9 if NOT "%PAPER%" == "" (
10 set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
11 )
12
13 if "%1" == "" goto help
14
15 if "%1" == "help" (
16 :help
17 echo.Please use `make ^<target^>` where ^<target^> is one of
18 echo. html to make standalone HTML files
19 echo. dirhtml to make HTML files named index.html in directories
20 echo. singlehtml to make a single large HTML file
21 echo. pickle to make pickle files
22 echo. json to make JSON files
23 echo. htmlhelp to make HTML files and a HTML help project
24 echo. qthelp to make HTML files and a qthelp project
25 echo. devhelp to make HTML files and a Devhelp project
26 echo. epub to make an epub
27 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
28 echo. text to make text files
29 echo. man to make manual pages
30 echo. changes to make an overview over all changed/added/deprecated items
31 echo. linkcheck to check all external links for integrity
32 echo. doctest to run all doctests embedded in the documentation if enabled
33 goto end
34 )
35
36 if "%1" == "clean" (
37 for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
38 del /q /s %BUILDDIR%\*
39 goto end
40 )
41
42 if "%1" == "html" (
43 %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
44 if errorlevel 1 exit /b 1
45 echo.
46 echo.Build finished. The HTML pages are in %BUILDDIR%/html.
47 goto end
48 )
49
50 if "%1" == "dirhtml" (
51 %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
52 if errorlevel 1 exit /b 1
53 echo.
54 echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
55 goto end
56 )
57
58 if "%1" == "singlehtml" (
59 %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
60 if errorlevel 1 exit /b 1
61 echo.
62 echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
63 goto end
64 )
65
66 if "%1" == "pickle" (
67 %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
68 if errorlevel 1 exit /b 1
69 echo.
70 echo.Build finished; now you can process the pickle files.
71 goto end
72 )
73
74 if "%1" == "json" (
75 %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
76 if errorlevel 1 exit /b 1
77 echo.
78 echo.Build finished; now you can process the JSON files.
79 goto end
80 )
81
82 if "%1" == "htmlhelp" (
83 %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
84 if errorlevel 1 exit /b 1
85 echo.
86 echo.Build finished; now you can run HTML Help Workshop with the ^
87 .hhp project file in %BUILDDIR%/htmlhelp.
88 goto end
89 )
90
91 if "%1" == "qthelp" (
92 %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
93 if errorlevel 1 exit /b 1
94 echo.
95 echo.Build finished; now you can run "qcollectiongenerator" with the ^
96 .qhcp project file in %BUILDDIR%/qthelp, like this:
97 echo.^> qcollectiongenerator %BUILDDIR%\qthelp\splinter.qhcp
98 echo.To view the help file:
99 echo.^> assistant -collectionFile %BUILDDIR%\qthelp\splinter.ghc
100 goto end
101 )
102
103 if "%1" == "devhelp" (
104 %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
105 if errorlevel 1 exit /b 1
106 echo.
107 echo.Build finished.
108 goto end
109 )
110
111 if "%1" == "epub" (
112 %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
113 if errorlevel 1 exit /b 1
114 echo.
115 echo.Build finished. The epub file is in %BUILDDIR%/epub.
116 goto end
117 )
118
119 if "%1" == "latex" (
120 %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
121 if errorlevel 1 exit /b 1
122 echo.
123 echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
124 goto end
125 )
126
127 if "%1" == "text" (
128 %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
129 if errorlevel 1 exit /b 1
130 echo.
131 echo.Build finished. The text files are in %BUILDDIR%/text.
132 goto end
133 )
134
135 if "%1" == "man" (
136 %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
137 if errorlevel 1 exit /b 1
138 echo.
139 echo.Build finished. The manual pages are in %BUILDDIR%/man.
140 goto end
141 )
142
143 if "%1" == "changes" (
144 %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
145 if errorlevel 1 exit /b 1
146 echo.
147 echo.The overview file is in %BUILDDIR%/changes.
148 goto end
149 )
150
151 if "%1" == "linkcheck" (
152 %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
153 if errorlevel 1 exit /b 1
154 echo.
155 echo.Link check complete; look for any errors in the above output ^
156 or in %BUILDDIR%/linkcheck/output.txt.
157 goto end
158 )
159
160 if "%1" == "doctest" (
161 %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
162 if errorlevel 1 exit /b 1
163 echo.
164 echo.Testing of doctests in the sources finished, look at the ^
165 results in %BUILDDIR%/doctest/output.txt.
166 goto end
167 )
168
169 :end
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Checking if a text pattern is or not present
6 :keywords: splinter, python, tutorial, element
7
8 ++++++++
9 Matchers
10 ++++++++
11
12 When working with AJAX and asynchronous JavaScript, it's common to have
13 elements which are not present in the HTML code (they are created with
14 JavaScript, dynamically). In this case you can use the methods
15 ``is_element_present`` and ``is_text_present`` to check the existence of an
16 element or text -- Splinter will load the HTML and JavaScript in the browser
17 and the check will be performed *before* processing JavaScript.
18
19 There is also the optional argument ``wait_time`` (given in seconds) -- it's a
20 timeout: if the verification method gets ``True`` it will return the result
21 (even if the ``wait_time`` is not over), if it doesn't get ``True``, the
22 method will wait until the ``wait_time`` is over (so it'll return the result).
23
24
25 Checking the presence of text
26 -----------------------------
27
28 The method ``is_text_present`` is responsible for checking if a text is present
29 in the page content. It returns ``True`` or ``False``.
30
31 .. highlight:: python
32
33 ::
34
35 browser = Browser()
36 browser.visit('https://splinter.readthedocs.io/')
37 browser.is_text_present('splinter') # True
38 browser.is_text_present('splinter', wait_time=10) # True, using wait_time
39 browser.is_text_present('text not present') # False
40
41
42 There's also a method to check if the text *is not* present:
43 ``is_text_not_present``. It works the same way but returns ``True`` if the text
44 is not present.
45
46 .. highlight:: python
47
48 ::
49
50 browser.is_text_not_present('text not present') # True
51 browser.is_text_not_present('text not present', wait_time=10) # True, using wait_time
52 browser.is_text_not_present('splinter') # False
53
54
55 Checking the presence of elements
56 ---------------------------------
57
58 Splinter provides 6 methods to check the presence of elements in the page, one
59 for each selector type: ``css``, ``xpath``, ``tag``, ``name``, ``id``,
60 ``value``, ``text``. Examples:
61
62 .. highlight:: python
63
64 ::
65
66 browser.is_element_present_by_css('h1')
67 browser.is_element_present_by_xpath('//h1')
68 browser.is_element_present_by_tag('h1')
69 browser.is_element_present_by_name('name')
70 browser.is_element_present_by_text('Hello World!')
71 browser.is_element_present_by_id('firstheader')
72 browser.is_element_present_by_value('query')
73 browser.is_element_present_by_value('query', wait_time=10) # using wait_time
74
75 As expected, these methods returns ``True`` if the element is present and
76 ``False`` if it is not present.
77
78 There's also the negative forms of these methods, as in ``is_text_present``:
79
80 .. highlight:: python
81
82 ::
83
84 browser.is_element_not_present_by_css('h6')
85 browser.is_element_not_present_by_xpath('//h6')
86 browser.is_element_not_present_by_tag('h6')
87 browser.is_element_not_present_by_name('unexisting-name')
88 browser.is_element_not_present_by_text('Not here :(')
89 browser.is_element_not_present_by_id('unexisting-header')
90 browser.is_element_not_present_by_id('unexisting-header', wait_time=10) # using wait_time
91
92
93 Checking the visibility of elements
94 ---------------------------------
95
96 There are two methods to check if the element is visible or hidden in the current page using either the selector type ``css`` or ``xpath``. It
97 returns ``True`` if the element is visible and ``False`` if the element in not visible.
98
99 .. highlight:: python
100
101 ::
102
103 browser.is_element_visible_by_css('h5')
104 browser.is_element_visible_by_css('h5', wait_time=10)
105 browser.is_element_visible_by_xpath('//h5')
106
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Mouse interatcion.
6 :keywords: splinter, python, tutorial, documentation, mouse interaction, mouseover, mouseout, double click, mouse events
7
8 ++++++++++++++++++
9 Mouse interactions
10 ++++++++++++++++++
11
12 **Note:** Most mouse interaction currently works only on Chrome driver and Firefox 27.0.1.
13
14 Splinter provides some methods for mouse interactions with elements in the page.
15 This feature is useful to test if an element appears on mouse over and
16 disappears on mouse out (eg.: subitems of a menu).
17
18 It's also possible to send a click, double click or right click to the element.
19
20 Here is a simple example: imagine you have this `jQuery <http://jquery.com>`_
21 event for mouse over and out:
22
23 .. highlight:: js
24
25 ::
26
27 $('.menu-links').mouseover(function(){
28 $(this).find('.subitem').show();
29 });
30
31 $('.menu-links').mouseout(function(){
32 $(this).find('.subitem').hide();
33 });
34
35 You can use Splinter to fire the event programatically:
36
37 .. highlight:: python
38
39 ::
40
41 browser.find_by_css('.menu-links').mouse_over()
42 # Code to check if the subitem is visible...
43 browser.find_by_css('.menu-links').mouse_out()
44
45
46 The methods available for mouse interactions are:
47
48 ``mouse_over``
49 --------------
50
51 .. highlight:: python
52
53
54 Puts the mouse above the element. Example:
55
56 ::
57
58 browser.find_by_tag('h1').mouse_over()
59
60
61 ``mouse_out``
62 -------------
63
64 .. highlight:: python
65
66 Puts the mouse out of the element. Example:
67
68 ::
69
70 browser.find_by_tag('h1').mouse_out()
71
72 ``click``
73 ---------
74
75 .. highlight:: python
76
77 Clicks on the element. Example:
78
79 ::
80
81 browser.find_by_tag('h1').click()
82
83 ``double_click``
84 ----------------
85
86 .. highlight:: python
87
88 Double-clicks on the element. Example:
89
90 ::
91
92 browser.find_by_tag('h1').double_click()
93
94 ``right_click``
95 ---------------
96
97 .. highlight:: python
98
99 Right-clicks on the element. Example:
100
101 ::
102
103 browser.find_by_tag('h1').right_click()
104
105 ``drag_and_drop``
106 -----------------
107
108 Yes, you can drag an element and drop it to another element! The example below
109 drags the ``<h1>...</h1>`` element and drop it to a container element
110 (identified by a CSS class).
111
112 .. highlight:: python
113
114 ::
115
116 draggable = browser.find_by_tag('h1')
117 target = browser.find_by_css('.container')
118 draggable.drag_and_drop(target)
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.0.1.
6 :keywords: splinter 0.0.1, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.0.1?
9 ================================
10
11 Features
12 -----------------
13
14 - support to firefox selenium 2 driver
15 - support to zope test browser
16 - navigating with Browser.visit
17 - get the title of the visited page
18 - get the html content of the visited page
19 - visited page's url can be accessed by the url attribute
20 - finding first element by tag, xpath, css selector, name and id
21 - find first link by xpath or text
22 - interacting with forms: text input, file, radio and check button
23 - verifying if element is visible or invisible
24 - executing and evaluating javascript
25 - debug with save and open page
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.0.2.
6 :keywords: splinter 0.0.2, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.0.2?
9 =============================
10
11 Features
12 --------
13
14 - fill instead of fill_in to fill inputs
15 - support to google chrome selenium 2 driver
16 - form interactions now support select
17 - issue #11: improve find's methods to return all/first/last elements
18
19 now finder methods (find_by_name, find_by_css_selector, find_by_tag, find_by_id, find_by_xpath) returns a ElementList object that contains a list of all found elements:
20
21 ::
22
23 browser.find_by_name('name')
24
25 .first - to find first element
26
27 ::
28
29 browser.find_by_name('name').first
30
31 .last - to find last element
32
33 ::
34
35 browser.find_by_name('name').last
36
37 And additionally, using index
38
39 ::
40
41 browser.find_by_name('name')[1]
42
43 An id should be unique in a web page, so find_by_id() method always returns a list with a single element.
44
45 Backward incompatible changes
46 -----------------------------
47
48 - issue #24 remove save_and_open_page method from splinter api. This feature is out of splinter's scope, hence should be implemented as an external package.
49 - now finder methods (find_by_name, find_by_css_selector, find_by_tag, find_by_id, find_by_xpath) returns a list with elements, to get the first element founded use `first` attribute
50
51 ::
52
53 browser.find_by_name('name').first
54
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.0.3.
6 :keywords: splinter 0.0.3, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.0.3?
9 =============================
10
11 Features
12 --------
13
14 - now splinter use selenium 2.0b3 for firefox and chrome driver
15 - zope.testbrowser.browser dependency is not required
16 - new method for reload a page
17 - find_by_css_selector is now deprecated, use find_by_css instead
18 - deprecated methods now throw "DeprecationWarning"
19 - methods for verify if element or text is present
20 - find_by methods wait for element
21 - added support for iframes and alerts
22 - added more specific exception messages for not found elements
23
24 Backward incompatible changes
25 -----------------------------
26
27 - you should update your selenium to 2.0b3 version
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.1.1.
6 :keywords: splinter 0.1.1, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.1.1?
9 =============================
10
11 - compability with Firefox 5
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.1.
6 :keywords: splinter 0.1, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.1?
9 ===========================
10
11 Features
12 --------
13
14 - capability to handle HTTP errors (using an exception) in Selenium drivers (Firefox and Chrome)
15 - capability to work with HTTP status code in Selenium drivers (Firefox and Chrome)
16 - browsing history (``back`` and ``forward`` methods in ``Browser`` class)
17 - improvements in documentation
18
19 Bugfixes
20 --------
21
22 - fixed Chrome driver instability
23 - fixed ``Browser.choose`` behaviour
24 - fixed WebDriver silenting routine
25
26 Backward incompatible changes
27 -----------------------------
28
29 - you should update your selenium to 2.0rc2 version
0 .. Copyright 2018 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.10.0.
6 :keywords: splinter 0.10.0, news
7
8 whats's new in splinter 0.10.0?
9 ==============================
10
11 * Scroll to elements before to execute action chains ()
12 * Using `options` instead `firefox_options` to avoid warnings (https://github.com/cobrateam/splinter/pull/634)
13 * Add support for `*args` parameter in `execute_script` (https://github.com/cobrateam/splinter/issues/436)
14 * Implement `__ne__` in `StatusCode` (https://github.com/cobrateam/splinter/issues/460)
15 * Using the new syntax `switch_to_alert` instead `switch_to.alert` to avoid webdriver warnings.
16 * `CookieManager. __eq__` returns a bool value (https://github.com/cobrateam/splinter/issues/308<Paste>)
17 * Fix find_by_text to be used inside a chain (https://github.com/cobrateam/splinter/issues/6281)
18 * Add support for selenium 3.141.0
0 .. Copyright 2019 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.11.0.
6 :keywords: splinter 0.11.0, news
7
8 whats's new in splinter 0.11.0?
9 ==============================
10
11 * Browser.get_alert() returns Alert instead of a wrapper object
12 * Add `browser.html_snapshot` method
13 * Allow browser.get_iframe() to accept a web element
14 * Fix mouse_out method
15 * ElementList is no longer a subclass of list
16 * Browser.get_alert() now waits for alert to present
17 * Use 'switch_to.alert' instead of deprecated 'switch_to_alert'
0 .. Copyright 2019 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.12.0.
6 :keywords: splinter 0.12.0, news
7
8 whats's new in splinter 0.12.0?
9 ==============================
10
11 * `find_by_text` now handle strings with quotation marks (https://github.com/cobrateam/splinter/issues/457)
12 * `find_link_by` methods are now chainable (https://github.com/cobrateam/splinter/pull/699)
13 * `ElementList.__getattr__()` no longer hide ElementNotFound (https://github.com/cobrateam/splinter/pull/707)
14 * Firefox headless mode now handle custom firefox_binary option (https://github.com/cobrateam/splinter/pull/714)
15 * Firefox driver now respects headless option in subsequent calls (https://github.com/cobrateam/splinter/pull/715)
16 * `Browser.get_alert()` returns None if no alert exists (https://github.com/cobrateam/splinter/issues/387)
17 * Retry WebElement.click if Exception is thrown (https://github.com/cobrateam/splinter/pull/725)
18 * `find_by` methods in WebDriverElement now uses retry mechanism (https://github.com/cobrateam/splinter/pull/727)
19 * `is_not_present/visible` returns True immediately after not finding anything (https://github.com/cobrateam/splinter/pull/732)
20 * Accept all valid arguments for Remote WebDriver (https://github.com/cobrateam/splinter/pull/734)
21 * Allow ActionChains when using Remote WebDriver (https://github.com/cobrateam/splinter/pull/738)
0 .. Copyright 2019 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.13.0.
6 :keywords: splinter 0.13.0, news
7
8 whats's new in splinter 0.13.0?
9 ===============================
10
11 * Patch Remote WebDriver to add retry attempts (https://github.com/cobrateam/splinter/pull/742)
12 * Add driver attribute to WebDriverElement. This fixes an issue where mouse interaction fails on nested elements (https://github.com/cobrateam/splinter/pull/740)
13 * Fix WebDriverElement.select and .select_by_text to search only inside the parent element (https://github.com/cobrateam/splinter/pull/729)
14 * find_by with 0 second wait_time only checks once (https://github.com/cobrateam/splinter/pull/739)
15 * Fix FlaskClient redirects (https://github.com/cobrateam/splinter/pull/721)
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.2.
6 :keywords: splinter 0.2, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.2?
9 ===========================
10
11 Features
12 --------
13
14 - :doc:`cookies manipulation </cookies>`
15 - find elements within an element
16 - improvements in `ElementList`
17
18 Backward incompatible changes
19 -----------------------------
20
21 - you should update your selenium to 2.1.0 version and your chrome driver. See more in :doc:`suport to new chrome driver </drivers/chrome>`
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.3.
6 :keywords: splinter 0.3, python, news, documentation, tutorial, web application
7
8 what's new in splinter 0.3?
9 ===========================
10
11 Features
12 --------
13
14 - support for browser extensions on :doc:`Firefox driver </drivers/firefox>`
15 - support for Firefox profiles on :doc:`Firefox driver </drivers/firefox>`
16 - support for mouse over and mouse out on :doc:`Chrome driver </drivers/chrome>`
17 - support for finding and clicking links by partial :meth:`text <splinter.driver.DriverAPI.click_link_by_partial_text>`
18 and :meth:`href <splinter.driver.DriverAPI.click_link_by_partial_href>`
19 - support for :meth:`finding by value <splinter.driver.DriverAPI.find_by_value>`
20
21 Documentation improvements
22 --------------------------
23
24 - complete :doc:`API reference </api/index>`
25 - instructions on :doc:`new drivers creation </contribute/writing-new-drivers>`
26
27 Backward incompatible changes
28 -----------------------------
29
30 - changes on :doc:`cookies manipulation </cookies>`. Affects only who used :meth:`cookies.delete <splinter.cookie_manager.CookieManagerAPI.delete>`
31 passing the ``cookie`` keyword.
32
33 Before version **0.3**:
34
35 .. highlight:: python
36
37 ::
38
39 >>> driver.cookies.delete(cookie='whatever')
40
41 Now:
42
43 .. highlight:: python
44
45 ::
46
47 >>> driver.cookies.delete('whatever')
48
49 Bugfixes
50 --------
51
52 - Fixed cookies behavior on Chrome driver (it was impossible to delete one cookie, Chrome was always deleting all cookies)
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.1.
6 :keywords: splinter 0.4.1, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.1?
9 ==============================
10
11 Features
12 --------
13
14 * Partial Windows support
15 * Internet Explorer driver
16 * Added ``type`` and ``fill`` methods to :doc:`ElementAPI </api/driver-and-element-api>`.
17 * Updated selenium to 2.13.1
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.10.
6 :keywords: splinter 0.4.10, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.10?
9 ===============================
10
11 This version does not work with firefox 17.
12
13 Improvements
14 ------------
15
16 * remove deprecated driver names
17 * update lxml version
18 * update selenium version to 2.29
19
20 Bugfix
21 ------
22
23 * set user-agent for request_handler requests
24 * update zope.testbrowser documentation regarding dependencies (cssselect)
25 * fix URL checking in request_handler (support for HTTPS)
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.2.
6 :keywords: splinter 0.4.2, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.2?
9 ==============================
10
11 Features
12 --------
13
14 * added new *browser* method *form_fill* to fill all form fields in one command
15
16 Bugfixes
17 --------
18
19 * fixed a bug in setup.py
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.3.
6 :keywords: splinter 0.4.3, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.3?
9 ==============================
10
11 Features
12 --------
13
14 * Updated selenium to 2.14
0 .. meta::
1 :description: New splinter features on version 0.4.4.1.
2 :keywords: splinter 0.4.4.1, python, news, documentation, tutorial, web application
3
4 whats's new in splinter 0.4.4.1?
5 ==============================
6
7 Bugfixes
8 --------
9
10 * update selenium version, to work with latest Firefox version
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.4.
6 :keywords: splinter 0.4.4, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.4?
9 ==============================
10
11 Features
12 --------
13
14 * Updated selenium to 2.17
15 * Method to change user-agent
16 * `dismiss` method in alert element
17
18
19 Bugfixes
20 --------
21
22 * request_handler now works with querystring
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.7.
6 :keywords: splinter 0.4.7, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4.7?
9 ==============================
10
11 Features
12 --------
13
14 * has_class method on Element
15 * fix documentation
16
17 Bugfixes and improvements
18 -------------------------
19
20 * improving `find_by_css` method to use native methods from drivers
0
1 .. Copyright 2012 splinter authors. All rights reserved.
2 Use of this source code is governed by a BSD-style
3 license that can be found in the LICENSE file.
4
5 .. meta::
6 :description: New splinter features on version 0.4.8.
7 :keywords: splinter 0.4.8, python, news, documentation, tutorial, web application
8
9 whats's new in splinter 0.4.8?
10 ==============================
11
12 Features
13 --------
14
15 * html and outer_html property on Element
16 * profile_preferences option to Firefox driver
17 * Support for handling browser pop-up windows for Firefox/Chrome drivers.
0
1
2 .. Copyright 2012 splinter authors. All rights reserved.
3 Use of this source code is governed by a BSD-style
4 license that can be found in the LICENSE file.
5
6 .. meta::
7 :description: New splinter features on version 0.4.9.
8 :keywords: splinter 0.4.9, python, news, documentation, tutorial, web application
9
10 whats's new in splinter 0.4.9?
11 ==============================
12
13 This version does not works with firefox 17.
14
15 Features
16 --------
17
18 * support for selenium remote web driver.
19
20 Bugfix
21 ------
22
23 * is_text_present and is_text_not_present works with html without body.
24 * fixed zopetestdriver attach_file behaviour.
25
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.4.
6 :keywords: splinter 0.4, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.4?
9 ============================
10
11 Features
12 --------
13
14 - support for double click, right click, drag and drop and other :doc:`mouse interactions </mouse-interaction>`
15 (only :doc:`Chrome </drivers/chrome>` driver)
16 - support for Python 2.5
17
18 Documentation improvements
19 --------------------------
20
21 - improved :doc:`API docs </api/index>`
22 - added docs for ``is_text_present`` method
23 - added API docs for ``is_element_present_by_*`` methods
24 - added docs for :doc:`mouse interactions </mouse-interaction>`
25
26 Deprecations
27 ------------
28
29 - simplified name of Selenium drivers, they're just ``chrome`` and ``firefox`` now (instead
30 of ``webdriver.chrome`` and ``webdriver.firefox``). The older names were deprecated.
31 - changed name of ``mouseover`` and ``mouseout`` methods to ``mouse_over`` and ``mouse_out``
32
33 IMPORTANT
34 ---------
35
36 The following deprecated methods will be **removed** in the next splinter release (0.5) from Browser classes:
37
38 - fill_in
39 - find_by_css_selector
40 - is_element_present_by_css_selector
41 - is_element_not_present_by_css_selector
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.5.0.
6 :keywords: splinter 0.5.0, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.5.0?
9 ==============================
10
11 Features
12 --------
13
14 * support for phantomjs web driver.
15 * zopetestdriver support is_text_present.
16
17 Bugfix
18 ------
19
20 * fixed an unicode issue with setup.py.
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.5.2.
6 :keywords: splinter 0.5.2, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.5.2?
9 ==============================
10
11 Improvements
12 ------------
13
14 * support password field.
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.5.3.
6 :keywords: splinter 0.5.3, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.5.3?
9 ==============================
10
11 Improvement
12 -----------
13
14 * added kwargs to the Chrome driver constructor
15 * updated selenium to 2.33.0.
16
17 Bugfix
18 ------
19
20 * fixed about:blank behaviour #233.
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.5.4.
6 :keywords: splinter 0.5.4, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.5.4?
9 ==============================
10
11 Improvement
12 -----------
13
14 * implemented `browser.cookies.all()` - #240.
15
16 Bugfix
17 ------
18
19 * `browser.type()` works with textarea - #216.
0 .. Copyright 2013 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.5.5.
6 :keywords: splinter 0.5.5, python, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.5.5?
9 ==============================
10
11 Improvements
12 ------------
13
14 * Handle "internet explorer" as remote driver.
15 * implemented `get_screenshot_as_file`.
16 * `fill_form` now supports custom field types.
17 * More robust `find_link_by_partial_text`.
18 * support for selenium 2.39.0.
19 * support for zope.testbrowser 4.0.4.
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.6.0.
6 :keywords: splinter 0.6.0, python, django, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.6.0?
9 ==============================
10
11 Features
12 --------
13
14 * support for django test client.
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.0.
6 :keywords: splinter 0.7.0, python, django, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.7.0?
9 ==============================
10
11 Features
12 --------
13
14 * Support for mouse_over, mouse_out in Firefox driver.
15 * New flask test client driver.
16 * Better support for browser windows.
17 * Support for custom headers in PhantomJS driver.
18 * Added webdriver fullscreen support.
19 * Added a way to wait until element is visible.
20
21 Bugfix
22 ------
23
24 * Support encoding in django client and zopetestbrowser drivers.
25 * Browser.cookies.all() are more consistent and added a verbose mode.
0 .. Copyright 2015 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.1.
6 :keywords: splinter 0.7.1, python, django, news, documentation, tutorial, web application
7
8 whats's new in splinter 0.7.1?
9 ==============================
10
11 * support Selenium 2.45.0.
12 * Django Client supports `**kwargs` parameters on constructor.
13 * Django Client handle redirects.
14 * ZopeTestBrowser has the `ignore_robots` parameter.
0 .. Copyright 2015 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.2.
6 :keywords: splinter 0.7.2, news
7
8 whats's new in splinter 0.7.2?
9 ==============================
10
11 * fix Python 3 compatibility, improving enconding/decoding in `browser.title` and `browser.html` - `#380 <https://github.com/cobrateam/splinter/pull/380>`_
0 .. Copyright 2015 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.3.
6 :keywords: splinter 0.7.3, news
7
8 whats's new in splinter 0.7.3?
9 ==============================
10
11 * support selenium 2.47.1
12 * add `select_by_text` method
13 * add `find_by_text`, `is_element_present_by_text`, `is_element_not_present_by_text`
14 * improved support to python 3
15 * cookie support for remote webdriver
16 * get `status_code` by lazy evaluation. It should minimize the proxy and duplicated requests problems
17
18 django client
19 -------------
20
21 * improved `is_text_present` performance. djangoclient doesn't have to wait for load
22 * support django 1.7 and 1.8
23 * fixed several bugs with python3 compatibility
24 * added default extra headers: `SERVER_PORT`, `SERVER_NAME` and `User-Agent`
25 * support custom headers
0 .. Copyright 2016 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.4.
6 :keywords: splinter 0.7.4, news
7
8 whats's new in splinter 0.7.4?
9 ==============================
10
11
12 * support Selenium 2.53.6
13 * find_by_text support quotes (`#420 <https://github.com/cobrateam/splinter/pull/420>`_).
14 * Selenium capabilities for Firefox driver
15 (`#417 <https://github.com/cobrateam/splinter/pull/417>`_).
16 * multi-select support for Django and Flask
17 (`#443 <https://github.com/cobrateam/splinter/pull/443>`_).
18 * custom headers support to Flask
19 (`#444 <https://github.com/cobrateam/splinter/pull/444>`_).
20 * add `in` operation for cookies
21 (`#445 <https://github.com/cobrateam/splinter/pull/445>`_).
22 * Support for `is_element_present_by_*` in non-javascript drivers
23 (`#463 <https://github.com/cobrateam/splinter/pull/463>`_).
24 * incognito mode for Google Chrome
25 (`#465 <https://github.com/cobrateam/splinter/pull/465>`_).
26 * support for clearing text field types
27 (`#479 <https://github.com/cobrateam/splinter/pull/479>`_).
28 * allow to pass a chrome Options instance to Browser
29 (``#494 <https://github.com/cobrateam/splinter/pull/494>`_).
30 * new click_link_by_id method
31 (`#498 <https://github.com/cobrateam/splinter/pull/498>`_).
32
33 Backward incompatible changes
34 -----------------------------
35
36 * RequestHandler is removed and the `status` use lazy evaluation.
0 .. Copyright 2016 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.5.
6 :keywords: splinter 0.7.5, news
7
8 whats's new in splinter 0.7.5?
9 ==============================
10
11 * Timeout settings for Firefox driver
12 * Remove default icognito mode in Chrome driver
13 * Make input a contro element in `django`, `flask` and `zope.testbrowser`
0 .. Copyright 2017 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.6.
6 :keywords: splinter 0.7.6, news
7
8 whats's new in splinter 0.7.6?
9 ==============================
10
11 * fix `fill_form` for `select` element.
12 * support chrome headless mode
0 .. Copyright 2017 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.7.7.
6 :keywords: splinter 0.7.7, news
7
8 whats's new in splinter 0.7.7?
9 ==============================
10
11 * `fill_form` more robust by requiring form ID
12 * support firefox `headless mode
13 * handle exceptions when calling quit on webdriver
0 .. Copyright 2017 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.8.0.
6 :keywords: splinter 0.8.0, news
7
8 whats's new in splinter 0.8.0?
9 ==============================
10
11 * add support for Firefox incognito mode (https://github.com/cobrateam/splinter/pull/578)
12 * allow return value for `execute_script` to be returned (https://github.com/cobrateam/splinter/pull/585)
13 * `chrome_options` parameter renamed to `options` (https://github.com/cobrateam/splinter/pull/590)
14 * removed deprecated `mouseover` method
15 * raises `NotImplementedError` on `status_code` in drivers based on webdriver
16 * `phantomjs` is deprecated (this driver will be removed in 0.9.0)
0 .. Copyright 2018 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on version 0.9.0.
6 :keywords: splinter 0.9.0, news
7
8 whats's new in splinter 0.9.0?
9 ==============================
10
11 * `phantomjs` support was removed (https://github.com/cobrateam/splinter/issues/592)
12 * add options argument for chrome driver (https://github.com/cobrateam/splinter/pull/345)
13 * (bugfix) avoid element.find_by_text searches whole dom (https://github.com/cobrateam/splinter/issues/612)
14 * add suport for zope.testbrowser 5+
15 * handle webdriver StaleElementReferenceException (https://github.com/cobrateam/splinter/issues/541)
16 * add support for Flask 1+
17 * add support for seleniu 3.14.0
18 * update lxml to 4.2.4
19 * update cssselect to 1.0.3
0 .. Copyright 2014 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: New splinter features on earlier versions.
6 :keywords: splinter, python, news, documentation, tutorial, web application
7
8 +++++++++++++
9 splinter news
10 +++++++++++++
11
12 See below the changes for each splinter release.
13
14 - :doc:`what's new in splinter 0.13.0 </news/0.13.0>`
15 - :doc:`what's new in splinter 0.12.0 </news/0.12.0>`
16 - :doc:`what's new in splinter 0.11.0 </news/0.11.0>`
17 - :doc:`what's new in splinter 0.10.0 </news/0.10.0>`
18 - :doc:`what's new in splinter 0.9.0 </news/0.9.0>`
19 - :doc:`what's new in splinter 0.8.0 </news/0.8.0>`
20 - :doc:`what's new in splinter 0.7.7 </news/0.7.7>`
21 - :doc:`what's new in splinter 0.7.6 </news/0.7.6>`
22 - :doc:`what's new in splinter 0.7.5 </news/0.7.5>`
23 - :doc:`what's new in splinter 0.7.4 </news/0.7.4>`
24 - :doc:`what's new in splinter 0.7.3 </news/0.7.3>`
25 - :doc:`what's new in splinter 0.7.2 </news/0.7.2>`
26 - :doc:`what's new in splinter 0.7.1 </news/0.7.1>`
27 - :doc:`what's new in splinter 0.7.0 </news/0.7.0>`
28 - :doc:`what's new in splinter 0.6.0 </news/0.6.0>`
29 - :doc:`what's new in splinter 0.5.5 </news/0.5.5>`
30 - :doc:`what's new in splinter 0.5.4 </news/0.5.4>`
31 - :doc:`what's new in splinter 0.5.3 </news/0.5.3>`
32 - :doc:`what's new in splinter 0.5.2 </news/0.5.2>`
33 - :doc:`what's new in splinter 0.5.0 </news/0.5.0>`
34 - :doc:`what's new in splinter 0.4.10 </news/0.4.10>`
35 - :doc:`what's new in splinter 0.4.9 </news/0.4.9>`
36 - :doc:`what's new in splinter 0.4.8 </news/0.4.8>`
37 - :doc:`what's new in splinter 0.4.7 </news/0.4.7>`
38 - :doc:`what's new in splinter 0.4.4.1 </news/0.4.4.1>`
39 - :doc:`what's new in splinter 0.4.4 </news/0.4.4>`
40 - :doc:`what's new in splinter 0.4.3 </news/0.4.3>`
41 - :doc:`what's new in splinter 0.4.2 </news/0.4.2>`
42 - :doc:`what's new in splinter 0.4.1 </news/0.4.1>`
43 - :doc:`what's new in splinter 0.4 </news/0.4>`
44 - :doc:`what's new in splinter 0.3 </news/0.3>`
45 - :doc:`what's new in splinter 0.2 </news/0.2>`
46 - :doc:`what's new in splinter 0.1.1 </news/0.1.1>`
47 - :doc:`what's new in splinter 0.1 </news/0.1>`
48 - :doc:`what's new in splinter 0.0.3 </news/0.0.3>`
49 - :doc:`what's new in splinter 0.0.2 </news/0.0.2>`
50 - :doc:`what's new in splinter 0.0.1 </news/0.0.1>`
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Take screenshot
6 :keywords: splinter, python, tutorial, screenshot
7
8 ++++++++++++++++++
9 Take screenshot
10 ++++++++++++++++++
11
12 Splinter can take current view screenshot easily:
13
14 .. highlight:: python
15
16 ::
17
18 browser = Browser()
19 screenshot_path = browser.screenshot('absolute_path/your_screenshot.png')
20
21 You should use the absolute path to save screenshot. If you don't use
22 an absolute path, the screenshot will be saved in a temporary file.
23
24 Take a full view screenshot:
25
26 .. highlight:: python
27
28 ::
29
30 browser = Browser()
31 screenshot_path = browser.screenshot('absolute_path/your_screenshot.png', full=True)
32
33 ++++++++++++++++++++++++++++
34 Take element screenshot
35 ++++++++++++++++++++++++++++
36 First, if you want to use this function, you should install the Pillow dependency:
37
38 ::
39
40 pip install Pillow
41
42 If the element in the current view:
43
44 .. highlight:: python
45
46 ::
47
48 browser = Browser()
49 browser.visit('http://example.com')
50 screenshot_path = browser.find_by_xpath('xpath_rule').first.screenshot('absolute_path/your_screenshot.png')
51
52 If the element not in the current view, you should do it like this:
53
54 .. highlight:: python
55
56 ::
57
58 browser = Browser()
59 browser.visit('http://example.com')
60 screenshot_path = browser.find_by_xpath('xpath_rule').first.screenshot('absolute_path/your_screenshot.png', full=True)
61
62
63 ++++++++++++++++++
64 Take html snapshot
65 ++++++++++++++++++
66 Splinter can also take a snapshot of the current HTML:
67
68 .. highlight:: python
69
70 ::
71
72 browser = Browser()
73 screenshot_path = browser.html_snapshot('absolute_path/your_screenshot.html')
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Selenium Keys
6 :keywords: splinter, python, tutorial, documentation, selenium integration, selenium keys, keyboard events
7
8 ++++++++++++++++++
9 Selenium Keys
10 ++++++++++++++++++
11
12 With Splinter You can use selenium keys implementation.
13
14 Here is a simple example:
15
16 .. highlight:: python
17
18 ::
19
20 from selenium.webdriver.common.keys import Keys
21 ElementAPI.type(Keys.RETURN)
22
23 Full list of all support keys can be found on official selenium documentation
24 `selenium.webdriver.common.keys <https://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html>`_
0 .. Copyright 2012 splinter authors. All rights reserved.
1 Use of this source code is governed by a BSD-style
2 license that can be found in the LICENSE file.
3
4 .. meta::
5 :description: Splinter tutorial, learn how to test your web applications
6 :keywords: splinter, python, tutorial, documentation, web application, tests, atdd, tdd, acceptance tests
7
8 +++++++++++++++++
9 Splinter Tutorial
10 +++++++++++++++++
11
12 Before starting, make sure Splinter is :doc:`installed </install>`
13
14 This tutorial provides a simple example, teaching step by step how to:
15
16 * search for ``splinter - python acceptance testing for web applications'`` in google.com, and
17 * find if splinter official website is listed among the search results
18
19 Create a Browser instance
20 =========================
21
22 First of all, import ``Browser`` class and instantiate it.
23
24 .. highlight:: python
25
26 ::
27
28 from splinter import Browser
29 browser = Browser()
30
31 **Note:** if you don't provide any driver argument to the ``Browser`` function, ``firefox`` will be used (`Browser function documentation <https://splinter.readthedocs.io/en/latest/api/driver-and-element-api.html>`_).
32
33
34 Visit Google website
35 ====================
36
37 Visit any website using the ``browser.visit`` method. Let's go to Google search page:
38
39 .. highlight:: python
40
41 ::
42
43 browser.visit('http://google.com')
44
45
46 Input search text
47 =================
48
49 After a page is loaded, you can perform actions, such as clicking, filling text input, checking radio and checkbox. Let's fill Google's search field with ``splinter - python acceptance testing for web applications``:
50
51 .. highlight:: python
52
53 ::
54
55 browser.fill('q', 'splinter - python acceptance testing for web applications')
56
57 Press the search button
58 =======================
59
60 Tell Splinter which button should be pressed. A button - or any other element - can be identified using its css, xpath, id, tag or name.
61
62 In order to find Google's search button, do:
63
64 .. highlight:: python
65
66 ::
67
68 button = browser.find_by_name('btnG')
69
70 Note that this ``btnG`` was found looking at Google's page source code.
71
72 With the button in hands, we can then press it:
73
74 .. highlight:: python
75
76 ::
77
78 button.click()
79
80
81 Note: Both steps presented above could be joined in a single line, such as:
82
83 .. highlight:: python
84
85 ::
86
87 browser.find_by_name('btnG').click()
88
89
90 Find out that Splinter official website is in the search results
91 ================================================================
92
93 After pressing the button, you can check if Splinter official website is among the search responses. This can be done like this:
94
95 .. highlight:: python
96
97 ::
98
99 if browser.is_text_present('splinter.readthedocs.io'):
100 print "Yes, found it! :)"
101 else:
102 print "No, didn't find it :("
103
104
105 In this case, we are just printing something. You might use assertions, if you're writing tests.
106
107 Close the browser
108 =================
109
110 When you've finished testing, close your browser using ``browser.quit``:
111
112 .. highlight:: python
113
114 ::
115
116 browser.quit()
117
118 All together
119 ============
120
121 Finally, the source code will be:
122
123 .. highlight:: python
124
125 ::
126
127 from splinter import Browser
128
129 browser = Browser() # defaults to firefox
130 browser.visit('http://google.com')
131 browser.fill('q', 'splinter - python acceptance testing for web applications')
132 browser.find_by_name('btnG').click()
133
134 if browser.is_text_present('splinter.readthedocs.io'):
135 print "Yes, the official website was found!"
136 else:
137 print "No, it wasn't found... We need to improve our SEO techniques"
138
139 browser.quit()
140
0 +++++++++++++++++
1 Why use Splinter?
2 +++++++++++++++++
3
4 Splinter is an abstraction layer on top of existing browser automation tools
5 such as `Selenium`_ and `zope.testbrowser`_. It has a :doc:`high-level API
6 </api/index>` that makes it easy to write automated tests of web applications.
7
8 For example, to fill out a form field with Splinter::
9
10 browser.fill('username', 'janedoe')
11
12 In Selenium, the equivalent code would be::
13
14 elem = browser.find_element.by_name('username')
15 elem.send_keys('janedoe')
16
17 Because Splinter is an abstraction layer, it supports multiple web automation
18 backends. With Splinter, you can use the same test code to do browser-based
19 testing with Selenium as the backend and "headless" testing (no GUI) with
20 zope.testbrowser as the backend.
21
22 Splinter has drivers for browser-based testing on:
23
24 * :doc:`Chrome </drivers/chrome>`
25 * :doc:`Firefox </drivers/firefox>`
26 * :doc:`Browsers on remote machines </drivers/remote>`
27
28 For headless testing, Splinter has drivers for:
29
30 * :doc:`zope.testbrowser </drivers/zope.testbrowser>`
31 * :doc:`Django client </drivers/django>`
32 * :doc:`Flask client </drivers/flask>`
33
34
35 .. _Selenium: http://seleniumhq.org
36 .. _zope.testbrowser: https://launchpad.net/zope.testbrowser
0 #!/bin/bash
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 active_displays=$(ls /tmp/.X*-lock | sed 's/\/tmp\/\.X\(.*\)-lock/\1/')
7 last_active_display=$(echo "$active_displays" | sort -r | head -1)
8 new_display=$(echo "$last_active_display + 1" | bc)
9 xephyr_command="Xephyr :$new_display"
10
11 $xephyr_command 2> /dev/null &
12 DISPLAY=:$new_display make test
13
14 my_xephyr_pid=$(ps -A -o pid,cmd | grep Xephyr | \
15 sed -n 's/[[:space:]]*\([0-9]*\) Xephyr :'"$new_display"'$/\1/p;')
16 kill -9 $my_xephyr_pid
Binary diff not shown
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 """
7 This snippet show how to "test" a Facebook feature: the creation of an event.
8
9 It creates an event by going to http://www.facebook.com, login and navigate to "Create an event" page.
10 """
11
12 import os
13 import unittest
14 import time
15 from splinter import Browser
16
17
18 class FacebookEventsTestCase(unittest.TestCase):
19 @classmethod
20 def setUpClass(cls):
21 cls.browser = Browser("firefox")
22
23 @classmethod
24 def tearDownClass(cls):
25 cls.browser.quit()
26
27 def do_login_if_need(self, username, password):
28 if self.browser.is_element_present_by_css("div.menu_login_container"):
29 self.browser.fill("email", username)
30 self.browser.fill("pass", password)
31 self.browser.find_by_css(
32 'div.menu_login_container input[type="submit"]'
33 ).first.click()
34 assert self.browser.is_element_present_by_css("li#navAccount")
35
36 def test_create_event(self):
37 "Should be able to create an event"
38 # Open home and login
39 self.browser.visit("http://www.facebook.com")
40 self.do_login_if_need(username="user", password="pass")
41
42 # Go to events page
43 self.browser.find_by_css("li#navItem_events a").first.click()
44
45 # Click on "Create an event button"
46 self.browser.find_by_css("div.uiHeaderTop a.uiButton").first.click()
47 time.sleep(1)
48
49 # Uploading the picture
50 picture_path = os.path.join(
51 os.path.abspath(os.path.dirname(__file__)), "img", "turtles.jpg"
52 )
53 self.browser.find_by_css("div.eventEditUpload a.uiButton").first.click()
54
55 if not self.browser.is_element_present_by_css(
56 "iframe#upload_pic_frame", wait_time=10
57 ):
58 self.fail("The upload pic iframe didn't appear :(")
59
60 with self.browser.get_iframe("upload_pic_frame") as frame:
61 frame.attach_file("pic", picture_path)
62 time.sleep(10)
63
64 # Filling the form
65 self.browser.fill("event_startIntlDisplay", "5/21/2011")
66 self.browser.select("start_time_min", "480")
67 self.browser.fill("name", "Splinter sprint")
68 self.browser.fill("location", "Rio de Janeiro, Brazil")
69 self.browser.fill(
70 "desc", "For more info, check out the #cobratem channel on freenode!"
71 )
72
73 self.browser.find_by_css('label.uiButton input[type="submit"]').first.click()
74 time.sleep(1)
75
76 # Checking if the event was created and we were redirect to its page
77 title = self.browser.find_by_css("h1 span").first.text
78 assert title == "Splinter sprint", title
0 #!/usr/bin/env python
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import unittest
7 from splinter import Browser
8
9
10 class TestGoogleSearch(unittest.TestCase):
11 @classmethod
12 def setUpClass(cls):
13 cls.browser = Browser()
14
15 @classmethod
16 def tearDownClass(cls):
17 cls.browser.quit()
18
19 def test_visiting_google_com_returns_a_page_with_Google_in_title(self):
20 self.browser.visit("http://www.google.com/")
21 self.assertIn("Google", self.browser.title)
22
23 def test_filling_Splinter_in_the_search_box_returns_Splinter_website(self):
24 self.browser.visit("http://www.google.com/")
25 self.browser.fill("q", "Splinter")
26 search_button = self.browser.find_by_name("btnG").first
27 while not search_button.visible:
28 # waits for the JavaScript to put the button on the page
29 pass
30 search_button.click()
31 self.assertTrue(self.browser.is_text_present("splinter.readthedocs.org"))
32
33
34 unittest.main()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5 from setuptools import setup, find_packages
6 import codecs
7
8
9 README = codecs.open("README.rst", encoding="utf-8").read()
10
11 setup(
12 name="splinter",
13 version="0.13.0",
14 url="https://github.com/cobrateam/splinter",
15 description="browser abstraction for web acceptance testing",
16 long_description=README,
17 author="CobraTeam",
18 author_email="[email protected]",
19 classifiers=[
20 "Programming Language :: Python :: 2",
21 "Programming Language :: Python :: 3",
22 ]
23 + [("Programming Language :: Python :: %s" % x) for x in "2.7 3.3 3.4 3.5 3.6 3.7 3.8".split()],
24 packages=find_packages(exclude=["docs", "tests", "samples"]),
25 include_package_data=True,
26 install_requires=["selenium>=3.141.0", "six"],
27 extras_require={
28 "zope.testbrowser": ["zope.testbrowser>=5.2.4", "lxml>=4.2.4", "cssselect"],
29 "django": ["Django>=1.7.11;python_version<'3.0'", "Django>=2.0.6;python_version>'3.3'", "lxml>=2.3.6", "cssselect", "six"],
30 "flask": ["Flask>=1.0.2", "lxml>=2.3.6", "cssselect"],
31 },
32 tests_require=["coverage", "flask"],
33 )
0 # Copyright 2016 splinter authors. All rights reserved.
1 # Use of this source code is governed by a BSD-style
2 # license that can be found in the LICENSE file.
3
4 from splinter.browser import Browser # NOQA
5
6
7 __version__ = "0.13.0"
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import sys
7
8 try:
9 from httplib import HTTPException
10 except ImportError:
11 from http.client import HTTPException
12
13 from urllib3.exceptions import MaxRetryError
14
15 from selenium.common.exceptions import WebDriverException
16
17 from splinter.driver.webdriver.firefox import WebDriver as FirefoxWebDriver
18 from splinter.driver.webdriver.remote import WebDriver as RemoteWebDriver
19 from splinter.driver.webdriver.chrome import WebDriver as ChromeWebDriver
20 from splinter.exceptions import DriverNotFoundError
21
22
23 _DRIVERS = {
24 "firefox": FirefoxWebDriver,
25 "remote": RemoteWebDriver,
26 "chrome": ChromeWebDriver,
27 }
28
29 if sys.version_info[0] <= 2:
30 try:
31 from splinter.driver.zopetestbrowser import ZopeTestBrowser
32
33 _DRIVERS["zope.testbrowser"] = ZopeTestBrowser
34 except ImportError:
35 pass
36
37 try:
38 import django # noqa
39 from splinter.driver.djangoclient import DjangoClient
40
41 _DRIVERS["django"] = DjangoClient
42 except ImportError:
43 pass
44
45 try:
46 import flask # noqa
47 from splinter.driver.flaskclient import FlaskClient
48
49 _DRIVERS["flask"] = FlaskClient
50 except ImportError:
51 pass
52
53
54 def get_driver(driver, retry_count=3, *args, **kwargs):
55 """Try to instantiate the driver.
56
57 Common selenium errors are caught and a retry attempt occurs.
58 This can mitigate issues running on Remote WebDriver.
59
60 """
61 for _ in range(retry_count):
62 try:
63 return driver(*args, **kwargs)
64 except (IOError, HTTPException, WebDriverException, MaxRetryError) as e:
65 pass
66
67 raise e
68
69
70 def Browser(driver_name="firefox", retry_count=3, *args, **kwargs):
71 """
72 Returns a driver instance for the given name.
73
74 When working with ``firefox``, it's possible to provide a profile name
75 and a list of extensions.
76
77 If you don't provide any driver_name, then ``firefox`` will be used.
78
79 If there is no driver registered with the provided ``driver_name``, this
80 function will raise a :class:`splinter.exceptions.DriverNotFoundError`
81 exception.
82 """
83
84 try:
85 driver = _DRIVERS[driver_name]
86 except KeyError:
87 raise DriverNotFoundError("No driver for %s" % driver_name)
88
89 return get_driver(driver, *args, **kwargs)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from splinter.meta import InheritedDocs
7
8
9 class CookieManagerAPI(InheritedDocs("_CookieManagerAPI", (object,), {})):
10 """
11 An API that specifies how a splinter driver deals with cookies.
12
13 You can add cookies using the :meth:`add <CookieManagerAPI.add>` method,
14 and remove one or all cookies using
15 the :meth:`delete <CookieManagerAPI.delete>` method.
16
17 A CookieManager acts like a ``dict``, so you can access the value of a
18 cookie through the [] operator, passing the cookie identifier:
19
20 >>> cookie_manager.add({'name': 'Tony'})
21 >>> assert cookie_manager['name'] == 'Tony'
22 """
23
24 def add(self, cookies):
25 """
26 Adds a cookie.
27
28 The ``cookie`` parameter is a ``dict`` where each key is an identifier
29 for the cookie value (like any ``dict``).
30
31 Example of use:
32
33 >>> cookie_manager.add({'name': 'Tony'})
34 """
35 raise NotImplementedError
36
37 def delete(self, *cookies):
38 """
39 Deletes one or more cookies. You can pass all the cookies identifier
40 that you want to delete.
41
42 If none identifier is provided, all cookies are deleted.
43
44 Examples:
45
46 >>> cookie_manager.delete() # deletes all cookies
47 >>> cookie_manager.delete('name', 'birthday',
48 'favorite_color') # deletes these three cookies
49 >>> cookie_manager.delete('name') # deletes one cookie
50 """
51 raise NotImplementedError
52
53 def all(self, verbose=False):
54 """
55 Returns all of the cookies.
56
57 **Note:** If you're using any webdriver and want more info about
58 the cookie, set the `verbose` parameter to `True` (in other
59 drivers, it won't make any difference). In this case, this method
60 will return a list of dicts, each with one cookie's info.
61
62 Examples:
63
64 >>> cookie_manager.add({'name': 'Tony'})
65 >>> cookie_manager.all()
66 [{'name': 'Tony'}]
67 """
68 raise NotImplementedError
69
70 def __getitem__(self, item):
71 raise NotImplementedError
72
73 def __eq__(self, other_object):
74 raise NotImplementedError
0 # -*- coding: utf-8 -*-:
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 """
7 This module contains the basic API for splinter drivers and elements.
8 """
9
10 from splinter.meta import InheritedDocs
11
12
13 class DriverAPI(InheritedDocs("_DriverAPI", (object,), {})):
14 """
15 Basic driver API class.
16 """
17
18 driver_name = None
19
20 @property
21 def title(self):
22 """
23 Title of current page.
24 """
25 raise NotImplementedError(
26 "%s doesn't support access to the title." % self.driver_name
27 )
28
29 def __enter__(self):
30 """
31 Context manager to use the browser safely.
32 """
33 raise NotImplementedError(
34 "%s doesn't support use by 'with' statement." % self.driver_name
35 )
36
37 def __exit__(self):
38 """
39 Context manager to use the browser safely.
40 """
41 raise NotImplementedError(
42 "%s doesn't support use by 'with' statement." % self.driver_name
43 )
44
45 @property
46 def html(self):
47 """
48 Source of current page.
49 """
50 raise NotImplementedError(
51 "%s doesn't support access to the html." % self.driver_name
52 )
53
54 @property
55 def url(self):
56 """
57 URL of current page.
58 """
59 raise NotImplementedError(
60 "%s doesn't support access to the url." % self.driver_name
61 )
62
63 def visit(self, url):
64 """
65 Visits a given URL.
66
67 The ``url`` parameter is a string.
68 """
69 raise NotImplementedError("%s doesn't visit any url." % self.driver_name)
70
71 def back(self):
72 """
73 Back to the last URL in the browsing history.
74
75 If there is no URL to back, this method does nothing.
76 """
77 raise NotImplementedError(
78 "%s doesn't support moving back in history." % self.driver_name
79 )
80
81 def forward(self):
82 """
83 Forward to the next URL in the browsing history.
84
85 If there is no URL to forward, this method does nothing.
86 """
87 raise NotImplementedError(
88 "%s doesn't support moving forward in history." % self.driver_name
89 )
90
91 def reload(self):
92 """
93 Revisits the current URL
94 """
95 raise NotImplementedError(
96 "%s doesn't support reloading the page." % self.driver_name
97 )
98
99 def get_alert(self):
100 """
101 Changes the context for working with alerts and prompts.
102
103 For more details, check the :doc:`docs about iframes, alerts and prompts </iframes-and-alerts>`
104 """
105 raise NotImplementedError("%s doesn't support alerts." % self.driver_name)
106
107 def get_iframe(self, name):
108 """
109 Changes the context for working with iframes.
110
111 For more details, check the :doc:`docs about iframes, alerts and prompts </iframes-and-alerts>`
112 """
113 raise NotImplementedError("%s doesn't support frames." % self.driver_name)
114
115 def execute_script(self, script, *args):
116 """
117 Executes a given JavaScript in the browser.
118
119 e.g.: ::
120 >>> browser.execute_script('document.getElementById("body").innerHTML = "<p>Hello world!</p>"')
121 """
122 raise NotImplementedError(
123 "%s doesn't support execution of arbitrary JavaScript." % self.driver_name
124 )
125
126 def evaluate_script(self, script, *args):
127 """
128 Similar to :meth:`execute_script <DriverAPI.execute_script>` method.
129
130 Executes javascript in the browser and returns the value of the expression.
131
132 e.g.: ::
133 >>> assert 4 == browser.evaluate_script('2 + 2')
134 """
135 raise NotImplementedError(
136 "%s doesn't support evaluation of arbitrary JavaScript." % self.driver_name
137 )
138
139 def find_by_css(self, css_selector):
140 """
141 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`,
142 using a CSS selector to query the current page content.
143 """
144 raise NotImplementedError(
145 "%s doesn't support finding elements by css selector." % self.driver_name
146 )
147
148 def find_by_xpath(self, xpath):
149 """
150 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`,
151 using a xpath selector to query the current page content.
152 """
153 raise NotImplementedError(
154 "%s doesn't support finding elements by xpath selector." % self.driver_name
155 )
156
157 def find_by_name(self, name):
158 """
159 Finds elements in current page by their name.
160
161 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`.
162 """
163 raise NotImplementedError(
164 "%s doesn't support finding elements by name." % self.driver_name
165 )
166
167 def find_by_id(self, id):
168 """
169 Finds an element in current page by its id.
170
171 Even when only one element is find, this method returns an instance of
172 :class:`ElementList <splinter.element_list.ElementList>`
173 """
174 raise NotImplementedError(
175 "%s doesn't support finding elements by id." % self.driver_name
176 )
177
178 def find_by_value(self, value):
179 """
180 Finds elements in current page by their value.
181
182 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
183 """
184 raise NotImplementedError(
185 "%s doesn't support finding elements by value." % self.driver_name
186 )
187
188 def find_by_text(self, text):
189 """
190 Finds elements in current page by their text.
191
192 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
193 """
194 raise NotImplementedError(
195 "%s doesn't support finding elements by text." % self.driver_name
196 )
197
198 def find_by_tag(self, tag):
199 """
200 Find all elements of a given tag in current page.
201
202 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
203 """
204 raise NotImplementedError(
205 "%s doesn't support finding elements by tag." % self.driver_name
206 )
207
208 def find_link_by_href(self, href):
209 """
210 Find all elements of a given tag in current page.
211
212 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
213 """
214 raise NotImplementedError(
215 "%s doesn't support finding links by href." % self.driver_name
216 )
217
218 def find_link_by_partial_href(self, partial_href):
219 """
220 Find links by looking for a partial ``str`` in their href attribute.
221
222 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
223 """
224 raise NotImplementedError(
225 "%s doesn't support finding links by partial href." % self.driver_name
226 )
227
228 def find_link_by_text(self, text):
229 """
230 Find links querying for their text.
231
232 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
233 """
234 raise NotImplementedError(
235 "%s doesn't support finding links by text." % self.driver_name
236 )
237
238 def find_link_by_partial_text(self, partial_text):
239 """
240 Find links by looking for a partial ``str`` in their text.
241
242 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
243 """
244 raise NotImplementedError(
245 "%s doesn't support finding links by partial text." % self.driver_name
246 )
247
248 def find_option_by_value(self, value):
249 """
250 Finds ``<option>`` elements by their value.
251
252 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
253 """
254 raise NotImplementedError(
255 "%s doesn't support finding options by value." % self.driver_name
256 )
257
258 def find_option_by_text(self, text):
259 """
260 Finds ``<option>`` elements by their text.
261
262 Returns an instance of :class:`ElementList <splinter.element_list.ElementList>`
263 """
264 raise NotImplementedError(
265 "%s doesn't support finding options by text." % self.driver_name
266 )
267
268 def is_text_present(self, text, wait_time=None):
269 """
270 Searchs for ``text`` in the browser and wait the seconds specified in ``wait_time``.
271
272 Returns True if finds a match for the ``text`` and False if not.
273 """
274 raise NotImplementedError(
275 "%s doesn't support checking if some text is present in the html. "
276 % self.driver_name
277 )
278
279 def type(self, name, value, slowly=False):
280 """
281 Types the ``value`` in the field identified by ``name``.
282
283 It's useful to test javascript events like keyPress, keyUp, keyDown, etc.
284
285 If ``slowly`` is True, this function returns an iterator which will type one character per iteration.
286 """
287 raise NotImplementedError(
288 "%s doesn't support typing on fields by name." % self.driver_name
289 )
290
291 def fill(self, name, value):
292 """
293 Fill the field identified by ``name`` with the content specified by ``value``.
294 """
295 raise NotImplementedError(
296 "%s doesn't support filling fields by name." % self.driver_name
297 )
298
299 def fill_form(self, field_values, form_id=None, name=None):
300 """
301 Fill the fields identified by ``name`` with the content specified by ``value`` in a dict.
302
303 Currently, fill_form supports the following fields: text, password, textarea, checkbox,
304 radio and select.
305
306 Checkboxes should be specified as a boolean in the dict.
307 """
308 raise NotImplementedError(
309 "%s doesn't support filling forms with a dict." % self.driver_name
310 )
311
312 def choose(self, name, value):
313 """
314 Chooses a value in a radio buttons group.
315
316 Suppose you have the two radio buttons in a page, with the name ``gender`` and values 'F' and 'M'.
317 If you use the ``choose`` method the following way:
318
319 >>> browser.choose('gender', 'F')
320
321 Then you're choosing the female gender.
322 """
323 raise NotImplementedError(
324 "%s doesn't support choosing options." % self.driver_name
325 )
326
327 def check(self, name):
328 """
329 Checks a checkbox by its name.
330
331 Example:
332
333 >>> browser.check("agree-with-terms")
334
335 If you call ``browser.check`` n times, the checkbox keeps checked, it never get unchecked.
336
337 To unckech a checkbox, take a look in the :meth:`uncheck <DriverAPI.uncheck>` method.
338 """
339 raise NotImplementedError(
340 "%s doesn't support checking elements." % self.driver_name
341 )
342
343 def uncheck(self, name):
344 """
345 Unchecks a checkbox by its name.
346
347 Example:
348
349 >>> browser.uncheck("send-me-emails")
350
351 If you call ``brower.uncheck`` n times, the checkbox keeps unchecked, it never get checked.
352
353 To check a checkbox, take a look in the :meth:`check <DriverAPI.check>` method.
354 """
355 raise NotImplementedError(
356 "%s doesn't support unchecking elements." % self.driver_name
357 )
358
359 def select(self, name, value):
360 """
361 Selects an ``<option>`` element in an ``<select>`` element using the ``name`` of the ``<select>`` and
362 the ``value`` of the ``<option>``.
363
364 Example:
365
366 >>> browser.select("state", "NY")
367 """
368 raise NotImplementedError(
369 "%s doesn't support selecting options in 'select' element."
370 % self.driver_name
371 )
372
373 def click_link_by_href(self, href):
374 """
375 Clicks in a link by its ``href`` attribute.
376 """
377 return self.find_link_by_href(href).first.click()
378
379 def click_link_by_partial_href(self, partial_href):
380 """
381 Clicks in a link by looking for partial content of ``href`` attribute.
382 """
383 return self.find_link_by_partial_href(partial_href).first.click()
384
385 def click_link_by_text(self, text):
386 """
387 Clicks in a link by its ``text``.
388 """
389 return self.find_link_by_text(text).first.click()
390
391 def click_link_by_partial_text(self, partial_text):
392 """
393 Clicks in a link by partial content of its text.
394 """
395 return self.find_link_by_partial_text(partial_text).first.click()
396
397 def click_link_by_id(self, id):
398 """
399 Clicks in a link by id.
400 """
401 return self.find_by_id(id).first.click()
402
403 def quit(self):
404 """
405 Quits the browser, closing its windows (if it has one).
406
407 After quit the browser, you can't use it anymore.
408 """
409 raise NotImplementedError("%s doesn't support quit" % self.driver_name)
410
411 def is_element_present_by_css(self, css_selector, wait_time=None):
412 """
413 Verify if the element is present in the current page by css, and wait the specified
414 time in ``wait_time``.
415
416 Returns True if the element is present and False if is not present.
417 """
418 raise NotImplementedError(
419 "%s doesn't support verifying if element is present by css"
420 % self.driver_name
421 )
422
423 def is_element_not_present_by_css(self, css_selector, wait_time=None):
424 """
425 Verify if the element is not present in the current page by css, and wait the specified time
426 in ``wait_time``.
427
428 Returns True if the element is not present and False if is present.
429 """
430 raise NotImplementedError(
431 "%s doesn't support verifying if element is not present by css"
432 % self.driver_name
433 )
434
435 def is_element_present_by_xpath(self, xpath, wait_time=None):
436 """
437 Verify if the element is present in the current page by xpath, and wait the specified
438 time in ``wait_time``.
439
440 Returns True if the element is present and False if is not present.
441 """
442 raise NotImplementedError(
443 "%s doesn't support verifying if element is present by xpath"
444 % self.driver_name
445 )
446
447 def is_element_not_present_by_xpath(self, xpath, wait_time=None):
448 """
449 Verify if the element is not present in the current page by xpath,
450 and wait the specified time in ``wait_time``.
451
452 Returns True if the element is not present and False if is present.
453 """
454 raise NotImplementedError(
455 "%s doesn't support verifying if element is not present by xpath"
456 % self.driver_name
457 )
458
459 def is_element_present_by_tag(self, tag, wait_time=None):
460 """
461 Verify if the element is present in the current page by tag,
462 and wait the specified time in ``wait_time``.
463
464 Returns True if the element is present and False if is not present.
465 """
466 raise NotImplementedError(
467 "%s doesn't support verifying if element is present by tag"
468 % self.driver_name
469 )
470
471 def is_element_not_present_by_tag(self, tag, wait_time=None):
472 """
473 Verify if the element is not present in the current page by tag,
474 and wait the specified time in ``wait_time``.
475
476 Returns True if the element is not present and False if is present.
477 """
478 raise NotImplementedError(
479 "%s doesn't support verifying if element is not present by tag"
480 % self.driver_name
481 )
482
483 def is_element_present_by_name(self, name, wait_time=None):
484 """
485 Verify if the element is present in the current page by name,
486 and wait the specified time in ``wait_time``.
487
488 Returns True if the element is present and False if is not present.
489 """
490 raise NotImplementedError(
491 "%s doesn't support verifying if element is present by name"
492 % self.driver_name
493 )
494
495 def is_element_not_present_by_name(self, name, wait_time=None):
496 """
497 Verify if the element is not present in the current page by name,
498 and wait the specified time in ``wait_time``.
499
500 Returns True if the element is not present and False if is present.
501 """
502 raise NotImplementedError(
503 "%s doesn't support verifying if element is not present by name"
504 % self.driver_name
505 )
506
507 def is_element_present_by_value(self, value, wait_time=None):
508 """
509 Verify if the element is present in the current page by value,
510 and wait the specified time in ``wait_time``.
511
512 Returns True if the element is present and False if is not present.
513 """
514 raise NotImplementedError(
515 "%s doesn't support verifying if element is present by value"
516 % self.driver_name
517 )
518
519 def is_element_not_present_by_value(self, value, wait_time=None):
520 """
521 Verify if the element is not present in the current page by value,
522 and wait the specified time in ``wait_time``.
523
524 Returns True if the element is not present and False if is present.
525 """
526 raise NotImplementedError(
527 "%s doesn't support verifying if element is not present by value"
528 % self.driver_name
529 )
530
531 def is_element_present_by_text(self, text, wait_time=None):
532 """
533 Verify if the element is present in the current page by text,
534 and wait the specified time in ``wait_time``.
535
536 Returns True if the element is present and False if is not present.
537 """
538 raise NotImplementedError(
539 "%s doesn't support verifying if element is present by text"
540 % self.driver_name
541 )
542
543 def is_element_not_present_by_text(self, text, wait_time=None):
544 """
545 Verify if the element is not present in the current page by text,
546 and wait the specified time in ``wait_time``.
547
548 Returns True if the element is not present and False if is present.
549 """
550 raise NotImplementedError(
551 "%s doesn't support verifying if element is not present by text"
552 % self.driver_name
553 )
554
555 def is_element_present_by_id(self, id, wait_time=None):
556 """
557 Verify if the element is present in the current page by id,
558 and wait the specified time in ``wait_time``.
559
560 Returns True if the element is present and False if is not present.
561 """
562 raise NotImplementedError(
563 "%s doesn't support verifying if element is present by id"
564 % self.driver_name
565 )
566
567 def is_element_not_present_by_id(self, id, wait_time=None):
568 """
569 Verify if the element is present in the current page by id,
570 and wait the specified time in ``wait_time``.
571
572 Returns True if the element is not present and False if is present.
573 """
574 raise NotImplementedError(
575 "%s doesn't support verifying if element is not present by id"
576 % self.driver_name
577 )
578
579 def screenshot(self, name=None, suffix=None):
580 """
581 Takes a screenshot of the current page and saves it locally.
582 """
583 raise NotImplementedError(
584 "%s doesn't support taking screenshots." % self.driver_name
585 )
586
587 @property
588 def cookies(self):
589 """
590 A :class:`CookieManager <splinter.cookie_manager.CookieManagerAPI>` instance.
591
592 For more details, check the :doc:`cookies manipulation section </cookies>`.
593 """
594 raise NotImplementedError(
595 "%s doesn't support cookies manipulation" % self.driver_name
596 )
597
598
599 class ElementAPI(InheritedDocs("_ElementAPI", (object,), {})):
600 """
601 Basic element API class.
602
603 Any element in the page can be represented as an instance of ``ElementAPI``.
604
605 Once you have an instance, you can easily access attributes like a ``dict``:
606
607 >>> element = browser.find_by_id("link-logo").first
608 >>> assert element['href'] == 'https://splinter.readthedocs.io'
609
610 You can also interact with the instance using the methods and properties listed below.
611 """
612
613 def _get_value(self):
614 raise NotImplementedError
615
616 def _set_value(self, value):
617 raise NotImplementedError
618
619 #: Value of the element, usually a form element
620 value = property(_get_value, _set_value)
621
622 @property
623 def text(self):
624 """
625 String of all of the text within the element. HTML tags are stripped.
626 """
627 raise NotImplementedError
628
629 def click(self):
630 """
631 Clicks in the element.
632 """
633 raise NotImplementedError
634
635 def check(self):
636 """
637 Checks the element, if it's "checkable" (e.g.: a checkbox).
638
639 If the element is already checked, this method does nothing. For unchecking
640 elements, take a loot in the :meth:`uncheck <ElementAPI.uncheck>` method.
641 """
642 raise NotImplementedError
643
644 def uncheck(self):
645 """
646 Unchecks the element, if it's "checkable" (e.g.: a checkbox).
647
648 If the element is already unchecked, this method does nothing. For checking
649 elements, take a loot in the :meth:`check <ElementAPI.check>` method.
650 """
651 raise NotImplementedError
652
653 @property
654 def checked(self):
655 """
656 Boolean property that says if the element is checked or not.
657
658 Example:
659
660 >>> element.check()
661 >>> assert element.checked
662 >>> element.uncheck()
663 >>> assert not element.checked
664 """
665 raise NotImplementedError
666
667 @property
668 def visible(self):
669 """
670 Boolean property that says if the element is visible or hidden in the current page.
671 """
672 raise NotImplementedError
673
674 def has_class(self, class_name):
675 """
676 Indicates whether the element has the given class.
677 """
678 raise NotImplementedError
679
680 def mouse_over(self):
681 """
682 Puts the mouse over the element.
683 """
684 raise NotImplementedError
685
686 def mouse_out(self):
687 """
688 Moves the mouse away from the element.
689 """
690 raise NotImplementedError
691
692 def clear(self):
693 """
694 Reset the field value.
695 """
696 raise NotImplementedError
697
698 def fill(self, value):
699 """
700 Fill the field with the content specified by ``value``.
701 """
702 raise NotImplementedError
703
704 def type(self, value, slowly=False):
705 """
706 Types the ``value`` in the field.
707
708 It's useful to test javascript events like keyPress, keyUp, keyDown, etc.
709
710 If ``slowly`` is True, this function returns an iterator which will type one character per iteration.
711
712 Also supports selenium.
713
714 Example:
715 >>> from selenium.webdriver.common.keys import Keys
716 >>> ElementAPI.type(Keys.RETURN)
717
718 """
719 raise NotImplementedError
720
721 def select(self, value, slowly=False):
722 """
723 Selects an ``<option>`` element in the element using the ``value`` of the ``<option>``.
724
725 Example:
726
727 >>> element.select("NY")
728 """
729 raise NotImplementedError
730
731 def screenshot(self):
732 """
733 Take screenshot of the element.
734 """
735 raise NotImplementedError
736
737 def __getitem__(self, attribute):
738 raise NotImplementedError
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from __future__ import with_statement
7 import six
8 from six.moves.urllib import parse
9
10 from splinter.cookie_manager import CookieManagerAPI
11 from splinter.request_handler.status_code import StatusCode
12
13 from .lxmldriver import LxmlDriver
14
15
16 class CookieManager(CookieManagerAPI):
17 def __init__(self, browser_cookies):
18 self._cookies = browser_cookies
19
20 def add(self, cookies):
21 if isinstance(cookies, list):
22 for cookie in cookies:
23 for key, value in cookie.items():
24 self._cookies[key] = value
25 return
26 for key, value in cookies.items():
27 self._cookies[key] = value
28
29 def delete(self, *cookies):
30 if cookies:
31 for cookie in cookies:
32 try:
33 del self._cookies[cookie]
34 except KeyError:
35 pass
36 else:
37 self._cookies.clear()
38
39 def all(self, verbose=False):
40 cookies = {}
41 for key, value in self._cookies.items():
42 cookies[key] = value
43 return cookies
44
45 def __getitem__(self, item):
46 return self._cookies[item].value
47
48 def __contains__(self, key):
49 return key in self._cookies
50
51 def __eq__(self, other_object):
52 if isinstance(other_object, dict):
53 cookies_dict = dict(
54 [(key, morsel.value) for key, morsel in self._cookies.items()]
55 )
56 return cookies_dict == other_object
57 return False
58
59
60 class DjangoClient(LxmlDriver):
61
62 driver_name = "django"
63
64 def __init__(self, user_agent=None, wait_time=2, **kwargs):
65 from django.test.client import Client
66
67 self._custom_headers = kwargs.pop("custom_headers", {})
68
69 client_kwargs = {}
70 for key, value in six.iteritems(kwargs):
71 if key.startswith("client_"):
72 client_kwargs[key.replace("client_", "")] = value
73
74 self._browser = Client(**client_kwargs)
75 self._user_agent = user_agent
76 self._cookie_manager = CookieManager(self._browser.cookies)
77 super(DjangoClient, self).__init__(wait_time=wait_time)
78
79 def __enter__(self):
80 return self
81
82 def __exit__(self, exc_type, exc_value, traceback):
83 pass
84
85 def _post_load(self):
86 self._forms = {}
87 try:
88 del self._html
89 except AttributeError:
90 pass
91 self.status_code = StatusCode(self._response.status_code, "")
92
93 def _handle_redirect_chain(self):
94 if self._response.redirect_chain:
95 for redirect_url, redirect_code in self._response.redirect_chain:
96 self._last_urls.append(redirect_url)
97 self._url = self._last_urls[-1]
98
99 def _set_extra_params(self, url):
100 extra = {}
101 components = parse.urlparse(url)
102 if components.hostname:
103 extra.update({"SERVER_NAME": components.hostname})
104 if components.port:
105 extra.update({"SERVER_PORT": components.port})
106 if self._user_agent:
107 extra.update({"User-Agent": self._user_agent})
108 if self._custom_headers:
109 extra.update(self._custom_headers)
110 return extra
111
112 def _do_method(self, method, url, data=None):
113 self._url = url
114 extra = self._set_extra_params(url)
115 func_method = getattr(self._browser, method.lower())
116 self._response = func_method(url, data=data, follow=True, **extra)
117 self._last_urls.append(url)
118 self._handle_redirect_chain()
119 self._post_load()
120
121 def submit_data(self, form):
122 return super(DjangoClient, self).submit(form).content
123
124 @property
125 def html(self):
126 return self._response.content.decode(self._response._charset or "utf-8")
0 class ElementPresentMixIn(object):
1 """ Support is_element_present_by_* methods for non-javascript drivers. """
2
3 def is_element_present_by_css(self, css_selector, wait_time=None):
4 return bool(self.find_by_css(css_selector))
5
6 def is_element_not_present_by_css(self, css_selector, wait_time=None):
7 return not self.is_element_present_by_css(css_selector, wait_time)
8
9 def is_element_present_by_xpath(self, xpath, wait_time=None):
10 return bool(self.find_by_xpath(xpath))
11
12 def is_element_not_present_by_xpath(self, xpath, wait_time=None):
13 return not self.is_element_present_by_xpath(xpath, wait_time)
14
15 def is_element_present_by_tag(self, tag, wait_time=None):
16 return bool(self.find_by_tag(tag))
17
18 def is_element_not_present_by_tag(self, tag, wait_time=None):
19 return not self.is_element_present_by_tag(tag, wait_time)
20
21 def is_element_present_by_name(self, name, wait_time=None):
22 return bool(self.find_by_name(name))
23
24 def is_element_not_present_by_name(self, name, wait_time=None):
25 return not self.is_element_present_by_name(name, wait_time)
26
27 def is_element_present_by_value(self, value, wait_time=None):
28 return bool(self.find_by_value(value))
29
30 def is_element_not_present_by_value(self, value, wait_time=None):
31 return not self.is_element_present_by_value(value, wait_time)
32
33 def is_element_present_by_text(self, text, wait_time=None):
34 return bool(self.find_by_text(text))
35
36 def is_element_not_present_by_text(self, text, wait_time=None):
37 return not self.is_element_present_by_text(text, wait_time)
38
39 def is_element_present_by_id(self, id, wait_time=None):
40 return bool(self.find_by_id(id))
41
42 def is_element_not_present_by_id(self, id, wait_time=None):
43 return not self.is_element_present_by_id(id, wait_time)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2014 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from __future__ import with_statement
7
8 try:
9 from urllib.parse import parse_qs, urlparse, urlencode, urlunparse
10 except:
11 from urlparse import parse_qs, urlparse, urlunparse
12 from urllib import urlencode
13
14 from splinter.cookie_manager import CookieManagerAPI
15 from splinter.request_handler.status_code import StatusCode
16
17 from .lxmldriver import LxmlDriver
18
19
20 class CookieManager(CookieManagerAPI):
21 def __init__(self, browser_cookies):
22 self._cookies = browser_cookies
23
24 def add(self, cookies):
25 if isinstance(cookies, list):
26 for cookie in cookies:
27 for key, value in cookie.items():
28 self._cookies.set_cookie("localhost", key, value)
29 return
30 for key, value in cookies.items():
31 self._cookies.set_cookie("localhost", key, value)
32
33 def delete(self, *cookies):
34 if cookies:
35 for cookie in cookies:
36 try:
37 self._cookies.delete_cookie("localhost", cookie)
38 except KeyError:
39 pass
40 else:
41 self._cookies.cookie_jar.clear()
42
43 def all(self, verbose=False):
44 cookies = {}
45 for cookie in self._cookies.cookie_jar:
46 cookies[cookie.name] = cookie.value
47 return cookies
48
49 def __getitem__(self, item):
50 cookies = dict([(c.name, c) for c in self._cookies.cookie_jar])
51 return cookies[item].value
52
53 def __contains__(self, key):
54 for cookie in self._cookies.cookie_jar:
55 if cookie.name == key:
56 return True
57 return False
58
59 def __eq__(self, other_object):
60 if isinstance(other_object, dict):
61 cookies_dict = dict([(c.name, c.value) for c in self._cookies.cookie_jar])
62 return cookies_dict == other_object
63 return False
64
65
66 class FlaskClient(LxmlDriver):
67
68 driver_name = "flask"
69
70 def __init__(self, app, user_agent=None, wait_time=2, custom_headers=None):
71 app.config["TESTING"] = True
72 self._browser = app.test_client()
73 self._cookie_manager = CookieManager(self._browser)
74 self._custom_headers = custom_headers if custom_headers else {}
75 super(FlaskClient, self).__init__(wait_time=wait_time)
76
77 def __enter__(self):
78 return self
79
80 def __exit__(self, exc_type, exc_value, traceback):
81 pass
82
83 def _post_load(self):
84 self._forms = {}
85 try:
86 del self._html
87 except AttributeError:
88 pass
89 self.status_code = StatusCode(self._response.status_code, "")
90
91 def _do_method(self, method, url, data=None):
92
93 # Set the initial URL and client/HTTP method
94 self._url = url
95 func_method = getattr(self._browser, method.lower())
96
97 # Continue to make requests until a non 30X response is recieved
98 while True:
99 self._last_urls.append(url)
100
101 # If we're making a GET request set the data against the URL as a
102 # query.
103 if method.lower() == "get":
104
105 # Parse the existing URL and it's query
106 url_parts = urlparse(url)
107 url_params = parse_qs(url_parts.query)
108
109 # Update any existing query dictionary with the `data` argument
110 url_params.update(data or {})
111 url_parts = url_parts._replace(query=urlencode(url_params, doseq=True))
112
113 # Rebuild the URL
114 url = urlunparse(url_parts)
115
116 # As the `data` argument will be passed as a keyword argument to
117 # the `func_method` we set it `None` to prevent it populating
118 # `flask.request.form` on `GET` requests.
119 data = None
120
121 # Call the flask client
122 self._response = func_method(
123 url, headers=self._custom_headers, data=data, follow_redirects=False
124 )
125
126 # Implement more standard `302`/`303` behaviour
127 if self._response.status_code in (302, 303):
128 func_method = getattr(self._browser, "get")
129
130 # If the response was not in the `30X` range we're done
131 if self._response.status_code not in (301, 302, 303, 305, 307):
132 break
133
134 # If the response was in the `30X` range get next URL to request
135 url = self._response.headers["Location"]
136
137 self._url = self._last_urls[-1]
138 self._post_load()
139
140 def submit_data(self, form):
141 return super(FlaskClient, self).submit(form).data
142
143 @property
144 def html(self):
145 return self._response.get_data(as_text=True)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2014 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from __future__ import with_statement
7 import os.path
8 import re
9 import time
10 import sys
11
12 import lxml.etree
13 import lxml.html
14 from lxml.cssselect import CSSSelector
15 from splinter.driver import DriverAPI, ElementAPI
16 from splinter.driver.element_present import ElementPresentMixIn
17 from splinter.driver.xpath_utils import _concat_xpath_from_str
18 from splinter.element_list import ElementList
19 from splinter.exceptions import ElementDoesNotExist
20
21
22 class LxmlDriver(ElementPresentMixIn, DriverAPI):
23 _response = ""
24 _url = ""
25
26 def __init__(self, user_agent=None, wait_time=2):
27 self.wait_time = wait_time
28 self._history = []
29 self._last_urls = []
30 self._forms = {}
31
32 def __enter__(self):
33 return self
34
35 def __exit__(self, exc_type, exc_value, traceback):
36 pass
37
38 def _do_method(self, action, url, data=None):
39 raise NotImplementedError(
40 "%s doesn't support doing http methods." % self.driver_name
41 )
42
43 def visit(self, url):
44 self._do_method("get", url)
45
46 def serialize(self, form):
47 data = {}
48
49 for key in form.inputs.keys():
50 input = form.inputs[key]
51 if getattr(input, "type", "") == "submit":
52 try:
53 form.remove(input)
54 # Issue 595: throws ValueError: Element not child of this node
55 except ValueError:
56 pass
57
58 for k, v in form.fields.items():
59 if v is None:
60 continue
61
62 if isinstance(v, lxml.html.MultipleSelectOptions):
63 data[k] = [val for val in v]
64 else:
65 data[k] = v
66
67 for key in form.inputs.keys():
68 input = form.inputs[key]
69 if getattr(input, "type", "") == "file" and key in data:
70 data[key] = open(data[key], "rb")
71
72 return data
73
74 def submit(self, form):
75 method = form.attrib.get("method", "get").lower()
76 action = form.attrib.get("action", "")
77 if action.strip() != ".":
78 url = os.path.join(self._url, action)
79 else:
80 url = self._url
81 self._url = url
82 data = self.serialize(form)
83
84 self._do_method(method, url, data=data)
85 return self._response
86
87 def submit_data(self, form):
88 raise NotImplementedError(
89 "%s doesn't support submitting then getting the data." % self.driver_name
90 )
91
92 def back(self):
93 self._last_urls.insert(0, self.url)
94 self.visit(self._last_urls[1])
95
96 def forward(self):
97 try:
98 self.visit(self._last_urls.pop())
99 except IndexError:
100 pass
101
102 def reload(self):
103 self.visit(self._url)
104
105 def quit(self):
106 pass
107
108 @property
109 def htmltree(self):
110 try:
111 return self._html
112 except AttributeError:
113 self._html = lxml.html.fromstring(self.html)
114 return self._html
115
116 @property
117 def title(self):
118 html = self.htmltree
119 return html.xpath("//title")[0].text_content().strip()
120
121 @property
122 def html(self):
123 raise NotImplementedError(
124 "%s doesn't support getting the html of the response." % self.driver_name
125 )
126
127 @property
128 def url(self):
129 return self._url
130
131 def find_option_by_value(self, value):
132 html = self.htmltree
133 element = html.xpath('//option[@value="%s"]' % value)[0]
134 control = LxmlControlElement(element.getparent(), self)
135 return ElementList(
136 [LxmlOptionElement(element, control)], find_by="value", query=value
137 )
138
139 def find_option_by_text(self, text):
140 html = self.htmltree
141 element = html.xpath('//option[normalize-space(text())="%s"]' % text)[0]
142 control = LxmlControlElement(element.getparent(), self)
143 return ElementList(
144 [LxmlOptionElement(element, control)], find_by="text", query=text
145 )
146
147 def find_by_css(self, selector):
148 xpath = CSSSelector(selector).path
149 return self.find_by_xpath(
150 xpath, original_find="css", original_selector=selector
151 )
152
153 def find_by_xpath(self, xpath, original_find=None, original_selector=None):
154 html = self.htmltree
155
156 elements = []
157
158 for xpath_element in html.xpath(xpath):
159 if self._element_is_link(xpath_element):
160 return self._find_links_by_xpath(xpath)
161 elif self._element_is_control(xpath_element):
162 elements.append((LxmlControlElement, xpath_element))
163 else:
164 elements.append((LxmlElement, xpath_element))
165
166 find_by = original_find or "xpath"
167 query = original_selector or xpath
168
169 return ElementList(
170 [element_class(element, self) for element_class, element in elements],
171 find_by=find_by,
172 query=query,
173 )
174
175 def find_by_tag(self, tag):
176 return self.find_by_xpath(
177 "//%s" % tag, original_find="tag", original_selector=tag
178 )
179
180 def find_by_value(self, value):
181 return self.find_by_xpath(
182 '//*[@value="%s"]' % value, original_find="value", original_selector=value
183 )
184
185 def find_by_text(self, text):
186 xpath_str = _concat_xpath_from_str(text)
187 return self.find_by_xpath(
188 xpath_str,
189 original_find="text",
190 original_selector=text,
191 )
192
193 def find_by_id(self, id_value):
194 return self.find_by_xpath(
195 '//*[@id="%s"][1]' % id_value,
196 original_find="id",
197 original_selector=id_value,
198 )
199
200 def find_by_name(self, name):
201 html = self.htmltree
202
203 xpath = '//*[@name="%s"]' % name
204 elements = []
205
206 for xpath_element in html.xpath(xpath):
207 elements.append(xpath_element)
208
209 find_by = "name"
210 query = xpath
211
212 return ElementList(
213 [LxmlControlElement(element, self) for element in elements],
214 find_by=find_by,
215 query=query,
216 )
217
218 def find_link_by_text(self, text):
219 return self._find_links_by_xpath("//a[text()='%s']" % text)
220
221 def find_link_by_href(self, href):
222 return self._find_links_by_xpath("//a[@href='%s']" % href)
223
224 def find_link_by_partial_href(self, partial_href):
225 return self._find_links_by_xpath("//a[contains(@href, '%s')]" % partial_href)
226
227 def find_link_by_partial_text(self, partial_text):
228 return self._find_links_by_xpath(
229 "//a[contains(normalize-space(.), '%s')]" % partial_text
230 )
231
232 def fill(self, name, value):
233 self.find_by_name(name=name).first.fill(value)
234
235 def fill_form(self, field_values, form_id=None, name=None):
236 form = None
237
238 if name is not None:
239 form = self.find_by_name(name)
240 if form_id is not None:
241 form = self.find_by_id(form_id)
242
243 for name, value in field_values.items():
244 if form:
245 element = form.find_by_name(name)
246 control = element.first._element
247 else:
248 element = self.find_by_name(name)
249 control = element.first._control
250 control_type = control.get("type")
251 if control_type == "checkbox":
252 if value:
253 control.value = value # control.options
254 else:
255 control.value = []
256 elif control_type == "radio":
257 control.value = (
258 value
259 ) # [option for option in control.options if option == value]
260 elif control_type == "select":
261 if isinstance(value, list):
262 control.value = value
263 else:
264 control.value = [value]
265 else:
266 # text, textarea, password, tel
267 control.value = value
268
269 def choose(self, name, value):
270 self.find_by_name(name).first._control.value = value
271
272 def check(self, name):
273 control = self.find_by_name(name).first._control
274 control.value = ["checked"]
275
276 def uncheck(self, name):
277 control = self.find_by_name(name).first._control
278 control.value = []
279
280 def attach_file(self, name, file_path):
281 control = self.find_by_name(name).first._control
282 control.value = file_path
283
284 def _find_links_by_xpath(self, xpath):
285 html = self.htmltree
286 links = html.xpath(xpath)
287 return ElementList(
288 [LxmlLinkElement(link, self) for link in links],
289 find_by="xpath",
290 query=xpath,
291 )
292
293 def select(self, name, value):
294 self.find_by_name(name).first._control.value = value
295
296 def is_text_present(self, text, wait_time=None):
297 wait_time = wait_time or self.wait_time
298 end_time = time.time() + wait_time
299
300 while time.time() < end_time:
301 if self._is_text_present(text):
302 return True
303 return False
304
305 def _is_text_present(self, text):
306 try:
307 body = self.find_by_tag("body").first
308 return text in body.text
309 except ElementDoesNotExist:
310 # This exception will be thrown if the body tag isn't present
311 # This has occasionally been observed. Assume that the
312 # page isn't fully loaded yet
313 return False
314
315 def is_text_not_present(self, text, wait_time=None):
316 wait_time = wait_time or self.wait_time
317 end_time = time.time() + wait_time
318
319 while time.time() < end_time:
320 if not self._is_text_present(text):
321 return True
322 return False
323
324 def _element_is_link(self, element):
325 return element.tag == "a"
326
327 def _element_is_control(self, element):
328 return element.tag in ["button", "input", "textarea"]
329
330 @property
331 def cookies(self):
332 return self._cookie_manager
333
334
335 class LxmlElement(ElementAPI):
336 def __init__(self, element, parent):
337 self._element = element
338 self.parent = parent
339
340 def __getitem__(self, attr):
341 return self._element.attrib[attr]
342
343 def find_by_css(self, selector):
344 elements = self._element.cssselect(selector)
345 return ElementList([self.__class__(element, self) for element in elements])
346
347 def find_by_xpath(self, selector):
348 elements = self._element.xpath(selector)
349 return ElementList([self.__class__(element, self) for element in elements])
350
351 def find_by_name(self, name):
352 elements = self._element.cssselect('[name="%s"]' % name)
353 return ElementList([self.__class__(element, self) for element in elements])
354
355 def find_by_tag(self, name):
356 elements = self._element.cssselect(name)
357 return ElementList([self.__class__(element, self) for element in elements])
358
359 def find_by_value(self, value):
360 elements = self._element.cssselect('[value="%s"]' % value)
361 return ElementList([self.__class__(element, self) for element in elements])
362
363 def find_by_text(self, text):
364 # Add a period to the xpath to search only inside the parent.
365 xpath_str = '.{}'.format(_concat_xpath_from_str(text))
366 return self.find_by_xpath(xpath_str)
367
368 def find_by_id(self, id):
369 elements = self._element.cssselect("#%s" % id)
370 return ElementList([self.__class__(element, self) for element in elements])
371
372 @property
373 def value(self):
374 return self._element.text_content()
375
376 @property
377 def text(self):
378 return self.value
379
380 @property
381 def outer_html(self):
382 return lxml.html.tostring(self._element, encoding="unicode").strip()
383
384 @property
385 def html(self):
386 return re.match(r"^<[^<>]+>(.*)</[^<>]+>$", self.outer_html, re.MULTILINE | re.DOTALL).group(1)
387
388 def has_class(self, class_name):
389 return len(self._element.find_class(class_name)) > 0
390
391
392 class LxmlLinkElement(LxmlElement):
393 def __init__(self, element, parent):
394 super(LxmlLinkElement, self).__init__(element, parent)
395 self._browser = parent
396
397 def __getitem__(self, attr):
398 return super(LxmlLinkElement, self).__getitem__(attr)
399
400 def click(self):
401 return self._browser.visit(self["href"])
402
403
404 class LxmlControlElement(LxmlElement):
405 def __init__(self, control, parent):
406 self._control = control
407 self.parent = parent
408
409 def __getitem__(self, attr):
410 return self._control.attrib[attr]
411
412 @property
413 def value(self):
414 return self._control.value
415
416 @property
417 def checked(self):
418 return bool(self._control.value)
419
420 def click(self):
421 parent_form = self._get_parent_form()
422
423 if self._control.get("type") == "submit":
424 name = self._control.get("name")
425
426 if name:
427 value = self._control.get("value", "")
428 parent_form.append(
429 lxml.html.Element("input", name=name, value=value, type="hidden")
430 )
431
432 return self.parent.submit_data(parent_form)
433
434 def fill(self, value):
435 parent_form = self._get_parent_form()
436 if sys.version_info[0] > 2:
437 parent_form.fields[self["name"]] = value
438 else:
439 if not isinstance(value, unicode):
440 value = value.decode("utf-8")
441 parent_form.fields[self["name"]] = value
442
443 def select(self, value):
444 self._control.value = value
445
446 def _get_parent_form(self):
447 parent_form = next(self._control.iterancestors("form"))
448 return self.parent._forms.setdefault(parent_form._name(), parent_form)
449
450
451 class LxmlOptionElement(LxmlElement):
452 def __init__(self, control, parent):
453 self._control = control
454 self.parent = parent
455
456 def __getitem__(self, attr):
457 return self._control.attrib[attr]
458
459 @property
460 def text(self):
461 return self._control.text
462
463 @property
464 def value(self):
465 return self._control.attrib["value"]
466
467 @property
468 def selected(self):
469 return self.parent.value == self.value
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import io
7 import os
8 import re
9 import sys
10 import tempfile
11 import time
12 from contextlib import contextmanager
13 import warnings
14
15 from selenium.webdriver.common.alert import Alert
16 from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, WebDriverException, StaleElementReferenceException, TimeoutException
17 from selenium.webdriver.common.action_chains import ActionChains
18 from selenium.webdriver.support import expected_conditions as EC
19 from selenium.webdriver.support.ui import WebDriverWait
20
21 from six import BytesIO
22
23 from splinter.driver import DriverAPI, ElementAPI
24 from splinter.driver.xpath_utils import _concat_xpath_from_str
25 from splinter.element_list import ElementList
26
27
28 if sys.version_info[0] > 2:
29 _meth_func = "__func__"
30 _func_name = "__name__"
31 else:
32 _meth_func = "im_func"
33 _func_name = "func_name"
34
35 # Patch contextmanager onto Selenium's Alert
36 def alert_enter(self):
37 return self
38
39 def alert_exit(self, type, value, traceback):
40 pass
41
42 Alert.__enter__ = alert_enter
43 Alert.__exit__ = alert_exit
44 Alert.fill_with = Alert.send_keys
45
46
47 class switch_window:
48 def __init__(self, browser, window_handle):
49 self.browser = browser
50 self.window_handle = window_handle
51
52 def __enter__(self):
53 self.current_window_handle = self.browser.driver.current_window_handle
54 self.browser.driver.switch_to.window(self.window_handle)
55
56 def __exit__(self, type, value, traceback):
57 if self.current_window_handle in self.browser.driver.window_handles:
58 self.browser.driver.switch_to.window(self.current_window_handle)
59
60
61 class Window(object):
62 """ A class representing a browser window """
63
64 def __init__(self, browser, name):
65 self._browser = browser
66 self.name = name
67
68 @property
69 def title(self):
70 """ The title of this window """
71 with switch_window(self._browser, self.name):
72 return self._browser.title
73
74 @property
75 def url(self):
76 """ The url of this window """
77 with switch_window(self._browser, self.name):
78 return self._browser.url
79
80 @property
81 def index(self):
82 """ The index of this window in browser.windows """
83 return self._browser.driver.window_handles.index(self.name)
84
85 @property
86 def prev(self):
87 """ Return the previous window """
88 prev_index = self.index - 1
89 prev_handle = self._browser.driver.window_handles[prev_index]
90 return Window(self._browser, prev_handle)
91
92 @property
93 def next(self):
94 """ Return the next window """
95 next_index = (self.index + 1) % len(self._browser.driver.window_handles)
96 next_handle = self._browser.driver.window_handles[next_index]
97 return Window(self._browser, next_handle)
98
99 def is_current():
100 doc = "Whether this window is currently the browser's active window."
101
102 def fget(self):
103 return self._browser.driver.current_window_handle == self.name
104
105 def fset(self, value):
106 if value is True:
107 self._browser.driver.switch_to.window(self.name)
108 else:
109 raise TypeError("can only set to True")
110
111 return locals()
112
113 is_current = property(**is_current())
114
115 def close(self):
116 """ Close this window. If this window is active, switch to previous window """
117 target = self.prev if (self.is_current and self.prev != self) else None
118
119 with switch_window(self._browser, self.name):
120 self._browser.driver.close()
121
122 if target is not None:
123 target.is_current = True
124
125 def close_others(self):
126 self.is_current = True
127 for window in self._browser.windows:
128 if window != self:
129 window.close()
130
131 def __eq__(self, other):
132 return self._browser == other._browser and self.name == other.name
133
134 def __ne__(self, other):
135 return not self.__eq__(other)
136
137 def __repr__(self):
138 return "<Window %s: %s>" % (self.name, self.url)
139
140
141 class Windows(object):
142
143 """ A class representing all open browser windows """
144
145 def __init__(self, browser):
146 self._browser = browser
147
148 def __len__(self):
149 return len(self._browser.driver.window_handles)
150
151 def __getitem__(self, key):
152 window_handles = self._browser.driver.window_handles
153 try:
154 return Window(self._browser, window_handles[key])
155 except TypeError:
156 if key not in window_handles:
157 raise KeyError(key)
158 return Window(self._browser, key)
159
160 def current():
161 doc = "The currently active window"
162
163 def fget(self):
164 current_handle = self._browser.driver.current_window_handle
165 return Window(self._browser, current_handle) if current_handle else None
166
167 def fset(self, value):
168 self._browser.driver.switch_to.window(value.name)
169
170 return locals()
171
172 current = property(**current())
173
174 def __repr__(self):
175 return str(
176 [
177 Window(self._browser, handle)
178 for handle in self._browser.driver.window_handles
179 ]
180 )
181
182
183 class FindLinks(object):
184 """Contains methods for finding links in a parent.
185
186 Accessed through the browser object or an element via the links attribute.
187
188 Example:
189
190 browser.links.find_by_href('foobar')
191
192 """
193 def __init__(self, parent):
194 self.parent = parent
195
196 def find_by_href(self, href):
197 return self.parent.find_by_xpath(
198 '//a[@href="{}"]'.format(href),
199 original_find="link by href",
200 original_query=href,
201 )
202
203 def find_by_partial_href(self, partial_href):
204 return self.parent.find_by_xpath(
205 '//a[contains(@href, "{}")]'.format(partial_href),
206 original_find="link by partial href",
207 original_query=partial_href,
208 )
209
210 def find_by_partial_text(self, partial_text):
211 return self.parent.find_by_xpath(
212 '//a[contains(normalize-space(.), "{}")]'.format(partial_text),
213 original_find="link by partial text",
214 original_query=partial_text,
215 )
216
217 def find_by_text(self, text):
218 return self.parent.find_by_xpath(
219 '//a[text()="{}"]'.format(text),
220 original_find="link by text",
221 original_query=text,
222 )
223
224
225 def _find(self, finder, selector):
226 """Search for elements. Returns a list of results.
227
228 Arguments:
229 finder: The function to use for the element search.
230 selector: The search query.
231
232 Returns:
233 list
234
235 """
236 elements = None
237 elem_list = []
238
239 try:
240 elements = finder(selector)
241 if not isinstance(elements, list):
242 elements = [elements]
243
244 except (
245 NoSuchElementException,
246 StaleElementReferenceException,
247 ):
248 # This exception is sometimes thrown if the page changes
249 # quickly
250 pass
251
252 if elements:
253 elem_list = [self.element_class(element, self) for element in elements]
254
255 return elem_list
256
257 def find_by(self, finder, selector, original_find=None, original_query=None, wait_time=None):
258 """Wrapper for finding elements.
259
260 Must be attached to a class.
261
262 Returns:
263 ElementList
264
265 """
266 elem_list = []
267
268 func_name = getattr(getattr(finder, _meth_func), _func_name)
269 find_by = original_find or func_name[func_name.rfind("_by_") + 4 :]
270 query = original_query or selector
271
272 # Zero second wait time means only check once
273 if wait_time == 0:
274 elem_list = _find(self, finder, selector)
275 else:
276 wait_time = wait_time or self.wait_time
277 end_time = time.time() + wait_time
278
279 while time.time() < end_time:
280 elem_list = _find(self, finder, selector)
281
282 if elem_list:
283 break
284
285 return ElementList(elem_list, find_by=find_by, query=query)
286
287
288 class BaseWebDriver(DriverAPI):
289 driver = None
290 find_by = find_by
291
292 def __init__(self, wait_time=2):
293 self.wait_time = wait_time
294 self.ori_window_size = None
295
296 self.links = FindLinks(self)
297
298 def __enter__(self):
299 return self
300
301 def __exit__(self, exc_type, exc_value, traceback):
302 self.quit()
303
304 @property
305 def title(self):
306 return self.driver.title
307
308 @property
309 def html(self):
310 return self.driver.page_source
311
312 @property
313 def url(self):
314 return self.driver.current_url
315
316 @property
317 def status_code(self):
318 raise NotImplementedError
319
320 def visit(self, url):
321 self.driver.get(url)
322
323 def back(self):
324 self.driver.back()
325
326 def forward(self):
327 self.driver.forward()
328
329 def reload(self):
330 self.driver.refresh()
331
332 def execute_script(self, script, *args):
333 return self.driver.execute_script(script, *args)
334
335 def evaluate_script(self, script, *args):
336 return self.driver.execute_script("return %s" % script, *args)
337
338 def is_element_visible(self, finder, selector, wait_time=None):
339 wait_time = wait_time or self.wait_time
340 end_time = time.time() + wait_time
341
342 while time.time() < end_time:
343 if finder(selector, wait_time=wait_time) and finder(selector, wait_time=wait_time).visible:
344 return True
345 return False
346
347 def is_element_not_visible(self, finder, selector, wait_time=None):
348 wait_time = wait_time or self.wait_time
349 end_time = time.time() + wait_time
350
351 while time.time() < end_time:
352 element = finder(selector, wait_time=0)
353 if not element or (element and not element.visible):
354 return True
355 return False
356
357 def is_element_visible_by_css(self, css_selector, wait_time=None):
358 return self.is_element_visible(self.find_by_css, css_selector, wait_time)
359
360 def is_element_not_visible_by_css(self, css_selector, wait_time=None):
361 return self.is_element_not_visible(self.find_by_css, css_selector, wait_time)
362
363 def is_element_visible_by_xpath(self, xpath, wait_time=None):
364 return self.is_element_visible(self.find_by_xpath, xpath, wait_time)
365
366 def is_element_not_visible_by_xpath(self, xpath, wait_time=None):
367 return self.is_element_not_visible(self.find_by_xpath, xpath, wait_time)
368
369 def is_element_present(self, finder, selector, wait_time=None):
370 wait_time = wait_time or self.wait_time
371 end_time = time.time() + wait_time
372
373 while time.time() < end_time:
374 if finder(selector, wait_time=wait_time):
375 return True
376 return False
377
378 def is_element_not_present(self, finder, selector, wait_time=None):
379 wait_time = wait_time or self.wait_time
380 end_time = time.time() + wait_time
381
382 while time.time() < end_time:
383 if not finder(selector, wait_time=0):
384 return True
385 return False
386
387 def is_element_present_by_css(self, css_selector, wait_time=None):
388 return self.is_element_present(self.find_by_css, css_selector, wait_time)
389
390 def is_element_not_present_by_css(self, css_selector, wait_time=None):
391 return self.is_element_not_present(self.find_by_css, css_selector, wait_time)
392
393 def is_element_present_by_xpath(self, xpath, wait_time=None):
394 return self.is_element_present(self.find_by_xpath, xpath, wait_time)
395
396 def is_element_not_present_by_xpath(self, xpath, wait_time=None):
397 return self.is_element_not_present(self.find_by_xpath, xpath, wait_time)
398
399 def is_element_present_by_tag(self, tag, wait_time=None):
400 return self.is_element_present(self.find_by_tag, tag, wait_time)
401
402 def is_element_not_present_by_tag(self, tag, wait_time=None):
403 return self.is_element_not_present(self.find_by_tag, tag, wait_time)
404
405 def is_element_present_by_name(self, name, wait_time=None):
406 return self.is_element_present(self.find_by_name, name, wait_time)
407
408 def is_element_not_present_by_name(self, name, wait_time=None):
409 return self.is_element_not_present(self.find_by_name, name, wait_time)
410
411 def is_element_present_by_value(self, value, wait_time=None):
412 return self.is_element_present(self.find_by_value, value, wait_time)
413
414 def is_element_not_present_by_value(self, value, wait_time=None):
415 return self.is_element_not_present(self.find_by_value, value, wait_time)
416
417 def is_element_present_by_text(self, text, wait_time=None):
418 return self.is_element_present(self.find_by_text, text, wait_time)
419
420 def is_element_not_present_by_text(self, text, wait_time=None):
421 return self.is_element_not_present(self.find_by_text, text, wait_time)
422
423 def is_element_present_by_id(self, id, wait_time=None):
424 return self.is_element_present(self.find_by_id, id, wait_time)
425
426 def is_element_not_present_by_id(self, id, wait_time=None):
427 return self.is_element_not_present(self.find_by_id, id, wait_time)
428
429 def get_alert(self, wait_time=None):
430 wait_time = wait_time or self.wait_time
431
432 try:
433 alert = WebDriverWait(self.driver, wait_time).until(EC.alert_is_present())
434 return alert
435 except TimeoutException:
436 return None
437
438 def is_text_present(self, text, wait_time=None):
439 wait_time = wait_time or self.wait_time
440 end_time = time.time() + wait_time
441
442 while time.time() < end_time:
443 try:
444 self.driver.find_element_by_tag_name("body").text.index(text)
445 return True
446 except ValueError:
447 pass
448 except NoSuchElementException:
449 # This exception will be thrown if the body tag isn't present
450 # This has occasionally been observed. Assume that the
451 # page isn't fully loaded yet
452 pass
453 except StaleElementReferenceException:
454 # This exception is sometimes thrown if the page changes
455 # quickly
456 pass
457 return False
458
459 def is_text_not_present(self, text, wait_time=None):
460 wait_time = wait_time or self.wait_time
461 end_time = time.time() + wait_time
462
463 while time.time() < end_time:
464 try:
465 self.driver.find_element_by_tag_name("body").text.index(text)
466 except ValueError:
467 return True
468 except NoSuchElementException:
469 # This exception will be thrown if the body tag isn't present
470 # This has occasionally been observed. Assume that the
471 # page isn't fully loaded yet
472 pass
473 except StaleElementReferenceException:
474 # This exception is sometimes thrown if the page changes
475 # quickly
476 pass
477 return False
478
479 @contextmanager
480 def get_iframe(self, frame_reference):
481
482 # If a WebDriverElement is provided, send the underlying element
483 if isinstance(frame_reference, WebDriverElement):
484 frame_reference = frame_reference._element
485
486 self.driver.switch_to.frame(frame_reference)
487
488 try:
489 yield self
490 finally:
491 self.driver.switch_to.frame(None)
492
493 def find_option_by_value(self, value):
494 return self.find_by_xpath(
495 '//option[@value="%s"]' % value,
496 original_find="option by value",
497 original_query=value,
498 )
499
500 def find_option_by_text(self, text):
501 return self.find_by_xpath(
502 '//option[normalize-space(text())="%s"]' % text,
503 original_find="option by text",
504 original_query=text,
505 )
506
507 def find_link_by_href(self, href):
508 warnings.warn(
509 'browser.find_link_by_href is deprecated.'
510 ' Use browser.links.find_by_href instead.',
511 FutureWarning,
512 )
513 return self.links.find_by_href(href)
514
515 def find_link_by_partial_href(self, partial_href):
516 warnings.warn(
517 'browser.find_link_by_partial_href is deprecated.'
518 ' Use browser.links.find_by_partial_href instead.',
519 FutureWarning,
520 )
521 return self.links.find_by_partial_href(partial_href)
522
523 def find_link_by_partial_text(self, partial_text):
524 warnings.warn(
525 'browser.find_link_by_partial_text is deprecated.'
526 ' Use browser.links.find_by_partial_text instead.',
527 FutureWarning,
528 )
529 return self.links.find_by_partial_text(partial_text)
530
531 def find_link_by_text(self, text):
532 warnings.warn(
533 'browser.find_link_by_text is deprecated.'
534 ' Use browser.links.find_by_text instead.',
535 FutureWarning,
536 )
537 return self.links.find_by_text(text)
538
539 def find_by_css(self, css_selector, wait_time=None):
540 return self.find_by(
541 self.driver.find_elements_by_css_selector,
542 css_selector,
543 original_find="css",
544 original_query=css_selector,
545 wait_time=wait_time,
546 )
547
548 def find_by_xpath(self, xpath, original_find=None, original_query=None, wait_time=None):
549 original_find = original_find or "xpath"
550 original_query = original_query or xpath
551 return self.find_by(
552 self.driver.find_elements_by_xpath,
553 xpath,
554 original_find=original_find,
555 original_query=original_query,
556 wait_time=wait_time,
557 )
558
559 def find_by_name(self, name, wait_time=None):
560 return self.find_by(
561 self.driver.find_elements_by_name,
562 name,
563 wait_time=wait_time,
564 )
565
566 def find_by_tag(self, tag, wait_time=None):
567 return self.find_by(
568 self.driver.find_elements_by_tag_name,
569 tag,
570 wait_time=wait_time,
571 )
572
573 def find_by_value(self, value, wait_time=None):
574 return self.find_by_xpath(
575 '//*[@value="{}"]'.format(value),
576 original_find="value",
577 original_query=value,
578 wait_time=wait_time,
579 )
580
581 def find_by_text(self, text=None, wait_time=None):
582 xpath_str = _concat_xpath_from_str(text)
583 return self.find_by_xpath(
584 xpath_str,
585 original_find="text",
586 original_query=text,
587 wait_time=wait_time,
588 )
589
590 def find_by_id(self, id, wait_time=None):
591 return self.find_by(
592 self.driver.find_element_by_id,
593 id,
594 wait_time=wait_time,
595 )
596
597 def fill(self, name, value):
598 field = self.find_by_name(name).first
599 field.value = value
600
601 attach_file = fill
602
603 def fill_form(self, field_values, form_id=None, name=None):
604 form = None
605
606 if name is not None:
607 form = self.find_by_name(name)
608 if form_id is not None:
609 form = self.find_by_id(form_id)
610
611 for name, value in field_values.items():
612 if form:
613 elements = form.find_by_name(name)
614 else:
615 elements = self.find_by_name(name)
616 element = elements.first
617 if (
618 element["type"] in ["text", "password", "tel"]
619 or element.tag_name == "textarea"
620 ):
621 element.value = value
622 elif element["type"] == "checkbox":
623 if value:
624 element.check()
625 else:
626 element.uncheck()
627 elif element["type"] == "radio":
628 for field in elements:
629 if field.value == value:
630 field.click()
631 elif element._element.tag_name == "select":
632 element.select(value)
633 else:
634 element.value = value
635
636 def type(self, name, value, slowly=False):
637 element = self.find_by_name(name).first._element
638 if slowly:
639 return TypeIterator(element, value)
640 element.send_keys(value)
641 return value
642
643 def choose(self, name, value):
644 fields = self.find_by_name(name)
645 for field in fields:
646 if field.value == value:
647 field.click()
648
649 def check(self, name):
650 self.find_by_name(name).first.check()
651
652 def uncheck(self, name):
653 self.find_by_name(name).first.uncheck()
654
655 def screenshot(self, name="", suffix=".png", full=False):
656
657 name = name or ""
658
659 (fd, filename) = tempfile.mkstemp(prefix=name, suffix=suffix)
660 # don't hold the file
661 os.close(fd)
662
663 if full:
664 self.full_screen()
665
666 self.driver.get_screenshot_as_file(filename)
667 self.recover_screen()
668 return filename
669
670 def select(self, name, value):
671 self.find_by_xpath(
672 '//select[@name="%s"]//option[@value="%s"]' % (name, value)
673 ).first._element.click()
674
675 def select_by_text(self, name, text):
676 self.find_by_xpath(
677 '//select[@name="%s"]/option[text()="%s"]' % (name, text)
678 ).first._element.click()
679
680 def quit(self):
681 try:
682 self.driver.quit()
683 except WebDriverException:
684 pass
685
686 def full_screen(self):
687 self.ori_window_size = self.driver.get_window_size()
688 width = self.driver.execute_script("return Math.max(document.body.scrollWidth, document.body.offsetWidth);")
689 height = self.driver.execute_script("return Math.max(document.body.scrollHeight, document.body.offsetHeight);")
690 self.driver.set_window_size(width, height)
691
692 def recover_screen(self):
693 if self.ori_window_size:
694 width = self.ori_window_size.get('width')
695 height = self.ori_window_size.get('height')
696 self.driver.set_window_size(width, height)
697 self.ori_window_size = None
698
699 def html_snapshot(self, name="", suffix=".html", encoding='utf-8'):
700 """Write the current html to a file."""
701 name = name or ""
702
703 (fd, filename) = tempfile.mkstemp(prefix=name, suffix=suffix)
704 # Don't hold the file
705 os.close(fd)
706
707 with io.open(filename, 'w', encoding=encoding) as f:
708 f.write(self.html)
709
710 return filename
711
712 @property
713 def cookies(self):
714 return self._cookie_manager
715
716 @property
717 def windows(self):
718 return Windows(self)
719
720
721 class TypeIterator(object):
722 def __init__(self, element, keys):
723 self._element = element
724 self._keys = keys
725
726 def __iter__(self):
727 for key in self._keys:
728 self._element.send_keys(key)
729 yield key
730
731
732 class WebDriverElement(ElementAPI):
733 find_by = find_by
734
735 def __init__(self, element, parent):
736 self._element = element
737 self.parent = parent
738
739 self.driver = self.parent.driver
740 self.wait_time = self.parent.wait_time
741 self.element_class = self.parent.element_class
742
743 self.links = FindLinks(self)
744
745 def _get_value(self):
746 return self["value"] or self._element.text
747
748 def _set_value(self, value):
749 if self._element.get_attribute("type") != "file":
750 self._element.clear()
751 self._element.send_keys(value)
752
753 value = property(_get_value, _set_value)
754
755 @property
756 def text(self):
757 return self._element.text
758
759 @property
760 def tag_name(self):
761 return self._element.tag_name
762
763 def clear(self):
764 if self._element.get_attribute("type") in [
765 "textarea",
766 "text",
767 "password",
768 "tel",
769 ]:
770 self._element.clear()
771
772 def fill(self, value):
773 self.value = value
774
775 def select(self, value=None, text=None):
776 finder = None
777 search_value = None
778
779 if text:
780 finder = 'text()'
781 search_value = text
782 elif value:
783 finder = '@value'
784 search_value = value
785
786 self.find_by_xpath(
787 './/option[{}="{}"]'.format(finder, search_value)
788 )._element.click()
789
790 def select_by_text(self, text):
791 self.select(text=text)
792
793 def type(self, value, slowly=False):
794 if slowly:
795 return TypeIterator(self._element, value)
796
797 self._element.send_keys(value)
798 return value
799
800 def click(self):
801 """Click an element.
802
803 If the element is not interactive due to being covered by another
804 element, the click will retry for self.parent.wait_time amount of
805 time.
806 """
807 end_time = time.time() + self.parent.wait_time
808 error = None
809 while time.time() < end_time:
810 try:
811 return self._element.click()
812 except(
813 ElementClickInterceptedException,
814 WebDriverException,
815 ) as e:
816 error = e
817
818 raise error
819
820 def check(self):
821 if not self.checked:
822 self.click()
823
824 def uncheck(self):
825 if self.checked:
826 self.click()
827
828 @property
829 def checked(self):
830 return self._element.is_selected()
831
832 selected = checked
833
834 @property
835 def visible(self):
836 return self._element.is_displayed()
837
838 @property
839 def html(self):
840 return self["innerHTML"]
841
842 @property
843 def outer_html(self):
844 return self["outerHTML"]
845
846 def find_by_css(self, selector, wait_time=None):
847 return self.find_by(
848 self._element.find_elements_by_css_selector,
849 selector,
850 original_find="css",
851 wait_time=wait_time,
852 )
853
854 def find_by_xpath(self, selector, wait_time=None):
855 return self.find_by(
856 self._element.find_elements_by_xpath,
857 selector,
858 original_find="xpath",
859 wait_time=wait_time,
860 )
861
862 def find_by_name(self, selector, wait_time=None):
863 return self.find_by(
864 self._element.find_elements_by_name,
865 selector,
866 original_find="name",
867 wait_time=wait_time,
868 )
869
870 def find_by_tag(self, selector, wait_time=None):
871 return self.find_by(
872 self._element.find_elements_by_tag_name,
873 selector,
874 original_find="tag",
875 wait_time=wait_time,
876 )
877
878 def find_by_value(self, value, wait_time=None):
879 selector = '[value="{}"]'.format(value)
880 return self.find_by(
881 self._element.find_elements_by_css_selector,
882 selector,
883 original_find="value",
884 original_query=value,
885 wait_time=wait_time,
886 )
887
888 def find_by_text(self, text, wait_time=None):
889 # Add a period to the xpath to search only inside the parent.
890 xpath_str = '.{}'.format(_concat_xpath_from_str(text))
891 return self.find_by(
892 self._element.find_elements_by_xpath,
893 xpath_str,
894 original_find="text",
895 original_query=text,
896 wait_time=wait_time,
897 )
898
899 def find_by_id(self, selector, wait_time=None):
900 return self.find_by(
901 self._element.find_elements_by_id,
902 selector,
903 original_find="id",
904 wait_time=wait_time,
905 )
906
907 def has_class(self, class_name):
908 return bool(
909 re.search(r"(?:^|\s)" + re.escape(class_name) + r"(?:$|\s)", self["class"])
910 )
911
912 def scroll_to(self):
913 """
914 Scroll to the current element.
915 """
916 self.driver.execute_script("arguments[0].scrollIntoView(true);", self._element)
917
918 def mouse_over(self):
919 """
920 Performs a mouse over the element.
921
922 """
923 self.scroll_to()
924 ActionChains(self.driver).move_to_element(self._element).perform()
925
926 def mouse_out(self):
927 """
928 Performs a mouse out the element.
929
930 """
931 self.scroll_to()
932 ActionChains(self.driver).move_to_element_with_offset(
933 self._element, -10, -10).click().perform()
934
935 def double_click(self):
936 """
937 Performs a double click in the element.
938
939 """
940 self.scroll_to()
941 ActionChains(self.driver).double_click(self._element).perform()
942
943 def right_click(self):
944 """
945 Performs a right click in the element.
946
947 """
948 self.scroll_to()
949 ActionChains(self.driver).context_click(self._element).perform()
950
951 def drag_and_drop(self, droppable):
952 """
953 Performs drag a element to another elmenet.
954
955 """
956 self.scroll_to()
957 ActionChains(self.driver).drag_and_drop(self._element, droppable._element).perform()
958
959 def screenshot(self, name='', suffix='.png', full=False):
960 name = name or ''
961
962 (fd, filename) = tempfile.mkstemp(prefix=name, suffix=suffix)
963 # don't hold the file
964 os.close(fd)
965
966 if full:
967 self.parent.full_screen()
968 target = self.screenshot_as_png()
969 self.parent.recover_screen()
970 target.save(filename)
971
972 return filename
973
974 def screenshot_as_png(self):
975 try:
976 from PIL import Image
977 except ImportError:
978 raise NotImplementedError('Element screenshot need the Pillow dependency. '
979 'Please use "pip install Pillow" install it.')
980
981 full_screen_png = self.driver.get_screenshot_as_png()
982
983 full_screen_bytes = BytesIO(full_screen_png)
984
985 im = Image.open(full_screen_bytes)
986 im_width, im_height = im.size[0], im.size[1]
987 window_size = self.driver.get_window_size()
988 window_width = window_size['width']
989
990 ratio = im_width * 1.0 / window_width
991 height_ratio = im_height / ratio
992
993 im = im.resize((int(window_width), int(height_ratio)))
994
995 location = self._element.location
996 x, y = location['x'], location['y']
997
998 pic_size = self._element.size
999 w, h = pic_size['width'], pic_size['height']
1000
1001 box = x, y, x + w, y + h
1002 box = [int(i) for i in box]
1003 target = im.crop(box)
1004
1005 return target
1006
1007 def __getitem__(self, attr):
1008 return self._element.get_attribute(attr)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from selenium.webdriver import Chrome
7 from selenium.webdriver.chrome.options import Options
8 from splinter.driver.webdriver import BaseWebDriver, WebDriverElement
9 from splinter.driver.webdriver.cookie_manager import CookieManager
10
11
12 class WebDriver(BaseWebDriver):
13
14 driver_name = "Chrome"
15
16 def __init__(
17 self,
18 options=None,
19 user_agent=None,
20 wait_time=2,
21 fullscreen=False,
22 incognito=False,
23 headless=False,
24 **kwargs
25 ):
26
27 options = Options() if options is None else options
28
29 if user_agent is not None:
30 options.add_argument("--user-agent=" + user_agent)
31
32 if incognito:
33 options.add_argument("--incognito")
34
35 if fullscreen:
36 options.add_argument("--kiosk")
37
38 if headless:
39 options.add_argument("--headless")
40 options.add_argument("--disable-gpu")
41
42 self.driver = Chrome(options=options, **kwargs)
43
44 self.element_class = WebDriverElement
45
46 self._cookie_manager = CookieManager(self.driver)
47
48 super(WebDriver, self).__init__(wait_time)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5 import sys
6
7 from splinter.cookie_manager import CookieManagerAPI
8
9 if sys.version_info[0] > 2:
10 from urllib.parse import urlparse
11 else:
12 from urlparse import urlparse # NOQA
13
14
15 class CookieManager(CookieManagerAPI):
16 def __init__(self, driver):
17 self.driver = driver
18
19 def add(self, cookies):
20 if isinstance(cookies, list):
21 for cookie in cookies:
22 for key, value in cookie.items():
23 self.driver.add_cookie({"name": key, "value": value})
24 return
25 for key, value in cookies.items():
26 self.driver.add_cookie({"name": key, "value": value})
27
28 def delete(self, *cookies):
29 if cookies:
30 for cookie in cookies:
31 self.driver.delete_cookie(cookie)
32 else:
33 self.driver.delete_all_cookies()
34
35 def all(self, verbose=False):
36 if not verbose:
37 cleaned_cookies = {}
38 cookies = self.driver.get_cookies()
39 for cookie in cookies:
40 if not cookie["domain"].startswith("."):
41 cookie_domain = cookie["domain"]
42 else:
43 cookie_domain = cookie["domain"][1:]
44
45 if cookie_domain in urlparse(self.driver.current_url).netloc:
46 cleaned_cookies[cookie["name"]] = cookie["value"]
47
48 return cleaned_cookies
49 return self.driver.get_cookies()
50
51 def __getitem__(self, item):
52 return self.driver.get_cookie(item)["value"]
53
54 def __contains__(self, key):
55 return self.driver.get_cookie(key) is not None
56
57 def __eq__(self, other_object):
58 cookies = {}
59 for cookie in self.driver.get_cookies():
60 cookies[cookie["name"]] = cookie["value"]
61
62 if isinstance(other_object, dict):
63 return dict(cookies) == other_object
64
65 return False
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5 import os
6 import six
7
8 from selenium.webdriver import DesiredCapabilities, Firefox
9 from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
10 from splinter.driver.webdriver import (
11 BaseWebDriver,
12 WebDriverElement as WebDriverElement,
13 )
14 from splinter.driver.webdriver.cookie_manager import CookieManager
15 from selenium.webdriver.common.keys import Keys
16 from selenium.webdriver.common.action_chains import ActionChains
17 from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
18 from selenium.webdriver.firefox.options import Options
19
20
21 class WebDriver(BaseWebDriver):
22
23 driver_name = "Firefox"
24
25 def __init__(
26 self,
27 profile=None,
28 extensions=None,
29 user_agent=None,
30 profile_preferences=None,
31 fullscreen=False,
32 wait_time=2,
33 timeout=90,
34 capabilities=None,
35 headless=False,
36 incognito=False,
37 **kwargs
38 ):
39
40 firefox_profile = FirefoxProfile(profile)
41 firefox_profile.set_preference("extensions.logging.enabled", False)
42 firefox_profile.set_preference("network.dns.disableIPv6", False)
43
44 firefox_capabilities = DesiredCapabilities().FIREFOX
45 firefox_capabilities["marionette"] = True
46
47 firefox_options = Options()
48
49 if capabilities:
50 for key, value in capabilities.items():
51 firefox_capabilities[key] = value
52
53 if user_agent is not None:
54 firefox_profile.set_preference("general.useragent.override", user_agent)
55
56 if profile_preferences:
57 for key, value in profile_preferences.items():
58 firefox_profile.set_preference(key, value)
59
60 if extensions:
61 for extension in extensions:
62 firefox_profile.add_extension(extension)
63
64 if headless:
65 os.environ.update({"MOZ_HEADLESS": "1"})
66 if 'firefox_binary' in kwargs:
67 if isinstance(kwargs['firefox_binary'], six.string_types):
68 binary = FirefoxBinary(kwargs['firefox_binary'])
69 else:
70 binary = kwargs['firefox_binary']
71 else:
72 binary = FirefoxBinary()
73 binary.add_command_line_options("-headless")
74 kwargs["firefox_binary"] = binary
75 else:
76 if "MOZ_HEADLESS" in os.environ:
77 del os.environ["MOZ_HEADLESS"]
78
79 if incognito:
80 firefox_options.add_argument("-private")
81
82 self.driver = Firefox(
83 firefox_profile,
84 capabilities=firefox_capabilities,
85 options=firefox_options,
86 timeout=timeout,
87 **kwargs
88 )
89
90 if fullscreen:
91 ActionChains(self.driver).send_keys(Keys.F11).perform()
92
93 self.element_class = WebDriverElement
94
95 self._cookie_manager = CookieManager(self.driver)
96
97 super(WebDriver, self).__init__(wait_time)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from selenium.webdriver import Remote
7 from selenium.webdriver.remote import remote_connection
8 from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
9 from splinter.driver.webdriver import (
10 BaseWebDriver,
11 WebDriverElement,
12 )
13 from splinter.driver.webdriver.cookie_manager import CookieManager
14 from splinter.driver.webdriver.remote_connection import patch_request
15
16 # MonkeyPatch RemoteConnection
17 remote_connection.RemoteConnection._request = patch_request
18
19
20 class WebDriver(BaseWebDriver):
21
22 driver_name = "remote"
23 # TODO: This constant belongs in selenium.webdriver.Remote
24 DEFAULT_URL = "http://127.0.0.1:4444/wd/hub"
25
26 def __init__(
27 self,
28 browser="firefox",
29 wait_time=2,
30 command_executor=DEFAULT_URL,
31 **kwargs
32 ):
33 browser_name = browser.upper()
34 # Handle case where user specifies IE with a space in it
35 if browser_name == "INTERNET EXPLORER":
36 browser_name = "INTERNETEXPLORER"
37
38 # If no desired capabilities specified, add default ones
39 caps = getattr(DesiredCapabilities, browser_name, {})
40 if kwargs.get('desired_capabilities'):
41 # Combine user's desired capabilities with default
42 caps.update(kwargs['desired_capabilities'])
43
44 kwargs['desired_capabilities'] = caps
45
46 self.driver = Remote(command_executor, **kwargs)
47
48 self.element_class = WebDriverElement
49
50 self._cookie_manager = CookieManager(self.driver)
51
52 super(WebDriver, self).__init__(wait_time)
0 import socket
1
2 try:
3 from httplib import HTTPException
4 except ImportError:
5 from http.client import HTTPException
6
7 import urllib3
8 from urllib3.exceptions import MaxRetryError
9
10 from selenium.webdriver.remote import remote_connection
11
12
13 # Get the original _request and store for future use in the monkey patched version as 'super'
14 old_request = remote_connection.RemoteConnection._request
15
16
17 def patch_request(self, *args, **kwargs):
18 """Override _request to set socket timeout to some appropriate value."""
19 exception = HTTPException('Unable to get response')
20 for _ in range(3):
21 try:
22 return old_request(self, *args, **kwargs)
23 except (socket.error, HTTPException, IOError, OSError, MaxRetryError) as exc:
24 exception = exc
25 self._conn = urllib3.PoolManager(timeout=self._timeout)
26 raise exception
0 def _concat_xpath_from_str(text):
1 """Take a string and splice it into an xpath concat locator.
2
3 Arguments:
4 text (str): Text block to scan.
5 """
6 concat_text = _recurse(
7 text,
8 split_on='\"',
9 wrapper="'{}',",
10 replacer="'\"',",
11 )
12 return '//*[text()=concat({} "")]'.format(concat_text)
13
14
15 def _recurse(text, split_on, wrapper, replacer, inner=False):
16 """Take a string, split it, then build a new string from the parts.
17
18 The position of the character used to split the original string will be
19 replaced in the new string by cap.
20
21 Arguments:
22 text (str): Text block to scan.
23 split_on (str): A character to remove via splitting.
24 wrapper (str): The wrapper for each sub-block.
25 replacer (str): The replacement for the removed character.
26 inner (bool): True if already inside a sub-block.
27
28 Returns:
29 str
30
31 """
32 final_value = ''
33 split_text = text.split(split_on)
34
35 # Ignore single length split lists in nested searches.
36 if inner and len(split_text) <= 1:
37 return
38
39 for index, item in enumerate(split_text):
40 # Check every block of text for a single quotation mark
41 sub_block = _recurse(
42 item,
43 split_on="\'",
44 wrapper='"{}",',
45 replacer='"\'",',
46 inner=True,
47 )
48
49 if sub_block:
50 final_value += sub_block
51
52 else:
53 final_value += wrapper.format(item)
54
55 # Don't cap the last item in the block.
56 if index != len(split_text) - 1:
57 final_value += replacer
58
59
60 return final_value
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import re
7
8 from lxml.cssselect import CSSSelector
9 from zope.testbrowser.browser import Browser, ListControl, SubmitControl
10 from splinter.element_list import ElementList
11 from splinter.exceptions import ElementDoesNotExist
12 from splinter.driver import DriverAPI, ElementAPI
13 from splinter.driver.element_present import ElementPresentMixIn
14 from splinter.driver.xpath_utils import _concat_xpath_from_str
15 from splinter.cookie_manager import CookieManagerAPI
16
17 import mimetypes
18 import lxml.html
19 import time
20
21
22 class CookieManager(CookieManagerAPI):
23 def __init__(self, driver):
24 self.driver = driver
25
26 def add(self, cookies):
27 if isinstance(cookies, list):
28 for cookie in cookies:
29 for key, value in cookie.items():
30 self.driver.cookies[key] = value
31 return
32 for key, value in cookies.items():
33 self.driver.cookies[key] = value
34
35 def delete(self, *cookies):
36 if cookies:
37 for cookie in cookies:
38 try:
39 del self.driver.cookies[cookie]
40 except KeyError:
41 pass
42 else:
43 self.driver.cookies.clearAll()
44
45 def all(self, verbose=False):
46 cookies = {}
47 for key, value in self.driver.cookies.items():
48 cookies[key] = value
49 return cookies
50
51 def __getitem__(self, item):
52 return self.driver.cookies[item]
53
54 def __contains__(self, key):
55 return key in self.driver.cookies
56
57 def __eq__(self, other_object):
58 if isinstance(other_object, dict):
59 return dict(self.driver.cookies) == other_object
60 return False
61
62
63 class ZopeTestBrowser(ElementPresentMixIn, DriverAPI):
64
65 driver_name = "zope.testbrowser"
66
67 def __init__(self, wait_time=2):
68 self.wait_time = wait_time
69 self._browser = Browser()
70
71 self._cookie_manager = CookieManager(self._browser)
72 self._last_urls = []
73
74 def __enter__(self):
75 return self
76
77 def __exit__(self, exc_type, exc_value, traceback):
78 pass
79
80 def visit(self, url):
81 self._browser.open(url)
82
83 def back(self):
84 self._last_urls.insert(0, self.url)
85 self._browser.goBack()
86
87 def forward(self):
88 try:
89 self.visit(self._last_urls.pop())
90 except IndexError:
91 pass
92
93 def reload(self):
94 self._browser.reload()
95
96 def quit(self):
97 pass
98
99 @property
100 def htmltree(self):
101 return lxml.html.fromstring(self.html.decode("utf-8"))
102
103 @property
104 def title(self):
105 return self._browser.title
106
107 @property
108 def html(self):
109 return self._browser.contents
110
111 @property
112 def url(self):
113 return self._browser.url
114
115 def find_option_by_value(self, value):
116 html = self.htmltree
117 element = html.xpath('//option[@value="%s"]' % value)[0]
118 control = self._browser.getControl(element.text)
119 return ElementList(
120 [ZopeTestBrowserOptionElement(control, self)], find_by="value", query=value
121 )
122
123 def find_option_by_text(self, text):
124 html = self.htmltree
125 element = html.xpath('//option[normalize-space(text())="%s"]' % text)[0]
126 control = self._browser.getControl(element.text)
127 return ElementList(
128 [ZopeTestBrowserOptionElement(control, self)], find_by="text", query=text
129 )
130
131 def find_by_css(self, selector):
132 xpath = CSSSelector(selector).path
133 return self.find_by_xpath(
134 xpath, original_find="css", original_selector=selector
135 )
136
137 def get_control(self, xpath_element):
138 return xpath_element
139
140 def find_by_xpath(self, xpath, original_find=None, original_selector=None):
141 html = self.htmltree
142
143 elements = []
144
145 for xpath_element in html.xpath(xpath):
146 if self._element_is_link(xpath_element):
147 return self._find_links_by_xpath(xpath)
148 elif self._element_is_control(xpath_element) and xpath_element.name:
149 return self.find_by_name(xpath_element.name)
150 else:
151 elements.append(self.get_control(xpath_element))
152
153 find_by = original_find or "xpath"
154 query = original_selector or xpath
155
156 return ElementList(
157 [ZopeTestBrowserElement(element, self) for element in elements],
158 find_by=find_by,
159 query=query,
160 )
161
162 def find_by_tag(self, tag):
163 return self.find_by_xpath(
164 "//%s" % tag, original_find="tag", original_selector=tag
165 )
166
167 def find_by_value(self, value):
168 return self.find_by_xpath(
169 '//*[@value="%s"]' % value, original_find="value", original_selector=value
170 )
171
172 def find_by_text(self, text):
173 xpath_str = _concat_xpath_from_str(text)
174 return self.find_by_xpath(
175 xpath_str,
176 original_find="text",
177 original_selector=text,
178 )
179
180 def find_by_id(self, id_value):
181 return self.find_by_xpath(
182 '//*[@id="%s"][1]' % id_value,
183 original_find="id",
184 original_selector=id_value,
185 )
186
187 def find_by_name(self, name):
188 elements = []
189 index = 0
190
191 while True:
192 try:
193 control = self._browser.getControl(name=name, index=index)
194 elements.append(control)
195 index += 1
196 except LookupError:
197 break
198 except NotImplementedError:
199 break
200 return ElementList(
201 [ZopeTestBrowserControlElement(element, self) for element in elements],
202 find_by="name",
203 query=name,
204 )
205
206 def find_link_by_text(self, text):
207 return self._find_links_by_xpath("//a[text()='%s']" % text)
208
209 def find_link_by_href(self, href):
210 return self._find_links_by_xpath("//a[@href='%s']" % href)
211
212 def find_link_by_partial_href(self, partial_href):
213 return self._find_links_by_xpath("//a[contains(@href, '%s')]" % partial_href)
214
215 def find_link_by_partial_text(self, partial_text):
216 return self._find_links_by_xpath(
217 "//a[contains(normalize-space(.), '%s')]" % partial_text
218 )
219
220 def fill(self, name, value):
221 self.find_by_name(name=name).first._control.value = value
222
223 def fill_form(self, field_values, form_id=None, name=None):
224 form = self._browser
225 if name or form_id:
226 form = self._browser.getForm(name=name, id=form_id)
227
228 for name, value in field_values.items():
229 control = form.getControl(name=name)
230
231 if control.type == "checkbox":
232 if value:
233 control.value = control.options
234 else:
235 control.value = []
236 elif control.type == "radio":
237 control.value = [
238 option for option in control.options if option == value
239 ]
240 elif control.type == "select":
241 control.value = [value]
242 else:
243 control.value = value
244
245 def choose(self, name, value):
246 control = self._browser.getControl(name=name)
247 control.value = [option for option in control.options if option == value]
248
249 def check(self, name):
250 control = self._browser.getControl(name=name)
251 control.value = control.options
252
253 def uncheck(self, name):
254 control = self._browser.getControl(name=name)
255 control.value = []
256
257 def attach_file(self, name, file_path):
258 filename = file_path.split("/")[-1]
259 control = self._browser.getControl(name=name)
260 content_type, _ = mimetypes.guess_type(file_path)
261 control.add_file(open(file_path), content_type, filename)
262
263 def _find_links_by_xpath(self, xpath):
264 html = self.htmltree
265 links = html.xpath(xpath)
266 return ElementList(
267 [ZopeTestBrowserLinkElement(link, self) for link in links],
268 find_by="xpath",
269 query=xpath,
270 )
271
272 def select(self, name, value):
273 self.find_by_name(name).first._control.value = [value]
274
275 def is_text_present(self, text, wait_time=None):
276 wait_time = wait_time or self.wait_time
277 end_time = time.time() + wait_time
278
279 while time.time() < end_time:
280 if self._is_text_present(text):
281 return True
282 return False
283
284 def _is_text_present(self, text):
285 try:
286 body = self.find_by_tag("body").first
287 return text in body.text
288 except ElementDoesNotExist:
289 # This exception will be thrown if the body tag isn't present
290 # This has occasionally been observed. Assume that the
291 # page isn't fully loaded yet
292 return False
293
294 def is_text_not_present(self, text, wait_time=None):
295 wait_time = wait_time or self.wait_time
296 end_time = time.time() + wait_time
297
298 while time.time() < end_time:
299 if not self._is_text_present(text):
300 return True
301 return False
302
303 def _element_is_link(self, element):
304 return element.tag == "a"
305
306 def _element_is_control(self, element):
307 return hasattr(element, "type")
308
309 @property
310 def cookies(self):
311 return self._cookie_manager
312
313
314 re_extract_inner_html = re.compile(r"^<[^<>]+>(.*)</[^<>]+>$")
315
316
317 class ZopeTestBrowserElement(ElementAPI):
318 def __init__(self, element, parent):
319 self._element = element
320 self.parent = parent
321
322 def __getitem__(self, attr):
323 return self._element.attrib[attr]
324
325 def find_by_css(self, selector):
326 elements = self._element.cssselect(selector)
327 return ElementList([self.__class__(element, self) for element in elements])
328
329 def find_by_xpath(self, selector):
330 elements = self._element.xpath(selector)
331 return ElementList([self.__class__(element, self) for element in elements])
332
333 def find_by_name(self, name):
334 elements = self._element.cssselect('[name="%s"]' % name)
335 return ElementList([self.__class__(element, self) for element in elements])
336
337 def find_by_tag(self, name):
338 elements = self._element.cssselect(name)
339 return ElementList([self.__class__(element, self) for element in elements])
340
341 def find_by_value(self, value):
342 elements = self._element.cssselect('[value="%s"]' % value)
343 return ElementList([self.__class__(element, self) for element in elements])
344
345 def find_by_text(self, text):
346 # Add a period to the xpath to search only inside the parent.
347 xpath_str = '.{}'.format(_concat_xpath_from_str(text))
348 return self.find_by_xpath(xpath_str)
349
350 def find_by_id(self, id):
351 elements = self._element.cssselect("#%s" % id)
352 return ElementList([self.__class__(element, self) for element in elements])
353
354 @property
355 def value(self):
356 return self._element.text_content()
357
358 @property
359 def text(self):
360 return self.value
361
362 @property
363 def outer_html(self):
364 return lxml.html.tostring(self._element, encoding="unicode").strip()
365
366 @property
367 def html(self):
368 return re_extract_inner_html.match(self.outer_html).group(1)
369
370 def has_class(self, class_name):
371 return len(self._element.find_class(class_name)) > 0
372
373
374 class ZopeTestBrowserLinkElement(ZopeTestBrowserElement):
375 def __init__(self, element, parent):
376 super(ZopeTestBrowserLinkElement, self).__init__(element, parent)
377 self._browser = parent._browser
378
379 def __getitem__(self, attr):
380 return super(ZopeTestBrowserLinkElement, self).__getitem__(attr)
381
382 def click(self):
383 return self._browser.open(self["href"])
384
385
386 class ZopeTestBrowserControlElement(ZopeTestBrowserElement):
387 def __init__(self, control, parent):
388 self._control = control
389 self.parent = parent
390
391 def __getitem__(self, attr):
392 try:
393 return getattr(self._control._control, attr)
394 except AttributeError:
395 return self._control._control.attrs[attr]
396
397 @property
398 def value(self):
399 value = self._control.value
400 if isinstance(self._control, ListControl) and len(value) == 1:
401 return value[0]
402 return value
403
404 @property
405 def checked(self):
406 return bool(self._control.value)
407
408 def click(self):
409 return self._control.click()
410
411 def fill(self, value):
412 self._control.value = value
413
414 def select(self, value):
415 self._control.value = [value]
416
417
418 class ZopeTestBrowserOptionElement(ZopeTestBrowserElement):
419 def __init__(self, control, parent):
420 self._control = control
421 self.parent = parent
422
423 def __getitem__(self, attr):
424 return getattr(self._control, attr)
425
426 @property
427 def text(self):
428 return self._control.labels[0]
429
430 @property
431 def value(self):
432 return self._control.optionValue
433
434 @property
435 def selected(self):
436 return self._control.selected
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from splinter.exceptions import ElementDoesNotExist
7
8
9 class ElementList(object):
10 """
11 List of elements. Each member of the list is (usually) an instance
12 of :class:`ElementAPI <splinter.driver.ElementAPI>`.
13
14 Beyond the traditional list methods, the ``ElementList`` provides some
15 other methods, listed below.
16
17 There is a peculiar behavior on ElementList: you never get an
18 ``IndexError``. Instead, you can an :class:`ElementDoesNotExist
19 <splinter.exceptions.ElementDoesNotExist>` exception when trying to
20 access an inexistent item in the list:
21
22 >>> element_list = ElementList([])
23 >>> element_list[0] # raises ElementDoesNotExist
24 """
25
26 def __init__(self, list, driver=None, find_by=None, query=None):
27 """
28 Creates the list.
29 """
30 self._container = []
31 self._container.extend(list)
32
33 self.driver = driver
34 self.find_by = find_by
35 self.query = query
36
37 def __getitem__(self, index):
38 if not isinstance(index, int) and not isinstance(index, slice):
39 return self.first[index]
40 try:
41 return self._container[index]
42 except IndexError:
43 raise ElementDoesNotExist(
44 u'no elements could be found with {0} "{1}"'.format(
45 self.find_by, self.query
46 )
47 )
48
49 @property
50 def first(self):
51 """
52 An alias to the first element of the list:
53
54 >>> assert element_list[0] == element_list.first
55 """
56 return self[0]
57
58 @property
59 def last(self):
60 """
61 An alias to the last element of the list:
62
63 >>> assert element_list[-1] == element_list.last
64 """
65 return self[-1]
66
67 def is_empty(self):
68 """
69 Returns ``True`` if the list is empty.
70 """
71 return len(self) == 0
72
73 def __getattr__(self, name):
74 try:
75 return getattr(self.first, name)
76 except AttributeError:
77 try:
78 return getattr(self._container, name)
79 except AttributeError:
80 raise AttributeError(
81 u"'{0}' object has no attribute '{1}'".format(
82 self.__class__.__name__, name
83 )
84 )
85
86 def __iter__(self):
87 for item in self._container:
88 yield item
89
90 def __len__(self):
91 """__len__ checks the internal container."""
92 return len(self._container)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class DriverNotFoundError(Exception):
8 """
9 Exception raised when a driver is not found.
10
11 Example:
12
13 >>> from splinter import Browser
14 >>> b = Browser('unknown driver') # raises DriverNotFoundError
15 """
16
17 pass
18
19
20 class ElementDoesNotExist(Exception):
21 """
22 Exception raised when an element is not found in the page.
23
24 The exception is raised only when someone tries to access the element,
25 not when the driver is finding it.
26
27 Example:
28
29 >>> elements = browser.find_by_id('unknown-id') # returns an empty list
30 >>> elements[0] # raises ElementDoesNotExist
31 """
32
33 pass
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class InheritedDocs(type):
8 def __new__(mcs, class_name, bases, dict):
9 items_to_patch = [
10 (k, v) for k, v in dict.items() if not k.startswith("__") and not v.__doc__
11 ]
12 for name, obj in items_to_patch:
13 doc = None
14 for base in bases:
15 if hasattr(base, name):
16 doc = getattr(base, name).__doc__
17
18 if doc:
19 if isinstance(obj, property) and not obj.fset:
20 obj.fget.__doc__ = doc
21 dict[name] = property(fget=obj.fget)
22 else:
23 obj.__doc__ = doc
24 break
25
26 return type.__new__(mcs, class_name, bases, dict)
0 # Copyright 2012 splinter authors. All rights reserved.
1 # Use of this source code is governed by a BSD-style
2 # license that can be found in the LICENSE file.
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class StatusCode(object):
8 def __init__(self, status_code, reason):
9 #: A message for the response (example: Success)
10 self.reason = reason
11 #: Code of the response (example: 200)
12 self.code = status_code
13
14 def __eq__(self, other):
15 return self.code == other
16
17 def __ne__(self, other):
18 return not self.__eq__(other)
19
20 def __str__(self):
21 return "{} - {}".format(self.code, self.reason)
22
23 def is_success(self):
24 """
25 Returns ``True`` if the response was succeed, otherwise, returns ``False``.
26 """
27 return self.code < 400
0 zope.testbrowser==5.2.4; python_version < '3.0'
1 lxml==4.2.4
2 Flask==1.0.2
3 selenium==3.141.0
4 coverage==4.5.1
5 cssselect>=1.0.3
6 argparse
7 Django>=1.7.11; python_version < '3.0'
8 Django>=2.0.6; python_version > '3.0'
9 flake8==3.5.0
10 six==1.11.0
11 twine==1.11.0
12 pytest==4.6.4
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import os
7
8 TESTS_ROOT = os.path.abspath(os.path.dirname(__file__))
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class AsyncFinderTests(object):
8 def test_find_by_css_should_found_an_async_element(self):
9 self.browser.find_by_css(".add-async-element").click()
10 self.assertEqual(1, len(self.browser.find_by_css(".async-element")))
11
12 def test_find_by_xpath_should_found_an_async_element(self):
13 self.browser.find_by_css(".add-async-element").click()
14 self.assertEqual(1, len(self.browser.find_by_xpath("//h4")))
15
16 def test_find_by_tag_should_found_an_async_element(self):
17 self.browser.find_by_css(".add-async-element").click()
18 self.assertEqual(1, len(self.browser.find_by_tag("h4")))
19
20 def test_find_by_id_should_found_an_async_element(self):
21 self.browser.find_by_css(".add-async-element").click()
22 self.assertEqual(1, len(self.browser.find_by_id("async-header")))
23
24 def test_find_by_name_should_found_an_async_element(self):
25 self.browser.find_by_css(".add-async-element").click()
26 self.assertEqual(1, len(self.browser.find_by_name("async-input")))
27
28 def test_find_by_value_should_found_an_async_element(self):
29 self.browser.find_by_css(".add-async-element").click()
30 self.assertEqual(1, len(self.browser.find_by_value("async-header-value")))
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import pytest
7 from selenium import webdriver
8 from selenium.common.exceptions import WebDriverException
9
10 from splinter import Browser
11
12 from .async_finder import AsyncFinderTests
13 from .click_elements import ClickElementsTest
14 from .cookies import CookiesTest
15 from .element_does_not_exist import ElementDoestNotExistTest
16 from .fake_webapp import EXAMPLE_APP
17 from .find_elements import FindElementsTest
18 from .form_elements import FormElementsTest
19 from .iframes import IFrameElementsTest
20 from .element import ElementTest
21 from .is_element_present import IsElementPresentTest
22 from .is_element_visible import IsElementVisibleTest
23 from .is_text_present import IsTextPresentTest
24 from .mouse_interaction import MouseInteractionTest
25 from .screenshot import ScreenshotTest
26 from .html_snapshot import HTMLSnapshotTest
27 from .type import SlowlyTypeTest
28 from .popups import PopupWindowsTest
29
30
31 def get_browser(browser_name, **kwargs):
32 if browser_name == 'chrome':
33 options = webdriver.chrome.options.Options()
34 options.add_argument("--disable-dev-shm-usage")
35 return Browser(
36 "chrome",
37 headless=True,
38 options=options,
39 **kwargs
40 )
41 else:
42 return Browser(
43 "firefox",
44 headless=True,
45 **kwargs
46 )
47
48
49 class BaseBrowserTests(
50 ElementTest,
51 FindElementsTest,
52 FormElementsTest,
53 ClickElementsTest,
54 CookiesTest,
55 SlowlyTypeTest,
56 IsTextPresentTest,
57 ):
58 def test_can_open_page(self):
59 """should be able to visit, get title and quit"""
60 self.browser.visit(EXAMPLE_APP)
61 self.assertEqual("Example Title", self.browser.title)
62
63 def test_can_back_on_history(self):
64 """should be able to back on history"""
65 self.browser.visit(EXAMPLE_APP)
66 self.browser.visit("{}iframe".format(EXAMPLE_APP))
67 self.browser.back()
68 self.assertEqual(EXAMPLE_APP, self.browser.url)
69
70 def test_can_forward_on_history(self):
71 """should be able to forward history"""
72 self.browser.visit(EXAMPLE_APP)
73 next_url = "{}iframe".format(EXAMPLE_APP)
74 self.browser.visit(next_url)
75 self.browser.back()
76 self.browser.forward()
77 self.assertEqual(next_url, self.browser.url)
78
79 def test_should_have_html(self):
80 self.browser.visit(EXAMPLE_APP)
81 html = self.browser.html
82 self.assertIn("<title>Example Title</title>", html)
83 self.assertIn('<h1 id="firstheader">Example Header</h1>', html)
84
85 def test_should_reload_a_page(self):
86 self.browser.visit(EXAMPLE_APP)
87 self.browser.reload()
88 self.assertEqual("Example Title", self.browser.title)
89
90 def test_should_have_url(self):
91 "should have access to the url"
92 self.assertEqual(EXAMPLE_APP, self.browser.url)
93
94 def test_accessing_attributes_of_links(self):
95 "should allow link's attributes retrieval"
96 foo = self.browser.find_link_by_text("FOO")
97 self.assertEqual("http://localhost:5000/foo", foo["href"])
98
99 def test_accessing_attributes_of_inputs(self):
100 "should allow input's attributes retrieval"
101 button = self.browser.find_by_css('input[name="send"]')
102 self.assertEqual("send", button["name"])
103
104 def test_accessing_attributes_of_simple_elements(self):
105 "should allow simple element's attributes retrieval"
106 header = self.browser.find_by_css("h1")
107 self.assertEqual("firstheader", header["id"])
108
109 def test_links_should_have_value_attribute(self):
110 foo = self.browser.find_link_by_href("http://localhost:5000/foo")
111 self.assertEqual("FOO", foo.value)
112
113 def test_should_receive_browser_on_parent(self):
114 'element should contains the browser on "parent" attribute'
115 element = self.browser.find_by_id("firstheader")
116 self.assertEqual(self.browser, element.parent)
117
118 def test_redirection(self):
119 """
120 when visiting /redirected, browser should be redirected to /redirected-location?come=get&some=true
121 browser.url should be updated
122 """
123 self.browser.visit("{}redirected".format(EXAMPLE_APP))
124 self.assertIn("I just been redirected to this location.", self.browser.html)
125 self.assertIn("redirect-location?come=get&some=true", self.browser.url)
126
127
128 class WebDriverTests(
129 BaseBrowserTests,
130 IFrameElementsTest,
131 ElementDoestNotExistTest,
132 IsElementPresentTest,
133 IsElementVisibleTest,
134 AsyncFinderTests,
135 MouseInteractionTest,
136 PopupWindowsTest,
137 ScreenshotTest,
138 HTMLSnapshotTest,
139 ):
140 def test_status_code(self):
141 with self.assertRaises(NotImplementedError):
142 self.browser.status_code
143
144 def test_can_execute_javascript(self):
145 "should be able to execute javascript"
146 self.browser.execute_script("$('body').empty()")
147 self.assertEqual("", self.browser.find_by_tag("body").value)
148
149 def test_can_evaluate_script(self):
150 "should evaluate script"
151 self.assertEqual(8, self.browser.evaluate_script("4+4"))
152
153 def test_can_see_the_text_for_an_element(self):
154 "should provide text for an element"
155 self.assertEqual(self.browser.find_by_id("simple_text").text, "my test text")
156
157 def test_the_text_for_an_element_strips_html_tags(self):
158 "should show that the text attribute strips html"
159 self.assertEqual(
160 self.browser.find_by_id("text_with_html").text, "another bit of text"
161 )
162
163 def test_can_verify_if_a_element_is_visible(self):
164 "should provide verify if element is visible"
165 self.assertTrue(self.browser.find_by_id("visible").visible)
166
167 def test_can_verify_if_a_element_is_invisible(self):
168 "should provide verify if element is invisible"
169 self.assertFalse(self.browser.find_by_id("invisible").visible)
170
171 def test_default_wait_time(self):
172 "should driver default wait time 2"
173 self.assertEqual(2, self.browser.wait_time)
174
175 def test_access_alerts_and_accept_them(self):
176 self.browser.visit(EXAMPLE_APP + "alert")
177 self.browser.find_by_tag("h1").click()
178 alert = self.browser.get_alert()
179 self.assertEqual("This is an alert example.", alert.text)
180 alert.accept()
181
182 def test_access_prompts_and_be_able_to_fill_then(self):
183 self.browser.visit(EXAMPLE_APP + "alert")
184 self.browser.find_by_tag("h2").click()
185
186 alert = self.browser.get_alert()
187 self.assertEqual("What is your name?", alert.text)
188 alert.fill_with("Splinter")
189 alert.accept()
190
191 response = self.browser.get_alert()
192 self.assertEqual("Splinter", response.text)
193 response.accept()
194
195 def test_access_confirm_and_accept_and_dismiss_them(self):
196 self.browser.visit(EXAMPLE_APP + "alert")
197
198 self.browser.find_by_tag("h3").click()
199 alert = self.browser.get_alert()
200
201 self.assertEqual("Should I continue?", alert.text)
202 alert.accept()
203
204 alert = self.browser.get_alert()
205 self.assertEqual("You say I should", alert.text)
206 alert.accept()
207
208 self.browser.find_by_tag("h3").click()
209 alert = self.browser.get_alert()
210 self.assertEqual("Should I continue?", alert.text)
211 alert.dismiss()
212 alert = self.browser.get_alert()
213 self.assertEqual("You say I should not", alert.text)
214 alert.accept()
215
216 def test_access_confirm_and_accept_and_dismiss_them_using_with(self):
217 self.browser.visit(EXAMPLE_APP + "alert")
218
219 self.browser.find_by_tag("h3").click()
220 with self.browser.get_alert() as alert:
221 self.assertEqual("Should I continue?", alert.text)
222 alert.accept()
223
224 with self.browser.get_alert() as alert:
225 self.assertEqual("You say I should", alert.text)
226 alert.accept()
227
228 self.browser.find_by_tag("h3").click()
229 with self.browser.get_alert() as alert:
230 self.assertEqual("Should I continue?", alert.text)
231 alert.dismiss()
232 with self.browser.get_alert() as alert:
233 self.assertEqual("You say I should not", alert.text)
234 alert.accept()
235
236 def test_access_alerts_using_with(self):
237 "should access alerts using 'with' statement"
238 self.browser.visit(EXAMPLE_APP + "alert")
239 self.browser.find_by_tag("h1").click()
240 with self.browser.get_alert() as alert:
241 self.assertEqual("This is an alert example.", alert.text)
242 alert.accept()
243
244 def test_get_alert_return_none_if_no_alerts(self):
245 "should return None if no alerts available"
246 alert = self.browser.get_alert()
247 self.assertEqual(None, alert)
248
249 def test_can_select_a_option_via_element_text(self):
250 "should provide a way to select a option via element"
251 self.assertFalse(self.browser.find_option_by_value("rj").selected)
252 self.browser.find_by_name("uf").select_by_text("Rio de Janeiro")
253 self.assertTrue(self.browser.find_option_by_value("rj").selected)
254
255 def test_should_be_able_to_change_user_agent(self):
256 driver_name = self.browser.driver_name.lower()
257 browser = get_browser(driver_name, user_agent="iphone")
258 browser.visit(EXAMPLE_APP + "useragent")
259 result = "iphone" in browser.html
260 browser.quit()
261 self.assertTrue(result)
262
263 def test_execute_script_returns_result_if_present(self):
264 assert self.browser.execute_script("return 42") == 42
265
266 def test_click_intercepted(self):
267 """Intercepted clicks should retry."""
268 self.browser.visit(EXAMPLE_APP + "click_intercepted")
269 self.browser.wait_time = 10
270 # Clicking this element adds a new element to the page.
271 self.browser.find_by_id("overlapped").click()
272 value = self.browser.find_by_id("added_container").value
273 assert "Added" == value
274 self.browser.wait_time = 2
275
276 def test_click_intercepted_fails(self):
277 """Intercepted clicks that never unblock should raise an error."""
278 self.browser.visit(EXAMPLE_APP + "click_intercepted")
279
280 with pytest.raises(WebDriverException) as e:
281 self.browser.find_by_id("overlapped2").click()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class ClickElementsTest(object):
8 def test_click_links(self):
9 self.browser.find_link_by_text("FOO").click()
10 self.assertIn("BAR!", self.browser.html)
11
12 def test_click_element_by_css_selector(self):
13 self.browser.find_by_css('a[href="http://localhost:5000/foo"]').click()
14 self.assertIn("BAR!", self.browser.html)
15
16 def test_click_input_by_css_selector(self):
17 self.browser.find_by_css('input[name="send"]').click()
18 self.assertIn("My name is: Master Splinter", self.browser.html)
19
20 def test_click_link_by_href(self):
21 self.browser.click_link_by_href("http://localhost:5000/foo")
22 self.assertIn("BAR!", self.browser.html)
23
24 def test_click_link_by_partial_href(self):
25 self.browser.click_link_by_partial_href("5000/foo")
26 self.assertIn("BAR!", self.browser.html)
27
28 def test_click_link_by_text(self):
29 self.browser.click_link_by_text("FOO")
30 self.assertIn("BAR!", self.browser.html)
31
32 def test_click_link_by_partial_text(self):
33 self.browser.click_link_by_partial_text("wordier")
34 self.assertIn("BAR!", self.browser.html)
35
36 def test_click_link_by_id(self):
37 self.browser.click_link_by_id("foo")
38 self.assertIn("BAR!", self.browser.html)
0 import sys
1
2 import os
3
4 from multiprocessing import Process
5
6 try:
7 from urllib import urlopen
8 except ImportError:
9 from urllib.request import urlopen
10
11 from tests.fake_webapp import start_flask_app, EXAMPLE_APP
12
13
14 class Env(object):
15 def __init__(self):
16 self.process = None
17 self.host = 'localhost'
18 self.port = 5000
19
20
21 env = Env()
22
23
24 def wait_until_start():
25 while True:
26 try:
27 results = urlopen(EXAMPLE_APP)
28 if results.code == 404:
29 raise Exception("%s returned unexpected 404" % EXAMPLE_APP)
30 break
31 except IOError:
32 pass
33
34
35 def wait_until_stop():
36 while True:
37 try:
38 results = urlopen(EXAMPLE_APP)
39 if results.code == 404:
40 break
41 except IOError:
42 break
43
44
45 def start_server():
46 sys.stderr = open(os.devnull, "w")
47 env.process = Process(target=start_flask_app, args=(env.host, env.port))
48 env.process.daemon = True
49 env.process.start()
50 wait_until_start()
51
52
53 def stop_server():
54 env.process.terminate()
55 env.process.join()
56 wait_until_stop()
57
58
59 def pytest_configure(config):
60 try:
61 start_server()
62 except Exception as e:
63 sys.stdout.write("Failed to start test server: %s\n\n" % e)
64 sys.exit(1)
65
66
67 def pytest_unconfigure(config):
68 stop_server()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class CookiesTest(object):
8 def test_create_and_access_a_cookie(self):
9 "should be able to create and access a cookie"
10 self.browser.cookies.add({"sha": "zam"})
11 self.assertEqual(self.browser.cookies["sha"], "zam")
12
13 def test_create_many_cookies_at_once_as_dict(self):
14 "should be able to create many cookies at once as dict"
15 cookies = {"sha": "zam", "foo": "bar"}
16 self.browser.cookies.add(cookies)
17 self.assertEqual(self.browser.cookies["sha"], "zam")
18 self.assertEqual(self.browser.cookies["foo"], "bar")
19
20 def test_create_many_cookies_at_once_as_list(self):
21 "should be able to create many cookies at once as list"
22 cookies = [{"sha": "zam"}, {"foo": "bar"}]
23 self.browser.cookies.add(cookies)
24 self.assertEqual(self.browser.cookies["sha"], "zam")
25 self.assertEqual(self.browser.cookies["foo"], "bar")
26
27 def test_create_some_cookies_and_delete_them_all(self):
28 "should be able to delete all cookies"
29 self.browser.cookies.add({"whatever": "and ever"})
30 self.browser.cookies.add({"anothercookie": "im bored"})
31 self.browser.cookies.delete()
32 self.assertEqual(self.browser.cookies, {})
33
34 def test_create_and_delete_a_cookie(self):
35 "should be able to create and destroy a cookie"
36 self.browser.cookies.delete()
37 self.browser.cookies.add({"cookie": "with milk"})
38 self.browser.cookies.delete("cookie")
39 self.assertEqual(self.browser.cookies, {})
40
41 def test_create_and_delete_many_cookies(self):
42 "should be able to create and destroy many cookies"
43 self.browser.cookies.delete()
44 self.browser.cookies.add({"acookie": "cooked"})
45 self.browser.cookies.add({"anothercookie": "uncooked"})
46 self.browser.cookies.add({"notacookie": "halfcooked"})
47 self.browser.cookies.delete("acookie", "notacookie")
48 self.assertEqual("uncooked", self.browser.cookies["anothercookie"])
49
50 def test_try_to_destroy_an_absent_cookie_and_nothing_happens(self):
51 self.browser.cookies.delete()
52 self.browser.cookies.add({"foo": "bar"})
53 self.browser.cookies.delete("mwahahahaha")
54 self.assertEqual(self.browser.cookies, {"foo": "bar"})
55
56 def test_create_and_get_all_cookies(self):
57 "should be able to create some cookies and retrieve them all"
58 self.browser.cookies.delete()
59 self.browser.cookies.add({"taco": "shrimp"})
60 self.browser.cookies.add({"lavar": "burton"})
61 self.assertEqual(len(self.browser.cookies.all()), 2)
62 self.browser.cookies.delete()
63 self.assertEqual(self.browser.cookies.all(), {})
64
65 def test_create_and_use_contains(self):
66 "should be able to create many cookies at once as dict"
67 cookies = {"sha": "zam"}
68 self.browser.cookies.add(cookies)
69 self.assertIn("sha", self.browser.cookies)
70 self.assertNotIn("foo", self.browser.cookies)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class ElementTest(object):
8 def test_element_has_class_when_element_has_the_class_as_first_class(self):
9 self.assertTrue(
10 self.browser.find_by_css(".has-class-first").has_class("has-class-first")
11 )
12
13 def test_element_has_class_when_element_has_the_class_as_middle_class(self):
14 self.assertTrue(
15 self.browser.find_by_css(".has-class-middle").has_class("has-class-middle")
16 )
17
18 def test_element_has_class_when_element_has_the_class_as_end_class(self):
19 self.assertTrue(
20 self.browser.find_by_css(".has-class-end").has_class("has-class-end")
21 )
22
23 def test_element_has_class_when_element_doesnt_have_the_class(self):
24 self.assertFalse(
25 self.browser.find_by_css(".has-class-first").has_class("has-class")
26 )
27
28 def test_element_outer_html(self):
29 self.assertEqual(
30 self.browser.find_by_id("html-property").outer_html,
31 u'<div id="html-property" class="outer html classes">'
32 u'inner <div class="inner-html">inner text</div> html test</div>',
33 )
34
35 def test_element_html(self):
36 self.assertEqual(
37 self.browser.find_by_id("html-property").html,
38 u'inner <div class="inner-html">inner text</div> html test',
39 )
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from splinter.exceptions import ElementDoesNotExist
7
8
9 class ElementDoestNotExistTest(object):
10 def test_element_query_should_raises_when_element_first_doest_exists(self):
11 with self.assertRaises(ElementDoesNotExist):
12 self.browser.find_by_css(".element-that-dont-exists").first
13
14 def test_element_list_raises_when_element_last_does_not_exists(self):
15 with self.assertRaises(ElementDoesNotExist):
16 self.browser.find_by_css(".element-that-dont-exists").last
17
18 def test_element_list_raises_when_element_does_not_exists(self):
19 with self.assertRaises(ElementDoesNotExist):
20 self.browser.find_by_css(".element-that-dont-exists")[2]
21
22 def test_element_list_raises_with_unicode_query(self):
23 with self.assertRaises(ElementDoesNotExist):
24 self.browser.find_by_css(u".element[title=título]").last
25
26 def test_element_list_contains_right_information_and_raises_right_exception(self):
27 "element list contains right information about query and raises nice exception message"
28 with self.assertRaises(ElementDoesNotExist) as cm:
29 element_list = self.browser.find_by_css(".element-that-dont-exists")
30 self.assertEqual("css", element_list.find_by)
31 self.assertEqual(".element-that-dont-exists", element_list.query)
32 element_list.first
33
34 expected_message = (
35 'no elements could be found with css ".element-that-dont-exists"'
36 )
37
38 e = cm.exception
39 self.assertEqual(expected_message, e.args[0])
40
41 def test_element_list_raises_when_element_first_doesnt_exists_in_element_context(
42 self
43 ):
44 "element list raises exception with right information in element context"
45 with self.assertRaises(ElementDoesNotExist) as cm:
46 element_list = self.browser.find_by_css("#inside").find_by_css(
47 ".inner-element-that-dont-exists"
48 )
49 self.assertEqual("css", element_list.find_by)
50 self.assertEqual(".inner-element-that-dont-exists", element_list.query)
51 element_list.first
52
53 expected_message = (
54 'no elements could be found with css ".inner-element-that-dont-exists"'
55 )
56
57 e = cm.exception
58 self.assertEqual(expected_message, e.args[0])
0 #!/usr/bin/env python
1 import os
2 import sys
3
4 if __name__ == "__main__":
5 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
6
7 from django.core.management import execute_from_command_line
8
9 execute_from_command_line(sys.argv)
0 """
1 Django settings for fake_django project.
2
3 For more information on this file, see
4 https://docs.djangoproject.com/en/1.6/topics/settings/
5
6 For the full list of settings and their values, see
7 https://docs.djangoproject.com/en/1.6/ref/settings/
8 """
9
10 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
11 import os
12
13 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
14
15
16 # Quick-start development settings - unsuitable for production
17 # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
18
19 # SECURITY WARNING: keep the secret key used in production secret!
20 SECRET_KEY = "(*g@g)+!toffn65+q$o)eivqorppbb)%$+f+ybjevymw=d5d8x"
21
22 # SECURITY WARNING: don't run with debug turned on in production!
23 DEBUG = True
24
25 TEMPLATE_DEBUG = True
26
27 ALLOWED_HOSTS = []
28
29
30 # Application definition
31
32 INSTALLED_APPS = (
33 "django.contrib.admin",
34 "django.contrib.auth",
35 "django.contrib.contenttypes",
36 "django.contrib.sessions",
37 "django.contrib.messages",
38 "django.contrib.staticfiles",
39 )
40
41 MIDDLEWARE_CLASSES = (
42 "django.contrib.sessions.middleware.SessionMiddleware",
43 "django.middleware.common.CommonMiddleware",
44 "django.middleware.csrf.CsrfViewMiddleware",
45 "django.contrib.auth.middleware.AuthenticationMiddleware",
46 "django.contrib.messages.middleware.MessageMiddleware",
47 "django.middleware.clickjacking.XFrameOptionsMiddleware",
48 )
49
50 ROOT_URLCONF = "urls"
51
52 WSGI_APPLICATION = "fake_django.wsgi.application"
53
54
55 # Database
56 # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
57
58 DATABASES = {
59 "default": {
60 "ENGINE": "django.db.backends.sqlite3",
61 "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
62 }
63 }
64
65 # Internationalization
66 # https://docs.djangoproject.com/en/1.6/topics/i18n/
67
68 LANGUAGE_CODE = "en-us"
69
70 TIME_ZONE = "UTC"
71
72 USE_I18N = True
73
74 USE_L10N = True
75
76 USE_TZ = True
77
78
79 # Static files (CSS, JavaScript, Images)
80 # https://docs.djangoproject.com/en/1.6/howto/static-files/
81
82 STATIC_URL = "/static/"
0 from django.conf.urls import include, url
1 from django.http import HttpResponse
2 from django.shortcuts import redirect
3 from django.contrib import admin
4 from django.contrib.auth.decorators import login_required
5 import six
6
7 if six.PY2:
8 from django.core.urlresolvers import reverse
9 else:
10 from django.urls import reverse
11
12 from tests.fake_webapp import (
13 EXAMPLE_HTML,
14 EXAMPLE_IFRAME_HTML,
15 EXAMPLE_ALERT_HTML,
16 EXAMPLE_TYPE_HTML,
17 EXAMPLE_NO_BODY_HTML,
18 EXAMPLE_POPUP_HTML,
19 EXAMPLE_REDIRECT_LOCATION_HTML,
20 )
21
22
23 admin.autodiscover()
24
25
26 def index(request):
27 return HttpResponse(EXAMPLE_HTML)
28
29
30 def iframed(request):
31 return HttpResponse(EXAMPLE_IFRAME_HTML)
32
33
34 def alertd(request):
35 return HttpResponse(EXAMPLE_ALERT_HTML)
36
37
38 def type(request):
39 return HttpResponse(EXAMPLE_TYPE_HTML)
40
41
42 def no_body(request):
43 return HttpResponse(EXAMPLE_NO_BODY_HTML)
44
45
46 def get_name(request):
47 return HttpResponse("My name is: Master Splinter")
48
49
50 def get_user_agent(request):
51 return HttpResponse(request.META["User-Agent"])
52
53
54 def post_form(request):
55 items = "\n".join("{}: {}".format(*item) for item in request.POST.items())
56 body = "<html><body>{}</body></html>".format(items)
57 return HttpResponse(body)
58
59
60 def request_headers(request):
61 body = "\n".join(
62 "%s: %s" % (key, value) for key, value in six.iteritems(request.META)
63 )
64 return HttpResponse(body)
65
66
67 def upload_file(request):
68 if request.method == "POST":
69 f = request.FILES["file"]
70 buffer = []
71 buffer.append("Content-type: %s" % f.content_type)
72 buffer.append("File content: %s" % f.read())
73
74 return HttpResponse("|".join(buffer))
75
76
77 def foo(request):
78 return HttpResponse("BAR!")
79
80
81 def query_string(request):
82 if request.query_string == "model":
83 return HttpResponse("query string is valid")
84 else:
85 raise Exception("500")
86
87
88 def popup(request):
89 return HttpResponse(EXAMPLE_POPUP_HTML)
90
91
92 @login_required
93 def auth_required(request):
94 return HttpResponse("Success!")
95
96
97 def redirected(request):
98 location = "{}?{}".format(reverse("redirect_location"), "come=get&some=true")
99 return redirect(location)
100
101
102 def redirect_location(request):
103 return HttpResponse(EXAMPLE_REDIRECT_LOCATION_HTML)
104
105
106 urlpatterns = [
107 url(r"^$", index),
108 url(r"^iframe$", iframed),
109 url(r"^alert$", alertd),
110 url(r"^type$", type),
111 url(r"^no_body$", no_body),
112 url(r"^name$", get_name),
113 url(r"^useragent$", get_user_agent),
114 url(r"^headers$", request_headers),
115 url(r"^upload$", upload_file),
116 url(r"^foo$", foo),
117 url(r"^query$", query_string),
118 url(r"^popup$", popup),
119 url(r"^authenticate$", auth_required),
120 url(r"^redirected", redirected),
121 url(r"^post", post_form),
122 url(r"^redirect-location", redirect_location, name="redirect_location"),
123 ]
124
125 if six.PY2:
126 urlpatterns.append(url(r"^admin/", include(admin.site.urls)))
127 else:
128 urlpatterns.append(url(r"^admin/", admin.site.urls))
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from flask import Flask, request, abort, Response, redirect, url_for
7 from os import path
8 from functools import wraps
9
10
11 this_folder = path.abspath(path.dirname(__file__))
12
13
14 def read_static(static_name):
15 return open(path.join(this_folder, "static", static_name)).read()
16
17
18 EXAMPLE_APP = "http://127.0.0.1:5000/"
19 EXAMPLE_HTML = read_static("index.html")
20 EXAMPLE_IFRAME_HTML = read_static("iframe.html")
21 EXAMPLE_ALERT_HTML = read_static("alert.html")
22 EXAMPLE_TYPE_HTML = read_static("type.html")
23 EXAMPLE_POPUP_HTML = read_static("popup.html")
24 EXAMPLE_NO_BODY_HTML = read_static("no-body.html")
25 EXAMPLE_REDIRECT_LOCATION_HTML = read_static("redirect-location.html")
26 EXAMPLE_MOUSE_HTML = read_static("mouse.html")
27 EXAMPLE_CLICK_INTERCEPTED_HTML = read_static("click_intercepted.html")
28
29 # Functions for http basic auth.
30 # Taken verbatim from http://flask.pocoo.org/snippets/8/
31
32
33 def check_auth(username, password):
34 """This function is called to check if a username /
35 password combination is valid.
36 """
37 return username == "admin" and password == "secret"
38
39
40 def authenticate():
41 """Sends a 401 response that enables basic auth"""
42 return Response(
43 "Could not verify your access level for that URL.\n"
44 "You have to login with proper credentials",
45 401,
46 {"WWW-Authenticate": 'Basic realm="Login Required"'},
47 )
48
49
50 def requires_auth(f):
51 @wraps(f)
52 def decorated(*args, **kwargs):
53 auth = request.authorization
54 if not auth or not check_auth(auth.username, auth.password):
55 return authenticate()
56 return f(*args, **kwargs)
57
58 return decorated
59
60
61 app = Flask(__name__)
62
63
64 @app.route("/")
65 def index():
66 return EXAMPLE_HTML
67
68
69 @app.route("/iframe")
70 def iframed():
71 return EXAMPLE_IFRAME_HTML
72
73
74 @app.route("/alert")
75 def alertd():
76 return EXAMPLE_ALERT_HTML
77
78
79 @app.route("/type")
80 def type():
81 return EXAMPLE_TYPE_HTML
82
83
84 @app.route("/no-body")
85 def no_body():
86 return EXAMPLE_NO_BODY_HTML
87
88
89 @app.route("/name", methods=["GET"])
90 def get_name():
91 return "My name is: Master Splinter"
92
93
94 @app.route("/useragent", methods=["GET"])
95 def get_user_agent():
96 return request.user_agent.string
97
98
99 @app.route("/post", methods=["POST"])
100 def post_form():
101 items = "\n".join("{}: {}".format(*item) for item in request.form.items())
102 return "<html><body>{}</body></html>".format(items)
103
104
105 @app.route("/upload", methods=["GET", "POST"])
106 def upload_file():
107 if request.method == "POST":
108 f = request.files["file"]
109 buffer = []
110 buffer.append("Content-type: %s" % f.content_type)
111 buffer.append("File content: %s" % f.stream.read())
112
113 return "|".join(buffer)
114
115
116 @app.route("/headers", methods=["GET"])
117 def request_headers():
118 return str(request.headers)
119
120
121 @app.route("/foo")
122 def foo():
123 return "BAR!"
124
125
126 @app.route("/query", methods=["GET"])
127 def query_string():
128 if request.query_string == b"model":
129 return "query string is valid"
130 else:
131 abort(500)
132
133
134 @app.route("/popup")
135 def popup():
136 return EXAMPLE_POPUP_HTML
137
138
139 @app.route("/authenticate")
140 @requires_auth
141 def auth_required():
142 return "Success!"
143
144
145 @app.route("/redirected", methods=["GET", "POST"])
146 def redirected():
147 location = "{}?{}".format(url_for("redirect_location"), "come=get&some=true")
148 return redirect(location)
149
150
151 @app.route("/redirect-location")
152 def redirect_location():
153 return EXAMPLE_REDIRECT_LOCATION_HTML
154
155
156 @app.route("/mouse")
157 def mouse():
158 return EXAMPLE_MOUSE_HTML
159
160
161 @app.route("/click_intercepted")
162 def click_intercepted():
163 return EXAMPLE_CLICK_INTERCEPTED_HTML
164
165
166 def start_flask_app(host, port):
167 """Runs the server."""
168 app.run(host=host, port=port)
169 app.config["DEBUG"] = False
170 app.config["TESTING"] = False
171
172
173 if __name__ == "__main__":
174 app.run()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from splinter.driver import ElementAPI
7 from splinter.element_list import ElementList
8
9
10 class FindElementsTest(object):
11 def test_finding_by_css(self):
12 value = self.browser.find_by_css("h1").value
13 self.assertEqual("Example Header", value)
14
15 def test_finding_by_xpath(self):
16 value = self.browser.find_by_xpath("//h1").value
17 self.assertEqual("Example Header", value)
18
19 def test_finding_by_tag(self):
20 value = self.browser.find_by_tag("h1").value
21 self.assertEqual("Example Header", value)
22
23 def test_finding_by_value(self):
24 value = self.browser.find_by_value("M").value
25 id = self.browser.find_by_id("gender-m")
26 self.assertEqual(id.value, value)
27
28 def test_finding_by_text(self):
29 element = self.browser.find_by_text("Complex")
30 self.assertEqual(element.value, "Complex")
31
32 def test_finding_by_text_with_quotation_marks(self):
33 element = self.browser.find_by_text("Quotation \" marks")
34 self.assertEqual(element.value, "Quotation \" marks")
35
36 def test_finding_by_id(self):
37 value = self.browser.find_by_id("firstheader").value
38 self.assertEqual("Example Header", value)
39
40 def test_finding_by_name(self):
41 value = self.browser.find_by_name("query").value
42 self.assertEqual("default value", value)
43
44 def test_finding_all_elements_by_css(self):
45 value = self.browser.find_by_css("h1")[0].value
46 self.assertEqual("Example Header", value)
47
48 def test_finding_all_elements_by_xpath(self):
49 value = self.browser.find_by_xpath("//h1")[0].value
50 self.assertEqual("Example Header", value)
51
52 def test_finding_all_elements_by_tag(self):
53 value = self.browser.find_by_tag("h1")[0].value
54 self.assertEqual("Example Header", value)
55
56 def test_finding_all_elements_by_id(self):
57 value = self.browser.find_by_id("firstheader").value
58 self.assertEqual("Example Header", value)
59
60 def test_finding_all_elements_by_name(self):
61 value = self.browser.find_by_name("query").value
62 self.assertEqual("default value", value)
63
64 def test_finding_all_links_by_text(self):
65 link = self.browser.find_link_by_text("Link for Example.com")[0]
66 self.assertEqual("http://example.com/", link["href"])
67
68 def test_finding_all_links_by_href(self):
69 link = self.browser.find_link_by_href("http://example.com/")[0]
70 self.assertEqual("http://example.com/", link["href"])
71
72 def test_finding_all_links_by_partial_href(self):
73 link = self.browser.find_link_by_partial_href("example.c")[0]
74 self.assertEqual("http://example.com/", link["href"])
75
76 def test_finding_all_links_by_partial_text(self):
77 link = self.browser.find_link_by_partial_text("FOO")[0]
78 self.assertEqual("http://localhost:5000/foo", link["href"])
79
80 def test_finding_last_element_by_css(self):
81 value = self.browser.find_by_css("h1").last.value
82 self.assertEqual("Example Last Header", value)
83
84 def test_finding_last_element_by_xpath(self):
85 value = self.browser.find_by_xpath("//h1").last.value
86 self.assertEqual("Example Last Header", value)
87
88 def test_finding_last_element_by_tag(self):
89 value = self.browser.find_by_tag("h1").last.value
90 self.assertEqual("Example Last Header", value)
91
92 def test_finding_last_element_by_id(self):
93 value = self.browser.find_by_id("firstheader").last.value
94 self.assertEqual("Example Header", value)
95
96 def test_last_element_is_same_than_first_element_in_find_by_id(self):
97 # a html page have contain one element by id
98 first = self.browser.find_by_id("firstheader").value
99 last = self.browser.find_by_id("firstheader").last.value
100 self.assertEqual(first, last)
101
102 def test_finding_last_element_by_name(self):
103 value = self.browser.find_by_name("input1").last.value
104 self.assertEqual("default last value", value)
105
106 def test_finding_last_link_by_text(self):
107 link = self.browser.find_link_by_text("Link for Example.com").last
108 self.assertEqual("http://example.com/last", link["href"])
109
110 def test_finding_last_link_by_href(self):
111 link = self.browser.find_link_by_href("http://example.com/").last
112 self.assertEqual("Link for last Example.com", link.text)
113
114 def test_finding_link_by_partial_href(self):
115 link = self.browser.find_link_by_partial_href("example.c").last
116 self.assertEqual("Link for last Example.com", link.text)
117
118 def test_finding_last_link_by_partial_text(self):
119 link = self.browser.find_link_by_partial_text("FOO").last
120 self.assertEqual("A wordier (and last) link to FOO", link.text)
121
122 def test_finding_element_by_css_using_slice(self):
123 value = self.browser.find_by_css("h1")[-1].value
124 self.assertEqual("Example Last Header", value)
125
126 def test_finding_element_by_xpath_using_slice(self):
127 value = self.browser.find_by_xpath("//h1")[-1].value
128 self.assertEqual("Example Last Header", value)
129
130 def test_finding_element_by_tag_using_slice(self):
131 value = self.browser.find_by_tag("h1")[-1].value
132 self.assertEqual("Example Last Header", value)
133
134 def test_finding_element_by_id_using_slice(self):
135 value = self.browser.find_by_id("firstheader")[-1].value
136 self.assertEqual("Example Header", value)
137
138 def test_all_elements_is_same_than_first_element_in_find_by_id(self):
139 # a html page have contain one element by id
140 first = self.browser.find_by_id("firstheader").value
141 some = self.browser.find_by_id("firstheader")[-1].value
142 self.assertEqual(first, some)
143
144 def test_finding_element_by_name_using_slice(self):
145 value = self.browser.find_by_name("input1")[-1].value
146 self.assertEqual("default last value", value)
147
148 def test_finding_link_by_text_using_slice(self):
149 link = self.browser.find_link_by_text("Link for Example.com")[-1]
150 self.assertEqual("http://example.com/last", link["href"])
151
152 def test_finding_link_by_href_using_slice(self):
153 "should find link by href using slice"
154 link = self.browser.find_link_by_href("http://example.com/")[-1]
155 self.assertEqual("Link for last Example.com", link.text)
156
157 def test_finding_links_by_text(self):
158 "should find links by text"
159 link = self.browser.find_link_by_text("Link for Example.com")
160 self.assertEqual("http://example.com/", link["href"])
161
162 def test_finding_links_by_href(self):
163 "should find links by href"
164 link = self.browser.find_link_by_href("http://example.com/")
165 self.assertEqual("http://example.com/", link["href"])
166
167 def test_find_by_css_in_element_context(self):
168 "should find elements by css in element context and should return splinter driver element"
169 elements = self.browser.find_by_css("#inside")
170 decendent = elements[0].find_by_css("h2")
171 self.assertEqual(decendent.text.strip(), "inside")
172 self.assertIsInstance(decendent, ElementList)
173 self.assertIsInstance(decendent[0], ElementAPI)
174
175 def test_find_by_xpath_in_element_context(self):
176 "should find elements by xpath in element context"
177 elements = self.browser.find_by_css("#inside")
178 decendent = elements[0].find_by_xpath("//h2")
179 self.assertEqual(decendent.text.strip(), "inside")
180 self.assertIsInstance(decendent, ElementList)
181 self.assertIsInstance(decendent.first, ElementAPI)
182
183 def test_find_by_name_in_element_context(self):
184 elements = self.browser.find_by_css("#inside")
185 decendent = elements[0].find_by_name("crazy-upload")
186 self.assertEqual(len(decendent), 1)
187 self.assertIsInstance(decendent, ElementList)
188 self.assertIsInstance(decendent.first, ElementAPI)
189
190 def test_find_by_tag_in_element_context(self):
191 elements = self.browser.find_by_css("#inside")
192 decendent = elements[0].find_by_tag("input")
193 self.assertEqual(len(decendent), 1)
194 self.assertIsInstance(decendent, ElementList)
195 self.assertIsInstance(decendent.first, ElementAPI)
196
197 def test_find_by_id_in_element_context(self):
198 elements = self.browser.find_by_css("#inside")
199 decendent = elements[0].find_by_id("visible")
200 self.assertEqual(len(decendent), 1)
201 self.assertIsInstance(decendent, ElementList)
202 self.assertIsInstance(decendent.first, ElementAPI)
203
204 def test_find_by_value_in_element_context(self):
205 elements = self.browser.find_by_css("#inside")
206 decendent = elements[0].find_by_value("crazy diamond")
207 self.assertEqual(len(decendent), 1)
208 self.assertIsInstance(decendent, ElementList)
209 self.assertIsInstance(decendent.first, ElementAPI)
210
211 def test_finding_by_text_in_element_context(self):
212 inside = self.browser.find_by_id("inside")
213 element = inside.find_by_text("Complex")
214
215 self.assertEqual(len(element), 1)
216 self.assertEqual(element["class"], "inside")
217 self.assertEqual(element.value, "Complex")
Binary diff not shown
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import time
7 import re
8 import unittest
9
10
11 def skipIfZope(f):
12 def wrapper(self, *args, **kwargs):
13 if self.__class__.__name__ == 'ZopeTestBrowserDriverTest':
14 return unittest.skip("skipping this test for zope testbrowser")
15 else:
16 f(self, *args, **kwargs)
17 return wrapper
18
19
20 class FormElementsTest(object):
21 def test_fill(self):
22 self.browser.fill("query", "LT-CS-01/2018")
23 value = self.browser.find_by_name("query").value
24 self.assertEqual("LT-CS-01/2018", value)
25
26 def test_fill_element(self):
27 self.browser.find_by_name("q").fill("new query")
28 time.sleep(1)
29 value = self.browser.find_by_name("q").value
30 self.assertEqual("new query", value)
31
32 @skipIfZope
33 def test_clicking_submit_input_doesnt_post_input_value_if_name_not_present(self):
34 self.browser.find_by_css("input.submit-input-no-name").click()
35 self.assertEqual(
36 self.browser.find_by_xpath("/descendant-or-self::*").text.strip(), ""
37 )
38
39 @skipIfZope
40 def test_clicking_submit_input_posts_empty_value_if_value_not_present(self):
41 self.browser.find_by_css('input[name="submit-input-no-value"]').click()
42 body_text = self.browser.find_by_xpath("/descendant-or-self::*").text.strip()
43 self.assertTrue(
44 re.match(r"^submit-input-no-value:(?:| Submit| Submit Query)$", body_text),
45 repr(body_text),
46 )
47
48 @skipIfZope
49 def test_clicking_submit_input_doesnt_post_input_value_if_empty(self):
50 self.browser.find_by_css("input.submit-input-empty").click()
51 self.assertEqual(
52 self.browser.find_by_xpath("/descendant-or-self::*").text.strip(), ""
53 )
54
55 def test_clicking_submit_input_posts_input_value_if_value_present(self):
56 self.browser.find_by_css('input[name="submit-input"]').click()
57 self.assertEqual(
58 self.browser.find_by_xpath("/descendant-or-self::*").text,
59 "submit-input: submit-input-value",
60 )
61
62 @skipIfZope
63 def test_clicking_submit_button_doesnt_post_button_value_if_name_not_present(self):
64 self.browser.find_by_css("button.submit-button-no-name").click()
65 self.assertEqual(self.browser.find_by_xpath("/descendant-or-self::*").text, "")
66
67 @skipIfZope
68 def test_clicking_submit_button_posts_empty_value_if_value_not_present(self):
69 self.browser.find_by_css('button[name="submit-button-no-value"]').click()
70 self.assertEqual(
71 self.browser.find_by_xpath("/descendant-or-self::*").text.strip(),
72 "submit-button-no-value:",
73 )
74
75 @skipIfZope
76 def test_clicking_submit_button_doesnt_post_button_value_if_empty(self):
77 self.browser.find_by_css("button.submit-button-empty").click()
78 self.assertEqual(
79 self.browser.find_by_xpath("/descendant-or-self::*").text.strip(), ""
80 )
81
82 @skipIfZope
83 def test_clicking_submit_button_posts_button_value_if_value_present(self):
84 self.browser.find_by_css('button[name="submit-button"]').click()
85
86 self.assertEqual(
87 self.browser.find_by_xpath("/descendant-or-self::*").text,
88 "submit-button: submit-button-value",
89 )
90
91 def test_submiting_a_form_and_verifying_page_content(self):
92 self.browser.fill("query", "my name")
93 self.browser.find_by_name("send").click()
94 self.assertIn("My name is: Master Splinter", self.browser.html)
95
96 def test_can_choose_a_radio_button(self):
97 "should provide a way to choose a radio button"
98 self.assertFalse(self.browser.find_by_id("gender-m").checked)
99 self.browser.choose("gender", "M")
100 self.assertTrue(self.browser.find_by_id("gender-m").checked)
101
102 def test_can_find_textarea_by_tag(self):
103 "should provide a way to find a textarea by tag_name"
104 tag = self.browser.find_by_tag("textarea").first
105 self.assertEqual("", tag.value)
106
107 def test_can_find_input_without_type(self):
108 "should recognize an input element that doesn't have a `type` attribute"
109 tag = self.browser.find_by_css('[name="typeless"]').first
110 self.assertEqual("default value", tag.value)
111
112 def test_can_find_button(self):
113 "should recognize a button"
114 tag = self.browser.find_by_css(".just-a-button").first
115 self.assertTrue(hasattr(tag, "click"))
116
117 def test_can_find_option_by_value(self):
118 "should provide a way to find select option by value"
119 self.assertEqual("Rio de Janeiro", self.browser.find_option_by_value("rj").text)
120
121 def test_can_get_value_attribute_for_a_option(self):
122 "should option have a value attribute"
123 self.assertEqual("rj", self.browser.find_option_by_value("rj")["value"])
124
125 def test_can_find_option_by_text(self):
126 "should provide a way to find select option by text"
127 self.assertEqual("rj", self.browser.find_option_by_text("Rio de Janeiro").value)
128
129 def test_can_select_a_option(self):
130 "should provide a way to select a option"
131 self.assertFalse(self.browser.find_option_by_value("rj").selected)
132 self.browser.select("uf", "rj")
133 self.assertTrue(self.browser.find_option_by_value("rj").selected)
134
135 def test_can_select_an_option_in_an_optgroup(self):
136 "should provide a way to select an option that is in an optgroup"
137 self.assertEqual(self.browser.find_by_name("food").value, "apples")
138 self.browser.select("food", "grapes")
139 self.assertEqual(self.browser.find_by_name("food").value, "grapes")
140
141 def test_can_select_a_option_via_element(self):
142 "should provide a way to select a option via element"
143 self.assertFalse(self.browser.find_option_by_value("rj").selected)
144 self.browser.find_by_name("uf").select("rj")
145 self.assertTrue(self.browser.find_option_by_value("rj").selected)
146
147 def test_can_check_a_checkbox(self):
148 "should provide a way to check a radio checkbox"
149 self.assertFalse(self.browser.find_by_name("some-check").checked)
150 self.browser.check("some-check")
151 self.assertTrue(self.browser.find_by_name("some-check").checked)
152
153 def test_check_keeps_checked_if_called_multiple_times(self):
154 "should keep a checkbox checked if check() is called multiple times"
155 self.assertFalse(self.browser.find_by_name("some-check").checked)
156 self.browser.check("some-check")
157 self.browser.check("some-check")
158 self.assertTrue(self.browser.find_by_name("some-check").checked)
159
160 def test_can_uncheck_a_checkbox(self):
161 "should provide a way to uncheck a radio checkbox"
162 self.assertTrue(self.browser.find_by_name("checked-checkbox").checked)
163 self.browser.uncheck("checked-checkbox")
164 self.assertFalse(self.browser.find_by_name("checked-checkbox").checked)
165
166 def test_uncheck_should_keep_unchecked_if_called_multiple_times(self):
167 "should keep a checkbox unchecked if uncheck() is called multiple times"
168 self.assertTrue(self.browser.find_by_name("checked-checkbox").checked)
169 self.browser.uncheck("checked-checkbox")
170 self.browser.uncheck("checked-checkbox")
171 self.assertFalse(self.browser.find_by_name("checked-checkbox").checked)
172
173 def test_can_fill_text_field_in_form(self):
174 "should provide a away to change field value"
175 self.browser.fill_form({"query": "new query"})
176 value = self.browser.find_by_name("query").value
177 self.assertEqual("new query", value)
178
179 def test_can_fill_password_field_in_form(self):
180 "should provide a way to change password value"
181 new_password = "new password"
182 self.browser.fill_form({"password": new_password})
183 value = self.browser.find_by_name("password").value
184 self.assertEqual(new_password, value)
185
186 def test_can_fill_more_than_one_field_in_form(self):
187 "should provide a away to change field value"
188 self.browser.fill("query", "my name")
189 self.assertFalse(self.browser.find_by_id("gender-m").checked)
190 self.assertFalse(self.browser.find_option_by_value("rj").selected)
191 self.assertFalse(self.browser.find_by_name("some-check").checked)
192 self.assertTrue(self.browser.find_by_name("checked-checkbox").checked)
193 self.browser.fill_form(
194 {
195 "query": "another new query",
196 "description": "Just another description value in the textarea",
197 "gender": "M",
198 "uf": "rj",
199 "some-check": True,
200 "checked-checkbox": False,
201 }
202 )
203 query_value = self.browser.find_by_name("query").value
204 self.assertEqual("another new query", query_value)
205 desc_value = self.browser.find_by_name("description").value
206 self.assertEqual("Just another description value in the textarea", desc_value)
207 self.assertTrue(self.browser.find_by_id("gender-m").checked)
208 self.assertTrue(self.browser.find_option_by_value("rj").selected)
209 self.assertTrue(self.browser.find_by_name("some-check").checked)
210 self.assertFalse(self.browser.find_by_name("checked-checkbox").checked)
211
212 def test_can_fill_tel_text_field(self):
213 "should provide a way to change a tel field value"
214 new_telephone = "555-0042"
215 self.browser.fill_form({"telephone": new_telephone})
216 value = self.browser.find_by_name("telephone").value
217 self.assertEqual(new_telephone, value)
218
219 def test_can_fill_unknown_text_field(self):
220 "should provide a way to change a unknown text field type that isn't specifically defined"
221 new_search_keyword = "foobar"
222 self.browser.fill_form({"search_keyword": new_search_keyword})
223 value = self.browser.find_by_name("search_keyword").value
224 self.assertEqual(new_search_keyword, value)
225
226 def test_can_fill_form_by_id(self):
227 "should be able to fill a form by its id"
228 self.browser.fill_form(
229 {"firstname": "John", "lastname": "Doe"}, form_id="login"
230 )
231 value = self.browser.find_by_name("firstname").value
232 self.assertEqual("John", value)
233
234 def test_can_clear_text_field_content(self):
235 self.browser.fill("query", "random query")
236 value = self.browser.find_by_name("query").value
237 self.assertEqual("random query", value)
238
239 self.browser.find_by_name("query").clear()
240 value = self.browser.find_by_name("query").value
241 self.assertFalse(value)
242
243 def test_can_clear_password_field_content(self):
244 self.browser.fill("password", "1nF4m310")
245 value = self.browser.find_by_name("password").value
246 self.assertEqual("1nF4m310", value)
247
248 self.browser.find_by_name("password").clear()
249 value = self.browser.find_by_name("password").value
250 self.assertFalse(value)
251
252 def test_can_clear_tel_field_content(self):
253 self.browser.fill("telephone", "5553743980")
254 value = self.browser.find_by_name("telephone").value
255 self.assertEqual("5553743980", value)
256
257 self.browser.find_by_name("telephone").clear()
258 value = self.browser.find_by_name("telephone").value
259 self.assertFalse(value)
0 import tempfile
1
2
3 class HTMLSnapshotTest(object):
4 def test_html_snapshot(self):
5 """Should take an html snapshot of the current page."""
6 filename = self.browser.html_snapshot()
7 self.assertTrue(tempfile.gettempdir() in filename)
8
9 def test_html_snapshot_with_prefix(self):
10 """Should add the prefix to the snapshot filename"""
11 filename = self.browser.html_snapshot(name="foobar")
12 self.assertTrue("foobar" in filename)
13
14 def test_html_snapshot_with_suffix(self):
15 """Should add the suffix to the snapshot filename"""
16 filename = self.browser.html_snapshot(suffix="xml")
17 self.assertEqual("xml", filename[-3:])
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class IFrameElementsTest(object):
8 def test_can_work_on_iframes_by_name(self):
9 """can work on iframes and switch back to the page"""
10 with self.browser.get_iframe("iframemodal-name") as frame:
11 value = frame.find_by_tag("h1").value
12 self.assertEqual(value, "IFrame Example Header")
13 value = self.browser.find_by_tag("h1").value
14 self.assertEqual("Example Header", value)
15
16 def test_can_work_on_iframes_by_id(self):
17 """can work on iframes and switch back to the page"""
18 with self.browser.get_iframe("iframemodal") as frame:
19 value = frame.find_by_tag("h1").value
20 self.assertEqual(value, "IFrame Example Header")
21 value = self.browser.find_by_tag("h1").value
22 self.assertEqual("Example Header", value)
23
24 def test_can_work_on_iframes_by_webelement(self):
25 """can work on iframes and switch back to the page"""
26 elem = self.browser.find_by_id('iframemodal').first
27
28 with self.browser.get_iframe(elem) as frame:
29 value = frame.find_by_tag("h1").value
30 self.assertEqual(value, "IFrame Example Header")
31 value = self.browser.find_by_tag("h1").value
32 self.assertEqual("Example Header", value)
33
34 def test_can_work_on_iframes_by_index(self):
35 """can work on iframes and switch back to the page"""
36 with self.browser.get_iframe(0) as frame:
37 value = frame.find_by_tag("h1").value
38 self.assertEqual(value, "IFrame Example Header")
39 value = self.browser.find_by_tag("h1").value
40 self.assertEqual("Example Header", value)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2015 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class IsElementPresentTest(object):
8 def test_is_element_present_by_css(self):
9 "should is element present by css verify if element is present"
10 self.browser.find_by_css(".add-async-element").click()
11 self.assertTrue(self.browser.is_element_present_by_css(".async-element"))
12
13 def test_is_element_present_by_css_using_a_custom_wait_time(self):
14 "should is element present by css verify if element is present using a custom wait time"
15 self.browser.find_by_css(".add-async-element").click()
16 self.assertTrue(
17 self.browser.is_element_present_by_css(".async-element2", wait_time=3)
18 )
19
20 def test_is_element_present_by_css_returns_false_if_element_is_not_present(self):
21 "should is element present by css returns False if element is not present"
22 self.assertFalse(self.browser.is_element_present_by_css(".async-elementzz"))
23
24 def test_is_element_not_present_by_css(self):
25 "should is element not present by css verify if element is not present"
26 self.assertTrue(self.browser.is_element_not_present_by_css(".async-element"))
27
28 def test_is_element_not_present_by_css_returns_false_if_element_is_present(self):
29 "should is element not present by css returns False if element is present"
30 self.assertFalse(self.browser.is_element_not_present_by_css("h1"))
31
32 def test_is_element_not_present_by_css_using_a_custom_wait_time(self):
33 "should is element not present by css verify if element is not present using a custom wait time"
34 self.assertTrue(
35 self.browser.is_element_not_present_by_css(".async-element", wait_time=3)
36 )
37
38 def test_is_element_present_by_xpath(self):
39 "should is element present by xpath verify if element is present"
40 self.browser.find_by_css(".add-async-element").click()
41 self.assertTrue(self.browser.is_element_present_by_xpath("//h4"))
42
43 def test_is_element_present_by_xpath_using_a_custom_wait_time(self):
44 "should is element present by xpath verify if element is present using a custom wait time"
45 self.browser.find_by_css(".add-async-element").click()
46 self.assertTrue(self.browser.is_element_present_by_xpath("//h5", wait_time=3))
47
48 def test_is_element_present_by_xpath_returns_false_if_element_is_not_present(self):
49 "should is element present by xpath returns false if element is not present"
50 self.assertTrue(self.browser.is_element_not_present_by_xpath("//h4"))
51
52 def test_is_element_not_present_by_xpath_returns_false_if_element_is_present(self):
53 """should is_element_not_present_by_xpath returns False if element is present"""
54 self.browser.find_by_css(".add-async-element").click()
55 self.browser.find_by_xpath("//h4")
56 assert not self.browser.is_element_not_present_by_xpath("//h4")
57
58 def test_is_element_not_present_by_xpath_using_a_custom_wait_time(self):
59 "should is element not present by xpath verify if element is not present using a custom wait time"
60 self.assertTrue(
61 self.browser.is_element_not_present_by_xpath("//h4", wait_time=3)
62 )
63
64 def test_is_element_present_by_tag(self):
65 "should is element present by tag verify if element is present"
66 self.browser.find_by_css(".add-async-element").click()
67 self.assertTrue(self.browser.is_element_present_by_tag("h4"))
68
69 def test_is_element_present_by_tag_using_a_custom_wait_time(self):
70 "should is element present by tag verify if element is present using a custom wait time"
71 self.browser.find_by_css(".add-async-element").click()
72 self.assertTrue(self.browser.is_element_present_by_tag("h4", wait_time=3))
73
74 def test_is_element_present_by_tag_returns_false_if_element_is_not_present(self):
75 "should is element present by tag returns false if element is not present"
76 self.assertFalse(self.browser.is_element_present_by_tag("h4"))
77
78 def test_is_element_not_present_by_tag(self):
79 "should is element not present by tag verify if element is not present"
80 self.assertTrue(self.browser.is_element_not_present_by_tag("h4"))
81
82 def test_is_element_not_present_by_tag_using_a_custom_wait_time(self):
83 "should is element not present by tag verify if element is not present using a custom wait time"
84 self.assertTrue(self.browser.is_element_not_present_by_tag("h4", wait_time=3))
85
86 def test_is_element_not_present_by_tag_returns_false_if_element_is_present(self):
87 """should is_element_not_present_by_tag returns False if element is present"""
88 self.browser.find_by_css(".add-async-element").click()
89 self.browser.find_by_tag("h4")
90 assert not self.browser.is_element_not_present_by_tag("h4")
91
92 def test_is_element_present_by_text(self):
93 "should is element present by text verify if element is present"
94 self.assertTrue(self.browser.is_element_present_by_text("Complex"))
95
96 def test_is_element_present_by_text_returns_false_if_element_is_not_present(self):
97 "should is element present by text verify if element is present"
98 self.assertFalse(self.browser.is_element_present_by_text("Not present"))
99
100 def test_is_element_not_present_by_text(self):
101 "should is element not present by text verify if element is not present"
102 self.assertTrue(self.browser.is_element_not_present_by_text("Not present"))
103
104 def test_is_element_not_present_by_text_returns_false_if_element_is_present(self):
105 "should is element not present by text returns False if element is present"
106 self.assertFalse(self.browser.is_element_not_present_by_text("Complex"))
107
108 def test_is_element_present_by_value(self):
109 "should is element present by value verify if element is present"
110 self.browser.find_by_css(".add-async-element").click()
111 self.assertTrue(self.browser.is_element_present_by_value("async-header-value"))
112
113 def test_is_element_present_by_value_using_a_custom_wait_time(self):
114 "should is element present by value verify if element is present using a custom wait time"
115 self.browser.find_by_css(".add-async-element").click()
116 self.assertTrue(
117 self.browser.is_element_present_by_value("async-header-value", wait_time=3)
118 )
119
120 def test_is_element_present_by_value_returns_false_if_element_is_not_present(self):
121 "should is element present by value returns False if element is not present"
122 self.assertFalse(self.browser.is_element_present_by_value("async-header-value"))
123
124 def test_is_element_not_present_by_value(self):
125 "should is element not present by value verify if element is not present"
126 self.assertTrue(
127 self.browser.is_element_not_present_by_value("async-header-value")
128 )
129
130 def test_is_element_not_present_by_value_using_a_custom_wait_time(self):
131 "should is element not present by value verify if element is not present using a custom wait time"
132 self.assertTrue(
133 self.browser.is_element_not_present_by_value(
134 "async-header-value", wait_time=3
135 )
136 )
137
138 def test_is_element_not_present_by_value_returns_false_if_element_is_present(self):
139 "should is element not present by value returns False if element is present"
140 self.assertFalse(self.browser.is_element_not_present_by_value("default value"))
141
142 def test_is_element_present_by_id(self):
143 "should is element present by id verify if element is present"
144 self.browser.find_by_css(".add-async-element").click()
145 self.assertTrue(self.browser.is_element_present_by_id("async-header"))
146
147 def test_is_element_present_by_id_using_a_custom_wait_time(self):
148 "should is element present by id verify if element is present using a custom wait time"
149 self.browser.find_by_css(".add-async-element").click()
150 self.assertTrue(
151 self.browser.is_element_present_by_id("async-header", wait_time=3)
152 )
153
154 def test_is_element_present_by_id_returns_false_if_element_is_not_present(self):
155 "should is element present by id returns False if element is not present"
156 self.assertFalse(self.browser.is_element_present_by_id("async-header"))
157
158 def test_is_element_not_present_by_id(self):
159 "should is element not present by id verify if element is not present"
160 self.assertTrue(self.browser.is_element_not_present_by_id("async-header"))
161
162 def test_is_element_not_present_by_id_using_a_custom_wait_time(self):
163 "should is element not present by id verify if element is not present using a custom wait time"
164 self.assertTrue(
165 self.browser.is_element_not_present_by_id("async-header", wait_time=3)
166 )
167
168 def test_is_element_not_present_by_id_returns_false_if_element_is_present(self):
169 """should is_element_not_present_by_id returns False if element is present"""
170 self.browser.find_by_css(".add-async-element").click()
171 self.browser.find_by_id("async-header")
172 assert not self.browser.is_element_not_present_by_id("async-header")
173
174
175 def test_is_element_present_by_name(self):
176 "should is element present by name verify if element is present"
177 self.browser.find_by_css(".add-async-element").click()
178 self.assertTrue(self.browser.is_element_present_by_name("async-input"))
179
180 def test_is_element_present_by_name_using_a_custom_wait_time(self):
181 "should is element present by name verify if element is present using a custom wait time"
182 self.browser.find_by_css(".add-async-element").click()
183 self.assertTrue(
184 self.browser.is_element_present_by_name("async-input", wait_time=3)
185 )
186
187 def test_is_element_present_by_name_returns_false_if_element_is_not_present(self):
188 "should is element present by name returns false if element is not present"
189 self.assertFalse(self.browser.is_element_present_by_name("async-input"))
190
191 def test_is_element_not_present_by_name(self):
192 "should is element not present by name verify if element is not present"
193 self.assertTrue(self.browser.is_element_not_present_by_name("async-input"))
194
195 def test_is_element_not_present_by_name_using_a_custom_wait_time(self):
196 "should is element not present by name verify if element is not present using a custom wait time"
197 self.assertTrue(
198 self.browser.is_element_not_present_by_name("async-input", wait_time=3)
199 )
200
201 def test_is_element_not_present_by_name_returns_false_if_element_is_present(self):
202 """should is_element_not_present_by_name returns False if element is present"""
203 self.browser.find_by_css(".add-async-element").click()
204 self.browser.find_by_name("async-input")
205 assert not self.browser.is_element_not_present_by_name("async-input")
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2015 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class IsElementPresentNoJSTest(object):
8 def test_is_element_present_by_css(self):
9 "should is element present by css verify if element is present"
10 self.assertTrue(self.browser.is_element_present_by_css("h1"))
11
12 def test_is_element_present_by_css_returns_false_if_element_is_not_present(self):
13 "should is element present by css returns False if element is not present"
14 self.assertFalse(self.browser.is_element_present_by_css(".async-elementzz"))
15
16 def test_is_element_not_present_by_css(self):
17 "should is element not present by css verify if element is not present"
18 self.assertTrue(self.browser.is_element_not_present_by_css(".async-element"))
19
20 def test_is_element_not_present_by_css_returns_false_if_element_is_present(self):
21 "should is element not present by css returns False if element is present"
22 self.assertFalse(self.browser.is_element_not_present_by_css("h1"))
23
24 def test_is_element_present_by_xpath(self):
25 "should is element present by xpath verify if element is present"
26 self.assertTrue(self.browser.is_element_present_by_xpath("//h1"))
27
28 def test_is_element_present_by_xpath_returns_false_if_element_is_not_present(self):
29 "should is element present by xpath returns false if element is not present"
30 self.assertTrue(self.browser.is_element_not_present_by_xpath("//h4"))
31
32 def test_is_element_not_present_by_xpath_returns_false_if_element_is_present(self):
33 "should is element not present by xpath returns false if element is present"
34 self.assertFalse(self.browser.is_element_not_present_by_xpath("//h1"))
35
36 def test_is_element_present_by_tag(self):
37 "should is element present by tag verify if element is present"
38 self.assertTrue(self.browser.is_element_present_by_tag("h1"))
39
40 def test_is_element_present_by_tag_returns_false_if_element_is_not_present(self):
41 "should is element present by tag returns false if element is not present"
42 self.assertFalse(self.browser.is_element_present_by_tag("h4"))
43
44 def test_is_element_not_present_by_tag(self):
45 "should is element not present by tag verify if element is not present"
46 self.assertTrue(self.browser.is_element_not_present_by_tag("h4"))
47
48 def test_is_element_not_present_by_tag_returns_false_if_element_is_present(self):
49 "should is element not present by tag returns false if element is present"
50 self.assertFalse(self.browser.is_element_not_present_by_tag("h1"))
51
52 def test_is_element_present_by_text(self):
53 "should is element present by text verify if element is present"
54 self.assertTrue(self.browser.is_element_present_by_text("Complex"))
55
56 def test_is_element_present_by_text_returns_false_if_element_is_not_present(self):
57 "should is element present by text verify if element is present"
58 self.assertFalse(self.browser.is_element_present_by_text("Not present"))
59
60 def test_is_element_not_present_by_text(self):
61 "should is element not present by text verify if element is not present"
62 self.assertTrue(self.browser.is_element_not_present_by_text("Not present"))
63
64 def test_is_element_not_present_by_text_returns_false_if_element_is_present(self):
65 "should is element not present by text returns False if element is present"
66 self.assertFalse(self.browser.is_element_not_present_by_text("Complex"))
67
68 def test_is_element_present_by_value(self):
69 "should is element present by value verify if element is present"
70 self.assertTrue(self.browser.is_element_present_by_value("M"))
71
72 def test_is_element_present_by_value_returns_false_if_element_is_not_present(self):
73 "should is element present by value returns False if element is not present"
74 self.assertFalse(self.browser.is_element_present_by_value("async-header-value"))
75
76 def test_is_element_not_present_by_value(self):
77 "should is element not present by value verify if element is not present"
78 self.assertTrue(
79 self.browser.is_element_not_present_by_value("async-header-value")
80 )
81
82 def test_is_element_not_present_by_value_returns_false_if_element_is_present(self):
83 "should is element not present by value returns False if element is present"
84 self.assertFalse(self.browser.is_element_not_present_by_value("default value"))
85
86 def test_is_element_present_by_id(self):
87 "should is element present by id verify if element is present"
88 self.assertTrue(self.browser.is_element_present_by_id("firstheader"))
89
90 def test_is_element_present_by_id_returns_false_if_element_is_not_present(self):
91 "should is element present by id returns False if element is not present"
92 self.assertFalse(self.browser.is_element_present_by_id("async-header"))
93
94 def test_is_element_not_present_by_id(self):
95 "should is element not present by id verify if element is not present"
96 self.assertTrue(self.browser.is_element_not_present_by_id("async-header"))
97
98 def test_is_element_not_present_by_id_returns_false_if_element_is_present(self):
99 "should is element not present by id returns False if element is present"
100 self.assertFalse(self.browser.is_element_not_present_by_id("firstheader"))
101
102 def test_is_element_present_by_name(self):
103 "should is element present by name verify if element is present"
104 self.assertTrue(self.browser.is_element_present_by_name("query"))
105
106 def test_is_element_present_by_name_returns_false_if_element_is_not_present(self):
107 "should is element present by name returns false if element is not present"
108 self.assertFalse(self.browser.is_element_present_by_name("async-input"))
109
110 def test_is_element_not_present_by_name(self):
111 "should is element not present by name verify if element is not present"
112 self.assertTrue(self.browser.is_element_not_present_by_name("async-input"))
113
114 def test_is_element_not_present_by_name_returns_false_if_element_is_present(self):
115 "should is element not present by name returns false if element is present"
116 self.assertFalse(self.browser.is_element_not_present_by_name("query"))
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2014 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class IsElementVisibleTest(object):
8 def test_is_element_visible_by_css(self):
9 "should is element visible by css verify if element is visible"
10 self.browser.find_by_css(".show-invisible-element").click()
11 self.assertTrue(self.browser.is_element_visible_by_css("#invisible"))
12
13 def test_is_element_visible_by_css_using_a_custom_wait_time(self):
14 "should is element visible by css verify if element is visible using a custom wait time"
15 self.browser.find_by_css(".show-invisible-element").click()
16 self.assertTrue(
17 self.browser.is_element_visible_by_css("#invisible", wait_time=3)
18 )
19
20 def test_is_element_visible_by_css_returns_false_if_element_is_not_visible(self):
21 "should is element visible by css returns False if element is not visible"
22 self.assertFalse(self.browser.is_element_visible_by_css("#invisible"))
23
24 def test_is_element_not_visible_by_css(self):
25 "should is element not visible by css verify if element is not visible"
26 self.assertTrue(self.browser.is_element_not_visible_by_css("#invisible"))
27
28 def test_is_element_not_visible_by_css_returns_false_if_element_is_visible(self):
29 "should is element not visible by css returns False if element is visible"
30 self.browser.find_by_css(".show-invisible-element").first.click()
31 self.assertFalse(self.browser.is_element_not_visible_by_css("#invisible"))
32
33 def test_is_element_not_visible_by_css_using_a_custom_wait_time(self):
34 "should is element not visible by css verify if element is not visible using a custom wait time"
35 self.assertTrue(
36 self.browser.is_element_not_visible_by_css("#invisible", wait_time=3)
37 )
38
39 def test_is_element_visible_by_xpath(self):
40 "should is element visible by xpath verify if element is visible"
41 self.browser.find_by_css(".show-invisible-element").click()
42 self.assertTrue(
43 self.browser.is_element_visible_by_xpath('//div[@id="invisible"]')
44 )
45
46 def test_is_element_visible_by_xpath_using_a_custom_wait_time(self):
47 "should is element visible by xpath verify if element is visible using a custom wait time"
48 self.browser.find_by_css(".show-invisible-element").click()
49 self.assertTrue(
50 self.browser.is_element_visible_by_xpath(
51 '//div[@id="invisible"]', wait_time=3
52 )
53 )
54
55 def test_is_element_visible_by_xpath_returns_false_if_element_is_not_visible(self):
56 "should is element visible by xpath returns false if element is not visible"
57 self.assertFalse(
58 self.browser.is_element_visible_by_xpath('//div[@id="invisible"]')
59 )
60
61 def test_is_element_not_visible_by_xpath(self):
62 "should is element not visible by xpath verify if element is not visible"
63 self.assertTrue(
64 self.browser.is_element_not_visible_by_xpath('//div[@id="invisible"]')
65 )
66
67 def test_is_element_not_visible_by_xpath_returns_false_if_element_is_visible(self):
68 "should is element not visible by xpath returns false if element is visible"
69 self.browser.find_by_css(".show-invisible-element").click()
70 self.assertFalse(
71 self.browser.is_element_not_visible_by_xpath('//div[@id="invisible"]')
72 )
73
74 def test_is_element_not_visible_by_xpath_using_a_custom_wait_time(self):
75 "should is element not visible by xpath verify if element is not visible using a custom wait time"
76 self.assertTrue(
77 self.browser.is_element_not_visible_by_xpath(
78 '//div[@id="invisible"]', wait_time=3
79 )
80 )
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5 from .fake_webapp import EXAMPLE_APP
6
7
8 class IsTextPresentTest(object):
9 def test_is_text_present(self):
10 "should verify if text is present"
11 self.assertTrue(self.browser.is_text_present("Example Header"))
12
13 def test_is_text_present_and_should_return_false(self):
14 "should verify if text is present and return false"
15 self.assertFalse(self.browser.is_text_present("Text that not exist"))
16
17 def test_is_text_present_and_should_wait_time(self):
18 "should verify if text is present and wait for five seconds"
19 self.browser.find_link_by_text("FOO").click()
20 self.assertTrue(self.browser.is_text_present("BAR!", wait_time=5))
21
22 def test_is_text_not_present(self):
23 "should verify if text is not present"
24 self.assertTrue(self.browser.is_text_not_present("Text that not exist"))
25
26 def test_is_text_not_present_and_should_return_false(self):
27 "should verify if text is not prasent and return false"
28 self.assertFalse(self.browser.is_text_not_present("Example Header"))
29
30 def test_is_text_not_present_and_should_wait_time(self):
31 "should verify if text is not present and wait for five seconds"
32 self.browser.find_link_by_text("FOO").click()
33 self.assertTrue(self.browser.is_text_not_present("another text", wait_time=5))
34
35 def test_is_text_present_no_body(self):
36 "should work properly (return false) even if there's no body"
37 self.browser.visit(EXAMPLE_APP + "no-body")
38 self.assertFalse(self.browser.is_text_present("No such text"))
39
40 def test_is_text_not_present_no_body(self):
41 "returns true if there's no body"
42 self.browser.visit(EXAMPLE_APP + "no-body")
43 self.assertTrue(self.browser.is_text_not_present("No such text"))
0 splinter mock file
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from .fake_webapp import EXAMPLE_APP
7
8
9 class MouseInteractionTest(object):
10 def test_mouse_over(self):
11 "Should be able to perform a mouse over on an element"
12 self.browser.visit(EXAMPLE_APP)
13 self.browser.find_by_css(".add-element-mouseover").mouse_over()
14 self.assertTrue(self.browser.is_element_present_by_id("what-is-your-name"))
15 self.browser.find_by_css(".add-element-mouseover").mouse_out()
16
17 def test_mouse_out(self):
18 "Should be able to perform a mouse out on an element"
19 self.browser.visit(EXAMPLE_APP)
20 element = self.browser.find_by_css(".add-element-mouseover")
21 element.mouse_over()
22 element.mouse_out()
23 self.assertTrue(self.browser.is_element_not_present_by_id("what-is-your-name"))
24
25 def test_mouse_out_top_left(self):
26 """Should be able to perform a mouse out on an element,
27 even if the element is at the top left corner of the screen.
28 """
29 self.browser.visit(EXAMPLE_APP + '/mouse')
30 element = self.browser.find_by_css(".add-element-mouseover")
31 element.mouse_over()
32 element.mouse_out()
33 self.assertTrue(self.browser.is_element_not_present_by_id("what-is-your-name"))
34
35 def test_double_click(self):
36 "double click should shows a hidden element"
37 self.browser.visit(EXAMPLE_APP)
38 button = self.browser.find_by_css(".db-button")
39 button.double_click()
40 element = self.browser.find_by_css(".should-be-visible-after-double-click")
41 self.assertTrue(element.visible)
42 self.assertTrue(self.browser.is_element_not_present_by_id("what-is-your-name"))
43
44 def test_right_click(self):
45 "should be able to perform a right click on an element"
46 self.browser.visit(EXAMPLE_APP)
47 element = self.browser.find_by_css(".right-clicable")
48 element.right_click()
49 self.assertEqual(
50 self.browser.find_by_css(".right-clicable").text, "right clicked"
51 )
52
53 def test_drag_and_drop(self):
54 """
55 should be able to perform a drag an element and drop in another element
56 """
57 droppable = self.browser.find_by_css(".droppable")
58 draggable = self.browser.find_by_css(".draggable")
59 draggable.drag_and_drop(droppable)
60 self.assertEqual(self.browser.find_by_css(".dragged").text, "yes")
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2015 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6
7 class PopupWindowsTest(object):
8 def test_lists_all_windows_as_window_instances(self):
9 self.browser.find_by_id("open-popup").click()
10 self.assertEqual(len(self.browser.windows), 2)
11 for window, handle in zip(
12 self.browser.windows, self.browser.driver.window_handles
13 ):
14 self.assertEqual(window.name, handle)
15
16 def test_current_is_a_window_instance_pointing_to_current_window(self):
17 self.assertEqual(
18 self.browser.windows.current.name, self.browser.driver.current_window_handle
19 )
20
21 def test_set_current_to_window_instance_sets_current_window(self):
22 last_current_window = self.browser.windows.current
23 self.browser.windows.current = self.browser.windows.current.next
24 self.assertNotEqual(self.browser.windows.current, last_current_window)
25
26 def test_next_prev_return_next_prev_windows(self):
27 self.browser.find_by_id("open-popup").click()
28 self.assertEqual(
29 self.browser.windows.current.next, self.browser.windows.current.prev
30 )
31 self.assertNotEqual(
32 self.browser.windows.current, self.browser.windows.current.next
33 )
34
35 def test_is_current_returns_true_if_current_window_else_false(self):
36 self.browser.find_by_id("open-popup").click()
37 self.assertTrue(self.browser.windows.current.is_current)
38 self.assertFalse(self.browser.windows.current.next.is_current)
39
40 # Close popup window
41 self.browser.windows.current.close_others()
42
43 def test_set_is_current_to_True_sets_window_to_current(self):
44 self.browser.find_by_id("open-popup").click()
45 next_window = self.browser.windows.current.next
46 self.assertFalse(next_window.is_current)
47 next_window.is_current = True
48 self.assertEqual(self.browser.windows.current, next_window)
49 self.assertTrue(next_window.is_current)
50
51 def test_get_window_by_index(self):
52 self.browser.find_by_id("open-popup").click()
53 self.assertEqual(
54 self.browser.windows[0].name, self.browser.driver.window_handles[0]
55 )
56
57 def test_get_window_by_name(self):
58 self.browser.find_by_id("open-popup").click()
59 window_handle = self.browser.driver.window_handles[0]
60 self.assertEqual(self.browser.windows[window_handle].name, window_handle)
61
62 def test_close_closes_window(self):
63 self.browser.find_by_id("open-popup").click()
64 current = self.browser.windows.current
65 current.next.close()
66 self.assertEqual(len(self.browser.windows), 1)
67 self.assertEqual(self.browser.windows.current, current)
68
69 def test_close_current_window_expect_previous_window_becomes_current(self):
70 self.browser.find_by_id("open-popup").click()
71 prev = self.browser.windows.current
72 current = prev.next
73 prev.next.is_current = True
74 current.close()
75 self.assertEqual(len(self.browser.windows), 1)
76 self.assertEqual(self.browser.windows.current, prev)
77
78 def test_close_others_expect_close_all_other_open_windows(self):
79 current = self.browser.windows.current
80 self.browser.find_by_id("open-popup").click()
81 current.close_others()
82 self.assertEqual(self.browser.windows[0], current)
83 self.assertEqual(len(self.browser.windows), 1)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2014 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import tempfile
7
8
9 class ScreenshotTest(object):
10 def test_take_screenshot(self):
11 "should take a screenshot of the current page"
12 filename = self.browser.screenshot()
13 self.assertTrue(tempfile.gettempdir() in filename)
14
15 def test_take_screenshot_with_prefix(self):
16 "should add the prefix to the screenshot file name"
17 filename = self.browser.screenshot(name="foobar")
18 self.assertTrue("foobar" in filename)
19
20 def test_take_screenshot_with_suffix(self):
21 "should add the suffix to the screenshot file name"
22 filename = self.browser.screenshot(suffix="jpeg")
23 self.assertEqual("jpeg", filename[-4:])
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Alert Example Title</title>
5 <script type="text/javascript" src="/static/jquery.min.js"></script>
6 <script type="text/javascript">
7 $(document).ready(function(){
8 $('.alerta').click(function() { alert('This is an alert example.'); });
9
10 $('.pergunta').click(function() { nome = prompt('What is your name?'); alert(nome); });
11
12 $('.confirmacao').click(function() {
13 answer = confirm('Should I continue?');
14 if (answer)
15 alert("You say I should");
16 else
17 alert("You say I should not");
18 });
19 })
20 </script>
21 </head>
22 <body>
23 <h1 class="alerta">Alert Example Title</h1>
24 <h2 class="pergunta">Prompt Example Subtitle</h2>
25 <h3 class="confirmacao">Confirm Example Subtitle</h3>
26 </body>
27 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Element covered by another element for 3 seconds</title>
5
6 <style>
7 .overlapper {
8 position: absolute;
9 background-color: #0000ff;
10 width: 200px;
11 height: 50px;
12 }
13
14 .overlapped {
15 display: block;
16 background-color: #ccc;
17 width: 200px;
18 height: 50px;
19 }
20 </style>
21
22 <script>
23 function addElement() {
24 var newDiv = document.createElement('div');
25 newDiv.id = 'added_container';
26 newDiv.style.width = '100px';
27 newDiv.style.height = '100px';
28 newDiv.style.backgroundColor = "green";
29 newDiv.innerHTML = 'Added';
30 document.getElementById("overlapped").appendChild(newDiv);
31 }
32
33 function moveBox() {
34 setTimeout(function () {
35 var newDiv = document.getElementById("overlapper")
36 newDiv.style.left = '400px';
37 }, 3000);
38 }
39
40 document.addEventListener('DOMContentLoaded', function() {
41 moveBox();
42
43 }, false);
44
45 </script>
46
47 </head>
48 <body>
49 <div id='overlapper' class='overlapper'>Moves in 5 seconds</div>
50 <a href='#' id='overlapped' class='overlapped' onclick="addElement()">Click Me</a>
51
52 <div id='overlapper2' class='overlapper'>Never moves</div>
53 <a href='#' id='overlapped2' class='overlapped'>Click Me</a>
54 </body>
55 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Example Title</title>
5 </head>
6 <body>
7 <h1 id="firstheader">IFrame Example Header</h1>
8 </body>
9 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Example Title</title>
5 <style>
6 .draggable {
7 display: block;
8 background-color: #0000ff;
9 width: 100px;
10 height: 30px;
11 }
12
13 .droppable {
14 display: block;
15 background-color: #ccc;
16 width: 200px;
17 height: 50px;
18 }
19 </style>
20 <script type="text/javascript" src="/static/jquery.min.js"></script>
21 <script type="text/javascript" src="/static/jquery-ui-1.8.16.custom.min.js"></script>
22 <script type="text/javascript">
23 $(document).ready(function() {
24 $(".draggable").draggable();
25 $(".droppable").droppable({
26 drop: function() {
27 $('.dragged').html('yes');
28 }
29 });
30 $("body").dblclick(function(){
31 $("body").css("background-color", "#ff0000");
32 });
33 $(".should-be-visible-after-double-click").hide();
34 $(".db-button").dblclick(function(){
35 $(".should-be-visible-after-double-click").show();
36 });
37 $(".add-async-element").click(function() {
38 setTimeout(function() {
39 $('body').append('<h4 id="async-header" value="async-header-value" class="async-element">async elment</h4>');
40 $('body').append('<input type="text" name="async-input" class="async-input" />');
41 }, 1200 );
42 setTimeout(function() {
43 $('body').append('<h5 id="async-header2" class="async-element2">async elment2</h5>');
44 $('body').append('<input type="text" name="async-input2" class="async-input2" />');
45 }, 2400 );
46 });
47
48 $('.right-clicable').bind('contextmenu', function(){
49 $(this).html('right clicked');
50 });
51
52 $(".remove-async-element").click(function() {
53 setTimeout(function() {
54 $('.async-element').remove();
55 $('.async-input').remove();
56 }, 1200 );
57 setTimeout(function() {
58 $('.async-element2').remove();
59 $('.async-input2').remove();
60 }, 2400 );
61 });
62
63 $(".show-invisible-element").click(function() {
64 $('#invisible').show();
65 });
66
67 $(".add-element-mouseover").mouseover(function () {
68 $('body').append('<label for="what-is-your-name" class="over-label">What is your name?</label>');
69 $('body').append('<input type="text" id="what-is-your-name" class="over-input" name="whatsname" />');
70 });
71
72 $(".add-element-mouseover").mouseout(function () {
73 $('.over-label').remove();
74 $('.over-input').remove();
75 });
76 });
77 </script>
78 </head>
79 <body>
80 <h1 id="firstheader">Example Header</h1>
81 <h1 id="firstheader">Example Last Header</h1>
82 <form action="name" method="GET">
83 <label for="query">Query</label>
84 <input type="text" name="q" />
85 <input type="text" name="input1" value="default value" />
86 <input type="text" name="input1" value="default last value" />
87 <input type="text" name="query" value="default value" />
88 <input name="typeless" value="default value" />
89 <input type="password" name="password" />
90 <input type="tel" name="telephone" />
91 <input type="search" name="search_keyword" />
92 <label for="send">Send</label>
93 <input type="submit" name="send" />
94 <input type="radio" name="gender" value="M" id="gender-m" /> Male
95 <input type="radio" name="gender" value="F" id="gender-f" /> Female
96 <input type="checkbox" name="some-check" value="choice" />
97 <input type="checkbox" name="checked-checkbox" value="choosed" checked="checked" />
98 <select name="uf">
99 <option value="mt">Mato Grosso</option>
100 <option value="rj">Rio de Janeiro</option>
101 </select>
102 <select name="food">
103 <optgroup label="Fruit">
104 <option value="apples" selected>Apples</option>
105 <option value="grapes">Grapes</option>
106 </optgroup>
107 <optgroup label="Vegetables">
108 <option value="carrots">Carrots</option>
109 <option value="celery">Celery</option>
110 </optgroup>
111 </select>
112 <select name="pets" multiple="multiple">
113 <option value="cat">Cat</option>
114 <option value="dog">Dog</option>
115 <option value="fish">Fish</option>
116 </select>
117 <label for="description">Description</label>
118 <textarea rows="3" cols="50" name="description"></textarea>
119 </form>
120 <form action="/upload" method="POST" enctype="multipart/form-data">
121 <input type="file" name="file">
122 <input type="submit" name="upload" value="submit" />
123 </form>
124
125 <form action="/post" method="POST">
126 <button type="submit" name="submit-button" value="submit-button-value">Submit button</button>
127 <button type="submit" name="submit-button-no-value">Submit button</button>
128 <button type="submit" class="submit-button-empty">Submit button</button>
129 <button type="submit" class="submit-button-no-name" value="submit-button-value">Submit button</button>
130 <input type="submit" name="submit-input" value="submit-input-value"/>
131 <input type="submit" name="submit-input-no-value"/>
132 <input type="submit" class="submit-input-empty"/>
133 <input type="submit" class="submit-input-no-name" value="submit-input-value"/>
134 </form>
135
136
137 <form action="/redirected" method="POST">
138 <button type="submit" name="redirect" value="submit">Go</button>
139 </form>
140
141
142 <form id='login'>
143 First name:<br>
144 <input type="text" name="firstname"><br>
145 Last name:<br>
146 <input type="text" name="lastname">
147 </form>
148
149
150 <a href="http://example.com/">Link for Example.com</a>
151 <a href="http://example.com/last">Link for Example.com</a>
152 <a href="http://example.com/">Link for last Example.com</a>
153 <div id="visible">visible</div>
154 <div id="invisible" style="display:none">invisible</div>
155 <a class="show-invisible-element" href="#">Show invisible element</a>
156 <div id="simple_text">my test text</div>
157 <div id="text_with_html">another <b>b</b>it of text</div>
158 <a href="http://localhost:5000/foo" id="foo">FOO</a>
159 <a href="http://localhost:5000/foo">
160 <i class="fa fa-fw fa-user"></i>
161 &nbsp;
162 <span>Complex</span>
163 <span>Quotation " marks</span>
164 <span style="display: inline;" class="env-DEV">Link</span>
165 </a>
166 <a href="http://localhost:5000/foo">A wordier (and last) link to FOO</a>
167 <a href="http://localhost:5000/bar">BAR: <span>first bar</span></a>
168 <a href="http://localhost:5000/bar">BAR: <span>second bar</span></a>
169 <a class="add-async-element" href="#">add async element</a>
170 <a class="remove-async-element" href="#">remove async element</a>
171 <a class="add-element-mouseover" href="#">addelement (mouseover)</a>
172 <button class="just-a-button"></button>
173 <iframe name= "iframemodal-name" id="iframemodal" src="/iframe"></iframe>
174 <div id="inside">
175 <h2>inside</h2>
176 <span class="inside">Complex</span>
177 <form>
178 <input id="visible" name="crazy-upload" type="text" value="crazy diamond" />
179 </form>
180 </div>
181 <a href="#" class="db-button" onClick="return false;">double click button</a>
182 <div class="should-be-visible-after-double-click">should-be-visible-after-double-click</div>
183 <div class="right-clicable">no right click</div>
184 <div class="draggable">draggable</div>
185 <div class="droppable">droppable</div>
186 <div class="dragged">no</div>
187 <div class="has-class-first has-class-middle has-class-end"></div>
188 <div id="html-property" class="outer html classes">inner <div class="inner-html">inner text</div> html test</div>
189 <a id="open-popup" href="javascript:poptastic('/popup')">Open pop-up window</a>
190 <script>
191 function poptastic(url) {
192 var pop = window.open(url, 'name', 'width=300, height=300');
193 if (window.focus) { pop.focus() }
194 }
195 </script>
196 <a id="pangram_pl" href="http://localhost:5000/pl">Jeżu klątw, spłódź Finom część gry hańb!</a>
197 <a id="pangram_ja" href="http://localhost:5000/ja">天 地 星 空</a>
198 <a id="pangram_ru" href="http://localhost:5000/ru">В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!</a>
199 <a id="pangram_eo" href="http://localhost:5000/eo">Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj.</a>
200 </body>
201 </html>
0 /*!
1 * jQuery UI 1.8.16
2 *
3 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
4 * Dual licensed under the MIT or GPL Version 2 licenses.
5 * http://jquery.org/license
6 *
7 * http://docs.jquery.com/UI
8 */
9 (function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
10 keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
11 this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
12 "overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
13 "mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
14 outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
15 "tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
16 a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
17 c.ui.isOverAxis(b,e,i)}})}})(jQuery);
18 ;/*!
19 * jQuery UI Widget 1.8.16
20 *
21 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
22 * Dual licensed under the MIT or GPL Version 2 licenses.
23 * http://jquery.org/license
24 *
25 * http://docs.jquery.com/UI/Widget
26 */
27 (function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
28 function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
29 d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
30 b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
31 "-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
32 c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
33 ;/*!
34 * jQuery UI Mouse 1.8.16
35 *
36 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
37 * Dual licensed under the MIT or GPL Version 2 licenses.
38 * http://jquery.org/license
39 *
40 * http://docs.jquery.com/UI/Mouse
41 *
42 * Depends:
43 * jquery.ui.widget.js
44 */
45 (function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
46 this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
47 this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
48 !(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
49 false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
50 ;/*
51 * jQuery UI Draggable 1.8.16
52 *
53 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
54 * Dual licensed under the MIT or GPL Version 2 licenses.
55 * http://jquery.org/license
56 *
57 * http://docs.jquery.com/UI/Draggables
58 *
59 * Depends:
60 * jquery.ui.core.js
61 * jquery.ui.mouse.js
62 * jquery.ui.widget.js
63 */
64 (function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
65 "original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
66 this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
67 this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
68 this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
69 _mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
70 false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
71 10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
72 !d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
73 a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
74 this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
75 10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
76 10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
77 (a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
78 "hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
79 10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
80 this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
81 !(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
82 if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
83 b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
84 526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
85 c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.16"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
86 h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
87 false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
88 this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
89 c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
90 this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
91 a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
92 "x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
93 c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
94 c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
95 width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
96 o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
97 p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
98 (p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
99 10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
100 ;/*
101 * jQuery UI Droppable 1.8.16
102 *
103 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
104 * Dual licensed under the MIT or GPL Version 2 licenses.
105 * http://jquery.org/license
106 *
107 * http://docs.jquery.com/UI/Droppables
108 *
109 * Depends:
110 * jquery.ui.core.js
111 * jquery.ui.widget.js
112 * jquery.ui.mouse.js
113 * jquery.ui.draggable.js
114 */
115 (function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
116 a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
117 this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
118 this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
119 d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
120 a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.16"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
121 switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
122 i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
123 "none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
124 a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parents(":not(body,html)").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);
125 if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parents(":not(body,html)").unbind("scroll.droppable");
126 a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
127 ;
0 /*!
1 * jQuery JavaScript Library v1.4.4
2 * http://jquery.com/
3 *
4 * Copyright 2010, John Resig
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
7 *
8 * Includes Sizzle.js
9 * http://sizzlejs.com/
10 * Copyright 2010, The Dojo Foundation
11 * Released under the MIT, BSD, and GPL Licenses.
12 *
13 * Date: Thu Nov 11 19:04:53 2010 -0500
14 */
15 (function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
16 h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
17 h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
18 "`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
19 e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
20 "margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
21 a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
22 C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
23 s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
24 j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
25 toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
26 -1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
27 if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
28 if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
29 b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
30 !F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
31 l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
32 z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
33 s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
34 s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
35 [];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
36 false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
37 k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
38 scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
39 false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
40 1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
41 "none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
42 c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
43 else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
44 a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
45 c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
46 a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
47 colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
48 1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
49 l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
50 "__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
51 if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
52 a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
53 attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
54 b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
55 c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
56 arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
57 d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
58 c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
59 w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
60 8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
61 "click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
62 d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
63 fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
64 d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
65 Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
66 c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
67 var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
68 "form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
69 xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
70 B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
71 "file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
72 0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
73 a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
74 1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
75 "object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
76 c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
77 (function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
78 [y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
79 break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
80 q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
81 l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
82 return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
83 B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
84 POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
85 i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
86 i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
87 "")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
88 m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
89 true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
90 g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
91 0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
92 "first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
93 i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
94 if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
95 g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
96 for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
97 i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
98 n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
99 function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
100 p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
101 t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
102 function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
103 c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
104 not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
105 h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
106 c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
107 2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
108 b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
109 e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
110 "<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
111 c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
112 wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
113 prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
114 this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
115 return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
116 else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
117 c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
118 b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
119 this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
120 prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
121 b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
122 1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
123 d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
124 jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
125 zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
126 h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
127 if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
128 d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
129 e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
130 ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
131 "object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
132 !this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
133 getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
134 script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
135 !T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
136 false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
137 A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
138 b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
139 c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
140 c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
141 encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
142 [b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
143 e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
144 if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
145 3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
146 d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
147 d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
148 "inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
149 1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
150 d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
151 Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
152 var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
153 this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
154 this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
155 c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
156 b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
157 h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
158 for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
159 parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
160 height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
161 f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
162 "marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
163 e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
164 c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
165 c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
166 b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Example Title</title>
5 <style>
6 .add-element-mouseover {
7 background-color: cyan;
8 position: absolute;
9 left: 0;
10 top: 0;
11 }
12
13 </style>
14 <script type="text/javascript" src="/static/jquery.min.js"></script>
15 <script type="text/javascript" src="/static/jquery-ui-1.8.16.custom.min.js"></script>
16 <script type="text/javascript">
17 $(document).ready(function() {
18 $(".add-element-mouseover").mouseover(function () {
19 $('body').append('<label for="what-is-your-name" class="over-label">What is your name?</label>');
20 $('body').append('<input type="text" id="what-is-your-name" class="over-input" name="whatsname" />');
21 });
22
23 $(".add-element-mouseover").mouseout(function () {
24 $('.over-label').remove();
25 $('.over-input').remove();
26 });
27 });
28 </script>
29 </head>
30 <body>
31 <a class="add-element-mouseover" href="#">Element at X:0 Y:0</a>
32
33 </body>
34 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>No body</title>
5 </head>
6 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Pop-up Window</title>
5 </head>
6 <body>
7 <h1>Pop-up Example Header</h1>
8 <a id="close-popup" href="javascript:window.close()">Close this window</a>
9 </body>
10 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <title>Redirect Location</title>
5 </head>
6 <body>
7 <h1>I just been redirected to this location.</h1>
8 </body>
9 </html>
0 <!DOCTYPE HTML>
1
2 <html>
3 <head>
4 <script type="text/javascript">
5 window.onload = function(f) {
6 var number = 0;
7 var name_input = document.getElementById('type-input-id');
8 var textarea_input = document.getElementById('type-textarea-id');
9 function showSuggest(e) {
10 var hidden_suggest = document.getElementById('suggest');
11 hidden_suggest.innerHTML += 'Hi, I am here #' + number + '! ';
12 number++;
13 };
14 name_input.onkeyup = showSuggest;
15 textarea_input.onkeyup = showSuggest;
16
17 };
18 </script>
19 </head>
20 <body>
21 <form method="GET" action="">
22 <input name="type-input" value="" id="type-input-id"/>
23 <textarea name="type-textarea" id="type-textarea-id" rows="3" cols="50"></textarea>
24 </form>
25
26 <span id="suggest"></span>
27 </body>
28 </html>
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from .fake_webapp import EXAMPLE_APP
7
8
9 class StatusCodeTest(object):
10 def test_should_visit_index_of_example_app_and_get_200_status_code(self):
11 self.browser.visit(EXAMPLE_APP)
12 self.assertEqual(200, self.browser.status_code)
13 self.assertEqual("200 - OK", str(self.browser.status_code))
14
15 def test_should_visit_error_of_example_app_and_not_get_200_status_code(self):
16 self.browser.visit(EXAMPLE_APP + 'error.html')
17 self.assertNotEqual(200, self.browser.status_code)
18 self.assertEqual('404 - Not Found', str(self.browser.status_code))
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 try:
7 import __builtin__ as builtins
8 except ImportError:
9 import builtins
10 import unittest
11 from imp import reload
12
13 from splinter.exceptions import DriverNotFoundError
14
15 from .fake_webapp import EXAMPLE_APP
16
17
18 class BrowserTest(unittest.TestCase):
19 def patch_driver(self, pattern):
20 self.old_import = builtins.__import__
21
22 def custom_import(name, *args, **kwargs):
23 if pattern in name:
24 return None
25 return self.old_import(name, *args, **kwargs)
26
27 builtins.__import__ = custom_import
28
29 def unpatch_driver(self, module):
30 builtins.__import__ = self.old_import
31 reload(module)
32
33 def browser_can_change_user_agent(self, webdriver):
34 from splinter import Browser
35
36 browser = Browser(driver_name=webdriver, user_agent="iphone")
37 browser.visit(EXAMPLE_APP + "useragent")
38 result = "iphone" in browser.html
39 browser.quit()
40
41 return result
42
43 def test_brower_can_still_be_imported_from_splinters_browser_module(self):
44 from splinter.browser import Browser # NOQA
45
46 def test_should_work_even_without_zope_testbrowser(self):
47 self.patch_driver("zope")
48 from splinter import browser
49
50 reload(browser)
51 self.assertNotIn("zope.testbrowser", browser._DRIVERS)
52 self.unpatch_driver(browser)
53
54 def test_should_raise_an_exception_when_browser_driver_is_not_found(self):
55 with self.assertRaises(DriverNotFoundError):
56 from splinter import Browser
57
58 Browser("unknown-driver")
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2015 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import os
7 import sys
8 import unittest
9 from six.moves.urllib import parse
10
11 import django
12 from splinter import Browser
13 from .base import BaseBrowserTests
14 from .fake_webapp import EXAMPLE_APP
15 from .is_element_present_nojs import IsElementPresentNoJSTest
16
17
18 sys.path.append("tests/fake_django")
19 os.environ["DJANGO_SETTINGS_MODULE"] = "settings"
20
21
22 django.setup()
23
24
25 class DjangoClientDriverTest(
26 BaseBrowserTests, IsElementPresentNoJSTest, unittest.TestCase
27 ):
28 @classmethod
29 def setUpClass(cls):
30 components = parse.urlparse(EXAMPLE_APP)
31 cls.browser = Browser(
32 "django",
33 wait_time=0.1,
34 client_SERVER_NAME=components.hostname,
35 client_SERVER_PORT=components.port,
36 )
37
38 def setUp(self):
39 self.browser.visit(EXAMPLE_APP)
40
41 @classmethod
42 def tearDownClass(self):
43 self.browser.quit()
44
45 def test_should_support_with_statement(self):
46 with Browser("django") as internet:
47 self.assertIsNotNone(internet)
48
49 def test_attach_file(self):
50 "should provide a way to change file field value"
51 file_path = os.path.join(
52 os.path.abspath(os.path.dirname(__file__)), "mockfile.txt"
53 )
54 self.browser.attach_file("file", file_path)
55 self.browser.find_by_name("upload").click()
56
57 html = self.browser.html
58 self.assertIn("text/plain", html)
59 self.assertIn(open(file_path, "rb").read().decode("utf-8"), html)
60
61 def test_forward_to_none_page(self):
62 "should not fail when trying to forward to none"
63 browser = Browser("django")
64 browser.visit(EXAMPLE_APP)
65 browser.forward()
66 self.assertEqual(EXAMPLE_APP, browser.url)
67 browser.quit()
68
69 def test_can_clear_password_field_content(self):
70 "django should not be able to clear"
71 with self.assertRaises(NotImplementedError):
72 self.browser.find_by_name("password").first.clear()
73
74 def test_can_clear_tel_field_content(self):
75 "django should not be able to clear"
76 with self.assertRaises(NotImplementedError):
77 self.browser.find_by_name("telephone").first.clear()
78
79 def test_can_clear_text_field_content(self):
80 "django should not be able to clear"
81 with self.assertRaises(NotImplementedError):
82 self.browser.find_by_name("query").first.clear()
83
84 def test_cant_switch_to_frame(self):
85 "django driver should not be able to switch to frames"
86 with self.assertRaises(NotImplementedError) as cm:
87 self.browser.get_iframe("frame_123")
88 self.fail()
89
90 e = cm.exception
91 self.assertEqual("django doesn't support frames.", e.args[0])
92
93 def test_simple_type(self):
94 """
95 django won't support type method
96 because it doesn't interact with JavaScript
97 """
98 with self.assertRaises(NotImplementedError):
99 self.browser.type("query", "with type method")
100
101 def test_simple_type_on_element(self):
102 """
103 django won't support type method
104 because it doesn't interact with JavaScript
105 """
106 with self.assertRaises(NotImplementedError):
107 self.browser.find_by_name("query").type("with type method")
108
109 def test_slowly_typing(self):
110 """
111 django won't support type method
112 because it doesn't interact with JavaScript
113 """
114 with self.assertRaises(NotImplementedError):
115 self.browser.type("query", "with type method", slowly=True)
116
117 def test_slowly_typing_on_element(self):
118 """
119 django won't support type method
120 on element because it doesn't interac with JavaScript
121 """
122 with self.assertRaises(NotImplementedError):
123 query = self.browser.find_by_name("query")
124 query.type("with type method", slowly=True)
125
126 def test_cant_mouseover(self):
127 "django should not be able to put the mouse over the element"
128 with self.assertRaises(NotImplementedError):
129 self.browser.find_by_css("#visible").mouse_over()
130
131 def test_cant_mouseout(self):
132 "django should not be able to mouse out of an element"
133 with self.assertRaises(NotImplementedError):
134 self.browser.find_by_css("#visible").mouse_out()
135
136 def test_links_with_nested_tags_xpath(self):
137 links = self.browser.find_by_xpath('//a/span[text()="first bar"]/..')
138 self.assertEqual(
139 len(links),
140 1,
141 'Found not exactly one link with a span with text "BAR ONE". %s'
142 % (map(lambda item: item.outer_html, links)),
143 )
144
145 def test_finding_all_links_by_non_ascii_text(self):
146 "should find links by non ascii text"
147 non_ascii_encodings = {
148 "pangram_pl": u"Jeżu klątw, spłódź Finom część gry hańb!",
149 "pangram_ja": u"天 地 星 空",
150 "pangram_ru": u"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
151 "pangram_eo": u"Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj.",
152 }
153 for key, text in non_ascii_encodings.items():
154 link = self.browser.find_link_by_text(text)
155 self.assertEqual(key, link["id"])
156
157
158 class DjangoClientDriverTestWithCustomHeaders(unittest.TestCase):
159 @classmethod
160 def setUpClass(cls):
161 custom_headers = {
162 "X-Splinter-Customheaders-1": "Hello",
163 "X-Splinter-Customheaders-2": "Bye",
164 }
165 cls.browser = Browser("django", custom_headers=custom_headers)
166
167 def test_create_a_phantomjs_with_custom_headers(self):
168 self.browser.visit(EXAMPLE_APP + "headers")
169 self.assertTrue(
170 self.browser.is_text_present("X-Splinter-Customheaders-1: Hello")
171 )
172 self.assertTrue(self.browser.is_text_present("X-Splinter-Customheaders-2: Bye"))
173
174 @classmethod
175 def tearDownClass(cls):
176 cls.browser.quit()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import unittest
7
8 from splinter.element_list import ElementList
9 from splinter.exceptions import ElementDoesNotExist
10
11
12 class Person(object):
13 """Very simple class, just for tests"""
14
15 def __init__(self):
16 self.current_action = None
17
18 def walk(self):
19 self.current_action = "walking"
20
21
22 class ElementListTest(unittest.TestCase):
23 def test_slice(self):
24 """ElementList should work with slice operations."""
25 elem_list = ElementList([1, 2, 3, 4, 5])
26 new_elem_list = elem_list[2:5]
27 self.assertTrue(len(new_elem_list) == 3)
28
29 def test_method_that_verifies_if_the_list_is_empty(self):
30 "should verify if the list is empty"
31 the_list = ElementList([1, 2, 3])
32 self.assertFalse(the_list.is_empty())
33 self.assertTrue(ElementList([]).is_empty())
34
35 def test_property_first_and_last(self):
36 """
37 should provide a \"first\" and a \"last\" properties
38 which returns the first and last element
39 """
40 the_list = ElementList([1, 2, 3])
41 self.assertEqual(the_list[0], the_list.first)
42 self.assertEqual(the_list[2], the_list.last)
43
44 def test_call_method_on_first_element(self):
45 """
46 when some method is missing on ElementList and
47 is present in element, it should be passed
48 """
49 the_list = ElementList([Person(), Person(), Person()])
50 the_list.walk()
51 the_person = the_list.first
52 self.assertEqual("walking", the_person.current_action)
53
54 def test_raise_exception_on_indexerror(self):
55 "should raise ElementDoesNotExist exception on IndexError"
56 with self.assertRaises(ElementDoesNotExist):
57 ElementList([]).first
58
59 def test_raise_exception_on_indexerror_with_unicode_query(self):
60 "should raise ElementDoesNotExist exception on IndexError"
61 with self.assertRaises(ElementDoesNotExist):
62 ElementList([], query=u".element[title=título]").first
63
64 def test_raise_attribute_error(self):
65 """
66 should raise AttributeError when trying to access
67 a non-existent method on list and element
68 """
69 with self.assertRaises(AttributeError):
70 the_list = ElementList([Person(), Person()])
71 the_list.talk()
72
73 def test_attribute_error_method_for_empty(self):
74 """
75 should raise ElementDoesNotExist when the list is empty
76 and someone tries to access a method or property on the child element.
77 """
78 with self.assertRaises(ElementDoesNotExist):
79 the_list = ElementList([])
80 the_list.unknown_method()
81
82 def test_attribute_error_content(self):
83 "should raise AttributeError with right content"
84 with self.assertRaises(AttributeError) as cm:
85 the_list = ElementList([Person(), Person()])
86 the_list.talk()
87
88 expected_message = "'ElementList' object has no attribute 'talk'"
89 e = cm.exception
90 self.assertEqual(expected_message, e.args[0])
91
92 def test_not_found_exception_with_query_and_method(self):
93 """
94 should receive the find method
95 and the query and use them in exception
96 """
97 with self.assertRaises(ElementDoesNotExist) as cm:
98 the_list = ElementList([], find_by="id", query="menu")
99 the_list.first
100
101 expected_message = 'no elements could be found with id "menu"'
102 e = cm.exception
103 self.assertEqual(expected_message, e.args[0])
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2014 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import os
7 import unittest
8
9 from splinter import Browser
10 from .base import BaseBrowserTests
11 from .fake_webapp import app, EXAMPLE_APP
12 from .is_element_present_nojs import IsElementPresentNoJSTest
13
14
15 class FlaskClientDriverTest(
16 BaseBrowserTests, IsElementPresentNoJSTest, unittest.TestCase
17 ):
18 @classmethod
19 def setUpClass(cls):
20 cls.browser = Browser("flask", app=app, wait_time=0.1)
21
22 def setUp(self):
23 self.browser.visit(EXAMPLE_APP)
24
25 @classmethod
26 def tearDownClass(self):
27 self.browser.quit()
28
29 def test_should_support_with_statement(self):
30 with Browser("flask", app=app) as internet:
31 self.assertIsNotNone(internet)
32
33 def test_attach_file(self):
34 "should provide a way to change file field value"
35 file_path = os.path.join(
36 os.path.abspath(os.path.dirname(__file__)), "mockfile.txt"
37 )
38 self.browser.attach_file("file", file_path)
39 self.browser.find_by_name("upload").click()
40
41 html = self.browser.html
42 self.assertIn("text/plain", html)
43 self.assertIn(open(file_path, "rb").read().decode("utf-8"), html)
44
45 def test_serialize_select_mutiple(self):
46 "should serialize a select with multiple values into a list"
47 self.browser.select("pets", ["cat", "dog"])
48 form = self.browser.find_by_name("send")._get_parent_form()
49 data = self.browser.serialize(form)
50 self.assertListEqual(data["pets"], ["cat", "dog"])
51
52 def test_forward_to_none_page(self):
53 "should not fail when trying to forward to none"
54 browser = Browser("flask", app=app)
55 browser.visit(EXAMPLE_APP)
56 browser.forward()
57 self.assertEqual(EXAMPLE_APP, browser.url)
58 browser.quit()
59
60 def test_can_clear_password_field_content(self):
61 "flask should not be able to clear"
62 with self.assertRaises(NotImplementedError):
63 self.browser.find_by_name("password").first.clear()
64
65 def test_can_clear_tel_field_content(self):
66 "flask should not be able to clear"
67 with self.assertRaises(NotImplementedError):
68 self.browser.find_by_name("telephone").first.clear()
69
70 def test_can_clear_text_field_content(self):
71 "flask should not be able to clear"
72 with self.assertRaises(NotImplementedError):
73 self.browser.find_by_name("query").first.clear()
74
75 def test_cant_switch_to_frame(self):
76 "flask should not be able to switch to frames"
77 with self.assertRaises(NotImplementedError) as cm:
78 self.browser.get_iframe("frame_123")
79 self.fail()
80
81 e = cm.exception
82 self.assertEqual("flask doesn't support frames.", e.args[0])
83
84 def test_simple_type(self):
85 """
86 flask won't support type method
87 because it doesn't interact with JavaScript
88 """
89 with self.assertRaises(NotImplementedError):
90 self.browser.type("query", "with type method")
91
92 def test_simple_type_on_element(self):
93 """
94 flask won't support type method
95 because it doesn't interact with JavaScript
96 """
97 with self.assertRaises(NotImplementedError):
98 self.browser.find_by_name("query").type("with type method")
99
100 def test_slowly_typing(self):
101 """
102 flask won't support type method
103 because it doesn't interact with JavaScript
104 """
105 with self.assertRaises(NotImplementedError):
106 self.browser.type("query", "with type method", slowly=True)
107
108 def test_slowly_typing_on_element(self):
109 """
110 flask won't support type method
111 on element because it doesn't interac with JavaScript
112 """
113 with self.assertRaises(NotImplementedError):
114 query = self.browser.find_by_name("query")
115 query.type("with type method", slowly=True)
116
117 def test_cant_mouseover(self):
118 "flask should not be able to put the mouse over the element"
119 with self.assertRaises(NotImplementedError):
120 self.browser.find_by_css("#visible").mouse_over()
121
122 def test_cant_mouseout(self):
123 "flask should not be able to mouse out of an element"
124 with self.assertRaises(NotImplementedError):
125 self.browser.find_by_css("#visible").mouse_out()
126
127 def test_links_with_nested_tags_xpath(self):
128 links = self.browser.find_by_xpath('//a/span[text()="first bar"]/..')
129 self.assertEqual(
130 len(links),
131 1,
132 'Found not exactly one link with a span with text "BAR ONE". %s'
133 % (map(lambda item: item.outer_html, links)),
134 )
135
136 def test_finding_all_links_by_non_ascii_text(self):
137 "should find links by non ascii text"
138 non_ascii_encodings = {
139 "pangram_pl": u"Jeżu klątw, spłódź Finom część gry hańb!",
140 "pangram_ja": u"天 地 星 空",
141 "pangram_ru": u"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
142 "pangram_eo": u"Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj.",
143 }
144 for key, text in non_ascii_encodings.items():
145 link = self.browser.find_link_by_text(text)
146 self.assertEqual(key, link["id"])
147
148 def test_redirection_on_post(self):
149 """
150 when submitting a form that POSTs to /redirected, browser should be redirected to GET /redirected-location?come=get&some=true
151 """
152 self.browser.find_by_name("redirect").click()
153 self.assertIn("I just been redirected to this location", self.browser.html)
154 self.assertIn("redirect-location?come=get&some=true", self.browser.url)
155
156
157 class FlaskClientDriverTestWithCustomHeaders(unittest.TestCase):
158 @classmethod
159 def setUpClass(cls):
160 custom_headers = {
161 "X-Splinter-Customheaders-1": "Hello",
162 "X-Splinter-Customheaders-2": "Bye",
163 }
164 cls.browser = Browser("flask", app=app, custom_headers=custom_headers)
165
166 def test_create_a_flask_client_with_custom_headers(self):
167 self.browser.visit(EXAMPLE_APP + "headers")
168 self.assertTrue(
169 self.browser.is_text_present("X-Splinter-Customheaders-1: Hello")
170 )
171 self.assertTrue(self.browser.is_text_present("X-Splinter-Customheaders-2: Bye"))
172
173 @classmethod
174 def tearDownClass(cls):
175 cls.browser.quit()
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import unittest
7
8 from splinter.meta import InheritedDocs
9
10
11 class SuperClass(InheritedDocs("_SuperClass", (object,), {})):
12 def say_hello(self):
13 """
14 Says hello
15 """
16 pass
17
18
19 class SubClass(SuperClass):
20 def say_hello(self):
21 print("hello")
22
23 @property
24 def name(self):
25 """
26 Stores the name
27 """
28 pass
29
30
31 class SubSubClass(SubClass):
32 def say_hello(self):
33 print("I can't say hello")
34
35 say_hi = say_hello
36
37 @property
38 def name(self):
39 pass
40
41
42 class MetaTest(unittest.TestCase):
43 def test_should_include_docs_from_superclass(self):
44 "should include doc from superclass"
45 self.assertEqual(SuperClass.say_hello.__doc__, SubClass.say_hello.__doc__)
46
47 def test_should_include_docs_from_any_class_in_hierarchy(self):
48 "should include doc from any class in hierarchy"
49 self.assertEqual(SuperClass.say_hello.__doc__, SubSubClass.say_hello.__doc__)
50
51 def test_change_docs_for_readonly_properties(self):
52 "should also change docs for readonly properties"
53 self.assertEqual(SubClass.name.__doc__, SubSubClass.name.__doc__)
54
55 def test_should_not_touch_the_class_type(self):
56 "shouldn't touch the type of the object"
57 self.assertEqual("SubSubClass", SubSubClass.__name__)
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import unittest
7
8 from splinter.request_handler.status_code import StatusCode
9
10
11 class RequestHandlerTestCase(unittest.TestCase):
12 def setUp(self):
13 self.status_code = StatusCode(200, "OK")
14
15 def test_should_receive_an_url_and_get_a_success_response(self):
16 self.assertTrue(self.status_code.is_success())
17
18 def test_should_compare_app_index_with_404_and_get_false(self):
19 self.assertNotEqual(self.status_code, 404)
0 import os
1
2 from .base import get_browser
3 from .fake_webapp import EXAMPLE_APP
4
5 import pytest
6
7
8 supported_browsers = ['chrome', 'firefox']
9
10
11 @pytest.mark.parametrize('browser_name', supported_browsers)
12 def test_attach_file(request, browser_name):
13 """Should provide a way to change file field value"""
14 browser = get_browser(browser_name)
15 request.addfinalizer(browser.quit)
16
17 file_path = os.path.join(
18 os.path.abspath(os.path.dirname(__file__)), "mockfile.txt"
19 )
20
21 browser.visit(EXAMPLE_APP)
22 browser.attach_file("file", file_path)
23 browser.find_by_name("upload").click()
24
25 html = browser.html
26 assert "text/plain" in html
27
28 with open(file_path, "r") as f:
29 assert str(f.read().encode("utf-8")) in html
30
31
32 @pytest.mark.parametrize('browser_name', supported_browsers)
33 def test_should_support_with_statement(browser_name):
34 with get_browser(browser_name):
35 pass
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import unittest
7
8 import pytest
9
10 from .fake_webapp import EXAMPLE_APP
11 from .base import WebDriverTests, get_browser
12
13
14 class ChromeBrowserTest(WebDriverTests, unittest.TestCase):
15 @pytest.fixture(autouse=True, scope='class')
16 def setup_browser(self, request):
17 request.cls.browser = get_browser('chrome', fullscreen=False)
18 request.addfinalizer(request.cls.browser.quit)
19
20 @pytest.fixture(autouse=True)
21 def visit_example_app(self, request):
22 self.browser.driver.set_window_size(1024, 768)
23 self.browser.visit(EXAMPLE_APP)
24
25
26 class ChromeBrowserFullscreenTest(WebDriverTests, unittest.TestCase):
27 @pytest.fixture(autouse=True, scope='class')
28 def setup_browser(self, request):
29 request.cls.browser = get_browser('chrome', fullscreen=True)
30 request.addfinalizer(request.cls.browser.quit)
31
32 @pytest.fixture(autouse=True)
33 def visit_example_app(self):
34 self.browser.visit(EXAMPLE_APP)
35
36
37 def test_should_support_with_statement_fullscreen():
38 with get_browser('chrome', fullscreen=True):
39 pass
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import os
7 import unittest
8
9 import pytest
10
11 from .fake_webapp import EXAMPLE_APP
12 from .base import WebDriverTests, get_browser
13
14
15 class FirefoxBrowserTest(WebDriverTests, unittest.TestCase):
16 @pytest.fixture(autouse=True, scope='class')
17 def setup_browser(self, request):
18 request.cls.browser = get_browser('firefox', fullscreen=False)
19 request.addfinalizer(request.cls.browser.quit)
20
21 @pytest.fixture(autouse=True)
22 def visit_example_app(self, request):
23 self.browser.visit(EXAMPLE_APP)
24
25
26 class FirefoxBrowserFullScreenTest(WebDriverTests, unittest.TestCase):
27 @pytest.fixture(autouse=True, scope='class')
28 def setup_browser(self, request):
29 request.cls.browser = get_browser('firefox', fullscreen=True)
30 request.addfinalizer(request.cls.browser.quit)
31
32 @pytest.fixture(autouse=True)
33 def visit_example_app(self):
34 self.browser.visit(EXAMPLE_APP)
35
36
37 def test_create_a_firefox_instance_with_extension(request):
38 extension_path = os.path.join(
39 os.path.abspath(os.path.dirname(__file__)), "firebug.xpi"
40 )
41 browser = get_browser('firefox', extensions=[extension_path])
42 request.addfinalizer(browser.quit)
43 "should be able to load an extension"
44 assert "[email protected]" in os.listdir(browser.driver.profile.extensionsDir)
45
46
47 def test_preference_set(request):
48 preferences = {
49 "dom.max_script_run_time": 360,
50 "devtools.inspector.enabled": True,
51 }
52 browser = get_browser('firefox', profile_preferences=preferences)
53 request.addfinalizer(browser.quit)
54
55 preferences = browser.driver.profile.default_preferences
56 assert "dom.max_script_run_time" in preferences
57
58 value = preferences.get("dom.max_script_run_time")
59 assert int(value) == 360
60
61
62 def test_capabilities_set(request):
63 browser = get_browser('firefox', capabilities={"pageLoadStrategy": "eager"})
64 request.addfinalizer(browser.quit)
65
66 capabilities = browser.driver.capabilities
67 assert "pageLoadStrategy" in capabilities
68 assert "eager" == capabilities.get("pageLoadStrategy")
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 try:
7 from urllib import urlopen
8 except ImportError:
9 from urllib.request import urlopen
10 import unittest
11
12 from splinter import Browser
13 from .fake_webapp import EXAMPLE_APP
14 from .base import WebDriverTests
15
16 import pytest
17
18
19 def selenium_server_is_running():
20 try:
21 from splinter.driver.webdriver.remote import WebDriver
22
23 page_contents = urlopen(WebDriver.DEFAULT_URL).read()
24 except IOError:
25 return False
26 return "WebDriver Hub" in page_contents
27
28
29 class RemoteBrowserTest(WebDriverTests, unittest.TestCase):
30 @pytest.fixture(autouse=True, scope='class')
31 def setup_browser(self, request):
32 request.cls.browser = Browser("remote")
33 request.addfinalizer(request.cls.browser.quit)
34
35 def setUp(self):
36 self.browser.visit(EXAMPLE_APP)
37
38 def test_support_with_statement(self):
39 "Remote should support with statement"
40 with Browser("remote"):
41 pass
42
43 def test_should_be_able_to_change_user_agent(self):
44 "Remote should not support custom user agent"
45 pass
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2013 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 import os
7 import unittest
8 import sys
9
10 from splinter import Browser
11 from .base import BaseBrowserTests
12 from .fake_webapp import EXAMPLE_APP
13 from .is_element_present_nojs import IsElementPresentNoJSTest
14
15
16 @unittest.skipIf(
17 sys.version_info[0] > 2,
18 "zope.testbrowser is not currently compatible with Python 3",
19 )
20 class ZopeTestBrowserDriverTest(
21 BaseBrowserTests, IsElementPresentNoJSTest, unittest.TestCase
22 ):
23 @classmethod
24 def setUpClass(cls):
25 cls.browser = Browser("zope.testbrowser", wait_time=0.1)
26
27 def setUp(self):
28 self.browser.visit(EXAMPLE_APP)
29
30 @classmethod
31 def tearDownClass(self):
32 self.browser.quit()
33
34 def test_should_support_with_statement(self):
35 with Browser("zope.testbrowser"):
36 pass
37
38 def test_attach_file(self):
39 "should provide a way to change file field value"
40 file_path = os.path.join(
41 os.path.abspath(os.path.dirname(__file__)), "mockfile.txt"
42 )
43 self.browser.attach_file("file", file_path)
44 self.browser.find_by_name("upload").click()
45
46 html = self.browser.html
47 self.assertIn("text/plain", html)
48 self.assertIn(open(file_path).read().encode("utf-8"), html)
49
50 def test_forward_to_none_page(self):
51 "should not fail when trying to forward to none"
52 browser = Browser("zope.testbrowser")
53 browser.visit(EXAMPLE_APP)
54 browser.forward()
55 self.assertEqual(EXAMPLE_APP, browser.url)
56 browser.quit()
57
58 def test_cant_switch_to_frame(self):
59 "zope.testbrowser should not be able to switch to frames"
60 with self.assertRaises(NotImplementedError) as cm:
61 self.browser.get_iframe("frame_123")
62 self.fail()
63
64 e = cm.exception
65 self.assertEqual("zope.testbrowser doesn't support frames.", e.args[0])
66
67 def test_simple_type(self):
68 """
69 zope.testbrowser won't support type method
70 because it doesn't interact with JavaScript
71 """
72 with self.assertRaises(NotImplementedError):
73 self.browser.type("query", "with type method")
74
75 def test_simple_type_on_element(self):
76 """
77 zope.testbrowser won't support type method
78 because it doesn't interact with JavaScript
79 """
80 with self.assertRaises(NotImplementedError):
81 self.browser.find_by_name("query").type("with type method")
82
83 def test_can_clear_password_field_content(self):
84 "zope.testbrowser should not be able to clear"
85 with self.assertRaises(NotImplementedError):
86 self.browser.find_by_name("password").first.clear()
87
88 def test_can_clear_tel_field_content(self):
89 "zope.testbrowser should not be able to clear"
90 with self.assertRaises(NotImplementedError):
91 self.browser.find_by_name("telephone").first.clear()
92
93 def test_can_clear_text_field_content(self):
94 "zope.testbrowser should not be able to clear"
95 with self.assertRaises(NotImplementedError):
96 self.browser.find_by_name("query").first.clear()
97
98 def test_slowly_typing(self):
99 """
100 zope.testbrowser won't support type method
101 because it doesn't interact with JavaScript
102 """
103 with self.assertRaises(NotImplementedError):
104 self.browser.type("query", "with type method", slowly=True)
105
106 def test_slowly_typing_on_element(self):
107 """
108 zope.testbrowser won't support type method
109 on element because it doesn't interac with JavaScript
110 """
111 with self.assertRaises(NotImplementedError):
112 query = self.browser.find_by_name("query")
113 query.type("with type method", slowly=True)
114
115 def test_cant_mouseover(self):
116 "zope.testbrowser should not be able to put the mouse over the element"
117 with self.assertRaises(NotImplementedError):
118 self.browser.find_by_css("#visible").mouse_over()
119
120 def test_cant_mouseout(self):
121 "zope.testbrowser should not be able to mouse out of an element"
122 with self.assertRaises(NotImplementedError):
123 self.browser.find_by_css("#visible").mouse_out()
124
125 def test_links_with_nested_tags_xpath(self):
126 links = self.browser.find_by_xpath('//a/span[text()="first bar"]/..')
127 self.assertEqual(
128 len(links),
129 1,
130 'Found not exactly one link with a span with text "BAR ONE". %s'
131 % (map(lambda item: item.outer_html, links)),
132 )
133
134 def test_finding_all_links_by_non_ascii_text(self):
135 "should find links by non ascii text"
136 non_ascii_encodings = {
137 "pangram_pl": u"Jeżu klątw, spłódź Finom część gry hańb!",
138 "pangram_ja": u"天 地 星 空",
139 "pangram_ru": u"В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!",
140 "pangram_eo": u"Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj.",
141 }
142 for key, text in non_ascii_encodings.iteritems():
143 link = self.browser.find_link_by_text(text)
144 self.assertEqual(key, link["id"])
0 # -*- coding: utf-8 -*-
1
2 # Copyright 2012 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 from .fake_webapp import EXAMPLE_APP
7
8
9 class SlowlyTypeTest(object):
10 def test_simple_type(self):
11 "should provide a away to change field value using type method"
12 self.browser.visit(EXAMPLE_APP)
13 self.browser.type("query", " with type method")
14 value = self.browser.find_by_name("query").value
15 self.assertEqual("default value with type method", value)
16
17 self.browser.type("description", "type into textarea")
18 value = self.browser.find_by_name("description").value
19 self.assertEqual("type into textarea", value)
20
21 def test_simple_type_on_element(self):
22 self.browser.visit(EXAMPLE_APP)
23 self.browser.find_by_name("query").type(" with type method")
24 value = self.browser.find_by_name("query").value
25 self.assertEqual("default value with type method", value)
26
27 self.browser.find_by_name("description").type("type into textarea")
28 value = self.browser.find_by_name("description").value
29 self.assertEqual("type into textarea", value)
30
31 def test_slowly_typing(self):
32 "should be able to slowly type some text in a field"
33 for name in ["type-input", "type-textarea"]:
34 self.browser.visit(EXAMPLE_APP + "type")
35 num = 0
36 num_max = 6
37 for key in self.browser.type(name, "typing", slowly=True):
38 self.assertEqual(self.browser.is_text_present("#%d" % num), True)
39 num += 1
40 self.assertEqual(num, num_max)
41
42 element = self.browser.find_by_name(name)
43 self.assertEqual(element.value, "typing")
44
45 def test_slowly_typing_on_element(self):
46 for name in ["type-input", "type-textarea"]:
47 self.browser.visit(EXAMPLE_APP + "type")
48 num = 0
49 num_max = 6
50 text_input = self.browser.find_by_name(name)
51 typing = text_input.type("typing", slowly=True)
52 for key in typing:
53 self.assertEqual(self.browser.is_text_present("#%d" % num), True)
54 num += 1
55 self.assertEqual(num, num_max)
56
57 element = self.browser.find_by_name(name)
58 self.assertEqual(element.value, "typing")
0 WRAPPED_SINGLE_QUOTE = '\"\'\"'
1 WRAPPED_DOUBLE_QUOTE = "\'\"\'"
2 XPATH_START = '//*[text()=concat('
3
4
5 class XpathConcatBuildTest(unittest.TestCase):
6 def test_build_xpath_concat_normal(self):
7 """Given a string with no escape characters
8 When I build a concat xpath
9 Then the xpath string is correctly built
10 """
11 result = _build_xpath_concat('No quotation marks.')
12 expected = "{}'No quotation marks.', \"\")]".format(XPATH_START)
13 self.assertEqual(result, expected)
14
15 def test_build_xpath_concat_double_quote(self):
16 """Given a string with double quotes
17 When I build a concat xpath
18 Then the xpath string is correctly built
19 """
20 result = _build_xpath_concat('Denis \"Snake\" Bélanger')
21 expected = "{}'Denis ',{},'Snake',{},' Bélanger', \"\")]".format(
22 XPATH_START, WRAPPED_DOUBLE_QUOTE, WRAPPED_DOUBLE_QUOTE,
23 )
24 self.assertEqual(result, expected)
25
26 def test_build_xpath_concat_single_quote(self):
27 """Given a string with single quotes
28 When I build a concat xpath
29 Then the xpath string is correctly built
30 """
31 result = _build_xpath_concat('Text with a single \' quotation mark.')
32 expected = "{}\"Text with a single \",{},\"quotation mark.\", \"\")]".format(
33 XPATH_START, WRAPPED_SINGLE_QUOTE,
34 )
35 self.assertEqual(result, expected)
36
37 def test_build_xpath_concat_multiple_types(self):
38 """Given a string with double quotes and single quotes
39 When I build a concat xpath
40 Then the xpath string is correctly built
41 """
42 result = _build_xpath_concat('Text with a single \' quotation mark and double " quotation mark.')
43 expected = "{}\"Text with a single \",{},\" quotation mark and double \",{},\' quotation mark.\', \"\")]".format(
44 XPATH_START, WRAPPED_SINGLE_QUOTE, WRAPPED_DOUBLE_QUOTE,
45 )
46 self.assertEqual(result, expected)
47
48 def test_build_xpath_concat_nested(self):
49 """Given a string with double quotes around a single quote
50 When I build a concat xpath
51 Then the xpath string is correctly built
52 """
53 result = _build_xpath_concat('A "real ol\' mess" of text.')
54 expected = "{}\'A \',{},\"real ol\",{},\" mess\",{},\' of text.\', \"\")]".format(
55 XPATH_START, WRAPPED_DOUBLE_QUOTE, WRAPPED_SINGLE_QUOTE, WRAPPED_DOUBLE_QUOTE,
56 )
57 self.assertEqual(result, expected)
0 [testenv]
1 deps = -rtest-requirements.txt
2 commands=
3 pytest {posargs}
0 #!/bin/bash
1
2 # Copyright 2016 splinter authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style
4 # license that can be found in the LICENSE file.
5
6 set -ev
7
8 if [ "${DRIVER}" = "tests/test_djangoclient.py" ]; then
9 pip install -q Django==${DJANGO_VERSION}
10 fi
11
12 if [ "${DRIVER}" = "tests/test_webdriver_remote.py" ]; then
13 sleep 1
14
15 wget https://selenium-release.storage.googleapis.com/3.10/selenium-server-standalone-3.10.0.jar -O selenium-server.jar
16 java -jar selenium-server.jar > /dev/null 2>&1 &
17 sleep 1
18 fi
19
20 if [ "${DRIVER}" = "tests/test_webdriver_chrome.py" ] || [ "${DRIVER}" = "tests/test_webdriver.py" ]; then
21 sleep 1
22
23 FILE=`mktemp`; wget "https://chromedriver.storage.googleapis.com/2.42/chromedriver_linux64.zip" -qO $FILE && unzip $FILE chromedriver -d ~; rm $FILE; chmod 777 ~/chromedriver;
24 export PATH=$HOME:$PATH
25 fi
26