├── .coveragerc
├── .gitattributes
├── .github
└── workflows
│ ├── publish.yaml
│ └── test.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── MANIFEST.in
├── README.rst
├── appveyor.yml
├── behave_webdriver
├── __init__.py
├── conditions.py
├── driver.py
├── fixtures.py
├── steps
│ ├── __init__.py
│ ├── actions.py
│ ├── actions_re.py
│ └── expectations.py
├── transformers.py
└── utils.py
├── ci
├── bfcache.reg
├── install.bat
└── runtests.bat
├── docs
├── Makefile
├── _static
│ └── behave-webdriver.gif
├── api.rst
├── browsers.rst
├── conf.py
├── examples.rst
├── index.rst
├── installation.rst
├── make.bat
├── quickstart.rst
├── requirements.txt
├── roadmap.rst
└── steps.rst
├── examples
├── minimal-project
│ └── features
│ │ ├── environment.py
│ │ ├── myFeature.feature
│ │ └── steps
│ │ └── my_steps.py
└── selenium-requests
│ └── features
│ ├── environment.py
│ ├── requests.feature
│ └── steps
│ └── selenium_steps.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── tests
├── demo-app
├── favicon.png
├── inc
│ └── script.js
├── index.html
├── page.html
└── runserver.py
├── experimental-features
├── elementPosition.feature
├── elementVisibility.feature
├── environment.py
├── githubSearch.feature.pending
├── login.feature.pending
├── pending.feature
└── steps
│ └── webdriver_steps.py
├── features
├── attribute.feature
├── baseUrl.feature
├── baseUrlWithParameterTransformation.feature
├── buttonPress.feature
├── checkTitle.feature
├── checkbox.feature
├── class.feature
├── click.feature
├── cookie.feature
├── drag.feature
├── elementExistence.feature
├── elementSize.feature
├── environment.py
├── focus.feature
├── form.feature
├── inputfield.feature
├── isEmpty.feature
├── isExisting.feature
├── modals.feature
├── moveTo.feature
├── multipleSelect.feature
├── sampleSnippets.feature
├── screenSize.feature
├── sctructure.feature
├── select.feature
├── steps
│ └── webdriver_steps.py
├── textComparison.feature
├── title.feature
├── transformation_fixture.feature
├── urlValidation.feature
├── wait.feature
├── window.feature
└── withinViewport.feature
└── unittests
├── test_css_xpath_discerning.py
├── test_fixture.py
├── test_from.py
└── test_parameter_trasformations.py
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = True
3 | source = behave_webdriver
4 |
5 | [report]
6 | exclude_lines =
7 | raise NotImplementedError
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | tests/features/* linguist-vendored
2 | tests/experimental-features/* linguest-vendored
3 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yaml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*.*.*'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 | - name: setup python
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: 3.9
18 |
19 | - name: build
20 | shell: bash
21 | run: |
22 | python -m pip install -r requirements.txt
23 | python -m pip install wheel
24 | python setup.py sdist bdist_wheel
25 | - name: Release PyPI
26 | shell: bash
27 | env:
28 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
29 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
30 | run: |
31 | pip install --upgrade twine
32 | twine upload dist/*
33 | - name: Release GitHub
34 | uses: softprops/action-gh-release@v1
35 | with:
36 | files: "dist/*"
37 | env:
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | jobs:
4 | test:
5 | name: test
6 | runs-on: ${{ matrix.os }}
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | os: [ubuntu-latest, macos-latest, windows-latest]
11 | browser: [Chrome, Firefox, Safari, Edge]
12 | exclude:
13 | - os: macos-latest
14 | browser: Edge
15 | - os: windows-latest
16 | browser: Safari
17 | - os: ubuntu-latest
18 | browser: Safari
19 | - os: ubuntu-latest
20 | browser: Edge
21 |
22 | steps:
23 | - name: Checkout code
24 | uses: actions/checkout@v2
25 | - uses: actions/setup-python@v2
26 | with:
27 | python-version: '3.9'
28 |
29 | - name: enable-safaridriver
30 | if: ${{ matrix.os == 'macos-latest' }}
31 | run: sudo safaridriver --enable
32 |
33 | - name: install-deps
34 | run: |
35 | python -m pip install -r ./requirements.txt
36 | python -m pip install coveralls pytest mock
37 |
38 | - name: run unittests
39 | env:
40 | BEHAVE_WEBDRIVER: ${{ matrix.browser }}
41 | BEHAVE_WEBDRIVER_HEADLESS: "1"
42 | ENV_BASE_URL: "http://127.0.0.1:8000/"
43 | run: |
44 | coverage run -m pytest tests/unittests
45 |
46 | - name: start-test-server
47 | shell: bash
48 | run: |
49 | cd tests/demo-app
50 | python ./runserver.py > /dev/null 2>&1 &
51 | cd ../..
52 |
53 | - name: regedit
54 | if: ${{ matrix.os == 'windows-latest' }}
55 | shell: cmd
56 | run: |
57 | regedit /s .\ci\bfcache.reg
58 |
59 | - name: run-behave-tests
60 | env:
61 | BEHAVE_WEBDRIVER: ${{ matrix.browser }}
62 | BEHAVE_WEBDRIVER_HEADLESS: "1"
63 | ENV_BASE_URL: "http://127.0.0.1:8000/"
64 | run: |
65 | coverage run -a -m behave tests/features
66 |
67 | - name: coveralls
68 | if: ${{ success() }}
69 | env:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71 | run: coveralls --service=github
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.so
6 | .Python
7 | env/
8 | build/
9 | develop-eggs/
10 | dist/
11 | downloads/
12 | eggs/
13 | .eggs/
14 | lib/
15 | lib64/
16 | parts/
17 | sdist/
18 | var/
19 | wheels/
20 | *.egg-info/
21 | .installed.cfg
22 | *.egg
23 | *.manifest
24 | *.spec
25 | pip-log.txt
26 | pip-delete-this-directory.txt
27 | htmlcov/
28 | .tox/
29 | .coverage
30 | .coverage.*
31 | .cache
32 | nosetests.xml
33 | coverage.xml
34 | *,cover
35 | .hypothesis/
36 | *.mo
37 | *.pot
38 | *.log
39 | local_settings.py
40 | instance/
41 | .webassets-cache
42 | .scrapy
43 | docs/_build/
44 | target/
45 | .ipynb_checkpoints
46 | .python-version
47 | celerybeat-schedule
48 | *.sage.py
49 | .env
50 | .venv
51 | venv/
52 | ENV/
53 | .spyderproject
54 | .ropeproject
55 | .idea/**/workspace.xml
56 | .idea/**/tasks.xml
57 | .idea/dictionaries
58 | .idea/**/dataSources/
59 | .idea/**/dataSources.ids
60 | .idea/**/dataSources.xml
61 | .idea/**/dataSources.local.xml
62 | .idea/**/sqlDataSources.xml
63 | .idea/**/dynamic.xml
64 | .idea/**/uiDesigner.xml
65 | .idea/**/gradle.xml
66 | .idea/**/libraries
67 | .idea/**/mongoSettings.xml
68 | *.iws
69 | /out/
70 | .idea_modules/
71 | atlassian-ide-plugin.xml
72 | com_crashlytics_export_strings.xml
73 | crashlytics.properties
74 | crashlytics-build.properties
75 | fabric.properties
76 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v4.4.0
4 | hooks:
5 | - id: check-yaml
6 | - id: end-of-file-fixer
7 | - id: trailing-whitespace
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Spencer Young
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.rst
3 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | behave-webdriver
2 | ================
3 |
4 | behave-webdriver is a step library intended to allow users to easily write `selenium`_ webdriver tests with the
5 | behave BDD testing framework.
6 | Inspired by the `webdriverio/cucumber-boilerplate`_ project.
7 |
8 | |docs| |status| |version| |pyversions| |coverage|
9 |
10 | For more details, see the `behave-webdriver documentation`_
11 |
12 | .. image:: https://raw.githubusercontent.com/spyoungtech/behave-webdriver/master/docs/_static/behave-webdriver.gif
13 |
14 |
15 |
16 |
17 | Installation
18 | ============
19 |
20 | Installation is easy via pip. The install will require ``behave`` and ``selenium``.::
21 |
22 | pip install behave-webdriver
23 |
24 | Using webdrivers
25 | ----------------
26 |
27 | Selenium requires that you provide executables for the webdriver you want to use. Further, unless you specify the path to
28 | the binary explicitly, selenium expects that this executable is in PATH. See these
29 | `driver installation notes`_ for more details.
30 |
31 |
32 | Usage
33 | =====
34 |
35 | Basic usage of this library with behave requires the following steps:
36 |
37 | 1. write your feature file
38 | 2. import the step implementations
39 | 3. set the ``behave_driver`` attribute on the behave ``context`` in your ``environment.py`` file.
40 | 4. run ``behave``
41 |
42 |
43 | Writing the feature file
44 | ------------------------
45 |
46 | .. code-block:: gherkin
47 |
48 | # my-minimal-project/features/myFeature.feature
49 | Feature: Sample Snippets test
50 | As a developer
51 | I should be able to use given text snippets
52 |
53 | Scenario: open URL
54 | Given the page url is not "http://webdriverjs.christian-bromann.com/"
55 | And I open the url "http://webdriverjs.christian-bromann.com/"
56 | Then I expect that the url is "http://webdriverjs.christian-bromann.com/"
57 | And I expect that the url is not "http://google.com"
58 |
59 |
60 | Scenario: click on link
61 | Given the title is not "two"
62 | And I open the url "http://webdriverjs.christian-bromann.com/"
63 | When I click on the link "two"
64 | Then I expect that the title is "two"
65 |
66 |
67 | Importing the step implementations
68 | ----------------------------------
69 |
70 | In order for your feature file steps to match our step implementations, behave needs to find them in your project.
71 | This is as simple as importing our step definitions into your own step implementation file.
72 |
73 | .. code-block:: python
74 |
75 | # features/steps/webdriver_example.py
76 | from behave_webdriver.steps import *
77 |
78 |
79 | For more information about `step implementations`_, see the behave tutorial.
80 |
81 |
82 | Set behave_driver in the environment
83 | ------------------------------------
84 |
85 | Our step implementations specifically look at the behave context for a ``behave_driver`` attribute to use to run your tests.
86 | In order for that to work, you'll have to provide this attribute in your ``environment.py`` file.
87 |
88 | .. code-block:: python
89 |
90 | # features/environment.py
91 | import behave_webdriver
92 |
93 | def before_all(context):
94 | context.behave_driver = behave_webdriver.Chrome()
95 |
96 | def after_all(context):
97 | # cleanup after tests run
98 | context.behave_driver.quit()
99 |
100 |
101 | The webdriver classes provided by behave-webdriver inherit from selenium's webdriver classes, so they will accept all
102 | same positional and keyword arguments that selenium accepts.
103 |
104 | Some webdrivers, such as Chrome, provide special classmethods like ``Chrome.headless`` which instantiates ``Chrome`` with
105 | options to run headless. This is useful, for example in headless testing environments.
106 |
107 | .. code-block:: python
108 |
109 | def before_all(context):
110 | context.behave_driver = behave_webdriver.Chrome.headless()
111 |
112 |
113 | Using a fixture
114 | ^^^^^^^^^^^^^^^
115 |
116 | *New in 0.1.1*
117 |
118 | You may also find it convenient to use a fixture to setup your driver as well. For example, to use our fixture with Firefox
119 |
120 | .. code-block:: python
121 |
122 | from behave_webdriver.fixtures import fixture_browser
123 | def before_all(context):
124 | use_fixture(fixture_browser, context, webdriver='Firefox')
125 |
126 | This will also ensure that the browser is torn down at the corresponding `cleanup point`_.
127 |
128 | .. _cleanup point: http://behave.readthedocs.io/en/stable/fixtures.html#fixture-cleanup-points
129 |
130 |
131 | Run behave
132 | ----------
133 |
134 | Then run the tests, just like any other behave test
135 |
136 | .. code-block:: bash
137 |
138 | behave
139 |
140 | You should then see an output as follows::
141 |
142 | Feature: Sample Snippets test # features/myFeature.feature:2
143 | As a developer
144 | I should be able to use given text snippets
145 | Scenario: open URL # features/myFeature.feature:6
146 | Given the page url is not "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:136 0.012s
147 | And I open the url "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:10 1.414s
148 | Then I expect that the url is "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/then.py:102 0.007s
149 | And I expect that the url is not "http://google.com" # ../../behave_webdriver/steps/then.py:102 0.007s
150 |
151 | Scenario: click on link # features/myFeature.feature:13
152 | Given the title is not "two" # ../../behave_webdriver/steps/given.py:81 0.006s
153 | And I open the url "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:10 0.224s
154 | When I click on the link "two" # ../../behave_webdriver/steps/when.py:21 0.622s
155 | Then I expect that the title is "two" # ../../behave_webdriver/steps/then.py:10 0.006s
156 |
157 | 1 feature passed, 0 failed, 0 skipped
158 | 2 scenarios passed, 0 failed, 0 skipped
159 | 8 steps passed, 0 failed, 0 skipped, 0 undefined
160 | Took 0m2.298s
161 |
162 | Advanced usage; extending behave-webdriver
163 | ==========================================
164 |
165 | behave-webdriver is designed with **you** in-mind. You are free to extend the behavior of our webdriver classes to suit your
166 | unique needs. You can subclass our webdriver classes, use a custom selenium webdriver, write your own mixin, or use
167 | a mixin somebody else provides for selenium.
168 |
169 |
170 | Example: selenium-requests
171 | --------------------------
172 |
173 | `selenium-requests`_ is a preexisting project that adds functionality of the popular ``requests`` library to selenium.
174 | It is simple to use ``selenium-requests`` with behave-webdriver.
175 | The following, and other examples, are available in the repo ``examples`` directory and in the full documentation.
176 |
177 | .. code-block:: python
178 |
179 | # examples/selenium-requests/features/environment.py
180 | from selenium import webdriver # or any custom webdriver
181 | from behave_webdriver.driver import BehaveDriverMixin
182 | from seleniumrequests import RequestMixin # or your own mixin
183 |
184 | class BehaveRequestDriver(BehaveDriverMixin, RequestMixin, webdriver.Chrome):
185 | pass
186 |
187 | def before_all(context):
188 | context.behave_driver = BehaveRequestDriver()
189 | .. code-block:: python
190 |
191 | # examples/selenium-requests/features/steps/selenium_steps.py
192 | from behave import *
193 | from behave_webdriver.steps import *
194 | from urllib.parse import urljoin
195 |
196 | @given('I send a {method} request to the page "{page}"')
197 | def send_request_page(context, method, page):
198 | url = urljoin(context.base_url, page)
199 | context.response = context.behave_driver.request(method, url)
200 |
201 | @then('I expect the response text contains "{text}"')
202 | def check_response_text_contains(context, text):
203 | assert text in context.response.text
204 | .. code-block:: gherkin
205 |
206 | # examples/selenium-requests/features/selenium-requests.feature
207 | Feature: Using selenium-requests
208 | As a developer
209 | I should be able to extend behave-webdriver with selenium-requests
210 |
211 | Scenario: use selenium-requests with behave-webdriver
212 | # use a behave-webdriver step
213 | Given the base url is "http://127.0.0.1:8000"
214 | # use your own steps using selenium-requests features
215 | Given I send a GET request to the page "/"
216 | Then I expect the response text contains "
DEMO APP
"
217 |
218 | Assuming you're in the repository root (and have the demo app running) just run like any other project with ``behave``
219 |
220 | Results ✨
221 | ^^^^^^^^^^
222 |
223 | .. code-block::
224 |
225 | (behave-webdriver) $ behave examples/selenium-requests/features
226 |
227 | DevTools listening on ws://127.0.0.1:12646/devtools/browser/1fe75b44-1c74-49fa-8e77-36c54d50cd24
228 | Feature: Using selenium-requests # examples/selenium-requests/features/requests.feature:1
229 | As a developer
230 | I should be able to extend behave-webdriver with selenium-requests
231 | Scenario: use selenium-requests with behave-webdriver # examples/selenium-requests/features/requests.feature:6
232 | Given the base url is "http://127.0.0.1:8000" # behave_webdriver/steps/actions.py:162
233 | Given I send a GET request to the page "/" # examples/selenium-requests/features/steps/selenium_steps.py:11
234 | Then I expect the response text contains "
DEMO APP
" # examples/selenium-requests/features/steps/selenium_steps.py:17
235 |
236 | 1 feature passed, 0 failed, 0 skipped
237 | 1 scenario passed, 0 failed, 0 skipped
238 | 3 steps passed, 0 failed, 0 skipped, 0 undefined
239 | Took 0m1.385s
240 |
241 |
242 | Getting help ⛑
243 | --------------
244 |
245 | If you have any unanswered questions or encounter any issues, please feel welcome to raise an issue. We recognize that
246 | testers come in all different shapes, sizes, and backgrounds. We welcome any and all questions that may arise from using
247 | this library.
248 |
249 | Contributing
250 | ------------
251 |
252 | Contributions are very much welcomed! If you have ideas or suggestions, please raise an issue or submit a PR.
253 |
254 | List of step definitions 📝
255 | ===========================
256 |
257 | We support all the steps supported by webdriverio/cucumber-boilerplate.
258 | We also support some additional niceties and plan to add more step definitions.
259 |
260 |
261 | Given Steps 👷
262 | --------------
263 |
264 | - ``I open the site "([^"]*)?"``
265 | - ``I open the url "([^"]*)?"``
266 | - ``I have a screen that is ([\d]+) by ([\d]+) pixels``
267 | - ``I have a screen that is ([\d]+) pixels (broad|tall)``
268 | - ``I have closed all but the first (window|tab)``
269 | - ``I pause for (\d+)*ms``
270 | - ``a (alertbox|confirmbox|prompt) is( not)* opened``
271 | - ``the base url is "([^"]*)?"``
272 | - ``the checkbox "([^"]*)?" is( not)* checked``
273 | - ``the cookie "([^"]*)?" contains( not)* the value "([^"]*)?"``
274 | - ``the cookie "([^"]*)?" does( not)* exist``
275 | - ``the element "([^"]*)?" contains( not)* the same text as element "([^"]*)?"``
276 | - ``the element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)``
277 | - ``the element "([^"]*)?" is( not)* empty``
278 | - ``the element "([^"]*)?" is( not)* enabled``
279 | - ``the element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis``
280 | - ``the element "([^"]*)?" is( not)* selected``
281 | - ``the element "([^"]*)?" is( not)* visible``
282 | - ``the element "([^"]*)?"( not)* contains any text``
283 | - ``the element "([^"]*)?"( not)* contains the text "([^"]*)?"``
284 | - ``the element "([^"]*)?"( not)* matches the text "([^"]*)?"``
285 | - ``the page url is( not)* "([^"]*)?"``
286 | - ``the title is( not)* "([^"]*)?"``
287 | - ``the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"``
288 | - ``there is (an|no) element "([^"]*)?" on the page``
289 |
290 |
291 |
292 | When Steps ▶️
293 | -------------
294 |
295 | - ``I open the site "([^"]*)?"``
296 | - ``I open the url "([^"]*)?"``
297 | - ``I accept the (alertbox|confirmbox|prompt)``
298 | - ``I add "{value}" to the inputfield "{element}"``
299 | - ``I clear the inputfield "{element}"``
300 | - ``I click on the button "{element}"``
301 | - ``I click on the element "{element}"``
302 | - ``I click on the link "{link_text}"``
303 | - ``I close the last opened (tab|window)``
304 | - ``I delete the cookie "{cookie_key}"``
305 | - ``I dismiss the (alertbox|confirmbox|prompt)``
306 | - ``I doubleclick on the element "{element}"``
307 | - ``I drag element "{from_element}" to element "{to_element}"``
308 | - ``I enter "([^"]*)?" into the (alertbox|confirmbox|prompt)``
309 | - ``I focus the last opened (tab|window)``
310 | - ``I move to element "{element}" with an offset of {x_offset:d},{y_offset:d}``
311 | - ``I move to element "{element}"``
312 | - ``I pause for {milliseconds:d}ms``
313 | - ``I press "{key}"``
314 | - ``I scroll to element "{element}"``
315 | - ``I select the option with the (text|value|name) "([^"]*)?" for element "([^"]*)?"``
316 | - ``I select the {nth} option for element "{element}"``
317 | - ``I set "{value}" to the inputfield "{element}"``
318 | - ``I set a cookie "{cookie_key}" with the content "{value}"``
319 | - ``I submit the form "{element}"``
320 |
321 | Then Steps ✔️
322 | -------------
323 |
324 | - ``I expect the screen is ([\d]+) by ([\d]+) pixels``
325 | - ``I expect a new (window|tab) has( not)* been opened``
326 | - ``I expect that a (alertbox|confirmbox|prompt) is( not)* opened``
327 | - ``I expect that a (alertbox|confirmbox|prompt)( not)* contains the text "([^"]*)?"``
328 | - ``I expect that checkbox "([^"]*)?" is( not)* checked``
329 | - ``I expect that cookie "([^"]*)?"( not)* contains "([^"]*)?"``
330 | - ``I expect that cookie "([^"]*)?"( not)* exists``
331 | - ``I expect that element "([^"]*)?" (has|does not have) the class "([^"]*)?"``
332 | - ``I expect that element "([^"]*)?" becomes( not)* visible``
333 | - ``I expect that element "([^"]*)?" does( not)* exist``
334 | - ``I expect that element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)``
335 | - ``I expect that element "([^"]*)?" is( not)* empty``
336 | - ``I expect that element "([^"]*)?" is( not)* enabled``
337 | - ``I expect that element "([^"]*)?" is( not)* focused``
338 | - ``I expect that element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis``
339 | - ``I expect that element "([^"]*)?" is( not)* selected``
340 | - ``I expect that element "([^"]*)?" is( not)* visible``
341 | - ``I expect that element "([^"]*)?" is( not)* within the viewport``
342 | - ``I expect that element "([^"]*)?"( not)* contains any text``
343 | - ``I expect that element "([^"]*)?"( not)* contains the same text as element "([^"]*)?"``
344 | - ``I expect that element "([^"]*)?"( not)* contains the text "([^"]*)?"``
345 | - ``I expect that element "([^"]*)?"( not)* matches the text "([^"]*)?"``
346 | - ``I expect that the path is( not)* "([^"]*)?"``
347 | - ``I expect that the title is( not)* "([^"]*)?"``
348 | - ``I expect that the url is( not)* "([^"]*)?"``
349 | - ``I expect that the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"``
350 | - ``I expect the url "([^"]*)?" is opened in a new (tab|window)``
351 | - ``I expect the url to( not)* contain "([^"]*)?"``
352 | - ``I wait on element "([^"]*)?"(?: for (\d+)ms)*(?: to( not)* (be checked|be enabled|be selected|be visible|contain a text|contain a value|exist))*``
353 |
354 |
355 | Acknowledgements ❤️
356 | ===================
357 |
358 | Special thanks to the authors and contributors of the `webdriverio/cucumber-boilerplate`_ project
359 |
360 | Special thanks to the authors and contributors of `behave`_
361 |
362 |
363 |
364 |
365 | .. _selenium-requests: https://github.com/cryzed/Selenium-Requests
366 |
367 | .. _environment controls: http://behave.readthedocs.io/en/stable/tutorial.html#environmental-controls
368 |
369 | .. _fixtures: http://behave.readthedocs.io/en/stable/fixtures.html
370 |
371 | .. _step implementations: http://behave.readthedocs.io/en/stable/tutorial.html#python-step-implementations
372 |
373 | .. _driver installation notes: http://selenium-python.readthedocs.io/installation.html#drivers
374 |
375 | .. _behave-webdriver documentation: http://behave-webdriver.readthedocs.io/en/stable/
376 |
377 | .. _selenium: https://github.com/SeleniumHQ/selenium
378 |
379 | .. _behave: https://github.com/behave/behave
380 |
381 | .. _webdriverio/cucumber-boilerplate: https://github.com/webdriverio/cucumber-boilerplate
382 |
383 |
384 |
385 | .. |docs| image:: https://readthedocs.org/projects/behave-webdriver/badge/?version=stable
386 | :target: http://behave-webdriver.readthedocs.io/en/stable/
387 |
388 | .. |status| image:: https://travis-ci.org/spyoungtech/behave-webdriver.svg?branch=master
389 | :target: https://travis-ci.org/spyoungtech/behave-webdriver
390 |
391 | .. |version| image:: https://img.shields.io/pypi/v/behave-webdriver.svg?colorB=blue
392 | :target: https://pypi.org/project/behave-webdriver/
393 |
394 | .. |pyversions| image:: https://img.shields.io/pypi/pyversions/behave-webdriver.svg?
395 | :target: https://pypi.org/project/behave-webdriver/
396 |
397 | .. |coverage| image:: https://coveralls.io/repos/github/spyoungtech/behave-webdriver/badge.svg
398 | :target: https://coveralls.io/github/spyoungtech/behave-webdriver
399 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 |
3 | environment:
4 | BEHAVE_WEBDRIVER: IE
5 |
6 | matrix:
7 | allow_failures:
8 | - BEHAVE_WEBDRIVER: IE
9 |
10 | services:
11 | - iis
12 |
13 | install:
14 | - regedit /s .\ci\bfcache.reg
15 | - cmd: .\ci\install.bat
16 |
17 | build: off
18 |
19 | test_script:
20 | - cmd: .\ci\runtests.bat
21 | - ps: |
22 | $wc = New-Object 'System.Net.WebClient';
23 | Get-ChildItem .\reports |
24 | Foreach-Object {
25 | $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $_.FullName))
26 | }
27 |
--------------------------------------------------------------------------------
/behave_webdriver/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = [
2 | 'Chrome',
3 | 'Firefox',
4 | 'Ie',
5 | 'Edge',
6 | 'Opera',
7 | 'Safari',
8 | 'BlackBerry',
9 | 'PhantomJS',
10 | 'Android',
11 | 'Remote',
12 | 'from_env',
13 | 'from_string',
14 | ]
15 | from behave_webdriver.driver import (Chrome,
16 | Firefox,
17 | Ie,
18 | Edge,
19 | Opera,
20 | Safari,
21 | BlackBerry,
22 | PhantomJS,
23 | Android,
24 | Remote)
25 | from behave_webdriver.utils import (from_env,
26 | from_string)
27 | from behave_webdriver.fixtures import (fixture_browser,
28 | before_all_factory,
29 | before_feature_factory,
30 | before_scenario_factory)
31 | from behave_webdriver.fixtures import use_fixture_tag
32 | from behave_webdriver import transformers
33 |
--------------------------------------------------------------------------------
/behave_webdriver/conditions.py:
--------------------------------------------------------------------------------
1 | """
2 | Provides additional expected conditions as well as *negatable* versions of selenium's expected conditions.
3 | """
4 |
5 | from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
6 | from selenium.webdriver.support import expected_conditions as EC
7 |
8 |
9 | class NegationMixin(object):
10 | """
11 | Provides the ability to test the negation of any existing expected condition (EC).
12 | Currently, there is an unsolved problem in certain ECs due to the way exceptions are caught.
13 | """
14 | def __init__(self, *args, **kwargs):
15 | negative = kwargs.pop('negative', False)
16 | super(NegationMixin, self).__init__(*args, **kwargs)
17 | self.negative = negative
18 |
19 | def __call__(self, *args, **kwargs):
20 | try:
21 | result = super(NegationMixin, self).__call__(*args, **kwargs)
22 | except StaleElementReferenceException:
23 | return False # Stale elements are unreliable, always try to regrab the element
24 | if self.negative:
25 | return not result
26 | return result
27 |
28 |
29 | class AnyTextMixin(object):
30 | """
31 | Provides default for text_ arguments when the EC expects it. An empty value will test true when
32 | tested against any other string. For example, with selenium's ``text_to_be_present_in_element`` that checks
33 | >>> if element_text:
34 | ... return self.text in element_text
35 | In effect, to the desired behavior of accepting just any text because ``'' in any_string`` is always ``True``
36 | >>> if element_text:
37 | ... return True
38 |
39 | This behavior only applies if the text_ keyword argument is not provided.
40 | This may cause problems if you try to provide text_ as a positional argument, so don't do that.
41 | """
42 | def __init__(self, *args, **kwargs):
43 | if 'text_' not in kwargs:
44 | kwargs['text_'] = ''
45 | super(AnyTextMixin, self).__init__(*args, **kwargs)
46 |
47 |
48 | class element_is_selected(NegationMixin, EC.element_located_to_be_selected):
49 | """
50 | Like selenium's element_located_to_be_selected but with the :ref:`~behave_webdriver.conditions.NegationMixin`.
51 | """
52 | pass
53 |
54 |
55 | class element_is_visible(NegationMixin, EC.visibility_of_element_located):
56 | """
57 | Like selenium's visibility_of_element_located but with the :ref:`~behave_webdriver.conditions.NegationMixin`.
58 | """
59 | pass
60 |
61 |
62 | class element_is_present(NegationMixin, EC.presence_of_element_located):
63 | """
64 | Like selenium's presence_of_element_located but with the :ref:`~behave_webdriver.conditions.NegationMixin`.
65 | """
66 | def __call__(self, driver):
67 | """
68 | extends __call__ to catch NoSuchElementException errors to support negation of element existing.
69 | """
70 | try:
71 | return super(element_is_present, self).__call__(driver)
72 | except NoSuchElementException:
73 | result = False
74 | if self.negative:
75 | return not result
76 | return result
77 |
78 |
79 | class element_is_enabled(object):
80 | """
81 | A new EC that checks a webelement's ``is_enabled`` method.
82 | Negation is supplied manually, rather than the usual mixin.
83 | """
84 | def __init__(self, locator, negative=False):
85 | self.locator = locator
86 | self.negative = negative
87 |
88 | def __call__(self, driver):
89 | try:
90 | element = driver.find_element(*self.locator)
91 | except StaleElementReferenceException:
92 | return False
93 | result = element.is_enabled()
94 | if self.negative:
95 | return not result
96 | return result
97 |
98 |
99 | class element_contains_text(NegationMixin, AnyTextMixin, EC.text_to_be_present_in_element):
100 | """
101 | Like selenium's text_to_be_present_in_element but with the :ref:`~behave_webdriver.conditions.NegationMixin`.
102 | and :ref:`~behave_webdriver.conditions.AnyTextMixin`.
103 | """
104 | def __call__(self, driver):
105 | """
106 | Same logic as in EC.text_to_be_present_in_element except StaleElementReferenceException is not caught
107 | this, for now, is needed to distinguish if a False return value is the result of this exception.
108 | """
109 | try:
110 | element = driver.find_element(*self.locator)
111 | result = bool(element.text)
112 | except StaleElementReferenceException:
113 | return False
114 |
115 | if self.negative:
116 | return not result
117 | return result
118 |
119 |
120 | class element_contains_value(NegationMixin, AnyTextMixin, EC.text_to_be_present_in_element_value):
121 | """
122 | Like selenium's text_to_be_present_in_element_value but with the :ref:`~behave_webdriver.conditions.NegationMixin`.
123 | and :ref:`~behave_webdriver.conditions.AnyTextMixin`.
124 | """
125 | pass
126 |
--------------------------------------------------------------------------------
/behave_webdriver/fixtures.py:
--------------------------------------------------------------------------------
1 | """
2 | Provides fixtures to initialize the web driver.
3 | """
4 |
5 | from behave import fixture, use_fixture
6 | from behave_webdriver.utils import _from_string, _from_env
7 | from behave_webdriver.driver import BehaveDriverMixin
8 | from functools import partial
9 | from behave_webdriver import transformers
10 | import six
11 | _env_webdriver_name = 'env'
12 |
13 | class DriverNotSet:
14 | pass
15 |
16 | @fixture
17 | def fixture_browser(context, *args, **kwargs):
18 | """
19 | webdriver setup fixture for behave context; sets ``context.behave_driver``.
20 |
21 | Will destroy the driver at the end of this fixture usage.
22 |
23 | :param webdriver: the webdriver to use -- can be a string (e.g. ``"Chrome"``) or a webdriver class. If omitted, will attempt to use the BEHAVE_WEBDRIVER environment variable
24 |
25 | :param default_driver: a fallback driver if webdriver keyword is not provided AND the BEHAVE_WEBDRIVER environment variable is not set. Defaults to 'Chrome.headless'
26 |
27 | :param args: arguments that will be passed as is to the webdriver.
28 | :param kwargs: keywords arguments that will be passed as is to the webdriver.
29 |
30 | Basic usage:
31 |
32 | >>> from behave import use_fixture
33 | >>> from behave_webdriver.fixtures import fixture_browser
34 | >>> def before_all(context):
35 | ... use_fixture(fixture_browser, context, webdriver='firefox')
36 |
37 | You may also provide webdriver class. Just be sure it inherits (or otherwise has method from) BehaveDriverMixin
38 |
39 | >>> from behave import use_fixture
40 | >>> from behave_webdriver.fixtures import fixture_browser
41 | >>> from behave_webdriver.driver import BehaveDriverMixin
42 | >>> from selenium.webdriver import Firefox
43 | >>> class FirefoxDriver(BehaveDriverMixin, Firefox):
44 | ... pass
45 | >>> def before_all(context):
46 | ... use_fixture(fixture_browser, context, webdriver=FirefoxDriver)
47 |
48 | positional arguments and additional keyword arguments are passed to the webdriver init:
49 |
50 | >>> from behave import use_fixture
51 | >>> from behave_webdriver.fixtures import fixture_browser
52 | >>> from behave_webdriver.driver import ChromeOptions
53 | >>> def before_all(context):
54 | ... options = ChromeOptions()
55 | ... options.add_argument('--ignore-gpu-blacklist')
56 | ... use_fixture(fixture_browser, context, webdriver='chrome', options=options)
57 |
58 | If the ``webdriver`` keyword is omitted, will attampt to get the driver from BEHAVE_WEBDRIVER or will use headless chrome as a final fallback if environment is not set and there is no ``default_driver`` specified
59 |
60 | >>> from behave import use_fixture
61 | >>> from behave_webdriver.fixtures import fixture_browser
62 | >>> def before_all(context):
63 | ... # try to use driver from BEHAVE_WEBDRIVER environment variable; use firefox as a fallback when env not set
64 | ... use_fixture(fixture_browser, context, default_driver='firefox')
65 |
66 | """
67 |
68 | webdriver = kwargs.pop('webdriver', None)
69 | default_driver = kwargs.pop('default_driver', 'Chrome.headless')
70 | if isinstance(webdriver, six.string_types):
71 | webdriver = _from_string(webdriver)
72 | if webdriver is None:
73 | webdriver = _from_env(default_driver=default_driver)
74 | old_driver_class = context.BehaveDriver if 'BehaveDriver' in context else DriverNotSet
75 | old_driver = context.behave_driver if 'behave_driver' in context else DriverNotSet
76 | context.behave_driver = webdriver(*args, **kwargs)
77 |
78 | def cleanup_driver(ctx, old_driver, old_driver_class):
79 | try:
80 | ctx.behave_driver.quit()
81 | finally:
82 | if old_driver_class is DriverNotSet and 'BehaveDriver' in ctx:
83 | del ctx.BehaveDriver
84 | else:
85 | ctx.BehaveDriver = old_driver_class
86 | if old_driver is DriverNotSet and 'behave_driver' in ctx:
87 | del ctx.behave_driver
88 | else:
89 | ctx.behave_driver = old_driver
90 |
91 | cleanup = partial(cleanup_driver, context, old_driver, old_driver_class)
92 | context.add_cleanup(cleanup)
93 |
94 |
95 | def before_all_factory(*args, **kwargs):
96 | """
97 | Create and return a ``before_all`` function that use the ``fixture_browser`` fixture with the corresponding arguments
98 | :param args: positional arguments of ``fixture_browser``
99 | :param kwargs: keywords arguments of ``fixture_browser``
100 |
101 | >>> from behave_webdriver.fixtures import before_all_factory
102 | >>> before_all = before_all_factory(webdriver='firefox')
103 | """
104 | def before_all(context):
105 | use_fixture(fixture_browser, context, *args, **kwargs)
106 | return before_all
107 |
108 |
109 | def before_feature_factory(*args, **kwargs):
110 | """
111 | Create and return a ``before_feature` function that use the ``fixture_browser`` fixture with the corresponding arguments
112 | :param args: positional arguments of ``fixture_browser``
113 | :param kwargs: keywords arguments of ``fixture_browser``
114 |
115 | >>> from behave_webdriver.fixtures import before_feature_factory
116 | >>> before_feature = before_feature_factory(webdriver='firefox')
117 | """
118 | def before_feature(context, feature):
119 | use_fixture(fixture_browser, context, *args, **kwargs)
120 | return before_feature
121 |
122 |
123 | def before_scenario_factory(*args, **kwargs):
124 | """
125 | Create and return a ``before_scenario`` function that use the ``fixture_browser`` fixture with the corresponding arguments
126 | :param args: positional arguments of ``fixture_browser``
127 | :param kwargs: keywords arguments of ``fixture_browser``
128 |
129 | >>> from behave_webdriver.fixtures import before_scenario_factory
130 | >>> before_scenario = before_scenario_factory(webdriver='firefox')
131 | """
132 | def before_scenario(context, scenario):
133 | use_fixture(fixture_browser, context, *args, **kwargs)
134 | return before_scenario
135 |
136 |
137 | class TransformerNotSet:
138 | pass
139 |
140 |
141 |
142 | @fixture
143 | def transformation_fixture(context, transformer_class, *args, **kwargs):
144 | old_transformer = context.transformer_class if 'transformer_class' in context else TransformerNotSet
145 | transformer_class = partial(transformer_class, *args, **kwargs)
146 | context.transformer_class = transformer_class
147 |
148 | def cleanup(context, old):
149 | if old is TransformerNotSet:
150 | del context.transformer_class
151 | else:
152 | context.transformer_class = old
153 | cleanup_transformer = partial(cleanup, context, old_transformer)
154 | context.add_cleanup(cleanup_transformer)
155 |
156 |
157 | def use_fixture_tag(context, tag, *args, **kwargs):
158 | if not tag.startswith('fixture'):
159 | return
160 | if tag.startswith('fixture.webdriver'):
161 | browser_name = '.'.join(tag.split('.')[2:])
162 | if browser_name == 'browser':
163 | browser_name = 'Chrome.headless'
164 | use_fixture(fixture_browser, context, *args, **kwargs)
165 |
166 | elif tag.startswith('fixture.transformer'):
167 | transformer_name = tag.split('.')[-1]
168 | transformer_class = getattr(transformers, transformer_name)
169 | use_fixture(transformation_fixture, context, transformer_class, **kwargs)
170 |
--------------------------------------------------------------------------------
/behave_webdriver/steps/__init__.py:
--------------------------------------------------------------------------------
1 | from .actions import *
2 | from .actions_re import *
3 | from .expectations import *
4 |
--------------------------------------------------------------------------------
/behave_webdriver/steps/actions.py:
--------------------------------------------------------------------------------
1 | import string
2 | from behave import *
3 | from behave_webdriver.transformers import matcher_mapping
4 | try:
5 | from urllib.parse import urljoin
6 | except ImportError:
7 | from urlparse import urljoin # Python 2
8 |
9 |
10 | if 'transform-parse' not in matcher_mapping:
11 | use_step_matcher('parse')
12 | else:
13 | use_step_matcher('transform-parse')
14 |
15 |
16 | @when('I pause for {milliseconds:d}ms')
17 | def sleep_ms(context, milliseconds):
18 | context.behave_driver.pause(milliseconds)
19 |
20 |
21 | @when('I click on the element "{element}"')
22 | def click_element(context, element):
23 | context.behave_driver.click_element(element)
24 |
25 |
26 | @when('I doubleclick on the element "{element}"')
27 | def doubleclick_element(context, element):
28 | context.behave_driver.doubleclick_element(element)
29 |
30 |
31 | @when('I click on the link "{link_text}"')
32 | def click_link(context, link_text):
33 | context.behave_driver.click_link_text(link_text)
34 |
35 |
36 | @when('I click on the button "{element}"')
37 | def click_button(context, element):
38 | context.behave_driver.click_element(element)
39 |
40 |
41 | @when('I set "{value}" to the inputfield "{element}"')
42 | def set_input(context, value, element):
43 | elem = context.behave_driver.get_element(element)
44 | elem.clear()
45 | elem.send_keys(value)
46 |
47 |
48 | @when('I add "{value}" to the inputfield "{element}"')
49 | def add_input(context, value, element):
50 | elem = context.behave_driver.get_element(element)
51 | elem.send_keys(value)
52 |
53 |
54 | @when('I clear the inputfield "{element}"')
55 | def clear_input(context, element):
56 | elem = context.behave_driver.get_element(element)
57 | elem.clear()
58 |
59 |
60 | @when('I drag element "{from_element}" to element "{to_element}"')
61 | def drag_element(context, from_element, to_element):
62 | context.behave_driver.drag_element(from_element, to_element)
63 |
64 |
65 | @when('I submit the form "{element}"')
66 | def submit_form(context, element):
67 | context.behave_driver.submit(element)
68 |
69 |
70 | @when('I set a cookie "{cookie_key}" with the content "{value}"')
71 | def set_cookie(context, cookie_key, value):
72 | context.behave_driver.add_cookie({'name': cookie_key, 'value': value})
73 |
74 |
75 | @when('I delete the cookie "{cookie_key}"')
76 | def delete_cookie(context, cookie_key):
77 | context.behave_driver.delete_cookie(cookie_key)
78 |
79 |
80 | @when('I press "{key}"')
81 | def press_button(context, key):
82 | context.behave_driver.press_button(key)
83 |
84 |
85 | @when('I scroll to element "{element}"')
86 | def scroll_to(context, element):
87 | context.behave_driver.scroll_to_element(element)
88 |
89 |
90 | @when('I select the {nth} option for element "{element}"')
91 | def select_nth_option(context, nth, element):
92 | index = int(''.join(char for char in nth if char in string.digits))
93 | context.behave_driver.select_option(element,
94 | by='index',
95 | by_arg=index)
96 |
97 |
98 | @when('I move to element "{element}" with an offset of {x_offset:d},{y_offset:d}')
99 | def move_to_element_offset(context, element, x_offset, y_offset):
100 | context.behave_driver.move_to_element(element, (x_offset, y_offset))
101 |
102 |
103 | @when('I move to element "{element}"')
104 | def move_to_element(context, element):
105 | context.behave_driver.move_to_element(element)
106 |
107 |
108 | use_step_matcher('parse')
109 |
--------------------------------------------------------------------------------
/behave_webdriver/steps/actions_re.py:
--------------------------------------------------------------------------------
1 | from behave import *
2 | from behave_webdriver.transformers import matcher_mapping
3 | try:
4 | from urllib.parse import urljoin
5 | except ImportError:
6 | from urlparse import urljoin # Python 2
7 |
8 |
9 | if 'transform-parse' not in matcher_mapping:
10 | use_step_matcher('re')
11 | else:
12 | use_step_matcher('transform-re')
13 |
14 |
15 | @when('I close the last opened (tab|window)')
16 | def close_last_tab(context, _):
17 | context.behave_driver.switch_to_window(context.behave_driver.last_opened_handle)
18 | context.behave_driver.close()
19 | context.behave_driver.switch_to_window(context.behave_driver.primary_handle)
20 |
21 |
22 | @when('I focus the last opened (tab|window)')
23 | def focus_last_tab(context, _):
24 | context.behave_driver.switch_to_window(context.behave_driver.last_opened_handle)
25 |
26 |
27 | @when('I select the option with the (text|value|name) "([^"]*)?" for element "([^"]*)?"')
28 | def select_option_by(context, attr, attr_value, element):
29 | attr_map = {'text': 'visible_text'}
30 | attr = attr_map.get(attr, attr)
31 | context.behave_driver.select_option(select_element=element,
32 | by=attr,
33 | by_arg=attr_value)
34 |
35 |
36 | @when('I accept the (alertbox|confirmbox|prompt)')
37 | def accept_alert(context, modal_type):
38 | context.behave_driver.alert.accept()
39 |
40 |
41 | @when('I dismiss the (alertbox|confirmbox|prompt)')
42 | def dismiss_alert(context, modal_type):
43 | context.behave_driver.alert.dismiss()
44 |
45 |
46 | @when('I enter "([^"]*)?" into the (alertbox|confirmbox|prompt)')
47 | def handle_prompt(context, text, modal_type):
48 | context.behave_driver.alert.send_keys(text)
49 |
50 |
51 | @given('I have closed all but the first (window|tab)')
52 | def close_secondary_windows(context, window_or_tab):
53 | if len(context.behave_driver.window_handles) > 1:
54 | for handle in context.behave_driver.window_handles[1:]:
55 | context.behave_driver.switch_to_window(handle)
56 | context.behave_driver.close()
57 | context.behave_driver.switch_to_window(context.behave_driver.primary_handle)
58 |
59 |
60 | @step('I open the url "([^"]*)?"')
61 | def open_url(context, url):
62 | context.behave_driver.open_url(url)
63 |
64 |
65 | @step('I open the site "([^"]*)?"')
66 | def open_site(context, url):
67 | base_url = getattr(context, 'base_url', 'http://localhost:8000')
68 | destination = urljoin(base_url, url)
69 | context.behave_driver.open_url(destination)
70 |
71 |
72 | @given('the base url is "([^"]*)?"')
73 | def set_base_url(context, url):
74 | if url.endswith('/'):
75 | url = url[:-1]
76 | context.base_url = url
77 |
78 |
79 | @given('I pause for (\d+)*ms')
80 | def pause(context, milliseconds):
81 | milliseconds = int(milliseconds)
82 | context.behave_driver.pause(milliseconds)
83 |
84 |
85 | @given('I have a screen that is ([\d]+) by ([\d]+) pixels')
86 | def set_screen_size(context, x, y):
87 | context.behave_driver.screen_size = (x, y)
88 |
89 |
90 | @given('I have a screen that is ([\d]+) pixels (broad|tall)')
91 | def set_screen_dimension(context, size, how):
92 | size = int(size)
93 | if how == 'tall':
94 | context.behave_driver.screen_size = (None, size)
95 | else:
96 | context.behave_driver.screen_size = (size, None)
97 |
--------------------------------------------------------------------------------
/behave_webdriver/steps/expectations.py:
--------------------------------------------------------------------------------
1 | from behave import *
2 | from behave_webdriver.transformers import matcher_mapping
3 | try:
4 | from urllib.parse import urlparse
5 | except ImportError:
6 | from urlparse import urlparse
7 |
8 | if 'transform-parse' not in matcher_mapping:
9 | use_step_matcher('re')
10 | else:
11 | use_step_matcher('transform-re')
12 |
13 |
14 | @given('the element "([^"]*)?" is( not)* visible')
15 | @then('I expect that element "([^"]*)?" becomes( not)* visible')
16 | @then('I expect that element "([^"]*)?" is( not)* visible')
17 | def check_element_visibility(context, element, negative):
18 | element_is_visible = context.behave_driver.element_visible(element)
19 | if negative:
20 | assert not element_is_visible, 'Expected element to not be visible, but it was'
21 | else:
22 | assert element_is_visible, 'Expected element to be visible, but it was not visible'
23 |
24 |
25 | @given('the title is( not)* "([^"]*)?"')
26 | @then('I expect that the title is( not)* "([^"]*)?"')
27 | def title(context, negative, value):
28 | if negative:
29 | assert context.behave_driver.title != value, 'Title was "{}"'.format(context.behave_driver.title)
30 | else:
31 | assert context.behave_driver.title == value, 'Title was "{}"'.format(context.behave_driver.title)
32 |
33 |
34 | @then('I expect that element "([^"]*)?" is( not)* within the viewport')
35 | def check_element_within_viewport(context, element, negative):
36 | element_in_viewport = context.behave_driver.element_in_viewport(element)
37 | if negative:
38 | assert not element_in_viewport, 'Element was completely within the viewport'
39 | else:
40 | assert element_in_viewport, 'Element was not completely within viewport'
41 |
42 |
43 | @given('the element "([^"]*)?" is( not)* enabled')
44 | @then('I expect that element "([^"]*)?" is( not)* enabled')
45 | def element_enabled(context, element, negative):
46 | enabled = context.behave_driver.element_enabled(element)
47 | if negative:
48 | assert not enabled
49 | else:
50 | assert enabled
51 |
52 |
53 | @given('the element "([^"]*)?" is( not)* selected')
54 | @then('I expect that element "([^"]*)?" is( not)* selected')
55 | def element_selected(context, element, negative):
56 | selected = context.behave_driver.element_selected(element)
57 | if negative:
58 | assert not selected
59 | else:
60 | assert selected
61 |
62 |
63 | @given('the checkbox "([^"]*)?" is( not)* checked')
64 | @then('I expect that checkbox "([^"]*)?" is( not)* checked')
65 | def element_checked(context, element, negative):
66 | checked = context.behave_driver.element_selected(element)
67 | if negative:
68 | assert not checked
69 | else:
70 | assert checked
71 |
72 |
73 | @given('there is (an|no) element "([^"]*)?" on the page')
74 | def element_exists(context, an_no, element):
75 | negative = an_no == 'no'
76 | exists = context.behave_driver.element_exists(element)
77 | if negative:
78 | assert not exists
79 | else:
80 | assert exists
81 |
82 |
83 | @then('I expect that element "([^"]*)?" does( not)* exist')
84 | def check_element_exists(context, element, negative):
85 | exists = context.behave_driver.element_exists(element)
86 | if negative:
87 | assert not exists, 'Expected the element does not exist, but element "{}" was located'.format(element)
88 | else:
89 | assert exists, 'Expected element to exist, but no element "{}" was located'.format(element)
90 |
91 |
92 | @given('the element "([^"]*)?" contains( not)* the same text as element "([^"]*)?"')
93 | @then('I expect that element "([^"]*)?"( not)* contains the same text as element "([^"]*)?"')
94 | def elements_same_text(context, first_element, negative, second_element):
95 | first_elem_text = context.behave_driver.get_element_text(first_element)
96 | second_elem_text = context.behave_driver.get_element_text(second_element)
97 | same = first_elem_text == second_elem_text
98 | if negative:
99 | assert not same, 'Element "{}" text "{}" is same as element "{}"'.format(first_element,
100 | first_elem_text,
101 | second_element)
102 | else:
103 | assert same, 'Element "{}" text "{}" is not same as element "{}" text "{}"'.format(first_element,
104 | first_elem_text,
105 | second_element,
106 | second_elem_text)
107 |
108 |
109 | @given('the element "([^"]*)?"( not)* matches the text "([^"]*)?"')
110 | @then('I expect that element "([^"]*)?"( not)* matches the text "([^"]*)?"')
111 | def element_matches_text(context, element, negative, text):
112 | elem_text = context.behave_driver.get_element_text(element)
113 | matches = elem_text == text
114 | if negative:
115 | assert not matches, 'Element "{}" text matches "{}"'.format(element,
116 | text)
117 | else:
118 | assert matches, 'The text "{}" did not match the element text "{}"'.format(text, elem_text)
119 |
120 |
121 | @given('the element "([^"]*)?"( not)* contains the text "([^"]*)?"')
122 | @then('I expect that element "([^"]*)?"( not)* contains the text "([^"]*)?"')
123 | def check_element_contains_text(context, element, negative, text):
124 | contains = context.behave_driver.element_contains(element, text)
125 | if negative:
126 | assert not contains, 'Element text does contain "{}"'.format(text)
127 | else:
128 | assert contains, 'Element text does not contain "{}"'.format(text)
129 |
130 |
131 | @given('the element "([^"]*)?"( not)* contains any text')
132 | @then('I expect that element "([^"]*)?"( not)* contains any text')
133 | def element_any_text(context, element, negative):
134 | any_text = bool(context.behave_driver.get_element_text(element))
135 | if negative:
136 | assert not any_text
137 | else:
138 | assert any_text
139 |
140 |
141 | @given('the element "([^"]*)?" is( not)* empty')
142 | @then('I expect that element "([^"]*)?" is( not)* empty')
143 | def check_element_empty(context, element, negative):
144 | elem_text = context.behave_driver.get_element_text(element)
145 | any_text = bool(elem_text)
146 | if negative:
147 | assert any_text is True
148 | else:
149 | assert any_text is False
150 |
151 |
152 | @given('the page url is( not)* "([^"]*)?"')
153 | @then('I expect that the url is( not)* "([^"]*)?"')
154 | def check_url(context, negative, value):
155 | current_url = context.behave_driver.current_url
156 | if negative:
157 | assert current_url != value, 'The url was "{}"'.format(current_url)
158 | else:
159 | assert current_url == value, 'Expected url to be "{}", but saw the url was "{}"'.format(value, current_url)
160 |
161 |
162 | @then('I expect the url to( not)* contain "([^"]*)?"')
163 | def check_url_contains(context, negative, value):
164 | current_url = context.behave_driver.current_url
165 | if negative:
166 | assert value not in current_url, 'url was "{}"'.format(current_url)
167 | else:
168 | assert value in current_url, 'url was "{}"'.format(current_url)
169 |
170 |
171 | @given('the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"')
172 | @then('I expect that the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"')
173 | def check_element_attribute(context, is_css, attr, element, negative, value):
174 | if is_css:
175 | attribute_value, value = context.behave_driver.get_element_attribute(element, attr, is_css, value)
176 | else:
177 | attribute_value = context.behave_driver.get_element_attribute(element, attr)
178 |
179 | if negative:
180 | assert attribute_value != value, 'Attribute value was "{}"'.format(attribute_value)
181 | else:
182 | assert attribute_value == value, 'Attribute value was "{}"'.format(attribute_value)
183 |
184 |
185 | @given('the cookie "([^"]*)?" contains( not)* the value "([^"]*)?"')
186 | @then('I expect that cookie "([^"]*)?"( not)* contains "([^"]*)?"')
187 | def check_cookie_value(context, cookie_key, negative, value):
188 | cookie = context.behave_driver.get_cookie(cookie_key)
189 | cookie_value = cookie.get('value')
190 | if negative:
191 | assert cookie_value != value, 'Cookie value was "{}"'.format(cookie_value)
192 | else:
193 | assert cookie_value == value, 'Cookie value was "{}"'.format(cookie_value)
194 |
195 |
196 | @given('the cookie "([^"]*)?" does( not)* exist')
197 | def cookie_exists(context, cookie_key, negative):
198 | cookie = context.behave_driver.get_cookie(cookie_key)
199 | if negative:
200 | assert cookie is None, 'Cookie exists: {}'.format(cookie)
201 | else:
202 | assert cookie is not None
203 |
204 |
205 | @then('I expect that cookie "([^"]*)?"( not)* exists')
206 | def check_cookie_exists(context, cookie_key, negative):
207 | cookie = context.behave_driver.get_cookie(cookie_key)
208 | if negative:
209 | assert cookie is None, u'Cookie was present: "{}"'.format(cookie)
210 | else:
211 | assert cookie is not None, 'Cookie was not found'
212 |
213 |
214 | @given('the element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)')
215 | @then('I expect that element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)')
216 | def check_element_size(context, element, negative, pixels, how):
217 | elem_size = context.behave_driver.get_element_size(element)
218 | if how == 'tall':
219 | axis = 'height'
220 | else:
221 | axis = 'width'
222 | if negative:
223 | assert elem_size[axis] != int(pixels), 'Element size was "{}"'.format(elem_size)
224 | else:
225 | assert elem_size[axis] == int(pixels), 'Element size was "{}"'.format(elem_size)
226 |
227 |
228 | @given('the element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis')
229 | @then('I expect that element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis')
230 | def check_element_position(context, element, negative, pos, axis):
231 | element_position = context.behave_driver.get_element_location(element)
232 | if negative:
233 | assert element_position[axis] != int(pos), 'Position was {} on the {} axis'.format(element_position[axis], axis)
234 | else:
235 | assert element_position[axis] == int(pos), 'Position was {} on the {} axis'.format(element_position[axis], axis)
236 |
237 |
238 | @given('a (alertbox|confirmbox|prompt) is( not)* opened')
239 | @then('I expect that a (alertbox|confirmbox|prompt) is( not)* opened')
240 | def check_modal(context, modal, negative):
241 | if negative:
242 | assert context.behave_driver.has_alert is False
243 | else:
244 | assert context.behave_driver.has_alert is True
245 |
246 |
247 | @then('I expect that the path is( not)* "([^"]*)?"')
248 | def check_path(context, negative, value):
249 | current_url = context.behave_driver.current_url
250 | path = urlparse(current_url).path
251 | if negative:
252 | assert path != value, 'The path was "{}"'.format(path)
253 | else:
254 | assert path == value, 'Expected the path to be "{}", but saw the path "{}"'.format(value, path)
255 |
256 |
257 | @then('I expect that element "([^"]*)?" (has|does not have) the class "([^"]*)?"')
258 | def check_element_has_class(context, element, has, classname):
259 | if 'not' in has:
260 | negative = True
261 | else:
262 | negative = False
263 |
264 | has_class = context.behave_driver.element_has_class(element, classname)
265 | if negative:
266 | assert not has_class, 'Classes were {}'.format(context.behave_driver.get_element_attribute(element, 'class'))
267 | else:
268 | assert has_class, 'Classes were {}'.format(context.behave_driver.get_element_attribute(element, 'class'))
269 |
270 |
271 | @then('I expect a new (window|tab) has( not)* been opened')
272 | def check_window_opened(context, _, negative):
273 | if negative:
274 | assert not context.behave_driver.secondary_handles
275 | else:
276 | assert bool(context.behave_driver.secondary_handles)
277 |
278 |
279 | @then('I expect the url "([^"]*)?" is opened in a new (tab|window)')
280 | def check_url_new_window(context, url, _):
281 | current_handle = context.behave_driver.primary_handle
282 | for handle in context.behave_driver.secondary_handles:
283 | context.behave_driver.switch_to_window(handle)
284 | if context.behave_driver.current_url == url:
285 | context.behave_driver.switch_to_window(current_handle)
286 | break
287 | else:
288 | context.behave_driver.switch_to_window(current_handle)
289 | if len(context.behave_driver.secondary_handles) < 1:
290 | raise AssertionError('No secondary handles found!')
291 | raise AssertionError("The url '{}' was not found in any handle")
292 |
293 |
294 | @then('I expect that element "([^"]*)?" is( not)* focused')
295 | def check_element_focused(context, element, negative):
296 | element_focused = context.behave_driver.element_focused(element)
297 | if negative:
298 | assert not element_focused
299 | else:
300 | assert element_focused
301 |
302 |
303 | @then('I expect that a (alertbox|confirmbox|prompt)( not)* contains the text "([^"]*)?"')
304 | def check_modal_text_contains(context, modal_type, negative, text):
305 | alert_text = context.behave_driver.alert.text
306 | if negative:
307 | assert not text in alert_text
308 | else:
309 | assert text in alert_text
310 |
311 |
312 | @then('I wait on element "([^"]*)?"(?: for (\d+)ms)*(?: to( not)* (be checked|be enabled|be selected|be visible|contain a text|contain a value|exist))*')
313 | def wait_for_element_condition(context, element, milliseconds, negative, condition):
314 | if milliseconds:
315 | digits = ''.join(char for char in milliseconds if char.isdigit())
316 | milliseconds = int(digits)
317 |
318 | result = context.behave_driver.wait_for_element_condition(element, milliseconds, negative, condition)
319 | if not negative:
320 | negative = ''
321 | assert result, 'was expecting element "{element}" to {negative} {condition}, but the result was {result}'.format(
322 | element=element,
323 | negative=negative,
324 | condition=condition,
325 | result=result)
326 |
327 |
328 | @then("I expect the screen is ([\d]+) by ([\d]+) pixels")
329 | def check_screen_size(context, x, y):
330 | screen_x, screen_y = context.behave_driver.screen_size
331 |
332 |
333 | use_step_matcher('parse')
334 |
--------------------------------------------------------------------------------
/behave_webdriver/transformers.py:
--------------------------------------------------------------------------------
1 | import os
2 | from behave.matchers import Match, ParseMatcher, RegexMatcher, MatchWithError
3 | from behave.matchers import matcher_mapping
4 | from collections import defaultdict
5 | import six
6 | from functools import partial
7 |
8 | class TransformerBase(object):
9 | """
10 | Defines the basic functions of a Transformer
11 | As implemented, it does effectively nothing. You are meant to subclass and override the methods.
12 |
13 | Don't forget to call ``super`` when extending ``__init__``
14 | """
15 | def __init__(self, context=None, func=None, **kwargs):
16 | """
17 |
18 | :param context: behave context
19 | :param func: the matched step function currently being executed
20 | :param kwargs: Not doing anything with these, but allowing us to swallow them.
21 | """
22 | self.context = context
23 | self.func = func
24 |
25 | def transform_value(self, value):
26 | return value
27 |
28 | def transform_args(self, args):
29 | return [self.transform_value(arg) for arg in args]
30 |
31 | def transform_kwargs(self, kwargs):
32 | return {key: self.transform_value(value) for key, value in kwargs.items()}
33 |
34 | def transform(self, args, kwargs):
35 | return self.transform_args(args), self.transform_kwargs(kwargs), self.func
36 |
37 |
38 | class FormatTransformer(TransformerBase):
39 | """
40 | Implements basic interpolation transformation startegy.
41 | Parameter value is transformed through .format method
42 | using named placeholders and values supplied as
43 | keyword arguments passed at the time of initialization.
44 | """
45 |
46 | def __init__(self, context=None, func=None, **kwargs):
47 | """
48 |
49 | :param context: behave context
50 | :param func: the matched step function currently being executed
51 | :param kwargs: keyword-value pairs used for formatting step strings.
52 | """
53 | suppress_missing = kwargs.pop('suppress_missing', False)
54 | if context is not None:
55 | kwargs.update(context=context)
56 | if func is not None:
57 | kwargs.update(func=func)
58 | super(FormatTransformer, self).__init__(**kwargs)
59 | self.transformations = kwargs
60 | if suppress_missing:
61 | self.transformations = defaultdict(lambda key: '', self.transformations)
62 |
63 | def transform_value(self, value):
64 | if not isinstance(value, six.string_types):
65 | return value # non-string arguments should be returned unadulterated
66 |
67 | return value.format(**self.transformations)
68 |
69 |
70 | class EnvironmentTransformer(FormatTransformer):
71 | """
72 | Like FormatTransformer, but additionally provides items from ``os.environ`` as keyword arguments
73 | """
74 | def __init__(self, *args, **kwargs):
75 | kwargs.update(os.environ)
76 | super(EnvironmentTransformer, self).__init__(*args, **kwargs)
77 |
78 |
79 | class FuncTransformer(TransformerBase):
80 | """
81 | Replaces the step function with a supplied new function!
82 | """
83 | def __init__(self, new_func, *args, **kwargs):
84 | self.new_func = new_func
85 | super(FuncTransformer, self).__init__(*args, **kwargs)
86 |
87 | def transform(self, *transform_args, **transform_kwargs):
88 | args, kwargs, _old_func = super(FuncTransformer, self).transform(*transform_args, **transform_kwargs)
89 | return args, kwargs, self.new_func
90 |
91 |
92 | class TransformingMatch(Match):
93 | """
94 | Tweak of the normal Match object
95 | When the ``transformer_class`` attribute, a subclass of ``behave_webdriver.transformers.TrransformerBase``,
96 | is present on the context, that class will be called with the context and decorated step function for the step
97 | currently being executed. This class has the ability to 'transform' the parsed arguments and the function itself.
98 | """
99 | def run(self, context):
100 | args = []
101 | kwargs = {}
102 | for arg in self.arguments:
103 | if arg.name is not None:
104 | kwargs[arg.name] = arg.value
105 | else:
106 | args.append(arg.value)
107 |
108 | with context.use_with_user_mode():
109 | # the above is a COPY/PASTE of the original `run` implementation,
110 | transformer_class = context.transformer_class if 'transformer_class' in context else None
111 | if transformer_class and ((isinstance(transformer_class, partial) and issubclass(transformer_class.func, TransformerBase)) or issubclass(transformer_class, TransformerBase)):
112 | transformer = transformer_class(context=context, func=self.func)
113 | args, kwargs, func = transformer.transform(args, kwargs)
114 | else:
115 | func = self.func
116 | func(context, *args, **kwargs)
117 |
118 |
119 | class TransformMixin(object):
120 | """
121 | Replaces the usual Match object with a TransformingMatch
122 | This can be mixed in with any matcher class and added to the mapping; you could even override existing matchers
123 |
124 | >>> from behave.matchers import RegexMatcher, matcher_mapping # any matcher will work
125 | >>> class TransformRegexMatcher(TransformMixin, RegexMatcher): pass
126 | >>> matcher_mapping['re'] = TransformRegexMatcher
127 | """
128 | def match(self, step):
129 | # -- PROTECT AGAINST: Type conversion errors (with ParseMatcher).
130 | try:
131 | result = self.check_match(step)
132 | except Exception as e: # pylint: disable=broad-except
133 | return MatchWithError(self.func, e)
134 |
135 | if result is None:
136 | return None # -- NO-MATCH
137 | # the above is a COPY/PASTE of original implementation; only the following line is changed
138 | return TransformingMatch(self.func, result)
139 |
140 |
141 | # behave-webdriver uses both ParseMatcher ('parse') and RegexMatcher ('re'); so we need a transforming version of each
142 | class TransformParseMatcher(TransformMixin, ParseMatcher):
143 | pass
144 |
145 |
146 | class TransformRegexMatcher(TransformMixin, RegexMatcher):
147 | pass
148 |
149 |
150 | # add the transforming matchers to the mapping so they can be used by ``use_step_matcher``.
151 | matcher_mapping['transform-parse'] = TransformParseMatcher
152 | matcher_mapping['transform-re'] = TransformRegexMatcher
153 |
--------------------------------------------------------------------------------
/behave_webdriver/utils.py:
--------------------------------------------------------------------------------
1 | from os import getenv
2 | import six
3 | from behave_webdriver.driver import (Chrome,
4 | Firefox,
5 | Ie,
6 | Edge,
7 | Opera,
8 | Safari,
9 | BlackBerry,
10 | PhantomJS,
11 | Android,
12 | Remote)
13 |
14 |
15 | def _from_string(webdriver_string):
16 | def get_driver_name(driver):
17 | return driver.__name__.upper()
18 | drivers = [Chrome, Firefox, Ie, Edge, Opera, Safari, BlackBerry, PhantomJS, Android, Remote]
19 | driver_map = {get_driver_name(d): d for d in drivers}
20 | driver_map['CHROME.HEADLESS'] = Chrome.headless
21 | Driver = driver_map.get(webdriver_string.upper(), None)
22 | if Driver is None:
23 | raise ValueError('No such driver "{}". Valid options are: {}'.format(webdriver_string,
24 | ', '.join(driver_map.keys())))
25 | return Driver
26 |
27 |
28 | def from_string(webdriver_string, *args, **kwargs):
29 | Driver = _from_string(webdriver_string)
30 | return Driver(*args, **kwargs)
31 |
32 |
33 | def _from_env(default_driver=None):
34 | browser_env = getenv('BEHAVE_WEBDRIVER', default_driver)
35 | if browser_env is None:
36 | raise ValueError('No driver found in environment variables and no default driver selection')
37 | if isinstance(browser_env, six.string_types):
38 | Driver = _from_string(browser_env)
39 | else:
40 | # if not a string, assume we have a webdriver instance
41 | Driver = browser_env
42 | return Driver
43 |
44 |
45 | def from_env(*args, **kwargs):
46 | default_driver = kwargs.pop('default_driver', None)
47 | if default_driver is None:
48 | default_driver = 'Chrome.headless'
49 | Driver = _from_env(default_driver=default_driver)
50 |
51 | return Driver(*args, **kwargs)
52 |
--------------------------------------------------------------------------------
/ci/bfcache.reg:
--------------------------------------------------------------------------------
1 | Windows Registry Editor Version 5.00
2 |
3 | [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE]
4 | "iexplore.exe"=dword:00000000
5 |
--------------------------------------------------------------------------------
/ci/install.bat:
--------------------------------------------------------------------------------
1 | C:\Python36\python.exe -m venv venv
2 |
3 | call venv\Scripts\activate.bat
4 |
5 | pip install -r .\requirements.txt
6 |
7 | pip install pytest mock coverage
8 |
9 | call deactivate
10 |
--------------------------------------------------------------------------------
/ci/runtests.bat:
--------------------------------------------------------------------------------
1 | call venv\Scripts\activate.bat
2 |
3 | powershell -Command "Import-Module WebAdministration;New-WebSite -Name demoapp -Port 8000 -PhysicalPath C:\projects\behave-webdriver\tests\demo-app"
4 |
5 | coverage run -m behave tests\features --format=progress2 --junit
6 |
7 | coverage run -a -m pytest tests\unittests --junitxml=reports\pytestresults.xml
8 |
9 | call deactivate
10 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = python -msphinx
7 | SPHINXPROJ = behave-webdriver
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/_static/behave-webdriver.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spyoungtech/behave-webdriver/9e660bcae5b1b345a32142070973476f71da8d04/docs/_static/behave-webdriver.gif
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | .. _api:
2 |
3 | ==============================
4 | behave-webdriver API Reference
5 | ==============================
6 |
7 | This reference is meant for those who want to develop upon, extend, or alter the behavior of behave-webdriver. This
8 | will contain information regarding the implementation of various methods. Many aspects of the BehaveDriver class deal
9 | closely with selenium webdriver instances, but this document will refrain from duplicating information that should be
10 | contained in the selenium documentation.
11 |
12 | behave-webdriver is designed with **you** in-mind. You are free to extend the behavior of our webdriver classes to suit your
13 | unique needs. You can subclass our webdriver classes, use a custom selenium webdriver, write your own mixin, or use
14 | a mixin somebody else provides for selenium.
15 |
16 | .. warning::
17 |
18 | While every effort is made to not make breaking changes, until a stable release, expect some things here to change, including breaking changes.
19 |
20 |
21 |
22 | The webdriver classes
23 | ---------------------
24 |
25 | behave-webdriver provides each of the same webdriver classes provided in ``selenium.webdriver``. Each class inherits from the :py:class:`~behave_webdriver.driver.BehaveDriverMixin`
26 | mixin as well as the respective ``selenium`` counterpart class.
27 |
28 | .. autoclass:: behave_webdriver.Chrome
29 | :members:
30 |
31 | .. autoclass:: behave_webdriver.Firefox
32 | :members:
33 |
34 | .. autoclass:: behave_webdriver.Ie
35 | :members:
36 |
37 | .. autoclass:: behave_webdriver.Safari
38 | :members:
39 |
40 | .. autoclass:: behave_webdriver.PhantomJS
41 | :members:
42 |
43 | .. autoclass:: behave_webdriver.Edge
44 | :members:
45 |
46 | .. autoclass:: behave_webdriver.Opera
47 | :members:
48 |
49 | .. autoclass:: behave_webdriver.BlackBerry
50 | :members:
51 |
52 | .. autoclass:: behave_webdriver.Android
53 | :members:
54 |
55 | .. autoclass:: behave_webdriver.Remote
56 | :members:
57 |
58 |
59 | The BehaveDriverMixin
60 | ---------------------
61 |
62 | The mixin class implements all of the general logic. If you want to alter how behave-webdriver behaves, this is probably the place to do it.
63 |
64 |
65 | .. autoclass:: behave_webdriver.driver.BehaveDriverMixin
66 | :members:
67 |
--------------------------------------------------------------------------------
/docs/browsers.rst:
--------------------------------------------------------------------------------
1 | Browser Support
2 | ===============
3 |
4 | behave-webdriver is designed so that you *can* use any of the webdriver classes you would normally use with Selenium,
5 | e.g. ``Chrome``, ``Firefox``, ``Remote``, etc... However, not all browsers were made equal and attempting to get identical
6 | behavior across browsers is... complicated, if not impossible.
7 |
8 | This document will aim to describe the status of support with the various webdrivers supported by Selenium. Where there
9 | are known issues or quirks related to this step library, there will be an effort to document them here, too. Be sure to also checkout the
10 | github issues and projects. browser-specific issues should be tagged accordingly.
11 |
12 | More specifics may be revealed in the :doc:`api` and in the source. The ecosystem around selenium/webdrivers is huge.
13 | This is not a repository or body of knowledge for all driver-related issues; just the ones that most directly affect this library.
14 |
15 | Unless otherwise noted, we are referring to the latest stable release of Selenium and each respective browser and driver.
16 | Keep in mind, this documentation may not necessarily be up-to-date with very recent releases.
17 |
18 |
19 | Chrome (recommended)
20 | --------------------
21 |
22 | Currently, Chrome is essentially the reference implementation. We primarily discuss issues with other webdrivers with
23 | respect to how Chrome behaves. In our experience so far, Chrome is the fastest and most well-behaving driver.
24 |
25 | We recommend Chrome and fully support the use of the Chrome webdriver with the latest versions of selenium and chrome/chromedriver.
26 | At the time of this writing (March 2018) that's selenium 3.10, Chrome 65, and chromedriver 2.36
27 | While earlier versions should work fine and we are willing to support them, they are not tested.
28 |
29 |
30 |
31 |
32 | Firefox (beta)
33 | --------------
34 |
35 | Firefox is officially supported as of v0.1.1
36 |
37 |
38 |
39 |
40 | Known issues
41 | ^^^^^^^^^^^^
42 |
43 | - ``submit`` on form elements is implemented by a (Selenium) JS shim and will not block for page load. Clicking the form button should block properly, however.
44 | - support for window handles is somewhat problematic
45 | - clicking elements requires they are in the viewport (we compensate for this by scrolling to an element before any click)
46 | - moving to an element *with an offset* that is bigger than the viewport is not (yet) supported
47 | - slower than Chrome
48 |
49 | Workarounds/Shims
50 | ^^^^^^^^^^^^^^^^^
51 |
52 | Shims and other workarounds for some known issues are implemented in the Firefox class.
53 |
54 | See :doc:`api` for more details.
55 |
56 |
57 | Ie
58 | --
59 |
60 | We have some preliminary support for Internet Explorer. It is tested in our `appveyor CI build`_.
61 |
62 | .. _appveyor CI build: https://ci.appveyor.com/project/spyoungtech/behave-webdriver
63 |
64 |
65 | Safari
66 | ------
67 |
68 | We have some preliminary support for Safari on OSX/Mac OS. It is tested as part of our `Travis CI build`_ (failures currently allowed).
69 |
70 | .. _Travis CI build: https://travis-ci.org/spyoungtech/behave-webdriver/
71 |
72 |
73 |
74 | PhantomJS
75 | ---------
76 |
77 |
78 | .. danger::
79 | Selenium support for PhantomJS has been deprecated and the `PhantomJS development has been suspended`_. As such,
80 | users are recommended to NOT use PhantomJS to begin with.
81 |
82 | PhantomJS is a low priority (see above). Users should expect issues with PhantomJS when using modern versions of selenium,
83 | and
84 |
85 |
86 | Known issues
87 | ^^^^^^^^^^^^
88 |
89 | - No support for alerts/modals
90 | - Cookies are problematic (cookies must have domain (and expiry?); setting cookies for localdomain not supported)
91 | - Memory-hungry
92 | - Unsupported (see above)
93 |
94 |
95 | .. _phantomJS development has been suspended: https://github.com/ariya/phantomjs/issues/15344
96 |
97 |
98 | Remote
99 | ------
100 |
101 | Remote is untested at this time.
102 |
103 |
104 |
105 | Edge
106 | ----
107 |
108 | Edge is untested at this time.
109 |
110 | Opera
111 | -----
112 |
113 | Opera is untested at this time.
114 |
115 |
116 | BlackBerry
117 | ----------
118 |
119 | BlackBerry is untested at this time.
120 |
121 | Android
122 | -------
123 |
124 | Android is untested at this time.
125 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # behave-webdriver documentation build configuration file, created by
5 | # sphinx-quickstart on Tue Oct 17 09:40:19 2017.
6 | #
7 | # This file is execfile()d with the current directory set to its
8 | # containing dir.
9 | #
10 | # Note that not all possible configuration values are present in this
11 | # autogenerated file.
12 | #
13 | # All configuration values have a default; values that are commented out
14 | # serve to show the default.
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #
20 | import os
21 | import sys
22 | sys.path.insert(0, os.path.abspath('..'))
23 |
24 |
25 | # -- General configuration ------------------------------------------------
26 |
27 | # If your documentation needs a minimal Sphinx version, state it here.
28 | #
29 | # needs_sphinx = '1.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = ['sphinx.ext.autodoc',
35 | 'sphinx.ext.coverage',
36 | 'sphinx.ext.viewcode']
37 | #autodoc_member_order = 'bysource'
38 |
39 | # Add any paths that contain templates here, relative to this directory.
40 | templates_path = ['_templates']
41 |
42 | # The suffix(es) of source filenames.
43 | # You can specify multiple suffix as a list of string:
44 | #
45 | # source_suffix = ['.rst', '.md']
46 | source_suffix = '.rst'
47 |
48 | # The master toctree document.
49 | master_doc = 'index'
50 |
51 | # General information about the project.
52 | project = 'behave-webdriver'
53 | copyright = '2017, Spencer Young'
54 | author = 'Spencer Young'
55 |
56 | # The version info for the project you're documenting, acts as replacement for
57 | # |version| and |release|, also used in various other places throughout the
58 | # built documents.
59 | #
60 | # The short X.Y version.
61 | version = '0.0.1'
62 | # The full version, including alpha/beta/rc tags.
63 | release = '0.0.1a'
64 |
65 | # The language for content autogenerated by Sphinx. Refer to documentation
66 | # for a list of supported languages.
67 | #
68 | # This is also used if you do content translation via gettext catalogs.
69 | # Usually you set "language" from the command line for these cases.
70 | language = None
71 |
72 | # List of patterns, relative to source directory, that match files and
73 | # directories to ignore when looking for source files.
74 | # This patterns also effect to html_static_path and html_extra_path
75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
76 |
77 | # The name of the Pygments (syntax highlighting) style to use.
78 | pygments_style = 'sphinx'
79 |
80 | # If true, `todo` and `todoList` produce output, else they produce nothing.
81 | todo_include_todos = False
82 |
83 |
84 | # -- Options for HTML output ----------------------------------------------
85 |
86 | # The theme to use for HTML and HTML Help pages. See the documentation for
87 | # a list of builtin themes.
88 | #
89 | html_theme = 'sphinx_rtd_theme' # 'alabaster'
90 |
91 | # Theme options are theme-specific and customize the look and feel of a theme
92 | # further. For a list of options available for each theme, see the
93 | # documentation.
94 | #
95 | # html_theme_options = {}
96 |
97 | # Add any paths that contain custom static files (such as style sheets) here,
98 | # relative to this directory. They are copied after the builtin static files,
99 | # so a file named "default.css" will overwrite the builtin "default.css".
100 | html_static_path = ['_static']
101 |
102 | # Custom sidebar templates, must be a dictionary that maps document names
103 | # to template names.
104 | #
105 | # This is required for the alabaster theme
106 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
107 | html_sidebars = {
108 | '**': [
109 | 'about.html',
110 | 'navigation.html',
111 | 'relations.html', # needs 'show_related': True theme option to display
112 | 'searchbox.html',
113 | 'donate.html',
114 | ]
115 | }
116 |
117 |
118 | # -- Options for HTMLHelp output ------------------------------------------
119 |
120 | # Output file base name for HTML help builder.
121 | htmlhelp_basename = 'behave-webdriverdoc'
122 |
123 |
124 | # -- Options for LaTeX output ---------------------------------------------
125 |
126 | latex_elements = {
127 | # The paper size ('letterpaper' or 'a4paper').
128 | #
129 | # 'papersize': 'letterpaper',
130 |
131 | # The font size ('10pt', '11pt' or '12pt').
132 | #
133 | # 'pointsize': '10pt',
134 |
135 | # Additional stuff for the LaTeX preamble.
136 | #
137 | # 'preamble': '',
138 |
139 | # Latex figure (float) alignment
140 | #
141 | # 'figure_align': 'htbp',
142 | }
143 |
144 | # Grouping the document tree into LaTeX files. List of tuples
145 | # (source start file, target name, title,
146 | # author, documentclass [howto, manual, or own class]).
147 | latex_documents = [
148 | (master_doc, 'behave-webdriver.tex', 'behave-webdriver Documentation',
149 | 'Spencer Young', 'manual'),
150 | ]
151 |
152 |
153 | # -- Options for manual page output ---------------------------------------
154 |
155 | # One entry per manual page. List of tuples
156 | # (source start file, name, description, authors, manual section).
157 | man_pages = [
158 | (master_doc, 'behave-webdriver', 'behave-webdriver Documentation',
159 | [author], 1)
160 | ]
161 |
162 |
163 | # -- Options for Texinfo output -------------------------------------------
164 |
165 | # Grouping the document tree into Texinfo files. List of tuples
166 | # (source start file, target name, title, author,
167 | # dir menu entry, description, category)
168 | texinfo_documents = [
169 | (master_doc, 'behave-webdriver', 'behave-webdriver Documentation',
170 | author, 'behave-webdriver', 'One line description of project.',
171 | 'Miscellaneous'),
172 | ]
173 |
--------------------------------------------------------------------------------
/docs/examples.rst:
--------------------------------------------------------------------------------
1 | Advanced usage; extending behave-webdriver
2 | ==========================================
3 |
4 | behave-webdriver is designed with **you** in-mind. You are free to extend the behavior of our webdriver classes to suit your
5 | unique needs. You can subclass our webdriver classes, use a custom selenium webdriver, write your own mixin, or use
6 | a mixin somebody else provides for selenium.
7 |
8 |
9 | Example: selenium-requests
10 | --------------------------
11 |
12 | `selenium-requests`_ is a preexisting project that adds functionality of the popular ``requests`` library to selenium.
13 | It is simple to use ``selenium-requests`` with behave-webdriver.
14 | The following, and other examples, are available in the repo ``examples`` directory and in the full documentation.
15 |
16 | .. code-block:: python
17 |
18 | # examples/selenium-requests/features/environment.py
19 | from selenium import webdriver # or any custom webdriver
20 | from behave_webdriver.driver import BehaveDriverMixin
21 | from seleniumrequests import RequestMixin # or your own mixin
22 |
23 | class BehaveRequestDriver(BehaveDriverMixin, RequestMixin, webdriver.Chrome):
24 | pass
25 |
26 | def before_all(context):
27 | context.behave_driver = BehaveRequestDriver()
28 | .. code-block:: python
29 |
30 | # examples/selenium-requests/features/steps/selenium_steps.py
31 | from behave import *
32 | from behave_webdriver.steps import *
33 | from urllib.parse import urljoin
34 |
35 | @given('I send a {method} request to the page "{page}"')
36 | def send_request_page(context, method, page):
37 | url = urljoin(context.base_url, page)
38 | context.response = context.behave_driver.request(method, url)
39 |
40 | @then('I expect the response text contains "{text}"')
41 | def check_response_text_contains(context, text):
42 | assert text in context.response.text
43 | .. code-block:: gherkin
44 |
45 | # examples/selenium-requests/features/selenium-requests.feature
46 | Feature: Using selenium-requests
47 | As a developer
48 | I should be able to extend behave-webdriver with selenium-requests
49 |
50 | Scenario: use selenium-requests with behave-webdriver
51 | # use a behave-webdriver step
52 | Given the base url is "http://127.0.0.1:8000"
53 | # use your own steps using selenium-requests features
54 | Given I send a GET request to the page "/"
55 | Then I expect the response text contains "
DEMO APP
"
56 |
57 | Assuming you're in the repository root (and have the demo app running) just run like any other project with ``behave``
58 |
59 | Results ✨
60 | ^^^^^^^^^^
61 |
62 | .. code-block:: guess
63 |
64 | (behave-webdriver) $ behave examples/selenium-requests/features
65 |
66 | DevTools listening on ws://127.0.0.1:12646/devtools/browser/1fe75b44-1c74-49fa-8e77-36c54d50cd24
67 | Feature: Using selenium-requests # examples/selenium-requests/features/requests.feature:1
68 | As a developer
69 | I should be able to extend behave-webdriver with selenium-requests
70 | Scenario: use selenium-requests with behave-webdriver # examples/selenium-requests/features/requests.feature:6
71 | Given the base url is "http://127.0.0.1:8000" # behave_webdriver/steps/actions.py:162
72 | Given I send a GET request to the page "/" # examples/selenium-requests/features/steps/selenium_steps.py:11
73 | Then I expect the response text contains "
DEMO APP
" # examples/selenium-requests/features/steps/selenium_steps.py:17
74 |
75 | 1 feature passed, 0 failed, 0 skipped
76 | 1 scenario passed, 0 failed, 0 skipped
77 | 3 steps passed, 0 failed, 0 skipped, 0 undefined
78 | Took 0m1.385s
79 |
80 | .. _selenium-requests: https://github.com/cryzed/Selenium-Requests
81 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. behave-webdriver documentation master file, created by
2 | sphinx-quickstart on Tue Oct 17 09:40:19 2017.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to behave-webdriver's documentation!
7 | ============================================
8 |
9 | behave-webdriver
10 | ----------------
11 |
12 | behave-webdriver is a step library intended to allow users to easily run browser automation tests (via `selenium`_)
13 | with the `behave`_ BDD testing framework.
14 |
15 | .. _selenium: https://github.com/SeleniumHQ/selenium
16 | .. _behave: https://github.com/behave/behave
17 |
18 | Inspired by, the webdriverio `cucumber-boilerplate`_ project.
19 |
20 | .. _cucumber-boilerplate: https://github.com/webdriverio/cucumber-boilerplate
21 |
22 | .. toctree::
23 | :maxdepth: 2
24 |
25 | installation
26 | quickstart
27 | api
28 | steps
29 | examples
30 | browsers
31 | roadmap
32 |
33 |
34 |
35 | Indices and tables
36 | ==================
37 |
38 | * :ref:`genindex`
39 | * :ref:`modindex`
40 | * :ref:`search`
41 |
42 |
43 |
44 | Goals
45 | -----
46 |
47 | * Make writing readable browser automation tests as Gherkin features easy.
48 | * Provide an easily extensible interface to the selenium driver (``BehaveDriver``)
49 | * To be (at least mostly) compatible with feature files written for `webdriverio/cucumber-boilerplate`_
50 |
51 |
52 | Status
53 | ------
54 |
55 | |version| |pyversions| |status|
56 |
57 | We currently test against Python2.7 and Python3.5+ using headless chrome. While all selenium's webdrivers are provided,
58 | they are not all supported at this time. We plan to add support for additional browsers in the future.
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | .. _raise an issue: https://github.com/spyoungtech/behave-webdriver/issues/new
67 |
68 |
69 | .. _selenium-requests: https://github.com/cryzed/Selenium-Requests
70 |
71 | .. _environment controls: http://behave.readthedocs.io/en/latest/tutorial.html#environmental-controls
72 |
73 | .. _fixtures: http://behave.readthedocs.io/en/latest/fixtures.html
74 |
75 | .. _step implementations: http://behave.readthedocs.io/en/latest/tutorial.html#python-step-implementations
76 |
77 | .. _driver installation notes: http://selenium-python.readthedocs.io/installation.html#drivers
78 |
79 | .. _behave-webdriver documentation: http://behave-webdriver.readthedocs.io/en/latest/
80 |
81 | .. _selenium: https://github.com/SeleniumHQ/selenium
82 |
83 | .. _behave: https://github.com/behave/behave
84 |
85 | .. _webdriverio/cucumber-boilerplate: https://github.com/webdriverio/cucumber-boilerplate
86 |
87 |
88 |
89 | .. |docs| image:: https://readthedocs.org/projects/behave-webdriver/badge/?version=latest
90 | :target: http://behave-webdriver.readthedocs.io/en/latest/
91 |
92 | .. |status| image:: https://travis-ci.org/spyoungtech/behave-webdriver.svg?branch=master
93 | :target: https://travis-ci.org/spyoungtech/behave-webdriver
94 |
95 | .. |version| image:: https://img.shields.io/pypi/v/behave-webdriver.svg?colorB=blue
96 | :target: https://pypi.org/project/behave-webdriver/
97 |
98 | .. |pyversions| image:: https://img.shields.io/pypi/pyversions/behave-webdriver.svg?
99 | :target: https://pypi.org/project/behave-webdriver/
100 |
101 | .. |coverage| image:: https://coveralls.io/repos/github/spyoungtech/behave-webdriver/badge.svg
102 | :target: https://coveralls.io/github/spyoungtech/behave-webdriver
103 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | behave-webdriver can be installed via ``pip``.
5 |
6 | .. code:: bash
7 |
8 | pip install behave-webdriver
9 |
10 | Using webdrivers
11 | ----------------
12 |
13 | Selenium requires that you provide executables for the webdriver you want to use. Further, unless you specify the path to
14 | the binary explicitly, selenium expects that this executable is in PATH. See these
15 | `driver installation notes`_ for more details.
16 |
17 | .. _driver installation notes: http://selenium-python.readthedocs.io/installation.html#drivers
18 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=python -msphinx
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 | set SPHINXPROJ=behave-webdriver
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed,
20 | echo.then set the SPHINXBUILD environment variable to point to the full
21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the
22 | echo.Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/docs/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quickstart
2 | ==========
3 |
4 | Ready to get started testing? This page will give you a quick introduction to behave-webdriver and how to use it. This
5 | assumes you have installed behave-webdriver and a webdriver on PATH. We also assume you got at least some familiarity
6 | with BDD/behave. If you're brand new to BDD in Python, you may want to check out the `behave docs`_ first.
7 |
8 | .. _behave docs: http://behave.readthedocs.io/en/latest/
9 |
10 | Basic usage of this library with behave requires the following steps:
11 |
12 | 1. import the step implementations
13 | 2. set the ``behave_driver`` attribute on the behave ``context`` in your ``environment.py`` file.
14 | 3. write your feature file
15 | 4. run ``behave``
16 |
17 | Importing the step implementations
18 | ----------------------------------
19 |
20 | In order for your feature file steps to match our step implementations, behave needs to find them in your project.
21 | This is as simple as importing our step definitions into your own step implementation file.
22 |
23 | .. code-block:: python
24 |
25 | # features/steps/webdriver_example.py
26 | from behave_webdriver.steps import *
27 |
28 |
29 | For more information about `step implementations`_, see the behave tutorial.
30 |
31 |
32 | Set behave_driver in the environment
33 | ------------------------------------
34 |
35 | Our step implementations specifically look at the behave context for a ``behave_driver`` attribute to use to run your tests.
36 | In order for that to work, you'll have to provide this attribute in your ``environment.py`` file.
37 |
38 | .. code-block:: python
39 |
40 | # features/environment.py
41 | import behave_webdriver
42 |
43 | def before_all(context):
44 | context.behave_driver = behave_webdriver.Chrome()
45 |
46 | def after_all(context):
47 | # cleanup after tests run
48 | context.behave_driver.quit()
49 |
50 |
51 | The webdriver classes provided by behave-webdriver inherit from selenium's webdriver classes, so they will accept all
52 | same positional and keyword arguments that selenium accepts.
53 |
54 | Some webdrivers, such as Chrome, provide special classmethods like ``Chrome.headless`` which instantiates ``Chrome`` with
55 | options to run headless. This is useful, for example in headless testing environments.
56 |
57 | .. code-block:: python
58 |
59 | def before_all(context):
60 | context.behave_driver = behave_webdriver.Chrome.headless()
61 |
62 | In the future, behave-webdriver will provide `fixtures`_ for the setup and teardown of webdrivers.
63 | See the behave tutorial for more information about `environment controls`_ .
64 |
65 | Writing the feature file
66 | ------------------------
67 |
68 | .. code-block:: gherkin
69 |
70 | # my-minimal-project/features/myFeature.feature
71 | Feature: Sample Snippets test
72 | As a developer
73 | I should be able to use given text snippets
74 |
75 | Scenario: open URL
76 | Given the page url is not "http://webdriverjs.christian-bromann.com/"
77 | And I open the url "http://webdriverjs.christian-bromann.com/"
78 | Then I expect that the url is "http://webdriverjs.christian-bromann.com/"
79 | And I expect that the url is not "http://google.com"
80 |
81 |
82 | Scenario: click on link
83 | Given the title is not "two"
84 | And I open the url "http://webdriverjs.christian-bromann.com/"
85 | When I click on the link "two"
86 | Then I expect that the title is "two"
87 |
88 | Run behave
89 | ----------
90 |
91 | Then run the tests, just like any other behave test
92 |
93 | .. code-block:: bash
94 |
95 | behave
96 |
97 | You should then see an output as follows::
98 |
99 | Feature: Sample Snippets test # features/myFeature.feature:2
100 | As a developer
101 | I should be able to use given text snippets
102 | Scenario: open URL # features/myFeature.feature:6
103 | Given the page url is not "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:136 0.012s
104 | And I open the url "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:10 1.414s
105 | Then I expect that the url is "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/then.py:102 0.007s
106 | And I expect that the url is not "http://google.com" # ../../behave_webdriver/steps/then.py:102 0.007s
107 |
108 | Scenario: click on link # features/myFeature.feature:13
109 | Given the title is not "two" # ../../behave_webdriver/steps/given.py:81 0.006s
110 | And I open the url "http://webdriverjs.christian-bromann.com/" # ../../behave_webdriver/steps/given.py:10 0.224s
111 | When I click on the link "two" # ../../behave_webdriver/steps/when.py:21 0.622s
112 | Then I expect that the title is "two" # ../../behave_webdriver/steps/then.py:10 0.006s
113 |
114 | 1 feature passed, 0 failed, 0 skipped
115 | 2 scenarios passed, 0 failed, 0 skipped
116 | 8 steps passed, 0 failed, 0 skipped, 0 undefined
117 | Took 0m2.298s
118 |
119 | Congratulations, you've just implemented a behavior-driven test without having to write a single step implementation!
120 |
121 | .. _environment controls: http://behave.readthedocs.io/en/latest/tutorial.html#environmental-controls
122 |
123 | .. _fixtures: http://behave.readthedocs.io/en/latest/fixtures.html
124 |
125 | .. _step implementations: http://behave.readthedocs.io/en/latest/tutorial.html#python-step-implementations
126 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | alabaster==0.7.10
2 | Babel==2.5.3
3 | behave==1.2.6
4 | certifi==2022.12.7
5 | chardet==3.0.4
6 | colorama==0.3.9
7 | coverage==4.5
8 | docutils==0.14
9 | idna==2.6
10 | imagesize==0.7.1
11 | Jinja2>=2.10.1
12 | MarkupSafe==1.0
13 | parse==1.8.2
14 | parse-type==0.4.2
15 | Pygments==2.2.0
16 | pytz==2017.3
17 | requests>=2.21.0
18 | selenium==3.9.0
19 | six==1.11.0
20 | snowballstemmer==1.2.1
21 | Sphinx==1.6.7
22 | sphinx-rtd-theme==0.2.4
23 | sphinxcontrib-websupport==1.0.1
24 | urllib3>=1.23
25 |
--------------------------------------------------------------------------------
/docs/roadmap.rst:
--------------------------------------------------------------------------------
1 | Roadmap
2 | =======
3 |
4 | Loosely organized collection of goals/milestones and ideas. Nothing here is necessarily concrete, but should give you an
5 | idea of where our heads are at for the development of behave-webdriver.
6 |
7 | You can help steer our roadmap in the right direction with suggestions, feedback, and other contributions. Please don't
8 | hesitate to `raise an issue`_ on Github.
9 |
10 |
11 | Immediate and Short Term
12 | ------------------------
13 |
14 | Immediate and short term goals are some milestones that we are actively working on or in our immediate forefront for development.
15 | Ideally, these things have clearly defined requirements and some work in progress.
16 |
17 |
18 |
19 | Documentation; recipes & tutorials
20 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21 |
22 | While documentation is something we'll be working on perpetually through development,
23 | we are particularly motivated on immediately providing at least some brief tutorials and recipes.
24 |
25 |
26 |
27 | Medium Term
28 | -----------
29 |
30 | Medium term goals are things we are committed to working on and implementing in the not-so-distant future.
31 | Ideally, this means we're working on these things passively and have at least a basic plan for the implementation.
32 |
33 |
34 | Device emulation
35 | ^^^^^^^^^^^^^^^^
36 |
37 | We want to provide support for steps that use device emulation features of drivers that support this. E.g. steps like
38 | ``Given I am using an iPhone 6``, ``Given I am using a Pixel 2``, etc.
39 |
40 |
41 |
42 | More step definitions
43 | ^^^^^^^^^^^^^^^^^^^^^
44 |
45 | We plan to implement additional step definitions to perform more actions with selenium and provide more robust
46 | interfaces for testing and automation. This will include things like taking & saving screenshots, retrieving/saving page source, and more.
47 |
48 | If you have ideas for step definitions you'd like to see implemented, `raise an issue`_ on Github. These contributions
49 | are welcomed and very much appreciated.
50 |
51 | Browser support (others)
52 | ^^^^^^^^^^^^^^^^^^^^^^^^
53 |
54 | Chrome and Firefox are in our forefront for browser support. We do however plan to test and provide best-effort
55 | support for all the webdrivers supported by selenium.
56 |
57 | We hope to get all browsers tested (but not necessarily passing) and attempt to make note of compatibility, behavior differences, and other browser-specific quirks.
58 |
59 | Would be nice to have more browsers tested in the CI builds as well.
60 |
61 | See :doc:`browsers` for more information.
62 |
63 |
64 |
65 | Long Term, ongoing, and Ideas
66 | -----------------------------
67 |
68 | These are some loose long-term milestones or ideas (which may or may not materialize) we have for the future.
69 | These are things that we would probably like to do, but have probably not put much effort into implementation or detailed plans.
70 | Anything we are remotely considering, but have not committed to, will be here, too.
71 |
72 | Assertion matcher
73 | ^^^^^^^^^^^^^^^^^
74 |
75 | Currently, standard python assertions are used. In the future, we may opt to use an assertion matching library such as
76 | pyhamcrest. Some time and effort will need to be put in to research a good choice in this area.
77 |
78 |
79 | Parallel support
80 | ^^^^^^^^^^^^^^^^
81 |
82 | While behave itself is planning to add parallel runner support in the future, its unlikely this will work well for
83 | browser testing. As such, we have this parallel support in the back of our minds, but it will probably be some time before
84 | it is introduced in a stable release.
85 |
86 |
87 | Use of other selenium libraries
88 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
89 |
90 | It may be possible for us to take advantage of previous work in this area, for example requestium, to enhance behave-webdriver.
91 | We want to explore these possibilities.
92 |
93 |
94 | Survey & reflection - path to a stable release
95 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
96 |
97 | While a stable (LTS) release itself is more of a long-term goal, we are constantly surveying the behave landscape and reviewing our API.
98 | We've made (what we feel are good) decisions in the design of behave-webdriver, but there's always room for improvement.
99 | Now particularly is a good time for us to ensure we are laying down a solid foundation to build upon for the future.
100 |
101 | Your feedback is immensely valuable in this regard and is sincerely appreciated.
102 | The best way to make suggestions or general comments is to `raise an issue`_ on Github.
103 |
104 |
105 |
106 |
107 | Better tests (ongoing)
108 | ^^^^^^^^^^^^^^^^^^^^^^
109 |
110 | Our github README boasts its coverage with a shiny badge from coveralls. The truth is that coverage isn't everything. There's undoubtedly cases
111 | where functionality is broken or doesn't work quite as expected. We want to find those to build better test cases, and
112 | improve the functionality of the library as a whole.
113 |
114 |
115 |
116 |
117 | Completed
118 | ---------
119 |
120 | Browser support (Firefox)
121 | ^^^^^^^^^^^^^^^^^^^^^^^^^
122 |
123 | Firefox is officially supported as of v0.1.1
124 |
125 | v0.1.0
126 | ^^^^^^
127 |
128 | Complete™ support with Google Chrome. We use the feature files (with modifications or additons in some cases) from
129 | cucumber-boilerplate as acceptance tests. While this is bound to be imperfect, it's a great start for v0.1
130 |
131 |
132 | Deferred
133 | --------
134 |
135 | Deferred items are things we previously comitted to but, for some reason or another, have placed on the backburner or
136 | suspended entirely.
137 |
138 | PhantomJS support
139 | ^^^^^^^^^^^^^^^^^
140 |
141 | While we will continue to provide best-effort support for all browsers, including PhantomJS, because PhantomJS has been
142 | deprecated for selenium and `phantomJS development has been suspended`_, PhantomJS is now a low priority.
143 |
144 | .. _raise an issue: https://github.com/spyoungtech/behave-webdriver/issues/new
145 |
146 |
147 | .. _phantomJS development has been suspended: https://github.com/ariya/phantomjs/issues/15344
148 |
--------------------------------------------------------------------------------
/docs/steps.rst:
--------------------------------------------------------------------------------
1 | ========================
2 | List of predefined steps
3 | ========================
4 |
5 |
6 |
7 | Given Steps 👷
8 | --------------
9 |
10 | - ``I open the site "([^"]*)?"``
11 | - ``I open the url "([^"]*)?"``
12 | - ``I have a screen that is ([\d]+) by ([\d]+) pixels``
13 | - ``I have a screen that is ([\d]+) pixels (broad|tall)``
14 | - ``I have closed all but the first (window|tab)``
15 | - ``I pause for (\d+)*ms``
16 | - ``a (alertbox|confirmbox|prompt) is( not)* opened``
17 | - ``the base url is "([^"]*)?"``
18 | - ``the checkbox "([^"]*)?" is( not)* checked``
19 | - ``the cookie "([^"]*)?" contains( not)* the value "([^"]*)?"``
20 | - ``the cookie "([^"]*)?" does( not)* exist``
21 | - ``the element "([^"]*)?" contains( not)* the same text as element "([^"]*)?"``
22 | - ``the element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)``
23 | - ``the element "([^"]*)?" is( not)* empty``
24 | - ``the element "([^"]*)?" is( not)* enabled``
25 | - ``the element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis``
26 | - ``the element "([^"]*)?" is( not)* selected``
27 | - ``the element "([^"]*)?" is( not)* visible``
28 | - ``the element "([^"]*)?"( not)* contains any text``
29 | - ``the element "([^"]*)?"( not)* contains the text "([^"]*)?"``
30 | - ``the element "([^"]*)?"( not)* matches the text "([^"]*)?"``
31 | - ``the page url is( not)* "([^"]*)?"``
32 | - ``the title is( not)* "([^"]*)?"``
33 | - ``the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"``
34 | - ``there is (an|no) element "([^"]*)?" on the page``
35 |
36 |
37 |
38 | When Steps ▶️
39 | -------------
40 |
41 | - ``I open the site "([^"]*)?"``
42 | - ``I open the url "([^"]*)?"``
43 | - ``I accept the (alertbox|confirmbox|prompt)``
44 | - ``I add "{value}" to the inputfield "{element}"``
45 | - ``I clear the inputfield "{element}"``
46 | - ``I click on the button "{element}"``
47 | - ``I click on the element "{element}"``
48 | - ``I click on the link "{link_text}"``
49 | - ``I close the last opened (tab|window)``
50 | - ``I delete the cookie "{cookie_key}"``
51 | - ``I dismiss the (alertbox|confirmbox|prompt)``
52 | - ``I doubleclick on the element "{element}"``
53 | - ``I drag element "{from_element}" to element "{to_element}"``
54 | - ``I enter "([^"]*)?" into the (alertbox|confirmbox|prompt)``
55 | - ``I focus the last opened (tab|window)``
56 | - ``I move to element "{element}" with an offset of {x_offset:d},{y_offset:d}``
57 | - ``I move to element "{element}"``
58 | - ``I pause for {milliseconds:d}ms``
59 | - ``I press "{key}"``
60 | - ``I scroll to element "{element}"``
61 | - ``I select the option with the (text|value|name) "([^"]*)?" for element "([^"]*)?"``
62 | - ``I select the {nth} option for element "{element}"``
63 | - ``I set "{value}" to the inputfield "{element}"``
64 | - ``I set a cookie "{cookie_key}" with the content "{value}"``
65 | - ``I submit the form "{element}"``
66 |
67 | Then Steps ✔️
68 | -------------
69 |
70 | - ``I expect the screen is ([\d]+) by ([\d]+) pixels``
71 | - ``I expect a new (window|tab) has( not)* been opened``
72 | - ``I expect that a (alertbox|confirmbox|prompt) is( not)* opened``
73 | - ``I expect that a (alertbox|confirmbox|prompt)( not)* contains the text "([^"]*)?"``
74 | - ``I expect that checkbox "([^"]*)?" is( not)* checked``
75 | - ``I expect that cookie "([^"]*)?"( not)* contains "([^"]*)?"``
76 | - ``I expect that cookie "([^"]*)?"( not)* exists``
77 | - ``I expect that element "([^"]*)?" (has|does not have) the class "([^"]*)?"``
78 | - ``I expect that element "([^"]*)?" becomes( not)* visible``
79 | - ``I expect that element "([^"]*)?" does( not)* exist``
80 | - ``I expect that element "([^"]*)?" is( not)* ([\d]+)px (broad|tall)``
81 | - ``I expect that element "([^"]*)?" is( not)* empty``
82 | - ``I expect that element "([^"]*)?" is( not)* enabled``
83 | - ``I expect that element "([^"]*)?" is( not)* focused``
84 | - ``I expect that element "([^"]*)?" is( not)* positioned at ([\d]+)px on the (x|y) axis``
85 | - ``I expect that element "([^"]*)?" is( not)* selected``
86 | - ``I expect that element "([^"]*)?" is( not)* visible``
87 | - ``I expect that element "([^"]*)?" is( not)* within the viewport``
88 | - ``I expect that element "([^"]*)?"( not)* contains any text``
89 | - ``I expect that element "([^"]*)?"( not)* contains the same text as element "([^"]*)?"``
90 | - ``I expect that element "([^"]*)?"( not)* contains the text "([^"]*)?"``
91 | - ``I expect that element "([^"]*)?"( not)* matches the text "([^"]*)?"``
92 | - ``I expect that the path is( not)* "([^"]*)?"``
93 | - ``I expect that the title is( not)* "([^"]*)?"``
94 | - ``I expect that the url is( not)* "([^"]*)?"``
95 | - ``I expect that the( css)* attribute "([^"]*)?" from element "([^"]*)?" is( not)* "([^"]*)?"``
96 | - ``I expect the url "([^"]*)?" is opened in a new (tab|window)``
97 | - ``I expect the url to( not)* contain "([^"]*)?"``
98 | - ``I wait on element "([^"]*)?"(?: for (\d+)ms)*(?: to( not)* (be checked|be enabled|be selected|be visible|contain a text|contain a value|exist))*``
99 |
--------------------------------------------------------------------------------
/examples/minimal-project/features/environment.py:
--------------------------------------------------------------------------------
1 | from behave_webdriver import Chrome
2 |
3 | def before_all(context):
4 | context.behave_driver = Chrome()
5 |
6 | def after_all(context):
7 | context.behave_driver.quit()
8 |
--------------------------------------------------------------------------------
/examples/minimal-project/features/myFeature.feature:
--------------------------------------------------------------------------------
1 | # minimal-project/features/myFeature.feature
2 | Feature: Sample Snippets test
3 | As a developer
4 | I should be able to use given text snippets
5 |
6 | Scenario: open URL
7 | Given the page url is not "http://webdriverjs.christian-bromann.com/"
8 | And I open the url "http://webdriverjs.christian-bromann.com/"
9 | Then I expect that the url is "http://webdriverjs.christian-bromann.com/"
10 | And I expect that the url is not "http://google.com"
11 |
12 |
13 | Scenario: click on link
14 | Given the title is not "two"
15 | And I open the url "http://webdriverjs.christian-bromann.com/"
16 | When I click on the link "two"
17 | Then I expect that the title is "two"
18 |
--------------------------------------------------------------------------------
/examples/minimal-project/features/steps/my_steps.py:
--------------------------------------------------------------------------------
1 | from behave_webdriver.steps import *
2 |
--------------------------------------------------------------------------------
/examples/selenium-requests/features/environment.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | from behave_webdriver.driver import BehaveDriverMixin
3 | from seleniumrequests import RequestMixin
4 |
5 | class BehaveRequestDriver(BehaveDriverMixin, RequestMixin, webdriver.Chrome):
6 | pass
7 |
8 | def before_all(context):
9 | context.behave_driver = BehaveRequestDriver()
10 |
11 | def after_all(context):
12 | context.behave_driver.quit()
13 |
--------------------------------------------------------------------------------
/examples/selenium-requests/features/requests.feature:
--------------------------------------------------------------------------------
1 | Feature: Using selenium-requests
2 | As a developer
3 | I should be able to extend behave-webdriver with selenium-requests
4 |
5 |
6 | Scenario: use selenium-requests with behave-webdriver
7 | Given the base url is "http://127.0.0.1:8000"
8 | Given I send a GET request to the page "/"
9 | Then I expect the response text contains "
9 |
10 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/demo-app/runserver.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """launch small http server
4 | """
5 |
6 | import sys
7 |
8 | try:
9 | from SimpleHTTPServer import SimpleHTTPRequestHandler
10 | except ImportError:
11 | from http.server import SimpleHTTPRequestHandler
12 |
13 | try:
14 | from SocketServer import TCPServer as HTTPServer
15 | except ImportError:
16 | from http.server import HTTPServer
17 |
18 | # simple web server
19 | # serves files relative to the current directory.
20 |
21 | server_port = 8000
22 | try:
23 | server_port = int(sys.argv[1])
24 | except:
25 | pass
26 |
27 | httpd = HTTPServer(("", server_port), SimpleHTTPRequestHandler)
28 | print("serving at port {0}".format(server_port))
29 | httpd.serve_forever()
30 |
--------------------------------------------------------------------------------
/tests/experimental-features/elementPosition.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the position of a given element
2 | As a developer
3 | I want to be able to test if a element has a certain position
4 |
5 | Background:
6 | Given I open the site "/"
7 | And I have a screen that is 800 by 600 pixels
8 | And the element "#square100x100" is 100px broad
9 | And the element "#square100x100" is 100px tall
10 | When I scroll to element "#square100x100"
11 |
12 | Scenario: The element #square100x100 is at XX pixels on the X axis
13 | Then I expect that element "#square100x100" is positioned at 40px on the x axis
14 |
15 | @Pending
16 | Scenario: The element #square100x100 is at XX pixels on the Y axis
17 | Then I expect that element "#square100x100" is positioned at 843px on the y axis
18 |
19 | Scenario: The element #square100x100 is not at YY pixels on the X axis
20 | Then I expect that element "#square100x100" is not positioned at 101px on the x axis
21 |
22 | Scenario: The element #square100x100 is not at YY pixels on the y axis
23 | Then I expect that element "#square100x100" is not positioned at 99px on the y axis
24 |
--------------------------------------------------------------------------------
/tests/experimental-features/elementVisibility.feature:
--------------------------------------------------------------------------------
1 | Feature: Test visibility of elements
2 | As a developer
3 | I want to be able to test the visibillity of a element
4 |
5 | Background:
6 | Given I open the url "http://localhost:8000/"
7 | And I pause for 1000ms
8 |
9 | Scenario: Invisible elements to be invisible
10 | Then I expect that element "#hidden" is not visible
11 |
12 | Scenario: Visible elements to be visible
13 | Then I expect that element "#visible" is visible
14 |
15 | Scenario: Element should become visible
16 | Given the element "#makeVisible" is not visible
17 | When I click on the element "#btnMakeVisible"
18 | Then I expect that element "#makeVisible" becomes visible
19 |
20 | Scenario: Element should become invisible
21 | Given the element "#makeInvisible" is visible
22 | When I click on the element "#btnMakeInvisible"
23 | Then I expect that element "#makeInvisible" becomes not visible
24 |
25 | Scenario: Element in the viewport
26 | Then I expect that element "h1" is within the viewport
27 |
28 | Scenario: Element outside the viewport
29 | When I scroll to element "#footer"
30 | Then I expect that element "h1" is not within the viewport
31 |
--------------------------------------------------------------------------------
/tests/experimental-features/environment.py:
--------------------------------------------------------------------------------
1 | from behave_webdriver import BehaveDriver
2 |
3 | def before_all(context):
4 | context.behave_driver = BehaveDriver.headless_chrome()
5 | def after_all(context):
6 | context.behave_driver.quit()
7 |
--------------------------------------------------------------------------------
/tests/experimental-features/githubSearch.feature.pending:
--------------------------------------------------------------------------------
1 | Feature: Github test
2 | As a Developer in Test
3 | I want to search for webdriverio repository
4 | So that I can use it in my future tests
5 |
6 | Scenario: open URL
7 | Given I open the url "https://github.com/"
8 | Then I expect that the url is "https://github.com/"
9 | And I expect that the title is "How people build software · GitHub"
10 |
11 | Scenario: search for webdriverio repository
12 | Given I open the url "https://github.com/search"
13 | And the inputfield ".input-block" not contains any text
14 | And I set "webdriverio" to the inputfield ".input-block"
15 | And I press "Space"
16 | And I add "selenium" to the inputfield ".input-block"
17 | When I submit the form "#search_form"
18 | Then I expect that element ".input-block" contains the text "webdriverio selenium"
19 | And I expect that element ".repo-list-item:first-child > .repo-list-description" contains the text "Webdriver/Selenium 2.0 JavaScript bindings for Node.js"
20 |
21 | Scenario: login with fake credentials
22 | Given I open the url "https://github.com/"
23 | When I log in to site with username "marketionist" and password "1234"
24 | Then I expect that element "#js-flash-container .flash-error" is visible
25 |
--------------------------------------------------------------------------------
/tests/experimental-features/login.feature.pending:
--------------------------------------------------------------------------------
1 | Feature: Github test
2 | As a Developer in Test
3 | I want to test if the github.com failed login screen displays a error
4 |
5 | Scenario: open URL
6 | Given I open the url "https://github.com/"
7 | Then I expect that the url is "https://github.com/"
8 | And I expect that the title is "How people build software · GitHub"
9 |
10 | Scenario: login with fake credentials
11 | Given I open the url "https://github.com/"
12 | When I log in to site with username "marketionist" and password "1234"
13 | Then I expect that element "#js-flash-container .flash-error" is visible
14 |
--------------------------------------------------------------------------------
/tests/experimental-features/pending.feature:
--------------------------------------------------------------------------------
1 | @Pending
2 | Feature: Pending scenario
3 | As a test framework
4 | I should be able skip all these scenarios
5 |
6 | Scenario: do somethimg
7 | Given I open the site "/"
8 | Then this will fail since this does not exist
9 |
--------------------------------------------------------------------------------
/tests/experimental-features/steps/webdriver_steps.py:
--------------------------------------------------------------------------------
1 | from behave_webdriver.steps import *
2 |
--------------------------------------------------------------------------------
/tests/features/attribute.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the attributes of a given element
2 | As a developer
3 | I want to be able to test the attributes of a given element
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: The attribute "role" of a element should be "note"
9 | Then I expect that the attribute "role" from element "#attributeComparison" is "note"
10 |
11 | Scenario: The attribute "role" of a element should not be "main"
12 | Then I expect that the attribute "role" from element "#attributeComparison" is not "main"
13 |
14 | Scenario: The CSS attribute "color" of a element should be "red"
15 | Then I expect that the css attribute "color" from element "#cssAttributeComparison" is "rgba(255, 0, 0, 1)"
16 |
17 | Scenario: The CSS attribute "color" of a element should not be "blue"
18 | Then I expect that the css attribute "color" from element "#cssAttributeComparison" is not " rgba(0, 255, 0, 1)"
19 |
20 | Scenario: The (missing) CSS attribute "border" of a element should not be "0"
21 | Then I expect that the css attribute "border" from element "#cssAttributeComparison" is not "0"
22 |
--------------------------------------------------------------------------------
/tests/features/baseUrl.feature:
--------------------------------------------------------------------------------
1 | Feature: Base URL configuration
2 | As a developer
3 | I should be able to change the base URL for opening pages.
4 |
5 | Scenario: Change the base url to http://127.0.0.1:8000/
6 | Given the base url is "http://127.0.0.1:8000/"
7 | When I open the site "/page.html"
8 | Then I expect that the url is "http://127.0.0.1:8000/page.html"
9 | And I expect that the url is not "http://localhost:8000/page.html"
10 |
--------------------------------------------------------------------------------
/tests/features/baseUrlWithParameterTransformation.feature:
--------------------------------------------------------------------------------
1 | Feature: Base URL configuration supports parameter transformation
2 | As a developer
3 | I should be able to change the base URL for opening pages
4 | And I should be able to replace hardcoded values with parameters.
5 |
6 | # {BASE_URL} / {ALT_BASE_URL} are transformed by transformer provided in environment.py
7 | # As long the URLs are valid, this test should still work even if the test url is not available at those urls.
8 | Scenario: Default base is http://localhost:8000/
9 | When I open the site "/"
10 | Then I expect that the url is "{BASE_URL}/"
11 |
12 | Scenario: Change the base url to http://127.0.0.1:8000/
13 | Given the base url is "{ALT_BASE_URL}/"
14 | When I open the site "/page.html"
15 | Then I expect that the url is "{ALT_BASE_URL}/page.html"
16 | And I expect that the url is not "{BASE_URL}/page.html"
17 |
--------------------------------------------------------------------------------
/tests/features/buttonPress.feature:
--------------------------------------------------------------------------------
1 | Feature: Test button press
2 | As a developer
3 | I want to be able to test if a certain action is performed when a certain
4 | button is pressed
5 |
6 | Background:
7 | Given I open the site "/"
8 |
9 | Scenario: Test if element responds to button press
10 | Given the element "#testKeyResponse" not contains any text
11 | When I press "a"
12 | Then I expect that element "#testKeyResponse" contains the text "65"
13 |
14 | Scenario: Test if element responds to button press
15 | Given the element "#testKeyResponse" not contains any text
16 | When I press "b"
17 | Then I expect that element "#testKeyResponse" not contains the text "65"
18 |
19 | # Escape key
20 | Scenario: Test if element responds to button press
21 | Given the element "#testKeyResponse" not contains any text
22 | When I press "Escape"
23 | Then I expect that element "#testKeyResponse" contains the text "27"
24 |
--------------------------------------------------------------------------------
/tests/features/checkTitle.feature:
--------------------------------------------------------------------------------
1 | Feature: Local server test
2 | As a developer
3 | I want the demo app have the correct title
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: Is not Google
9 | Then I expect that the title is not "Google"
10 |
11 | Scenario: Is correct
12 | Then I expect that the title is "DEMO APP"
13 |
--------------------------------------------------------------------------------
/tests/features/checkbox.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the selected state of a checkbox
2 | As a developer
3 | I want to be able to test the selected state of a checkbox
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: The checkbox should not be selected by default
9 | Then I expect that checkbox "#checkbox" is not checked
10 |
11 | Scenario: The checkbox should be checked when clicked
12 | Given the checkbox "#checkbox" is not checked
13 | When I click on the element "#checkbox"
14 | Then I expect that checkbox "#checkbox" is checked
15 |
16 | Scenario: The checkbox should deselect when clicked twice
17 | Given the checkbox "#checkbox" is not checked
18 | When I click on the element "#checkbox"
19 | And I click on the element "#checkbox"
20 | Then I expect that checkbox "#checkbox" is not checked
21 |
--------------------------------------------------------------------------------
/tests/features/class.feature:
--------------------------------------------------------------------------------
1 | # I expect that element "$string" (has|does not have) the class "$string"
2 |
3 | Feature: Test if a given element has a certain CSS class
4 | As a developer
5 | I want to be able to test if a element has a certain CSS class
6 |
7 | Background:
8 | Given I open the site "/"
9 |
10 | Scenario: Element #classTest should have the class "class1"
11 | Then I expect that element "#classTest" has the class "class1"
12 |
13 | Scenario: Element #classTest should also have the class "class2"
14 | Then I expect that element "#classTest" has the class "class2"
15 |
16 | Scenario: Element #classTest should not have the class "class3"
17 | Then I expect that element "#classTest" does not have the class "class3"
18 |
--------------------------------------------------------------------------------
/tests/features/click.feature:
--------------------------------------------------------------------------------
1 | Feature: Test how clicks are handled on a certain element
2 | As a developer
3 | I want to be able to test how (double) clicks are handled by certain elements
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | @skip_safari
9 | Scenario: Single click on a link should navigate to another page
10 | When I click on the link "Navigate to example.com"
11 | Then I expect the url to contain "https://example.com"
12 |
13 | @Isolate
14 | Scenario: Single click on the button #toggleMessage should display an message
15 | When I click on the button "#toggleMessage"
16 | Then I expect that element "#message1" is visible
17 | And I expect that element "#message2" is not visible
18 |
19 | @firefox_bug
20 | @skip_safari
21 | Scenario: Double click on the button #toggleMessage should display another message
22 | When I doubleclick on the element "#toggleMessage"
23 | Then I expect that element "#message1" is not visible
24 | And I expect that element "#message2" is visible
25 |
26 | @skip_safari
27 | Scenario: Single click on the element #toggleBackground should make the elemnt red
28 | When I click on the element "#toggleBackground"
29 | @firefox_bug
30 | Scenario: Double click on the element #toggleBackground should make the elemnt blue
31 | When I doubleclick on the element "#toggleBackground"
32 |
--------------------------------------------------------------------------------
/tests/features/cookie.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the existens and content of cookies
2 | As a developer
3 | I want to be able to test the existence and/or the content of cookies
4 |
5 | Background:
6 | Given I open the site "/"
7 | And I pause for 500ms
8 |
9 | Scenario: The cookie "test" should exist
10 | Then I expect that cookie "test" exists
11 |
12 | Scenario: The cookie "test2" should not exist
13 | Given the cookie "test" contains the value "yumyum"
14 | Then I expect that cookie "test2" not exists
15 |
16 | Scenario: The cookie "test" should contain the value "yumyum"
17 | Given the cookie "test" contains not the value "out of date"
18 | Then I expect that cookie "test" contains "yumyum"
19 |
20 | Scenario: The cookie "test" should not contain the value "out of date"
21 | Then I expect that cookie "test" not contains "out of date"
22 |
23 | Scenario: The cookie "test3" should be created
24 | When I set a cookie "test3" with the content "more cookies"
25 | Then I expect that cookie "test3" exists
26 | And I expect that cookie "test3" contains "more cookies"
27 |
28 | Scenario: The cookie "test3" should be deletable
29 | Then I expect that cookie "test3" exists
30 | When I delete the cookie "test3"
31 | Then I expect that cookie "test3" not exists
32 |
--------------------------------------------------------------------------------
/tests/features/drag.feature:
--------------------------------------------------------------------------------
1 | Feature: Test draggable elements
2 | As a developer
3 | I want to be able to test a given draggable element
4 |
5 | Background:
6 | Given I open the site "/"
7 | And I have a screen that is 1024 by 768 pixels
8 | When I scroll to element "#draggable"
9 |
10 | Scenario: Drag to dropzone
11 | When I drag element "#draggable" to element "#droppable"
12 | Then I expect that element "#droppable" contains the text "Dropped!"
13 |
--------------------------------------------------------------------------------
/tests/features/elementExistence.feature:
--------------------------------------------------------------------------------
1 | Feature: Test existence of elements
2 | As a developer
3 | I want to be able to test the existence of a element
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: None existing element check
9 | Then I expect that element "#noneExisting" does not exist
10 |
11 | Scenario: Existing element check
12 | Then I expect that element "#exisiting" does exist
13 |
--------------------------------------------------------------------------------
/tests/features/elementSize.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the width and height of a given element
2 | As a developer
3 | I want to be able to test if a element has a certain width and/or height
4 |
5 | Background:
6 | Given I open the site "/"
7 | @skip_safari
8 | Scenario: The element #square100x100 whould have a width of 100px
9 | Then I expect that element "#square100x100" is 100px broad
10 |
11 | Scenario: The element #square100x100 whould have a height of 100px
12 | Then I expect that element "#square100x100" is 100px tall
13 | @skip_safari
14 | Scenario: The element #square100x100 whould not have a width of 101px
15 | Then I expect that element "#square100x100" is not 101px broad
16 |
17 | Scenario: The element #square100x100 whould not have a height of 99px
18 | Then I expect that element "#square100x100" is not 99px tall
19 |
--------------------------------------------------------------------------------
/tests/features/environment.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import shutil
4 | from os import getcwd
5 | from os.path import abspath, join
6 | from sys import version_info
7 | from behave_webdriver import before_all_factory, use_fixture_tag
8 | from behave_webdriver.driver import Chrome, ChromeOptions
9 | from functools import partial
10 | from behave_webdriver.fixtures import transformation_fixture, fixture_browser
11 | from behave_webdriver.transformers import FormatTransformer
12 | from behave.fixture import use_fixture
13 | import behave_webdriver
14 |
15 |
16 | def get_driver(**kwargs):
17 | args = []
18 | kwargs.setdefault('default_wait', 5)
19 | Driver = behave_webdriver.utils._from_env(default_driver='Chrome')
20 | if Driver == behave_webdriver.Chrome:
21 | opts = ChromeOptions()
22 | opts.add_argument('--no-sandbox') # for travis build
23 | kwargs['chrome_options'] = opts
24 |
25 | pwd_driver_path = os.path.abspath(os.path.join(os.getcwd(), Driver._driver_name))
26 | if sys.version_info[0] < 3:
27 | ex_path = pwd_driver_path
28 | else:
29 | ex_path = shutil.which(Driver._driver_name) or pwd_driver_path
30 | kwargs['executable_path'] = ex_path
31 | if os.environ.get('BEHAVE_WEBDRIVER_HEADLESS', None) and hasattr(Driver, 'headless'):
32 | Driver = Driver.headless
33 | return Driver, kwargs
34 |
35 | #context.behave_driver = context.BehaveDriver()
36 |
37 | def before_all(context):
38 | driver, kwargs = get_driver()
39 | context.BehaveDriver = partial(driver, **kwargs)
40 | use_fixture(fixture_browser, context, webdriver=driver, **kwargs)
41 | use_fixture(transformation_fixture, context, FormatTransformer, BASE_URL='http://localhost:8000', ALT_BASE_URL='http://127.0.0.1:8000')
42 |
43 |
44 | def before_tag(context, tag):
45 | use_fixture_tag(context, tag)
46 |
47 |
48 | def before_feature(context, feature):
49 | if "skip_safari" in feature.tags and os.environ.get("BEHAVE_WEBDRIVER", '').lower() == 'safari':
50 | feature.skip()
51 | return
52 | if "fresh_driver" in feature.tags:
53 | context.behave_driver.quit()
54 | context.behave_driver = context.BehaveDriver()
55 | context.behave_driver.default_wait = 5
56 |
57 |
58 | def before_scenario(context, scenario):
59 | if "skip_firefox" in scenario.effective_tags and os.environ.get("BEHAVE_WEBDRIVER", '').lower() == 'firefox':
60 | scenario.skip("Skipping because @skip_firefox tag (usually this is because of a known-issue with firefox)")
61 | return
62 | if "skip_safari" in scenario.effective_tags and os.environ.get("BEHAVE_WEBDRIVER", '').lower() == 'safari':
63 | scenario.skip("Skipping because safari has issues we're not dealing with right now")
64 | return
65 |
--------------------------------------------------------------------------------
/tests/features/focus.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the focus state of a given element
2 | As a developer
3 | I want to be able to test if a element has a certain focus state
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: The element #textinput should not have the focus by default
9 | Then I expect that element "#textinput" is not focused
10 |
11 | Scenario: The element #textinput should have the focus when selected
12 | When I click on the element "#textinput"
13 | Then I expect that element "#textinput" is focused
14 |
--------------------------------------------------------------------------------
/tests/features/form.feature:
--------------------------------------------------------------------------------
1 | Feature: Test form submission
2 | As a developer
3 | I want to be able to test form submission
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: Test if a message is shown when the form is submitted
9 | Given the element "#formSubmitTest .message" is not visible
10 | When I submit the form "#formSubmitTest"
11 | Then I expect that element "#formSubmitTest .message" is visible
12 |
--------------------------------------------------------------------------------
/tests/features/inputfield.feature:
--------------------------------------------------------------------------------
1 | Feature: Test input fields on a page
2 | As a developer
3 | I want to be able to test input fields on a certain page
4 |
5 | Background:
6 | Given I open the site "/"
7 | Then I expect that element "#testInput" not contains any text
8 |
9 | Scenario: Set the content of a input field
10 | When I set "test" to the inputfield "#testInput"
11 | Then I expect that element "#testInput" contains any text
12 | And I expect that element "#testInput" contains the text "test"
13 |
14 | Scenario: Add content to a input field
15 | When I set "test" to the inputfield "#testInput"
16 | Then I expect that element "#testInput" contains any text
17 | When I add " more tests" to the inputfield "#testInput"
18 | Then I expect that element "#testInput" contains the text "test more tests"
19 |
20 | Scenario: Clear the content of a input field
21 | When I set "test" to the inputfield "#testInput"
22 | Then I expect that element "#testInput" contains any text
23 | And I expect that element "#testInput" contains the text "test"
24 | When I clear the inputfield "#testInput"
25 | Then I expect that element "#testInput" not contains any text
26 |
--------------------------------------------------------------------------------
/tests/features/isEmpty.feature:
--------------------------------------------------------------------------------
1 | Feature: Test input fields on a page
2 | As a developer
3 | I want to be able to test input fields on a certain page
4 |
5 | Background:
6 | Given I open the site "/"
7 | Then I expect that element "#testInput" is empty
8 |
9 | Scenario: Set the content of a input field
10 | When I set "test" to the inputfield "#testInput"
11 | Then I expect that element "#testInput" is not empty
12 | And I expect that element "#testInput" contains the text "test"
13 |
14 | Scenario: Add content to a input field
15 | When I set "test" to the inputfield "#testInput"
16 | Then I expect that element "#testInput" is not empty
17 | When I add " more tests" to the inputfield "#testInput"
18 | Then I expect that element "#testInput" contains the text "test more tests"
19 |
20 | Scenario: Clear the content of a input field
21 | When I set "test" to the inputfield "#testInput"
22 | Then I expect that element "#testInput" is not empty
23 | And I expect that element "#testInput" contains the text "test"
24 | When I clear the inputfield "#testInput"
25 | Then I expect that element "#testInput" is empty
26 |
--------------------------------------------------------------------------------
/tests/features/isExisting.feature:
--------------------------------------------------------------------------------
1 | Feature: Github test
2 | As a Developer in Test
3 | I want to search for webdriverio repository
4 | And check if some elements are existing and others are not
5 |
6 | Scenario: open URL
7 | Given I open the url "https://github.com/spyoungtech/behave-webdriver"
8 | Then I expect that element ".octicon-mark-github" does exist
9 | And I expect that element ".some-other-element" does not exist
10 |
--------------------------------------------------------------------------------
/tests/features/modals.feature:
--------------------------------------------------------------------------------
1 | Feature: Test modals
2 | As a developer
3 | I want to be able to test the onening, closing and contens of modal windows
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: Test if alert is opened accepted
9 | Given a alertbox is not opened
10 | When I click on the element "#openAlert"
11 | Then I expect that a alertbox is opened
12 | And I expect that a alertbox contains the text "I am a alert box!"
13 | And I expect that a alertbox not contains the text "Other Text"
14 | When I accept the alertbox
15 | Then I expect that a alertbox is not opened
16 |
17 | Scenario: Test if alert is opened & dismissed
18 | Given a alertbox is not opened
19 | When I click on the element "#openAlert"
20 | Then I expect that a alertbox is opened
21 | When I dismiss the alertbox
22 | Then I expect that a alertbox is not opened
23 |
24 | Scenario: Test if confirm is canceled
25 | Given a confirmbox is not opened
26 | And the element "#confirmResult" not contains any text
27 | When I click on the element "#openConfirm"
28 | Then I expect that a confirmbox is opened
29 | And I expect that a alertbox contains the text "I am a confirm box!"
30 | When I dismiss the confirmbox
31 | Then I expect that a confirmbox is not opened
32 | And I expect that element "#confirmResult" contains the text "false"
33 |
34 | Scenario: Test if confirm is accepted
35 | Given a confirmbox is not opened
36 | And the element "#confirmResult" not contains any text
37 | When I click on the element "#openConfirm"
38 | Then I expect that a confirmbox is opened
39 | When I accept the confirmbox
40 | Then I expect that a confirmbox is not opened
41 | And I expect that element "#confirmResult" contains the text "true"
42 |
43 | Scenario: Test if prompt is opened & dismissed
44 | Given a prompt is not opened
45 | And the element "#promptResult" not contains any text
46 | When I click on the element "#openPrompt"
47 | Then I expect that a prompt is opened
48 | And I expect that a alertbox contains the text "I am a prompt!"
49 | When I dismiss the prompt
50 | Then I expect that a prompt is not opened
51 | And I expect that element "#promptResult" contains the text "null"
52 |
53 | Scenario: Test if prompt is accepted
54 | Given a prompt is not opened
55 | And the element "#promptResult" not contains any text
56 | When I click on the element "#openPrompt"
57 | Then I expect that a prompt is opened
58 | When I accept the prompt
59 | Then I expect that a prompt is not opened
60 | And I expect that element "#promptResult" not contains any text
61 |
62 | Scenario: Test if prompt has text entered
63 | Given a prompt is not opened
64 | And the element "#promptResult" not contains any text
65 | When I click on the element "#openPrompt"
66 | Then I expect that a prompt is opened
67 | When I enter "test 1 2 3" into the prompt
68 | And I accept the prompt
69 | Then I expect that a prompt is not opened
70 | And I expect that element "#promptResult" contains the text "test 1 2 3"
71 |
--------------------------------------------------------------------------------
/tests/features/moveTo.feature:
--------------------------------------------------------------------------------
1 | Feature: Test moveTo elements
2 | As a developer
3 | I want to be able to test if I can move to a element
4 | with an optional relative X and Y position
5 |
6 | Background:
7 | Given I open the site "/"
8 | And I have a screen that is 1024 by 768 pixels
9 | When I scroll to element "#moveTo"
10 |
11 | Scenario: Move to just the element
12 | When I move to element "#moveTo"
13 | Then I expect that element "#moveTo" has the class "moveToClass"
14 | When I move to element "body"
15 | Then I expect that element "#moveTo" does not have the class "moveToClass"
16 |
17 | Scenario: Move to the element with a X and Y offset
18 | When I move to element "#moveTo" with an offset of 15,5
19 | Then I expect that element "#moveTo" has the class "moveToClass"
20 | @skip_firefox
21 | Scenario: Move to the element with a too large offset
22 | When I move to element "#moveTo" with an offset of 941,21
23 | Then I expect that element "#moveTo" does not have the class "moveToClass"
24 |
--------------------------------------------------------------------------------
/tests/features/multipleSelect.feature:
--------------------------------------------------------------------------------
1 | # Created by spenceryoung at 3/2/2018
2 | Feature: Test Multiple Select elements
3 | As a developer
4 | I want to be able to test if certain elements in a multiple select field are selected
5 |
6 | Background:
7 | Given I open the site "/page.html"
8 | Then I expect that element "#yes" is not selected
9 | Then I expect that element "#yes2" is not selected
10 | Then I expect that element "#no" is not selected
11 | Then I expect that element "#affirmative" is not selected
12 | Then I expect that element "#negative" is not selected
13 |
14 | Scenario: Test if multiple values are selected by text
15 | When I select the option with the text "Yes" for element "#selectElementTest"
16 | Then I expect that element "#yes" is selected
17 | And I expect that element "#yes2" is selected
18 | And I expect that element "#no" is not selected
19 | And I expect that element "#affirmative" is not selected
20 | And I expect that element "#negative" is not selected
21 |
22 | Scenario: Test is multiple values are selected by value
23 | When I select the option with the value "1" for element "#selectElementTest"
24 | Then I expect that element "#yes" is selected
25 | And I expect that element "#affirmative" is selected
26 | And I expect that element "#yes2" is not selected
27 | And I expect that element "#no" is not selected
28 | And I expect that element "#negative" is not selected
29 |
30 | Scenario: Trying to select non-existant elements raises an error
31 | Then I expect that executing the step 'When I select the option with the name "x" for element "#selectElementTest"' raises an exception
32 |
--------------------------------------------------------------------------------
/tests/features/sampleSnippets.feature:
--------------------------------------------------------------------------------
1 | Feature: Sample Snippets test
2 | As a developer
3 | I should be able to use given text snippets
4 |
5 | Scenario: open URL
6 | Given the page url is not "http://guinea-pig.webdriver.io/"
7 | And I open the url "http://guinea-pig.webdriver.io/"
8 | Then I expect that the url is "http://guinea-pig.webdriver.io/"
9 | And I expect that the url is not "http://google.com"
10 |
11 | Scenario: open sub page of weburl
12 | Given the page url is not "http://guinea-pig.webdriver.io/two.html"
13 | And I open the url "http://guinea-pig.webdriver.io/"
14 | Then I expect that the url is "http://guinea-pig.webdriver.io/"
15 | And I expect that the url is not "http://google.com"
16 | @skip_safari
17 | Scenario: click on link
18 | Given the title is not "two"
19 | And I open the url "http://guinea-pig.webdriver.io/"
20 | When I click on the link "two"
21 | Then I expect that the title is "two"
22 | @skip_safari
23 | Scenario: click on button
24 | Given I open the url "http://guinea-pig.webdriver.io/"
25 | And the element ".btn1_clicked" is not visible
26 | When I click on the button ".btn1"
27 | Then I expect that element ".btn1_clicked" is visible
28 |
29 | @skip_safari
30 | @firefox_bug
31 | Scenario: double click on a button
32 | Given I open the url "http://guinea-pig.webdriver.io/"
33 | And the element ".btn1_dblclicked" is not visible
34 | When I doubleclick on the element ".btn1"
35 | Then I expect that element ".btn1_dblclicked" is visible
36 |
37 | Scenario: click on element
38 | Given I open the url "http://guinea-pig.webdriver.io/"
39 | And the element ".btn1_clicked" is not visible
40 | When I click on the element ".btn1"
41 | Then I expect that element ".btn1_clicked" is visible
42 |
43 | @skip_safari
44 | Scenario: add value to an input element
45 | Given I open the url "http://guinea-pig.webdriver.io/"
46 | And the element "//html/body/section/form/input[1]" not contains the text "abc"
47 | When I add "bc" to the inputfield "//html/body/section/form/input[1]"
48 | Then I expect that element "//html/body/section/form/input[1]" contains the text "abc"
49 |
50 | Scenario: set value to an input element
51 | Given I open the url "http://guinea-pig.webdriver.io/"
52 | And the element "//html/body/section/form/input[1]" not contains the text "bc"
53 | When I set "bc" to the inputfield "//html/body/section/form/input[1]"
54 | Then I expect that element "//html/body/section/form/input[1]" contains the text "bc"
55 |
56 | Scenario: clear value of input element
57 | Given I open the url "http://guinea-pig.webdriver.io/"
58 | When I set "test" to the inputfield "//html/body/section/form/input[1]"
59 | And I clear the inputfield "//html/body/section/form/input[1]"
60 | Then I expect that element "//html/body/section/form/input[1]" not contains any text
61 |
62 | Scenario: drag n drop
63 | Given I open the url "http://guinea-pig.webdriver.io/"
64 | And the element ".searchinput" not contains the text "Dropped!"
65 | When I drag element "#overlay" to element ".red"
66 | Then I expect that element ".searchinput" contains the text "Dropped!"
67 | @firefox_bug
68 | Scenario: submit form
69 | Given I open the url "http://guinea-pig.webdriver.io/"
70 | And there is no element ".gotDataA" on the page
71 | When I submit the form ".send"
72 | Then I wait on element ".gotDataA" for 5000ms to be visible
73 |
74 | Scenario: wait for element
75 | Given I open the url "http://guinea-pig.webdriver.io/"
76 | And there is no element ".lateElem" on the page
77 | Then I wait on element ".lateElem" for 5000ms to be visible
78 |
79 | Scenario: wait for element using default wait time
80 | Given I open the url "http://guinea-pig.webdriver.io/"
81 | And there is no element ".lateElem" on the page
82 | Then I wait on element ".lateElem" to be visible
83 |
84 | Scenario: pause
85 | Given I open the url "http://guinea-pig.webdriver.io/"
86 | And there is no element ".lateElem" on the page
87 | When I pause for 3000ms
88 | Then I expect that element ".lateElem" is visible
89 |
90 | Scenario: query title
91 | Given I open the url "http://guinea-pig.webdriver.io/"
92 | And the title is "WebdriverJS Testpage"
93 | And the title is not "Other title"
94 | Then I expect that the title is "WebdriverJS Testpage"
95 | And I expect that the title is not "Other title"
96 |
97 | Scenario: check visibility
98 | Given I open the url "http://guinea-pig.webdriver.io/"
99 | And the element ".btn1" is visible
100 | And the element ".btn1_clicked" is not visible
101 | Then I expect that element ".btn1" is visible
102 | And I expect that element ".btn1_clicked" is not visible
103 |
104 | Scenario: compare texts
105 | Given I open the url "http://guinea-pig.webdriver.io/"
106 | And the element "#secondPageLink" contains the same text as element "#secondPageLink"
107 | And the element "#secondPageLink" contains not the same text as element "#githubRepo"
108 | Then I expect that element "#secondPageLink" contains the same text as element "#secondPageLink"
109 | And I expect that element "#secondPageLink" not contains the same text as element "#githubRepo"
110 |
111 | Scenario: check text content
112 | Given I open the url "http://guinea-pig.webdriver.io/"
113 | And the element "#secondPageLink" contains the text "two"
114 | And the element "#secondPageLink" not contains the text "andere linktext"
115 | Then I expect that element "#secondPageLink" contains the text "two"
116 | And I expect that element "#secondPageLink" not contains the text "anderer linktext"
117 |
118 | Scenario: check input content
119 | Given I open the url "http://guinea-pig.webdriver.io/"
120 | And the element "//html/body/section/form/input[1]" contains the text "a"
121 | And the element "//html/body/section/form/input[1]" not contains the text "aa"
122 | Then I expect that element "//html/body/section/form/input[1]" contains the text "a"
123 | And I expect that element "//html/body/section/form/input[1]" not contains the text "aa"
124 |
125 | Scenario: check attribut
126 | Given I open the url "http://guinea-pig.webdriver.io/"
127 | And the attribute "data-foundby" from element "#newWindow" is "partial link text"
128 | And the attribute "data-foundby" from element "#newWindow" is not "something else"
129 | Then I expect that the attribute "data-foundby" from element "#newWindow" is "partial link text"
130 | And I expect that the attribute "data-foundby" from element "#newWindow" is not "something else"
131 |
132 | Scenario: check css attribut
133 | Given I open the url "http://guinea-pig.webdriver.io/"
134 | And the css attribute "background-color" from element ".red" is "rgba(255, 0, 0, 1)"
135 | And the css attribute "background-color" from element ".red" is not "rgba(0, 255, 0, 1)"
136 | Then I expect that the css attribute "background-color" from element ".red" is "rgba(255, 0, 0, 1)"
137 | And I expect that the css attribute "background-color" from element ".red" is not "rgba(0, 255, 0, 1)"
138 | @firefox_bug
139 | Scenario: check width and height
140 | Given I open the url "http://guinea-pig.webdriver.io/"
141 | And the element ".red" is 102px broad
142 | And the element ".red" is 102px tall
143 | And the element ".red" is not 103px broad
144 | And the element ".red" is not 103px tall
145 | Then I expect that element ".red" is 102px broad
146 | And I expect that element ".red" is 102px tall
147 | And I expect that element ".red" is not 103px broad
148 | And I expect that element ".red" is not 103px tall
149 |
150 | @skip_safari
151 | Scenario: check offset
152 | Given I open the url "http://guinea-pig.webdriver.io/"
153 | And the element ".red" is positioned at 15px on the x axis
154 | And the element ".red" is positioned at 268px on the y axis
155 | And the element ".red" is not positioned at 16px on the x axis
156 | And the element ".red" is not positioned at 246px on the y axis
157 | Then I expect that element ".red" is positioned at 15px on the x axis
158 | And I expect that element ".red" is positioned at 268px on the y axis
159 | And I expect that element ".red" is not positioned at 16px on the x axis
160 | And I expect that element ".red" is not positioned at 246px on the y axis
161 |
162 | Scenario: check selected
163 | Given I open the url "http://guinea-pig.webdriver.io/"
164 | And the checkbox ".checkbox_notselected" is not checked
165 | When I click on the element ".checkbox_notselected"
166 | Then I expect that checkbox ".checkbox_notselected" is checked
167 |
168 | Scenario: set / read cookie
169 | Given I open the url "http://guinea-pig.webdriver.io/"
170 | And the cookie "test" does not exist
171 | When I set a cookie "test" with the content "test123"
172 | Then I expect that cookie "test" exists
173 | And I expect that cookie "test" contains "test123"
174 | And I expect that cookie "test" not contains "test1234"
175 |
176 | Scenario: delete cookie
177 | Given I open the url "http://guinea-pig.webdriver.io/"
178 | And the cookie "test" does exist
179 | When I delete the cookie "test"
180 | Then I expect that cookie "test" not exists
181 |
--------------------------------------------------------------------------------
/tests/features/screenSize.feature:
--------------------------------------------------------------------------------
1 | Feature: Screen Size
2 | As a developer
3 | I want to be able to check and modify the screen size
4 |
5 | Background:
6 | Given I have a screen that is 600 by 500 pixels
7 |
8 | Scenario: change the screen size
9 | Given I have a screen that is 500 by 420 pixels
10 | Then I expect the screen is 500 by 420 pixels
11 |
12 | Scenario: Change just Y dimension
13 | Given I have a screen that is 700 pixels tall
14 | Then I expect the screen is 600 by 700 pixels
15 |
16 | Scenario: Change just X dimension
17 | Given I have a screen that is 700 pixels broad
18 | Then I expect the screen is 700 by 500 pixels
19 |
--------------------------------------------------------------------------------
/tests/features/sctructure.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the page structure
2 | As a developer
3 | I want to be able to test if a page has a certain structure
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: Test if the page has a H1 I expect its at the top of the page
9 | Given there is an element "h1" on the page
10 | When I scroll to element "h1"
11 | Then I expect that element "h1" is visible
12 |
13 | Scenario: Test if the page has only one H1 element
14 | Given there is no element "h1:nth-child(n+2)" on the page
15 |
--------------------------------------------------------------------------------
/tests/features/select.feature:
--------------------------------------------------------------------------------
1 | @skip_safari
2 | Feature: Test select elements
3 | As a developer
4 | I want to be able to test if a certain value is selected for a certain
5 | select element
6 |
7 | Background:
8 | Given I open the site "/"
9 |
10 | Scenario: Test if we can select the second option of a select element
11 | Then I expect that element "#selectElementTest option:nth-child(2)" is not selected
12 | When I select the 1st option for element "#selectElementTest"
13 | Then I expect that element "#selectElementTest option:nth-child(2)" is selected
14 |
15 | Scenario: Test if we can select the third option of a select element
16 | Then I expect that element "#selectElementTest option:nth-child(3)" is not selected
17 | When I select the 2nd option for element "#selectElementTest"
18 | Then I expect that element "#selectElementTest option:nth-child(3)" is selected
19 |
20 | Scenario: Test if we can select the fourth option of a select element
21 | Then I expect that element "#selectElementTest option:nth-child(4)" is not selected
22 | When I select the 3th option for element "#selectElementTest"
23 | Then I expect that element "#selectElementTest option:nth-child(4)" is selected
24 |
25 | Scenario: Test if we can select the first option of a select element
26 | When I select the 1st option for element "#selectElementTest"
27 | Then I expect that element "#selectElementTest option:nth-child(1)" is not selected
28 | When I select the 0th option for element "#selectElementTest"
29 | Then I expect that element "#selectElementTest option:nth-child(1)" is selected
30 |
31 | Scenario: Test if we can select a option by its name
32 | Then I expect that element "#selectElementTest option:nth-child(2)" is not selected
33 | When I select the option with the name "secondOption" for element "#selectElementTest"
34 | Then I expect that element "#selectElementTest option:nth-child(2)" is selected
35 |
36 | Scenario: Test if we can select a option by its value
37 | Then I expect that element "#selectElementTest option:nth-child(3)" is not selected
38 | When I select the option with the value "third" for element "#selectElementTest"
39 | Then I expect that element "#selectElementTest option:nth-child(3)" is selected
40 |
41 | Scenario: Test if we can select a option by its visible text
42 | Then I expect that element "#selectElementTest option:nth-child(4)" is not selected
43 | When I select the option with the text "Option #4" for element "#selectElementTest"
44 | Then I expect that element "#selectElementTest option:nth-child(4)" is selected
45 |
46 | Scenario: Test if we can select a option
47 | by its value using XPath selectors only
48 | Then I expect that element "#selectElementTest option:nth-child(2)" is not selected
49 | When I select the option with the value "second" for element "//select[@id='selectElementTest']"
50 | Then I expect that element "#selectElementTest option:nth-child(2)" is selected
51 |
52 | Scenario: Test if we can select a option by its index using XPath selectors
53 | Then I expect that element "#selectElementTest option:nth-child(4)" is not selected
54 | When I select the 3rd option for element "//select[@id='selectElementTest']"
55 | Then I expect that element "#selectElementTest option:nth-child(4)" is selected
56 |
57 | Scenario: Test if we can select a option by its visible text using XPath selectors
58 | Then I expect that element "#selectElementTest option:nth-child(3)" is not selected
59 | When I select the option with the text "Option #3" for element "//select[@id='selectElementTest']"
60 | Then I expect that element "#selectElementTest option:nth-child(3)" is selected
61 |
62 | Scenario: Test if we can select a option by its name using XPath selectors
63 | Then I expect that element "#selectElementTest option:nth-child(2)" is not selected
64 | When I select the option with the name "secondOption" for element "//select[@id='selectElementTest']"
65 | Then I expect that element "#selectElementTest option:nth-child(2)" is selected
66 |
--------------------------------------------------------------------------------
/tests/features/steps/webdriver_steps.py:
--------------------------------------------------------------------------------
1 | from behave import *
2 | from behave_webdriver.steps import *
3 |
4 | use_step_matcher('re')
5 |
6 | @then("""I expect that executing the step '([^']*)?' raises an exception""")
7 | def test_step_raises_exception(context, step_text):
8 | try:
9 | context.execute_steps(step_text)
10 | except Exception as e:
11 | print(e)
12 | else:
13 | raise AssertionError('Step did not raise exception')
14 |
--------------------------------------------------------------------------------
/tests/features/textComparison.feature:
--------------------------------------------------------------------------------
1 | Feature: Test text contents of elements
2 | As a developer
3 | I want to be able to test the text inside a element against the text inside
4 | another element
5 |
6 | Background:
7 | Given I open the site "/"
8 |
9 | Scenario: Elements containing different text
10 | Then I expect that element "#textComparison1" not contains the same text as element "#textComparison2"
11 |
12 | Scenario: Elements containing the same text
13 | Then I expect that element "#textComparison1" contains the same text as element "#textComparison3"
14 |
15 | Scenario: Elements containing no text
16 | Then I expect that element "#textComparison4" contains the same text as element "#textComparison5"
17 |
18 | Scenario: Elements containing text and elements
19 | Then I expect that element "#textComparison1" contains the same text as element "#textComparison6"
20 |
21 | Scenario: Elements containing text inside a child element
22 | Then I expect that element "#textComparison1" contains the same text as element "#textComparison7"
23 |
24 | Scenario: Elements containing text with encoded strings
25 | Then I expect that element "#textComparison8" contains the same text as element "#textComparison9"
26 |
27 | Scenario: Element containing different text
28 | Then I expect that element "#textDoesNotContainCucumber" not contains the text "This element contains cucumber"
29 |
30 | Scenario: Element containing the same text
31 | Then I expect that element "#textDoesContainCucumber" contains the text "This element contains cucumber"
32 |
33 | Scenario: Input containing different text
34 | Then I expect that element "#valueDoesNotContainCucumber" not contains the text "This input contains cucumber"
35 |
36 | Scenario: Input containing the same text
37 | Then I expect that element "#valueDoesContainCucumber" contains the text "This input contains cucumber"
38 |
39 | Scenario: Test element matches text
40 | Then I expect that element "#textDoesContainCucumber" matches the text "This element contains cucumber"
41 | And I expect that element "#textDoesContainCucumber" not matches the text "Something else"
42 |
43 | Scenario: Test Input matches text
44 | Then I expect that element "#valueDoesContainCucumber" matches the text "This input contains cucumber"
45 | And I expect that element "#valueDoesContainCucumber" not matches the text "Something else"
46 |
--------------------------------------------------------------------------------
/tests/features/title.feature:
--------------------------------------------------------------------------------
1 | Feature: Test the page title
2 | As a developer
3 | I want to be able to test if a page has a certain title
4 |
5 | Background:
6 | Given I open the site "/"
7 |
8 | Scenario: Test if the demo app has the title "DEMO APP"
9 | Given the title is "DEMO APP"
10 | Then I expect that element "h1" contains the same text as element ".subtitle"
11 |
12 | Scenario: Test if the demo app does not have the title "Google"
13 | Given the title is not "Google"
14 | And the page url is not "https://www.google.com/"
15 | Then I expect that element "h1" not contains the text "Google"
16 |
--------------------------------------------------------------------------------
/tests/features/transformation_fixture.feature:
--------------------------------------------------------------------------------
1 | Feature: Using a transformation fixture from feature file
2 |
3 | @fixture.transformer.EnvironmentTransformer
4 | Scenario: transform step from environment variable
5 | Given the base url is "{ENV_BASE_URL}"
6 | When I open the site "/page.html"
7 | Then I expect that the url is "{ENV_BASE_URL}page.html"
8 |
--------------------------------------------------------------------------------
/tests/features/urlValidation.feature:
--------------------------------------------------------------------------------
1 | Feature: Test if the url is a certain value
2 | As a developer
3 | I want to be able to test if the url is a certain value
4 |
5 | Scenario: The url should not be http://www.google.com/
6 | Given I open the site "/"
7 | Then I expect that the url is not "http://www.google.com/"
8 |
9 | Scenario: The url should be http://localhost:8000/
10 | Given I open the site "/"
11 | Then I expect that the url is "http://localhost:8000/"
12 |
13 | Scenario: The path should not be /index.html
14 | Given I open the site "/"
15 | Then I expect that the path is not "/index.html"
16 |
17 | Scenario: The path should be /index.html
18 | Given I open the site "/index.html"
19 | Then I expect that the path is "/index.html"
20 |
21 | Scenario: The url should not contain "google"
22 | Given I open the site "/"
23 | Then I expect the url to not contain "google"
24 |
25 | Scenario: The url should not contain "index"
26 | Given I open the site "/index.html"
27 | Then I expect the url to contain "index"
28 |
--------------------------------------------------------------------------------
/tests/features/wait.feature:
--------------------------------------------------------------------------------
1 | Feature: Test waiting for actions
2 | As a developer
3 | I want to be able to test if delayed actions are being performed
4 |
5 | Background:
6 | Given I open the site "/"
7 | And I pause for 1000ms
8 |
9 | Scenario: Test if element becomes checked after 2000 ms
10 | Given the checkbox "#waitForCheckedElement" is not checked
11 | When I click on the button "#waitForCheckedBtn"
12 | Then I wait on element "#waitForCheckedElement" for 2000ms to be checked
13 |
14 | Scenario: Test if element becomes checked
15 | Given the checkbox "#waitForCheckedElement" is not checked
16 | When I click on the button "#waitForCheckedBtn"
17 | Then I wait on element "#waitForCheckedElement" for 1000ms to be checked
18 |
19 | Scenario: Test if element becomes enabled
20 | Given the element "#waitForEnabledElement" is not enabled
21 | When I click on the button "#waitForEnabledBtn"
22 | Then I wait on element "#waitForEnabledElement" for 1000ms to be enabled
23 |
24 | Scenario: Test if element becomes selected
25 | Given the element "#waitForSelectedElement option:nth-child(2)" is not selected
26 | When I click on the button "#waitForSelectedBtn"
27 | Then I wait on element "#waitForSelectedElement option:nth-child(2)" for 1000ms to be selected
28 |
29 | Scenario: Test if element becomes visible
30 | Given the element "#waitForVisibleElement" is not visible
31 | When I click on the button "#waitForVisibleBtn"
32 | Then I wait on element "#waitForVisibleElement" for 1000ms to be visible
33 |
34 | Scenario: Test if element to contain a text
35 | Given the element "#waitForContainsTextElement" not contains any text
36 | When I click on the button "#waitForContainsTextBtn"
37 | Then I wait on element "#waitForContainsTextElement" for 1000ms to contain a text
38 |
39 | Scenario: Test if element to contain a value
40 | Given the element "#waitForContainsValueElement" not contains any text
41 | When I click on the button "#waitForContainsValueBtn"
42 | Then I wait on element "#waitForContainsValueElement" for 1000ms to contain a value
43 |
44 | Scenario: Test if element to exist
45 | Given there is no element "#waitForCreateElement > span" on the page
46 | When I click on the button "#waitForCreateBtn"
47 | Then I wait on element "#waitForCreateElement > span" for 1000ms
48 |
49 | Scenario: Test if element exists
50 | Given there is no element "#waitForCreateElement > span" on the page
51 | When I click on the button "#waitForCreateBtn"
52 | Then I wait on element "#waitForCreateElement > span" for 1000ms to exist
53 |
54 | Scenario: Test if element becomes unchecked
55 | When I click on the button "#waitForCheckedBtn"
56 | And I pause for 1000ms
57 | Then I expect that checkbox "#waitForCheckedElement" is checked
58 | When I click on the button "#waitForCheckedBtn"
59 | Then I wait on element "#waitForCheckedElement" for 1000ms to not be checked
60 |
61 | Scenario: Test if element becomes disabled
62 | When I click on the button "#waitForEnabledBtn"
63 | And I pause for 1000ms
64 | Then I expect that element "#waitForEnabledElement" is enabled
65 | When I click on the button "#waitForEnabledBtn"
66 | Then I wait on element "#waitForEnabledElement" for 1000ms to not be enabled
67 |
68 | Scenario: Test if element becomes not selected
69 | When I click on the button "#waitForSelectedBtn"
70 | And I pause for 1000ms
71 | Then I expect that element "#waitForSelectedElement option:nth-child(2)" is selected
72 | When I click on the button "#waitForSelectedBtn"
73 | Then I wait on element "#waitForSelectedElement option:nth-child(2)" for 1000ms to not be selected
74 |
75 | Scenario: Test if element becomes not visible
76 | When I click on the button "#waitForVisibleBtn"
77 | And I pause for 1000ms
78 | Then I expect that element "#waitForVisibleElement" is visible
79 | When I click on the button "#waitForVisibleBtn"
80 | Then I wait on element "#waitForVisibleElement" for 1000ms to not be visible
81 |
82 | Scenario: Test if element to not contain a text
83 | When I click on the button "#waitForContainsTextBtn"
84 | And I pause for 1000ms
85 | Then I expect that element "#waitForContainsTextElement" contains any text
86 | When I click on the button "#waitForContainsTextBtn"
87 | Then I wait on element "#waitForContainsTextElement" for 1000ms to not contain a text
88 |
89 | Scenario: Test if element to not contain a value
90 | When I click on the button "#waitForContainsValueBtn"
91 | And I pause for 1000ms
92 | Then I expect that element "#waitForContainsValueElement" contains any text
93 | When I click on the button "#waitForContainsValueBtn"
94 | Then I wait on element "#waitForContainsValueElement" for 2000ms to not contain a value
95 |
96 | Scenario: Test if element not exists
97 | When I click on the button "#waitForCreateBtn"
98 | And I pause for 1000ms
99 | Then I expect that element "#waitForCreateElement > span" does exist
100 | When I click on the button "#waitForCreateBtn"
101 | Then I wait on element "#waitForCreateElement > span" for 1000ms to not exist
102 |
--------------------------------------------------------------------------------
/tests/features/window.feature:
--------------------------------------------------------------------------------
1 | Feature: Test if new windows/tabs are being opened
2 | As a developer
3 | I want to be able to test if a element opens a new window/tab
4 |
5 | Background:
6 | Given I have closed all but the first tab
7 | And I open the site "/"
8 |
9 | Scenario: Test if a new window/tab is not being opened
10 | Given the page url is "http://localhost:8000/"
11 | Then I expect a new window has not been opened
12 |
13 | Scenario: Test if a default link does not open a new window/tab
14 | When I click on the element "#linkSameWindow"
15 | Then I expect a new window has not been opened
16 |
17 | Scenario: Test if a link with target="_blank" does open a new window/tab
18 | When I click on the element "#linkNewWindow"
19 | Then I expect a new window has been opened
20 |
21 | Scenario: Test if a window/tab from "google.com" has the correct url
22 | When I click on the element "#linkNewWindow"
23 | Then I expect the url "http://example.com/" is opened in a new window
24 |
25 | Scenario: Test all opened windows/tabs are now closed
26 | Given the page url is "http://localhost:8000/"
27 | Then I expect a new window has not been opened
28 |
29 | @skip_safari
30 | Scenario: Test if we can close the last opened window/tab
31 | When I click on the element "#linkNewWindow"
32 | Then I expect a new window has been opened
33 | When I close the last opened window
34 | Then I expect a new window has not been opened
35 |
36 | Scenario: Test if we can focus the last opened window/tab
37 | When I click on the element "#linkNewWindow"
38 | Then I expect a new window has been opened
39 | When I focus the last opened window
40 | Then I expect that the url is "http://example.com/"
41 | When I close the last opened window
42 | Then I expect that the url is "http://localhost:8000/"
43 |
44 | Scenario: Test checking for new window without new widow raises an error
45 | Then I expect that executing the step 'Then I expect the url "/" is opened in a new window' raises an exception
46 |
47 | Scenario: Test checking for new window with nonexistent url fails
48 | When I click on the element "#linkNewWindow"
49 | Then I expect that executing the step 'Then I expect the url "foo" is opened in a new window' raises an exception
50 |
--------------------------------------------------------------------------------
/tests/features/withinViewport.feature:
--------------------------------------------------------------------------------
1 | @fresh_driver
2 | Feature: Viewport test
3 | As a Developer in Test
4 | I want to visit the Google result page for the term "test"
5 | And make sure I have the logo within the viewport and make sure the footer is not
6 |
7 | Scenario: Header in viewport, footer outside viewport
8 | Given I open the site "/"
9 | And I have a screen that is 1024 by 768 pixels
10 | And I pause for 1000ms
11 | Then I expect that element "h1" is within the viewport
12 | And I expect that element "footer" is not within the viewport
13 |
--------------------------------------------------------------------------------
/tests/unittests/test_css_xpath_discerning.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | import sys
4 | import os
5 | present_dir = os.path.dirname(os.path.realpath(__file__))
6 | root_dir = os.path.abspath(os.path.join(present_dir, '..', '..'))
7 | sys.path.insert(0, root_dir)
8 | from behave_webdriver.driver import BehaveDriverMixin
9 | from selenium.webdriver.common.by import By
10 |
11 |
12 | def _init_get_element_mocks():
13 | class DriverTest(BehaveDriverMixin):
14 | pass
15 | mock_el = mock.MagicMock(name='Html element')
16 | DriverTest.find_element = mock.MagicMock(name='find_element', return_value=mock_el)
17 | DriverTest.find_element_by_xpath = mock.MagicMock(name='find_element_by_xpath', return_value=mock_el)
18 | DriverTest.find_element_by_css_selector = mock.MagicMock(name='find_element_by_css_selector', return_value=mock_el)
19 | return DriverTest, mock_el
20 |
21 |
22 | def test_get_element_with_by():
23 | DriverTest, mock_el = _init_get_element_mocks()
24 | el = DriverTest().get_element('/my_weird_id', by=By.ID)
25 | assert el is mock_el
26 | assert DriverTest.find_element.called
27 | assert DriverTest.find_element_by_xpath.not_called
28 | assert DriverTest.find_element_by_css_selector.not_called
29 |
30 |
31 | def test_get_element_with_xpath_expression():
32 | DriverTest, mock_el = _init_get_element_mocks()
33 | el = DriverTest().get_element('/my_xpath/expression')
34 | assert el is mock_el
35 | assert DriverTest.find_element.not_called
36 | assert DriverTest.find_element_by_xpath.called
37 | assert DriverTest.find_element_by_css_selector.not_called
38 |
39 |
40 | def test_get_element_with_css_selector():
41 | DriverTest, mock_el = _init_get_element_mocks()
42 | el = DriverTest().get_element('div.specific-class[title="tooltip"]')
43 | assert el is mock_el
44 | assert DriverTest.find_element.not_called
45 | assert DriverTest.find_element_by_xpath.not_called
46 | assert DriverTest.find_element_by_css_selector.called
47 |
48 |
49 | def _init_wait_for_element_condition_mocks():
50 | with mock.patch('behave_webdriver.driver.element_is_present') as mock_element_is_present:
51 | with mock.patch('behave_webdriver.driver.WebDriverWait') as mock_WebDriverWait:
52 | mock_el = mock.MagicMock(name='Html element')
53 | mock_web_driver_wait = mock.MagicMock(name='web_driver_wait')
54 | mock_web_driver_wait.until.return_value = mock_el
55 | mock_WebDriverWait.return_value = mock_web_driver_wait
56 | class DriverTest(BehaveDriverMixin):
57 | pass
58 | yield DriverTest, mock_el, mock_WebDriverWait, mock_web_driver_wait, mock_element_is_present
59 |
60 |
61 | def test_wait_for_element_condition_with_xpath_expression():
62 | for DriverTest, mock_el, mock_WebDriverWait, mock_web_driver_wait, mock_element_is_present \
63 | in _init_wait_for_element_condition_mocks():
64 | driver_test = DriverTest()
65 | el = driver_test.wait_for_element_condition('/my_xpath/expression', None, None, None)
66 | assert el is mock_el
67 | assert mock_WebDriverWait.called_with(driver_test, driver_test.default_wait)
68 | assert mock_web_driver_wait.until.called
69 | assert mock_element_is_present.called_with((By.XPATH, '/my_xpath/expression'), False)
70 |
71 |
72 | def test_wait_for_element_condition_with_css_selector():
73 | for DriverTest, mock_el, mock_WebDriverWait, mock_web_driver_wait, mock_element_is_present \
74 | in _init_wait_for_element_condition_mocks():
75 | driver_test = DriverTest()
76 | el = driver_test.wait_for_element_condition('div.specific-class[title="tooltip"]', None, None, None)
77 | assert el is mock_el
78 | assert mock_WebDriverWait.called_with(driver_test, driver_test.default_wait)
79 | assert mock_web_driver_wait.until.called
80 | assert mock_element_is_present.called_with((By.CSS_SELECTOR, '/my_xpath/expression'), False)
81 |
--------------------------------------------------------------------------------
/tests/unittests/test_fixture.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | import sys
4 | import os
5 | present_dir = os.path.dirname(os.path.realpath(__file__))
6 | root_dir = os.path.abspath(os.path.join(present_dir, '..', '..'))
7 | sys.path.insert(0, root_dir)
8 | import behave_webdriver.fixtures
9 |
10 |
11 | def test_before_all_factory():
12 | with mock.patch('behave_webdriver.fixtures.use_fixture') as mock_use_fixture:
13 | args = (4, True, "test")
14 | kwargs = {
15 | 'webdriver_name': 'Firefox',
16 | 'options': {'param': 'value'},
17 | }
18 | before_all = behave_webdriver.fixtures.before_all_factory(*args, **kwargs)
19 | ctx = mock.MagicMock()
20 | before_all(ctx)
21 | assert mock_use_fixture.called_with(ctx, *args, **kwargs)
22 |
23 |
24 | def test_before_feature_factory():
25 | with mock.patch('behave_webdriver.fixtures.use_fixture') as mock_use_fixture:
26 | args = (4, True, "test")
27 | kwargs = {
28 | 'webdriver_name': 'Firefox',
29 | 'options': {'param': 'value'},
30 | }
31 | before_feature = behave_webdriver.fixtures.before_feature_factory(*args, **kwargs)
32 | ctx = mock.MagicMock()
33 | feature = mock.MagicMock()
34 | before_feature(ctx, feature)
35 | assert mock_use_fixture.called_with(ctx, *args, **kwargs)
36 |
37 |
38 | def test_before_scenario_factory():
39 | with mock.patch('behave_webdriver.fixtures.use_fixture') as mock_use_fixture:
40 | args = (4, True, "test")
41 | kwargs = {
42 | 'webdriver_name': 'Firefox',
43 | 'options': {'param': 'value'},
44 | }
45 | before_scenario = behave_webdriver.fixtures.before_scenario_factory(*args, **kwargs)
46 | ctx = mock.MagicMock()
47 | scenario = mock.MagicMock()
48 | before_scenario(ctx, scenario)
49 | assert mock_use_fixture.called_with(ctx, *args, **kwargs)
50 |
--------------------------------------------------------------------------------
/tests/unittests/test_from.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | import sys
4 | import os
5 | present_dir = os.path.dirname(os.path.realpath(__file__))
6 | root_dir = os.path.abspath(os.path.join(present_dir, '..', '..'))
7 | sys.path.insert(0, root_dir)
8 | import behave_webdriver.utils
9 |
10 | driver_names = ['Chrome', 'Firefox', 'Ie', 'Edge', 'Opera', 'Safari', 'BlackBerry', 'PhantomJS', 'Android', 'Remote']
11 |
12 |
13 | @pytest.mark.parametrize("driver_name", driver_names)
14 | def test_browser_from_string(driver_name):
15 | driver_qual_name = 'behave_webdriver.utils.' + driver_name
16 | with mock.patch(driver_qual_name) as mock_driver:
17 | mock_driver.__name__ = driver_name
18 | driver = behave_webdriver.utils.from_string(driver_name)
19 | assert mock_driver.called
20 |
21 |
22 | @pytest.mark.parametrize("driver_name", driver_names)
23 | def test_browser_from_env(driver_name):
24 | driver_qual_name = 'behave_webdriver.utils.' + driver_name
25 | with mock.patch.dict(os.environ, {'BEHAVE_WEBDRIVER': driver_name}), mock.patch(driver_qual_name) as mock_driver:
26 | mock_driver.__name__ = driver_name
27 | driver = behave_webdriver.utils.from_env()
28 | assert mock_driver.called
29 |
30 |
31 | def test_default_from_env_driver_as_driver():
32 | with mock.patch.dict(os.environ, clear=True):
33 | def_driver = behave_webdriver.utils.Chrome
34 | Driver = behave_webdriver.utils._from_env(default_driver=def_driver)
35 | assert Driver is def_driver
36 |
37 |
38 | def test_default_from_env_driver_as_string():
39 | with mock.patch.dict(os.environ, clear=True):
40 | expected_driver = behave_webdriver.utils.Chrome
41 | Driver = behave_webdriver.utils._from_env(default_driver='Chrome')
42 | assert Driver is expected_driver
43 |
44 |
45 | def test_env_raises_for_absent_drivername():
46 |
47 | with pytest.raises(ValueError) as excinfo, mock.patch.dict(os.environ, clear=True):
48 | driver = behave_webdriver.utils._from_env()
49 | assert "No driver found in environment variables and no default" in str(excinfo.value)
50 |
51 |
52 | def test_string_raises_for_invalid_drivername_and_contains_options():
53 | with pytest.raises(ValueError) as excinfo:
54 | driver = behave_webdriver.utils._from_string('foo')
55 | assert 'No such driver "foo"' in str(excinfo.value)
56 | assert all(dname.upper() in str(excinfo.value) for dname in driver_names)
57 |
--------------------------------------------------------------------------------
/tests/unittests/test_parameter_trasformations.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import sys
3 | import os
4 | present_dir = os.path.dirname(os.path.realpath(__file__))
5 | root_dir = os.path.abspath(os.path.join(present_dir, '..', '..'))
6 | sys.path.insert(0, root_dir)
7 | from behave_webdriver.transformers import FormatTransformer
8 |
--------------------------------------------------------------------------------