New upstream version 0.13.0
Sophie Brun
4 years ago
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 | # 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 | # 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 | | |
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>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 | #!/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 |