From 0bdc2802f257c23558b3e9b518cde76205bc0576 Mon Sep 17 00:00:00 2001 From: Joseph Nuthalapati Date: Mon, 19 Feb 2018 13:33:31 +0530 Subject: [PATCH] functional-tests: Merge plinth-tester into plinth - Rename Plinth-tester to FreedomBox - Add pytest.ini to discover the root directory for tests easily - Update README to say that py.test should be run from functional_tests directory. - Add geckodriver.log to .gitignore Signed-off-by: Joseph Nuthalapati Reviewed-by: James Valleroy --- functional_tests/.gitignore | 197 ++++++++++++++++++ functional_tests/README.md | 61 ++++++ functional_tests/config.ini | 4 + .../features/anonymity_network.feature | 34 +++ .../features/bittorrent_client_deluge.feature | 34 +++ .../bittorrent_client_transmission.feature | 34 +++ .../features/block_sandbox.feature | 34 +++ .../features/calendar_and_addressbook.feature | 34 +++ functional_tests/features/chat_server.feature | 34 +++ .../collaborative_text_editor.feature | 34 +++ .../features/configuration.feature | 51 +++++ .../features/date_and_time.feature | 33 +++ .../features/email_client.feature | 34 +++ .../features/file_sharing.feature | 49 +++++ .../features/file_synchronization.feature | 34 +++ functional_tests/features/irc_client.feature | 34 +++ .../features/news_feed_reader.feature | 34 +++ .../features/server_administration.feature | 34 +++ .../features/service_discovery.feature | 33 +++ .../features/single_sign_on.feature | 35 ++++ functional_tests/features/sip_server.feature | 34 +++ .../features/socks5_proxy.feature | 36 ++++ .../features/storage_snapshots.feature | 29 +++ .../features/users_and_groups.feature | 40 ++++ functional_tests/features/voice_chat.feature | 34 +++ .../features/voip_and_chat_server.feature | 36 ++++ functional_tests/features/web_proxy.feature | 34 +++ .../features/wiki_and_blog.feature | 34 +++ functional_tests/features/wiki_engine.feature | 34 +++ functional_tests/features/xmpp_client.feature | 27 +++ functional_tests/pytest.ini | 0 functional_tests/step_definitions/__init__.py | 0 .../step_definitions/application.py | 113 ++++++++++ .../step_definitions/interface.py | 84 ++++++++ functional_tests/step_definitions/service.py | 51 +++++ functional_tests/step_definitions/site.py | 42 ++++ functional_tests/step_definitions/system.py | 87 ++++++++ functional_tests/support/__init__.py | 25 +++ functional_tests/support/application.py | 130 ++++++++++++ functional_tests/support/interface.py | 101 +++++++++ functional_tests/support/service.py | 55 +++++ functional_tests/support/site.py | 59 ++++++ functional_tests/support/system.py | 88 ++++++++ functional_tests/test_plinth.py | 26 +++ functional_tests/todo.org | 60 ++++++ 45 files changed, 2130 insertions(+) create mode 100644 functional_tests/.gitignore create mode 100644 functional_tests/README.md create mode 100644 functional_tests/config.ini create mode 100644 functional_tests/features/anonymity_network.feature create mode 100644 functional_tests/features/bittorrent_client_deluge.feature create mode 100644 functional_tests/features/bittorrent_client_transmission.feature create mode 100644 functional_tests/features/block_sandbox.feature create mode 100644 functional_tests/features/calendar_and_addressbook.feature create mode 100644 functional_tests/features/chat_server.feature create mode 100644 functional_tests/features/collaborative_text_editor.feature create mode 100644 functional_tests/features/configuration.feature create mode 100644 functional_tests/features/date_and_time.feature create mode 100644 functional_tests/features/email_client.feature create mode 100644 functional_tests/features/file_sharing.feature create mode 100644 functional_tests/features/file_synchronization.feature create mode 100644 functional_tests/features/irc_client.feature create mode 100644 functional_tests/features/news_feed_reader.feature create mode 100644 functional_tests/features/server_administration.feature create mode 100644 functional_tests/features/service_discovery.feature create mode 100644 functional_tests/features/single_sign_on.feature create mode 100644 functional_tests/features/sip_server.feature create mode 100644 functional_tests/features/socks5_proxy.feature create mode 100644 functional_tests/features/storage_snapshots.feature create mode 100644 functional_tests/features/users_and_groups.feature create mode 100644 functional_tests/features/voice_chat.feature create mode 100644 functional_tests/features/voip_and_chat_server.feature create mode 100644 functional_tests/features/web_proxy.feature create mode 100644 functional_tests/features/wiki_and_blog.feature create mode 100644 functional_tests/features/wiki_engine.feature create mode 100644 functional_tests/features/xmpp_client.feature create mode 100644 functional_tests/pytest.ini create mode 100644 functional_tests/step_definitions/__init__.py create mode 100644 functional_tests/step_definitions/application.py create mode 100644 functional_tests/step_definitions/interface.py create mode 100644 functional_tests/step_definitions/service.py create mode 100644 functional_tests/step_definitions/site.py create mode 100644 functional_tests/step_definitions/system.py create mode 100644 functional_tests/support/__init__.py create mode 100644 functional_tests/support/application.py create mode 100644 functional_tests/support/interface.py create mode 100644 functional_tests/support/service.py create mode 100644 functional_tests/support/site.py create mode 100644 functional_tests/support/system.py create mode 100644 functional_tests/test_plinth.py create mode 100644 functional_tests/todo.org diff --git a/functional_tests/.gitignore b/functional_tests/.gitignore new file mode 100644 index 000000000..c72dd6158 --- /dev/null +++ b/functional_tests/.gitignore @@ -0,0 +1,197 @@ + +# Created by https://www.gitignore.io/api/vim,emacs,macos,python,vagrant + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +### Vagrant ### +.vagrant/ +Vagrantfile + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +# auto-generated tag files +tags + +# End of https://www.gitignore.io/api/vim,emacs,macos,python,vagrant + +test_plinth/ +geckodriver.log \ No newline at end of file diff --git a/functional_tests/README.md b/functional_tests/README.md new file mode 100644 index 000000000..813b92143 --- /dev/null +++ b/functional_tests/README.md @@ -0,0 +1,61 @@ +# Install Dependencies + +``` +$ sudo apt install python3-pytest +$ pip3 install splinter +$ pip3 install pytest-splinter +$ pip3 install pytest-bdd +$ sudo apt install xvfb # optional, to avoid opening browser windows +$ pip3 install pytest-xvfb # optional, to avoid opening browser windows +``` + +- Install the latest version of geckodriver. +It's usually a single binary which you can place at /usr/local/bin/geckodriver + +- Install the latest version of Mozilla Firefox. +Download and extract the latest version from the Firefox website and symlink the binary named `firefox` to /usr/local/bin. + +Geckodriver will then use whatever version of Firefox you symlink as /usr/local/bin/firefox. + +# Run FreedomBox Service + +*Warning*: Functional tests will change the configuration of the system + under test, including changing the hostname and users. Therefore you + should run the tests using FreedomBox running on a throw-away VM. + +The VM should have NAT port-forwarding enabled so that 4430 on the +host forwards to 443 on the guest. The web interface of FreedomBox +should be accessible from the host system at https://localhost:4430/. + +# Setup FreedomBox Service for tests + +Create a new user as follows: + +* Username: tester +* Password: testingtesting + +This step is optional if a fresh install of Plinth is being +tested. Functional tests will create the required user using FreedomBox's +first boot process. + +# Run Functional Tests + +From the directory functional_tests, run + +``` +$ py.test +``` + +The full test suite can take a long time to run (over 15 minutes). You +can also specify which tests to run, by tag or keyword: + +``` +$ py.test -k essential +``` + +If xvfb is installed and you still want to see browser windows, use the +`--no-xvfb` command-line argument. + +``` +$ py.test --no-xvfb -k mediawiki +``` diff --git a/functional_tests/config.ini b/functional_tests/config.ini new file mode 100644 index 000000000..047801387 --- /dev/null +++ b/functional_tests/config.ini @@ -0,0 +1,4 @@ +[DEFAULT] +url = https://localhost:4430 +username = tester +password = testingtesting diff --git a/functional_tests/features/anonymity_network.feature b/functional_tests/features/anonymity_network.feature new file mode 100644 index 000000000..a4c3c6d21 --- /dev/null +++ b/functional_tests/features/anonymity_network.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @tor +Feature: Anonymity Network + Manage Tor configuration. + +Background: + Given I'm a logged in user + Given the tor application is installed + +Scenario: Enable tor application + Given the tor application is disabled + When I enable the tor application + Then the tor service should be running + +Scenario: Disable tor application + Given the tor application is enabled + When I disable the tor application + Then the tor service should not be running diff --git a/functional_tests/features/bittorrent_client_deluge.feature b/functional_tests/features/bittorrent_client_deluge.feature new file mode 100644 index 000000000..901cbcbd4 --- /dev/null +++ b/functional_tests/features/bittorrent_client_deluge.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @deluge +Feature: BitTorrent Client + Run the Deluge BitTorrent client. + +Background: + Given I'm a logged in user + Given the deluge application is installed + +Scenario: Enable deluge application + Given the deluge application is disabled + When I enable the deluge application + Then the deluge site should be available + +Scenario: Disable deluge application + Given the deluge application is enabled + When I disable the deluge application + Then the deluge site should not be available diff --git a/functional_tests/features/bittorrent_client_transmission.feature b/functional_tests/features/bittorrent_client_transmission.feature new file mode 100644 index 000000000..da9567382 --- /dev/null +++ b/functional_tests/features/bittorrent_client_transmission.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @transmission +Feature: BitTorrent Client + Run the Transmission BitTorrent client. + +Background: + Given I'm a logged in user + Given the transmission application is installed + +Scenario: Enable transmission application + Given the transmission application is disabled + When I enable the transmission application + Then the transmission site should be available + +Scenario: Disable transmission application + Given the transmission application is enabled + When I disable the transmission application + Then the transmission site should not be available diff --git a/functional_tests/features/block_sandbox.feature b/functional_tests/features/block_sandbox.feature new file mode 100644 index 000000000..b5244ff2a --- /dev/null +++ b/functional_tests/features/block_sandbox.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @minetest +Feature: Block Sandbox + Run the Minetest server + +Background: + Given I'm a logged in user + Given the minetest application is installed + +Scenario: Enable minetest application + Given the minetest application is disabled + When I enable the minetest application + Then the minetest service should be running + +Scenario: Disable minetest application + Given the minetest application is enabled + When I disable the minetest application + Then the minetest service should not be running diff --git a/functional_tests/features/calendar_and_addressbook.feature b/functional_tests/features/calendar_and_addressbook.feature new file mode 100644 index 000000000..3c5fe1e0b --- /dev/null +++ b/functional_tests/features/calendar_and_addressbook.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @radicale +Feature: Calendar and Addressbook + Configure CalDAV/CardDAV server. + +Background: + Given I'm a logged in user + Given the radicale application is installed + +Scenario: Enable radicale application + Given the radicale application is disabled + When I enable the radicale application + Then the radicale service should be running + +Scenario: Disable radicale application + Given the radicale application is enabled + When I disable the radicale application + Then the radicale service should not be running diff --git a/functional_tests/features/chat_server.feature b/functional_tests/features/chat_server.feature new file mode 100644 index 000000000..67612f29c --- /dev/null +++ b/functional_tests/features/chat_server.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @ejabberd +Feature: Chat Server + Run ejabberd chat server. + +Background: + Given I'm a logged in user + Given the ejabberd application is installed + +Scenario: Enable ejabberd application + Given the ejabberd application is disabled + When I enable the ejabberd application + Then the ejabberd service should be running + +Scenario: Disable ejabberd application + Given the ejabberd application is enabled + When I disable the ejabberd application + Then the ejabberd service should not be running diff --git a/functional_tests/features/collaborative_text_editor.feature b/functional_tests/features/collaborative_text_editor.feature new file mode 100644 index 000000000..cb91fb224 --- /dev/null +++ b/functional_tests/features/collaborative_text_editor.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @infinoted +Feature: Collaborative Text Editor + Run Gobby Server - Infinoted + +Background: + Given I'm a logged in user + Given the infinoted application is installed + +Scenario: Enable infinoted application + Given the infinoted application is disabled + When I enable the infinoted application + Then the infinoted service should be running + +Scenario: Disable infinoted application + Given the infinoted application is enabled + When I disable the infinoted application + Then the infinoted service should not be running diff --git a/functional_tests/features/configuration.feature b/functional_tests/features/configuration.feature new file mode 100644 index 000000000..134972f79 --- /dev/null +++ b/functional_tests/features/configuration.feature @@ -0,0 +1,51 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@system @essential +Feature: Configuration + Configure the system. + +Background: + Given I'm a logged in user + +Scenario: Change hostname + When I change the hostname to mybox + Then the hostname should be mybox + +Scenario: Change domain name + When I change the domain name to mydomain + Then the domain name should be mydomain + +Scenario Outline: Change language + When I change the language to + Then Plinth language should be + + Examples: + | language | + | Danish | + | German | + | Spanish | + | French | + | Norwegian Bokmål | + | Dutch | + | Polish | + | Portuguese | + | Russian | + | Swedish | + | Telugu | + | Turkish | + | Simplified Chinese | diff --git a/functional_tests/features/date_and_time.feature b/functional_tests/features/date_and_time.feature new file mode 100644 index 000000000..2ff8e0624 --- /dev/null +++ b/functional_tests/features/date_and_time.feature @@ -0,0 +1,33 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@essential @date-and-time @system +Feature: Date and Time + Configure time zone and network time service. + +Background: + Given I'm a logged in user + +Scenario: Enable network time application + Given the network time application is disabled + When I enable the network time application + Then the network time service should be running + +Scenario: Disable network time application + Given the network time application is enabled + When I disable the network time application + Then the network time service should not be running diff --git a/functional_tests/features/email_client.feature b/functional_tests/features/email_client.feature new file mode 100644 index 000000000..dae2c6212 --- /dev/null +++ b/functional_tests/features/email_client.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @roundcube +Feature: Email Client + Run webmail client. + +Background: + Given I'm a logged in user + Given the roundcube application is installed + +Scenario: Enable roundcube application + Given the roundcube application is disabled + When I enable the roundcube application + Then the roundcube site should be available + +Scenario: Disable roundcube application + Given the roundcube application is enabled + When I disable the roundcube application + Then the roundcube site should not be available diff --git a/functional_tests/features/file_sharing.feature b/functional_tests/features/file_sharing.feature new file mode 100644 index 000000000..9a5496636 --- /dev/null +++ b/functional_tests/features/file_sharing.feature @@ -0,0 +1,49 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @coquelicot +Feature: File Sharing + Run Coquelicot File Sharing server. + +Background: + Given I'm a logged in user + Given the coquelicot application is installed + +Scenario: Enable coquelicot application + Given the coquelicot application is disabled + When I enable the coquelicot application + Then the coquelicot service should be running + +Scenario: Disable coquelicot application + Given the coquelicot application is enabled + When I disable the coquelicot application + Then the coquelicot service should not be running + +Scenario: Modify maximum upload size + Given the coquelicot application is enabled + When I modify the maximum file size of coquelicot to 256 + Then the maximum file size of coquelicot should be 256 + +Scenario: Modify upload password + Given the coquelicot application is enabled + When I modify the coquelicot upload password to whatever123 + Then I should be able to login to coquelicot with password whatever123 + +Scenario: Modify maximum upload size in disabled case + Given the coquelicot application is disabled + When I modify the maximum file size of coquelicot to 123 + Then the coquelicot service should not be running diff --git a/functional_tests/features/file_synchronization.feature b/functional_tests/features/file_synchronization.feature new file mode 100644 index 000000000..e30902d33 --- /dev/null +++ b/functional_tests/features/file_synchronization.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @syncthing +Feature: File Synchronization + Run Syncthing File Synchronization server. + +Background: + Given I'm a logged in user + Given the syncthing application is installed + +Scenario: Enable syncthing application + Given the syncthing application is disabled + When I enable the syncthing application + Then the syncthing service should be running + +Scenario: Disable syncthing application + Given the syncthing application is enabled + When I disable the syncthing application + Then the syncthing service should not be running diff --git a/functional_tests/features/irc_client.feature b/functional_tests/features/irc_client.feature new file mode 100644 index 000000000..b1ce7c732 --- /dev/null +++ b/functional_tests/features/irc_client.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @quassel +Feature: IRC Client + Run Quassel core. + +Background: + Given I'm a logged in user + Given the quassel application is installed + +Scenario: Enable quassel application + Given the quassel application is disabled + When I enable the quassel application + Then the quassel service should be running + +Scenario: Disable quassel application + Given the quassel application is enabled + When I disable the quassel application + Then the quassel service should not be running diff --git a/functional_tests/features/news_feed_reader.feature b/functional_tests/features/news_feed_reader.feature new file mode 100644 index 000000000..f021b7de6 --- /dev/null +++ b/functional_tests/features/news_feed_reader.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @ttrss +Feature: News Feed Reader + Run TT-RSS News Feed Reader. + +Background: + Given I'm a logged in user + Given the ttrss application is installed + +Scenario: Enable ttrss application + Given the ttrss application is disabled + When I enable the ttrss application + Then the ttrss service should be running + +Scenario: Disable ttrss application + Given the ttrss application is enabled + When I disable the ttrss application + Then the ttrss service should not be running diff --git a/functional_tests/features/server_administration.feature b/functional_tests/features/server_administration.feature new file mode 100644 index 000000000..48cb0a03d --- /dev/null +++ b/functional_tests/features/server_administration.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@system +Feature: Server Administration + Run server administration application - Cockpit. + +Background: + Given I'm a logged in user + Given the cockpit application is installed + +Scenario: Enable cockpit application + Given the cockpit application is disabled + When I enable the cockpit application + Then the cockpit site should be available + +Scenario: Disable cockpit application + Given the cockpit application is enabled + When I disable the cockpit application + Then the cockpit site should not be available diff --git a/functional_tests/features/service_discovery.feature b/functional_tests/features/service_discovery.feature new file mode 100644 index 000000000..ef0cec088 --- /dev/null +++ b/functional_tests/features/service_discovery.feature @@ -0,0 +1,33 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@system @essential +Feature: Service Discovery + Configure service discovery. + +Background: + Given I'm a logged in user + +Scenario: Enable service discovery application + Given the service discovery application is disabled + When I enable the service discovery application + Then the service discovery service should be running + +Scenario: Disable service discovery application + Given the service discovery application is enabled + When I disable the service discovery application + Then the service discovery service should not be running diff --git a/functional_tests/features/single_sign_on.feature b/functional_tests/features/single_sign_on.feature new file mode 100644 index 000000000..6ffbf9293 --- /dev/null +++ b/functional_tests/features/single_sign_on.feature @@ -0,0 +1,35 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@sso @essential @system +Feature: Single Sign On + Test Single Sign On features. + +Background: + Given I'm a logged in user + Given the syncthing application is installed + Given the syncthing application is enabled + + +Scenario: Logged out Plinth user cannot access Syncthing web interface + Given I'm a logged out user + When I access syncthing application + Then I should be prompted for login + +Scenario: Logged in Plinth user can access Syncthing web interface + When I access syncthing application + Then the syncthing site should be available diff --git a/functional_tests/features/sip_server.feature b/functional_tests/features/sip_server.feature new file mode 100644 index 000000000..6fbc43a7d --- /dev/null +++ b/functional_tests/features/sip_server.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @sip +Feature: SIP Server + Make audio and video calls. + +Background: + Given I'm a logged in user + Given the repro application is installed + +Scenario: Enable repro application + Given the repro application is disabled + When I enable the repro application + Then the repro service should be running + +Scenario: Disable repro application + Given the repro application is enabled + When I disable the repro application + Then the repro service should not be running diff --git a/functional_tests/features/socks5_proxy.feature b/functional_tests/features/socks5_proxy.feature new file mode 100644 index 000000000..6cfa0dbff --- /dev/null +++ b/functional_tests/features/socks5_proxy.feature @@ -0,0 +1,36 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @shadowsocks +Feature: Socks5 Proxy + Run the Shadowsocks Socks5 proxy client. + +Background: + Given I'm a logged in user + Given the shadowsocks application is installed + Given the shadowsocks application is configured + +Scenario: Enable shadowsocks application + Given the shadowsocks application is disabled + When I enable the shadowsocks application + Then the shadowsocks service should be running + +Scenario: Disable shadowsocks application + Given the shadowsocks application is enabled + When I disable the shadowsocks application + Then the shadowsocks service should not be running + diff --git a/functional_tests/features/storage_snapshots.feature b/functional_tests/features/storage_snapshots.feature new file mode 100644 index 000000000..2e98d6006 --- /dev/null +++ b/functional_tests/features/storage_snapshots.feature @@ -0,0 +1,29 @@ +# +# This file is part of Plinth-tester. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@system @snapshots +Feature: Storage Snapshots + Run storage snapshots application - Snapper. + +Background: + Given I'm a logged in user + Given the snapshot application is installed + +Scenario: Create a snapshot + Given the list of snapshots is empty + When I manually create a snapshot + Then there should be 1 snapshot in the list diff --git a/functional_tests/features/users_and_groups.feature b/functional_tests/features/users_and_groups.feature new file mode 100644 index 000000000..e7b56ac4a --- /dev/null +++ b/functional_tests/features/users_and_groups.feature @@ -0,0 +1,40 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@system @essential @users-groups +Feature: Users and Groups + Manage users and groups. + +Background: + Given I'm a logged in user + +Scenario: Create user + Given the user alice doesn't exist + When I create a user named alice with password secret123 + Then alice should be listed as a user + +Scenario: Rename user + Given the user alice exists + Given the user bob doesn't exist + When I rename the user alice to bob + Then alice should not be listed as a user + Then bob should be listed as a user + +Scenario: Delete user + Given the user alice exists + When I delete the user alice + Then alice should not be listed as a user diff --git a/functional_tests/features/voice_chat.feature b/functional_tests/features/voice_chat.feature new file mode 100644 index 000000000..2a86d27b4 --- /dev/null +++ b/functional_tests/features/voice_chat.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @mumble +Feature: Voice Chat + Run Mumble voice chat server. + +Background: + Given I'm a logged in user + Given the mumble application is installed + +Scenario: Enable mumble application + Given the mumble application is disabled + When I enable the mumble application + Then the mumble service should be running + +Scenario: Disable mumble application + Given the mumble application is enabled + When I disable the mumble application + Then the mumble service should not be running diff --git a/functional_tests/features/voip_and_chat_server.feature b/functional_tests/features/voip_and_chat_server.feature new file mode 100644 index 000000000..7d45bb710 --- /dev/null +++ b/functional_tests/features/voip_and_chat_server.feature @@ -0,0 +1,36 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @matrixsynapse +Feature: VoIP and Chat Server + Run Matrix Synapse server + +Background: + Given I'm a logged in user + Given the domain name is set to mydomain + Given the matrixsynapse application is installed + Given the domain name for matrixsynapse is set to mydomain + +Scenario: Enable matrixsynapse application + Given the matrixsynapse application is disabled + When I enable the matrixsynapse application + Then the matrixsynapse service should be running + +Scenario: Disable matrixsynapse application + Given the matrixsynapse application is enabled + When I disable the matrixsynapse application + Then the matrixsynapse service should not be running diff --git a/functional_tests/features/web_proxy.feature b/functional_tests/features/web_proxy.feature new file mode 100644 index 000000000..d9808a3ab --- /dev/null +++ b/functional_tests/features/web_proxy.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @privoxy +Feature: Web Proxy + Proxy web connections for enhanced privacy. + +Background: + Given I'm a logged in user + Given the privoxy application is installed + +Scenario: Enable privoxy application + Given the privoxy application is disabled + When I enable the privoxy application + Then the privoxy service should be running + +Scenario: Disable privoxy application + Given the privoxy application is enabled + When I disable the privoxy application + Then the privoxy service should not be running diff --git a/functional_tests/features/wiki_and_blog.feature b/functional_tests/features/wiki_and_blog.feature new file mode 100644 index 000000000..57f4d5e37 --- /dev/null +++ b/functional_tests/features/wiki_and_blog.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @ikiwiki +Feature: Wiki and Blog + Manage wikis and blogs. + +Background: + Given I'm a logged in user + Given the wiki application is installed + +Scenario: Enable wiki application + Given the wiki application is disabled + When I enable the wiki application + Then the wiki site should be available + +Scenario: Disable wiki application + Given the wiki application is enabled + When I disable the wiki application + Then the wiki site should not be available diff --git a/functional_tests/features/wiki_engine.feature b/functional_tests/features/wiki_engine.feature new file mode 100644 index 000000000..331fd1769 --- /dev/null +++ b/functional_tests/features/wiki_engine.feature @@ -0,0 +1,34 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @mediawiki +Feature: Wiki Engine + Manage wikis, multimedia and more. + +Background: + Given I'm a logged in user + Given the mediawiki application is installed + +Scenario: Enable mediawiki application + Given the mediawiki application is disabled + When I enable the mediawiki application + Then the mediawiki site should be available + +Scenario: Disable mediawiki application + Given the mediawiki application is enabled + When I disable the mediawiki application + Then the mediawiki site should not be available diff --git a/functional_tests/features/xmpp_client.feature b/functional_tests/features/xmpp_client.feature new file mode 100644 index 000000000..b26a46d3c --- /dev/null +++ b/functional_tests/features/xmpp_client.feature @@ -0,0 +1,27 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +@apps @jsxc +Feature: XMPP Client + Run the JSXC XMPP client. + +Background: + Given I'm a logged in user + +Scenario: Install jsxc application + Given the jsxc application is installed + Then the jsxc site should be available diff --git a/functional_tests/pytest.ini b/functional_tests/pytest.ini new file mode 100644 index 000000000..e69de29bb diff --git a/functional_tests/step_definitions/__init__.py b/functional_tests/step_definitions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/functional_tests/step_definitions/application.py b/functional_tests/step_definitions/application.py new file mode 100644 index 000000000..af2650622 --- /dev/null +++ b/functional_tests/step_definitions/application.py @@ -0,0 +1,113 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import given, parsers, then, when + +from support import application + + +@given(parsers.parse('the {app_name:w} application is installed')) +def application_is_installed(browser, app_name): + application.install(browser, app_name) + + +@given(parsers.parse('the {app_name:w} application is enabled')) +def application_is_enabled(browser, app_name): + application.enable(browser, app_name) + + +@given(parsers.parse('the {app_name:w} application is disabled')) +def application_is_disabled(browser, app_name): + application.disable(browser, app_name) + + +@given(parsers.parse('the network time application is enabled')) +def ntp_is_enabled(browser): + application.enable(browser, 'ntp') + + +@given(parsers.parse('the network time application is disabled')) +def ntp_is_disabled(browser): + application.disable(browser, 'ntp') + + +@given(parsers.parse('the service discovery application is enabled')) +def avahi_is_enabled(browser): + application.enable(browser, 'avahi') + + +@given(parsers.parse('the service discovery application is disabled')) +def avahi_is_disabled(browser): + application.disable(browser, 'avahi') + + +@when(parsers.parse('I enable the {app_name:w} application')) +def enable_application(browser, app_name): + application.enable(browser, app_name) + + +@when(parsers.parse('I disable the {app_name:w} application')) +def disable_application(browser, app_name): + application.disable(browser, app_name) + + +@when(parsers.parse('I enable the network time application')) +def enable_ntp(browser): + application.enable(browser, 'ntp') + + +@when(parsers.parse('I disable the network time application')) +def disable_ntp(browser): + application.disable(browser, 'ntp') + + +@when(parsers.parse('I enable the service discovery application')) +def enable_avahi(browser): + application.enable(browser, 'avahi') + + +@when(parsers.parse('I disable the service discovery application')) +def disable_avahi(browser): + application.disable(browser, 'avahi') + + +@given( + parsers.parse('the domain name for {app_name:w} is set to {domain_name:w}') +) +def select_domain_name(browser, app_name, domain_name): + application.select_domain_name(browser, app_name, domain_name) + + +@given('the shadowsocks application is configured') +def configure_shadowsocks(browser): + application.configure_shadowsocks(browser) + + +@when( + parsers.parse('I modify the maximum file size of coquelicot to {size:d}')) +def modify_max_file_size(browser, size): + application.modify_max_file_size(browser, size) + + +@then(parsers.parse('the maximum file size of coquelicot should be {size:d}')) +def assert_max_file_size(browser, size): + assert application.get_max_file_size(browser) == size + + +@when(parsers.parse('I modify the coquelicot upload password to {password:w}')) +def modify_upload_password(browser, password): + application.modify_upload_password(browser, password) diff --git a/functional_tests/step_definitions/interface.py b/functional_tests/step_definitions/interface.py new file mode 100644 index 000000000..69a2c3c46 --- /dev/null +++ b/functional_tests/step_definitions/interface.py @@ -0,0 +1,84 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import given, parsers, then, when + +from support import config, interface + + +default_url = config['DEFAULT']['url'] + + +@given("I'm a logged in user") +def logged_in_user(browser): + interface.login(browser, default_url, config['DEFAULT']['username'], + config['DEFAULT']['password']) + + +@given("I'm a logged out user") +def logged_out_user(browser): + browser.visit(default_url + '/plinth/accounts/logout/') + + +@then(parsers.parse('I should be prompted for login')) +def prompted_for_login(browser): + assert interface.is_login_prompt(browser) + + +@given(parsers.parse("the user {name:w} doesn't exist")) +def new_user_does_not_exist(browser, name): + interface.nav_to_module(browser, 'users') + delete_link = browser.find_link_by_href( + '/plinth/sys/users/' + name + '/delete/') + if delete_link: + delete_link.first.click() + browser.find_by_value('Delete ' + name).click() + + +@given(parsers.parse('the user {name:w} exists')) +def test_user_exists(browser, name): + interface.nav_to_module(browser, 'users') + user_link = browser.find_link_by_href( + '/plinth/sys/users/' + name + '/edit/') + if not user_link: + create_user(browser, name, 'secret123') + + +@when( + parsers.parse('I create a user named {name:w} with password {password:w}')) +def create_user(browser, name, password): + interface.create_user(browser, name, password) + + +@when(parsers.parse('I rename the user {old_name:w} to {new_name:w}')) +def rename_user(browser, old_name, new_name): + interface.rename_user(browser, old_name, new_name) + + +@when(parsers.parse('I delete the user {name:w}')) +def delete_user(browser, name): + interface.delete_user(browser, name) + + +@then(parsers.parse('{name:w} should be listed as a user')) +def new_user_is_listed(browser, name): + assert interface.is_user(browser, name) + + +@then(parsers.parse('{name:w} should not be listed as a user')) +def new_user_is_not_listed(browser, name): + assert not interface.is_user(browser, name) diff --git a/functional_tests/step_definitions/service.py b/functional_tests/step_definitions/service.py new file mode 100644 index 000000000..5729dd67a --- /dev/null +++ b/functional_tests/step_definitions/service.py @@ -0,0 +1,51 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import parsers, then + +from support import service +from support.service import eventually + + +@then(parsers.parse('the {service_name:w} service should be running')) +def service_should_be_running(browser, service_name): + assert eventually(service.is_running, args=[browser, service_name]) + + +@then(parsers.parse('the {service_name:w} service should not be running')) +def service_should_not_be_running(browser, service_name): + assert eventually(service.is_not_running, args=[browser, service_name]) + + +@then(parsers.parse('the network time service should be running')) +def ntp_should_be_running(browser): + assert service.is_running(browser, 'ntp') + + +@then(parsers.parse('the network time service should not be running')) +def ntp_should_not_be_running(browser): + assert not service.is_running(browser, 'ntp') + + +@then(parsers.parse('the service discovery service should be running')) +def avahi_should_be_running(browser): + assert service.is_running(browser, 'avahi') + + +@then(parsers.parse('the service discovery service should not be running')) +def avahi_should_not_be_running(browser): + assert not service.is_running(browser, 'avahi') diff --git a/functional_tests/step_definitions/site.py b/functional_tests/step_definitions/site.py new file mode 100644 index 000000000..e179be40b --- /dev/null +++ b/functional_tests/step_definitions/site.py @@ -0,0 +1,42 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import parsers, then, when + +from support import site + + +@then(parsers.parse('the {site_name:w} site should be available')) +def site_should_be_available(browser, site_name): + assert site.is_available(browser, site_name) + + +@then(parsers.parse('the {site_name:w} site should not be available')) +def site_should_not_be_available(browser, site_name): + assert not site.is_available(browser, site_name) + + +@when(parsers.parse('I access {app_name:w} application')) +def access_application(browser, app_name): + site.access_url(browser, app_name) + + +@then( + parsers.parse( + 'I should be able to login to coquelicot with password {password:w}')) +def verify_upload_password(browser, password): + site.verify_coquelicot_upload_password(browser, password) diff --git a/functional_tests/step_definitions/system.py b/functional_tests/step_definitions/system.py new file mode 100644 index 000000000..3d7b108ba --- /dev/null +++ b/functional_tests/step_definitions/system.py @@ -0,0 +1,87 @@ +# +# This file is part of Plinth-tester. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import given, parsers, then, when + +from support import system + +language_codes = { + 'Danish': 'da', + 'German': 'de', + 'Spanish': 'es', + 'French': 'fr', + 'Norwegian Bokmål': 'nb', + 'Dutch': 'nl', + 'Polish': 'pl', + 'Portuguese': 'pt', + 'Russian': 'ru', + 'Swedish': 'sv', + 'Telugu': 'te', + 'Turkish': 'tr', + 'Simplified Chinese': 'zh-hans', +} + + +@given(parsers.parse('the domain name is set to {domain:w}')) +def set_domain_name(browser, domain): + system.set_domain_name(browser, domain) + + +@when(parsers.parse('I change the hostname to {hostname:w}')) +def change_hostname_to(browser, hostname): + system.set_hostname(browser, hostname) + + +@when(parsers.parse('I change the domain name to {domain:w}')) +def change_domain_name_to(browser, domain): + system.set_domain_name(browser, domain) + + +@when('I change the language to ') +def change_language(browser, language): + system.set_language(browser, language_codes[language]) + + +@then(parsers.parse('the hostname should be {hostname:w}')) +def hostname_should_be(browser, hostname): + assert system.get_hostname(browser) == hostname + + +@then(parsers.parse('the domain name should be {domain:w}')) +def domain_name_should_be(browser, domain): + assert system.get_domain_name(browser) == domain + + +@then('Plinth language should be ') +def plinth_language_should_be(browser, language): + assert system.check_language(browser, language_codes[language]) + + +@given('the list of snapshots is empty') +def empty_snapshots_list(browser): + system.delete_all_snapshots(browser) + + +@when('I manually create a snapshot') +def create_snapshot(browser): + system.create_snapshot(browser) + + +@then(parsers.parse('there should be {count:d} snapshot in the list')) +def verify_snapshot_count(browser, count): + num_snapshots = system.get_snapshot_count(browser) + assert num_snapshots == count diff --git a/functional_tests/support/__init__.py b/functional_tests/support/__init__.py new file mode 100644 index 000000000..b179d0577 --- /dev/null +++ b/functional_tests/support/__init__.py @@ -0,0 +1,25 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import configparser +import os + +config = configparser.ConfigParser() +config.read('config.ini') + +config['DEFAULT']['url'] = os.environ.get('FREEDOMBOX_URL', + config['DEFAULT']['url']) diff --git a/functional_tests/support/application.py b/functional_tests/support/application.py new file mode 100644 index 000000000..5e06385f8 --- /dev/null +++ b/functional_tests/support/application.py @@ -0,0 +1,130 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from time import sleep + +from support import config, interface +from support.service import eventually + +# unlisted apps just use the app_name as module name +app_module = { + 'ntp': 'datetime', + 'wiki': 'ikiwiki', + 'tt-rss': 'ttrss', +} + +app_checkbox_id = { + 'tor': 'id_tor-enabled', +} + +app_config_updating_text = { + 'tor': 'Tor configuration is being updated', +} + +default_url = config['DEFAULT']['url'] + + +def get_app_module(app_name): + module = app_name + if app_name in app_module: + module = app_module[app_name] + return module + + +def get_app_checkbox_id(app_name): + checkbox_id = 'id_is_enabled' + if app_name in app_checkbox_id: + checkbox_id = app_checkbox_id[app_name] + return checkbox_id + + +def install(browser, app_name): + interface.nav_to_module(browser, get_app_module(app_name)) + install = browser.find_by_value('Install') + if install: + install.click() + while browser.is_text_present('Installing') \ + or browser.is_text_present('Performing post-install operation'): + sleep(1) + sleep(2) + + +def _change_status(browser, app_name, change_status_to='enabled'): + interface.nav_to_module(browser, get_app_module(app_name)) + checkbox = browser.find_by_id(get_app_checkbox_id(app_name)) + checkbox.check() if change_status_to == 'enabled' else checkbox.uncheck() + browser.find_by_value('Update setup').click() + if app_name == app_config_updating_text: + wait_for_config_update(browser, app_name) + + +def enable(browser, app_name): + _change_status(browser, app_name, 'enabled') + + +def disable(browser, app_name): + _change_status(browser, app_name, 'disabled') + + +def wait_for_config_update(browser, app_name): + while browser.is_text_present(app_config_updating_text['tor']): + sleep(1) + + +def select_domain_name(browser, app_name, domain_name): + browser.visit('{}/plinth/apps/{}/setup/'.format(default_url, app_name)) + drop_down = browser.find_by_id('id_domain_name') + drop_down.select(domain_name) + browser.find_by_value('Update setup').click() + + +def configure_shadowsocks(browser): + """Configure shadowsocks client with some fake server details""" + browser.visit('{}/plinth/apps/shadowsocks/'.format(default_url)) + browser.find_by_id('id_server').fill('some.shadow.tunnel') + browser.find_by_id('id_password').fill('fakepassword') + browser.find_by_value('Update setup').click() + + +def modify_max_file_size(browser, size): + """Change the maximum file size of coquelicot to the given value""" + browser.visit('{}/plinth/apps/coquelicot/'.format(default_url)) + browser.find_by_id('id_max_file_size').fill(size) + browser.find_by_value('Update setup').click() + # Wait for the service to restart after updating maximum file size + eventually(message_or_setting_unchanged, + args=[browser, 'Maximum file size updated']) + + +def message_or_setting_unchanged(browser, message): + return browser.is_text_present(message) or browser.is_text_present( + 'Setting unchanged') + + +def get_max_file_size(browser): + """Get the maximum file size of coquelicot""" + browser.visit('{}/plinth/apps/coquelicot/'.format(default_url)) + return int(browser.find_by_id('id_max_file_size').value) + + +def modify_upload_password(browser, password): + """Change the upload password for coquelicot to the given value""" + browser.visit('{}/plinth/apps/coquelicot/'.format(default_url)) + browser.find_by_id('id_upload_password').fill(password) + browser.find_by_value('Update setup').click() + # Wait for the service to restart after updating password + eventually(browser.is_text_present, args=['Upload password updated']) diff --git a/functional_tests/support/interface.py b/functional_tests/support/interface.py new file mode 100644 index 000000000..9051b8d4f --- /dev/null +++ b/functional_tests/support/interface.py @@ -0,0 +1,101 @@ +# +# This file is part of Plinth-tester. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +from support import config + +sys_modules = [ + 'avahi', 'cockpit', 'config', 'datetime', 'diagnostics', 'firewall', + 'letsencrypt', 'monkeysphere', 'names', 'networks', 'power', 'snapshot', + 'upgrades', 'users' +] + +default_url = config['DEFAULT']['url'] + + +def login(browser, url, username, password): + browser.visit(url) + if browser.find_by_id('logout-nojs'): + return # already logged in + + login_button = browser.find_link_by_href('/plinth/accounts/login/') + if login_button: + login_button.first.click() + login_submit = browser.find_by_value('Login') + if login_button: + browser.fill('username', username) + browser.fill('password', password) + login_submit.click() + else: + browser.visit(default_url + '/plinth/firstboot/welcome') + browser.find_by_value('Start Setup').click() + create_admin_account(browser, 'tester', 'testingtesting') + login(browser, url, username, password) + + +def is_login_prompt(browser): + return all( + [browser.find_by_id('id_username'), + browser.find_by_id('id_password')]) + + +def nav_to_module(browser, module): + browser.find_link_by_href('/plinth/').first.click() + if module in sys_modules: + browser.find_link_by_href('/plinth/sys/').first.click() + browser.find_link_by_href('/plinth/sys/' + module + '/').first.click() + else: + browser.find_link_by_href('/plinth/apps/').first.click() + browser.find_link_by_href('/plinth/apps/' + module + '/').first.click() + + +def submit(browser): + browser.find_by_value('Submit').click() + + +def create_user(browser, name, password): + nav_to_module(browser, 'users') + browser.find_link_by_href('/plinth/sys/users/create/').first.click() + browser.find_by_id('id_username').fill(name) + browser.find_by_id('id_password1').fill(password) + browser.find_by_id('id_password2').fill(password) + browser.find_by_value('Create User').click() + + +def rename_user(browser, old_name, new_name): + nav_to_module(browser, 'users') + browser.find_link_by_href('/plinth/sys/users/' + old_name + + '/edit/').first.click() + browser.find_by_id('id_username').fill(new_name) + browser.find_by_value('Save Changes').click() + + +def delete_user(browser, name): + nav_to_module(browser, 'users') + browser.find_link_by_href('/plinth/sys/users/' + name + + '/delete/').first.click() + browser.find_by_value('Delete ' + name).click() + + +def is_user(browser, name): + nav_to_module(browser, 'users') + return browser.is_text_present(name) + + +def create_admin_account(browser, username, password): + browser.find_by_id('id_username').fill(username) + browser.find_by_id('id_password1').fill(password) + browser.find_by_id('id_password2').fill(password) + browser.find_by_value('Create Account').click() diff --git a/functional_tests/support/service.py b/functional_tests/support/service.py new file mode 100644 index 000000000..1dee6b8d3 --- /dev/null +++ b/functional_tests/support/service.py @@ -0,0 +1,55 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from time import sleep + +from support import interface + +# unlisted services just use the service_name as module name +service_module = { + 'ntp': 'datetime', +} + + +def get_service_module(service_name): + module = service_name + if service_name in service_module: + module = service_module[service_name] + return module + + +def is_running(browser, service_name): + interface.nav_to_module(browser, get_service_module(service_name)) + return browser.is_text_present('is running') + + +def is_not_running(browser, service_name): + interface.nav_to_module(browser, get_service_module(service_name)) + return browser.is_text_present('is not running') + + +def eventually(function, args, timeout=30): + """Execute a function returning a boolean expression till it returns + True or a timeout is reached""" + counter = 1 + while counter < timeout: + if function(*args): + return True + else: + counter += 1 + sleep(1) + return False diff --git a/functional_tests/support/site.py b/functional_tests/support/site.py new file mode 100644 index 000000000..b9b6a8367 --- /dev/null +++ b/functional_tests/support/site.py @@ -0,0 +1,59 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from time import sleep + +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.keys import Keys + +from support import config +from support.service import eventually + +# unlisted sites just use '/' + site_name as url +site_url = { + 'wiki': '/ikiwiki', + 'jsxc': '/plinth/apps/jsxc/jsxc/', + 'cockpit': '/_cockpit/' +} + + +def get_site_url(site_name): + url = '/' + site_name + if site_name in site_url: + url = site_url[site_name] + return url + + +def is_available(browser, site_name): + browser.visit(config['DEFAULT']['url'] + get_site_url(site_name)) + sleep(3) + browser.reload() + return browser.title != '404 Not Found' + + +def access_url(browser, site_name): + browser.visit(config['DEFAULT']['url'] + get_site_url(site_name)) + + +def verify_coquelicot_upload_password(browser, password): + browser.visit(config['DEFAULT']['url'] + '/coquelicot') + browser.find_by_id('upload_password').fill(password) + actions = ActionChains(browser.driver) + actions.send_keys(Keys.RETURN) + actions.perform() + assert eventually(browser.is_element_present_by_css, + args=['div[style*="display: none;"]']) diff --git a/functional_tests/support/system.py b/functional_tests/support/system.py new file mode 100644 index 000000000..501d7b2a9 --- /dev/null +++ b/functional_tests/support/system.py @@ -0,0 +1,88 @@ +# +# This file is part of Plinth-tester. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from support import config + +from .interface import nav_to_module, submit + +config_page_title_language_map = { + 'da': 'Generel Konfiguration', + 'de': 'Allgemeine Konfiguration', + 'es': 'Configuración general', + 'fr': 'Configuration générale', + 'nb': 'Generelt oppsett', + 'nl': 'Algemene Instellingen', + 'pl': 'Ustawienia główne', + 'pt': 'Configuração Geral', + 'ru': 'Общие настройки', + 'sv': 'Allmän Konfiguration', + 'te': 'సాధారణ ఆకృతీకరణ', + 'tr': 'Genel Yapılandırma', + 'zh-hans': '常规配置', +} + + +def get_hostname(browser): + nav_to_module(browser, 'config') + return browser.find_by_id('id_configuration-hostname').value + + +def set_hostname(browser, hostname): + nav_to_module(browser, 'config') + browser.find_by_id('id_configuration-hostname').fill(hostname) + submit(browser) + + +def get_domain_name(browser): + nav_to_module(browser, 'config') + return browser.find_by_id('id_configuration-domainname').value + + +def set_domain_name(browser, domain_name): + nav_to_module(browser, 'config') + browser.find_by_id('id_configuration-domainname').fill(domain_name) + submit(browser) + + +def set_language(browser, language_code): + nav_to_module(browser, 'config') + browser.find_by_xpath( + '//select[@id="id_configuration-language"]//option[@value="' \ + + language_code + '"]' + ).first.click() + submit(browser) + + +def check_language(browser, language_code): + nav_to_module(browser, 'config') + return browser.title == config_page_title_language_map[language_code] + + +def delete_all_snapshots(browser): + browser.visit(config['DEFAULT']['url'] + '/plinth/sys/snapshot/all/delete') + browser.find_by_value('Delete Snapshots').click() + + +def create_snapshot(browser): + nav_to_module(browser, 'snapshot') + browser.find_by_value('Create Snapshot').click() + + +def get_snapshot_count(browser): + nav_to_module(browser, 'snapshot') + # Subtract 1 for table header + return len(browser.find_by_xpath('//tr')) - 1 diff --git a/functional_tests/test_plinth.py b/functional_tests/test_plinth.py new file mode 100644 index 000000000..3012b7d14 --- /dev/null +++ b/functional_tests/test_plinth.py @@ -0,0 +1,26 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from pytest_bdd import scenarios + +from step_definitions.application import * +from step_definitions.interface import * +from step_definitions.service import * +from step_definitions.site import * +from step_definitions.system import * + +scenarios('features') diff --git a/functional_tests/todo.org b/functional_tests/todo.org new file mode 100644 index 000000000..f69c1274b --- /dev/null +++ b/functional_tests/todo.org @@ -0,0 +1,60 @@ +* Feature: Users and Groups +** TODO Scenario: Add user to wiki group +** TODO Scenario: Remove user from wiki group +** TODO Scenario: Set user SSH key +** TODO Scenario: Clear user SSH key +** TODO Scenario: Make user inactive +** TODO Scenario: Make user active +** TODO Scenario: Change user password + +* Feature: Software Upgrades +** TODO Scenario: Disable automatic upgrades +** TODO Scenario: Enable automatic upgrades + +* Feature: Date & Time +** TODO Scenario: Change timezone + +* Feature: Help +** TODO Scenario: Visit the wiki +** TODO Scenario: Visit the mailing list +** TODO Scenario: Visit the IRC channel +** TODO Scenario: View the manual +** TODO Scenario: View the about page + +* Feature: Monkeysphere +** TODO Import key + +* Feature: Tor +** TODO Scenario: Disable Tor Hidden Service +** TODO Scenario: Enable Tor Hidden Service +** TODO Scenario: Disable software package download over Tor +** TODO Scenario: Enable software package download over Tor + +* Feature: Bookmarks +** TODO Enable/Disable + +* Feature: Chat Server +** TODO Check service +** TODO Check domain name display + +* Feature: Dynamic DNS Client +** TODO Scenario: Configure GnuDIP service +** TODO Scenario: Configure noip.com service +** TODO Scenario: Configure selfhost.bz service +** TODO Scenario: Configure freedns.afraid.org service +** TODO Scenario: Configure other update URL service + +* Feature: News Feed Reader +** TODO Enable/Disable + +* Feature: Public Visibility +** TODO Scenario: Enable PageKite +** TODO Scenario: Disable PageKite +** TODO Scenario: Enable standard services +** TODO Scenario: Disable standard services +** TODO Scenario: Add custom service +** TODO Scenario: Delete custom service + +* Feature: Wiki and Blog +** TODO Create wiki +** TODO Delete wiki