Skip to content

Commit

Permalink
Merge pull request #513 from mpsonntag/frontendTests
Browse files Browse the repository at this point in the history
Frontend tests

LGTM
  • Loading branch information
achilleas-k authored Oct 28, 2020
2 parents c68083e + 9347c10 commit 759c318
Show file tree
Hide file tree
Showing 11 changed files with 918 additions and 691 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The current project setup is still using an outdated build tool (`activator`) an
- Added error messages on abstract reference validation, if the input is numeric. For details see issue #499.
- The outdated mapbox.js plugin was updated to the latest version. See issue #510 for details.
- An issue was resolved where the sortid of an abstract was removed when an abstract was re-submitted. See issue #507 for details.
- The outdated mathjax.js plugin was updated to version 2.7.7. See issue #506 for details.


# Release v1.2
Expand Down
6 changes: 6 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ The current version is stable.
A previous version of the application can be found in the "oldstable" branch.

Please find the latest release notes in the [release section](https://github.com/G-Node/GCA-Web/releases).


## Frontend tests using Selenium

The application suite provides frontend test using Selenium. A full setup and usage description can be found in the
[frontend test readme](./test/frontend/Readme.md).
4 changes: 2 additions & 2 deletions app/controllers/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ class Application(implicit val env: Environment[Login, CachedCookieAuthenticator
|https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-debug.js
|https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-timepicker-addon/1.6.1/jquery-ui-timepicker-addon.min.css
|https://fonts.googleapis.com/css?family=EB+Garamond|Open+Sans
|https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/MathJax.js?delayStartupUntil=configured
|https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/extensions/MathMenu.js
|https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?delayStartupUntil=configured
|https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/extensions/MathMenu.js
|
|# Styles
|/assets/stylesheets/_g-node-bootstrap.less
Expand Down
2 changes: 1 addition & 1 deletion app/views/components/mathjax.scala.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@()


<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/MathJax.js?delayStartupUntil=configured"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?delayStartupUntil=configured"></script>

<!-- default mathjax configuration -->
<script type="text/x-mathjax-config">
Expand Down
8 changes: 6 additions & 2 deletions app/views/mob_template.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,15 @@
<li><a href="@routes.Application.datenschutz">Datenschutz</a></li>
</ul>
</div>
<div class="pull-right"><p>The G-Node Conference Site &mdash; © <a href="http://www.g-node.org">G-Node</a> 2019 </p></div>
<div class="pull-right">
<p>The G-Node Conference Site &mdash; ©
<a href="http://www.g-node.org">G-Node</a> 2013-
<script>document.write(new Date().getFullYear());</script>
</p>
</div>
</div>
</div>
</div>


</body>
</html>
7 changes: 6 additions & 1 deletion app/views/template.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,12 @@
<li><a href="@routes.Application.datenschutz">Datenschutz</a></li>
</ul>
</div>
<div class="pull-right"><p>The G-Node Conference Site &mdash; © <a href="http://www.g-node.org">G-Node</a> 2019 </p></div>
<div class="pull-right">
<p>The G-Node Conference Site &mdash; ©
<a href="http://www.g-node.org">G-Node</a> 2013-
<script>document.write(new Date().getFullYear());</script>
</p>
</div>
</div>
</div>
</div>
Expand Down
14 changes: 7 additions & 7 deletions public/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
self._cacheVersion = "v20";
self._cacheVersion = "v21";

self.resourcesToCache = [
// Views
Expand Down Expand Up @@ -69,12 +69,12 @@ self.resourcesToCache = [
"https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-debug.js",
"https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-timepicker-addon/1.6.1/jquery-ui-timepicker-addon.min.css",
"https://fonts.googleapis.com/css?family=EB+Garamond|Open+Sans",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/MathJax.js?delayStartupUntil=configured",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/extensions/MathMenu.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/extensions/tex2jax.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/jax/output/HTML-CSS/config.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/jax/input/TeX/config.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/extensions/MathZoom.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?delayStartupUntil=configured",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/extensions/MathMenu.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/extensions/tex2jax.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/jax/output/HTML-CSS/config.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/jax/input/TeX/config.js",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/extensions/MathZoom.js",

// Styles
"/assets/stylesheets/_g-node-bootstrap.less",
Expand Down
186 changes: 76 additions & 110 deletions test/frontend/Readme.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,36 @@
Frontend Testing With Selenium
=============================

This file contains a description about the steps necessary in order to start testing with selenium
as well as some remarks about setting up new tests.
This file contains a description about necessary steps to start testing with selenium as well as some remarks about setting up new tests.

# Preliminary requirements
- Make sure that `activator test` has been run
- Make sure GCA-Web is running locally at the default port `9000` using `activator start`

# Steps to Set Up Selenium

Note this setup has been tested and used exclusively with Python 3.6. Using Python 2 is not
# Required Selenium setup
Note that this setup has been tested and used exclusively with Python 3.6. Using Python 2 is not
encouraged.

Before the tests can run, several steps are needed in order for them to work.

First of course, you need to install python-selenium, if you haven't done so already,
after setting up a virtual environment (not necessarily in this order):

`pip install selenium pytest`

Next, you need to get the drivers for the browsers your interested in, at the moment,
Google Chrome and Firefox are used. The drivers can be downloaded from this page:

https://www.seleniumhq.org/download/
- Download the Selenium server (`selenium-server-standalone-[version].jar`) from the [selenium page](https://www.seleniumhq.org/download)
- Information on how to get the webdrivers for Chrome and Firefox ("Geckodriver") can also be found on the page above. Since the location of these drivers has changed in the past, selenium does not link directly. The latest locations for driver downloads at this point where
- Firefox: [github](https://github.com/mozilla/geckodriver/releases)
- Chrome: [sites.google.com](https://sites.google.com/a/chromium.org/chromedriver)
- Make sure the server jar and the webdrivers are in a location that is available via the `PATH` environmental variable.
- Install the required python dependencies; `pip install selenium pytest`.
- Open a python shell and run the following code to check if the webdrivers work
from selenium import webdriver
cdriver = webdriver.Chrome()
fdriver = webdriver.Firefox()

under "Third Party Drivers, Bindings, and Plugins".
- If any problems arise at this point, check whether the driver that was downloaded supports the actually installed corresponding browser.

Place them on your local machine and make sure they are available via the `PATH` environmental variable.

Open a python shell and run the following code:

from selenium import webdriver
cdriver = webdriver.Chrome()
fdriver = webdriver.Firefox()

If any problems arise at this point, check whether the driver that was downloaded supports the
actually installed corresponding browser.
# Preliminary requirements

From the same source download the Selenium standalone server, something
along the lines of `selenium-server-standalone-[version].jar`,
and have it available.
## Test database setup
- Make sure that `activator test` has been run; all frontend tests depend on the the h2 test database content which is created when the backend tests are executed.
- Make sure GCA-Web is running locally at the default port `9000` using `activator run`.

To allow parallel and cross browser testing, and even giving the possibility of testing
with different operating systems and on different machines, we next set up selenium grid.
In order to do this, we need the selenium standalone server, which is already included as a java jar
in the "selenium" directory. Be sure to check out newer versions from time to time on the same page as above.
A hub is set up accessible via port 4444 by default, where nodes with different browsers can be added.
## Selenium preparation
To allow parallel and cross browser testing and even giving the possibility of testing with different operating systems and on different machines, we next set up selenium grid using the selenium server jar mentioned above.

The hub can be started by opening a terminal, changing to the selenium folder and then typing in the following command
(replace with you current version of the jar):
The hub can be started by opening a terminal, changing to the selenium folder and then typing in the following command (use your current version of the jar):

```
java -jar selenium-server-standalone-3.141.59.jar -role hub
Expand All @@ -62,95 +43,80 @@ java -jar selenium-server-standalone-3.141.59.jar -role node
```

If you now visit [http://localhost:4444/grid/console](http://localhost:4444/grid/console/) you can see the node
registered and which browsers are available on each of them.
You can set the port, the browser running on the node and various other parameters.
It is also possible now to register nodes from different machines, given you know the IP of the hub, by expanding the
command above to
registered and which browsers are available on each of them. You can set the port, the browser running on the node and various other parameters. It is also possible now to register nodes from different machines, given you know the IP of the hub, by expanding the command above to

```
java -jar selenium-server-standalone-3.141.59.jar -role node -hub http://$IP of hub$:4444/grid/register
```

Using just your local machine, you can also add more nodes via the same command,
speeding up the run time of the tests, as they will get distributed over these nodes by pytest.
For more helpful information about selenium grid, check out [the docs](https://www.seleniumhq.org/docs/07_selenium_grid.jsp)
and this [article on medium.com](https://medium.com/@arnonaxelrod/running-cross-browser-selenium-tests-in-parallel-using-selenium-grid-and-docker-containers-9ee293b86cfd)
You can also add more nodes via the same command, speeding up the run time of the tests, as they will get distributed over these nodes by `pytest`.

After this preparation, open yet another terminal. Before starting the tests, be sure to create a file containing
session cookies:
## Running the tests

```
python load_cookies.py
```
After the preparation steps above, open another terminal and navigate to the GCA-Web `test/frontend` folder.
The tests can be run by `python -m pytest` or `python -m pytest [testfile]`.

Now we can run the tests via the `pytest` or alternatively the `python -m pytest` command. Without any arguments,
all tests in the directory will be executed.
Specific tests can always be run by adding the file name to this command.
Using the `pytest-xdist` module, parallel testing is possible, decreasing the overall run time of the tests. Please note that `pytest-xdist` is an extension plugin, not part of the `pytest` distribution and needs to be installed separately.
Try to keep the number of open browser windows around 4-6,
otherwise the usage of memory space will increase times again.
Using the `pytest-xdist` module, parallel testing is possible, decreasing the overall run time of the tests. Please note that `pytest-xdist` is an extension plugin, not part of the `pytest` distribution and needs to be installed separately. Try to keep the number of open browser windows around 4-6, otherwise the usage of memory space will increase times again.

##Possible Problem Solutions
# Potential issues

- Make sure that you browser and corresponding browser driver versions are compatible. This is likely the
cause behind errors of type

selenium.common.exceptions.SessionNotCreatedException: Message:
Unable to create new service: ChromeDriverService

- Make absolutely sure that you browser and corresponding browser driver versions are compatible. This is likely the
cause behind errors of type:
``selenium.common.exceptions.SessionNotCreatedException: Message: Unable to create new service: ChromeDriverService``
- If you obtain a connection error in the browser, be sure that no proxy server is set in your browser.
This might also cause the browser to load slowly.

# Creating New Tests
# Creating new tests

There are some issues you can encounter, when adding tests.
Also there are many different ways to set up the frontend tests with selenium, unittest and pytest.

Using `pytest` and fixtures, the unittest.TestCase class does not work.
For each class, you can add another fixture in the conftest.py file.
Driver set-up and starting at the login page is already contained in the `maximize_login(request)` function.
Working with the selenium standalone server, please set up new drivers via:

```python
driver = webdriver.Remote(
command_executor="http://" + Cookies.get_host_ip() + ":4444/wd/hub",
desired_capabilities={'browserName': '$driver_name$', 'javascriptEnabled': True}
)
```
- Using `pytest` and fixtures, the `unittest.TestCase` class does not work.
- For each class, you can add another fixture in the `conftest.py` file.
- Driver set-up and starting at the login page is already contained in the `maximize_login(request)` function.
- Working with the selenium standalone server, set up new drivers via

In case, you want to run the tests without the standalone server, replace the remote webdriver.
```python
driver = webdriver.Remote(
command_executor="http://" + Cookies.get_host_ip() + ":4444/wd/hub",
desired_capabilities={'browserName': '$driver_name$', 'javascriptEnabled': True}
)
```

```python
driver = webdriver.Firefox()
#or
driver = webdriver.Chrome()
```
- In case you want to run the tests without the standalone server, replace the remote webdriver. You only need to do this once in the `maximize_login(request)` function.

You only need to do this once in the `maximize_login(request)` function.

If you want to add cookies, first open up another page, load the cookies and the go to the desired page or reload.
Compare with the already used set up functions in the conftest.py file.
An issue with selenium is that using `localhost` in URLs can cause problems with, e.g. loading cookies.
As a workaround, always use the `Cookies.get_host_ip()`.

With most browser drivers, elements searched for by functions of the type ```driver.get_element_by_*()```, will be
found automatically, even if they're outside of the section currently viewed on the screen. In Firefox resp. with
Gecko driver, an error will be returned in such cases. It is important for Firefox to always navigate/scroll to the
element before examination by using ```move_to_element_by_*()```. For often used actions like ```click()```,
```send_keys()``` or ```get_attribute()``` functions including this are predefined in the conftest.py
file.

Generally tests run stable. In some incidences a test might fail due to local issues, repeating the test might work
then. To avoid such problems, it is useful to include
```python
WebDriverWait(driver, 30).until(
# insert expected condition, e.g.
EC.presence_of_element_located((By.XPATH, ''))
)
```
after processes that are expected to need some time to execute, e.g. when saving a form or loading a page or modal.
```python
driver = webdriver.Firefox()
# or
driver = webdriver.Chrome()
```

- If you want to add cookies, first open up another page, load the cookies and the go to the desired page or reload. Compare with the already used set up functions in the `conftest.py` file. An issue with selenium is that using `localhost` in URLs can cause problems with, e.g. loading cookies. As a workaround, always use the `Cookies.get_host_ip()`.

- With most browser drivers, elements searched for by functions of the type `driver.get_element_by_*()` will be
found automatically, even if they're outside of the section currently viewed on the screen. In Firefox (Geckodriver) an error will be returned in such cases. It is important for Firefox to always navigate/scroll to the element before examination by using `move_to_element_by_*()`. For often used actions like the `click()`, `send_keys()` or `get_attribute()` functions including this are predefined in the `conftest.py` file.

For all things python and selenium, it's good to have a look at this page:
- Generally tests run stable. In some incidences a test might fail due to local issues, repeating the test might work then. To avoid such problems, it is useful to include the following code snipped after processes that are expected to need some time to execute, e.g. when saving a form or loading a page or modal.

```python
WebDriverWait(driver, 30).until(
# insert expected condition, e.g.
EC.presence_of_element_located((By.XPATH, ''))
)
```

# References and links

- For all things python and selenium, it's good to have a look at this page
https://selenium-python.readthedocs.io/

A short tutorial for setting up selenium grid and creating a test class using unittest.TestCase instead of fixtures
can be found here:
- For more helpful information about selenium grid, check out the selenium documentation
https://www.selenium.dev/documentation/en/

- For information on selenium and docker, see [this article on medium.com](https://medium.com/@arnonaxelrod/running-cross-browser-selenium-tests-in-parallel-using-selenium-grid-and-docker-containers-9ee293b86cfd)

- A short tutorial for setting up selenium grid and creating a test class using `unittest.TestCase` instead of fixtures can be found here
https://gist.github.com/dzitkowskik/0fc641cf59af0dc3de62


Loading

0 comments on commit 759c318

Please sign in to comment.