Skip to content

Commit

Permalink
Working functional test, with VCR, xvfb, Makefile and cassettes.
Browse files Browse the repository at this point in the history
  • Loading branch information
ntoll committed Feb 17, 2020
1 parent ff8a114 commit c969282
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 23 deletions.
18 changes: 13 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,27 @@ clean: ## Clean the workspace of generated resources
find . -name __pycache__ -print0 | xargs -0 rm -rf

TESTS ?= tests
FTESTS ?= tests/functional
TESTOPTS ?= -v
.PHONY: test
test: ## Run the application tests in parallel (for rapid development)
@TEST_CMD="python -m pytest -v -n 4 --cov-config .coveragerc --cov-report html --cov-report term-missing --cov=securedrop_client --cov-fail-under 100 $(TESTOPTS) $(TESTS)" ; \
@TEST_CMD="python -m pytest -v -n 4 --ignore=$(FTESTS) --cov-config .coveragerc --cov-report html --cov-report term-missing --cov=securedrop_client --cov-fail-under 100 $(TESTOPTS) $(TESTS)" ; \
if command -v xvfb-run > /dev/null; then \
xvfb-run $$TEST_CMD ; else \
xvfb-run -a $$TEST_CMD ; else \
$$TEST_CMD ; fi

.PHONY: test-random
test-random: ## Run the application tests in random order
@TEST_CMD="python -m pytest -v --random-order-bucket=global --cov-config .coveragerc --cov-report html --cov-report term-missing --cov=securedrop_client --cov-fail-under 100 $(TESTOPTS) $(TESTS)" ; \
@TEST_CMD="python -m pytest -v --ignore=$(FTESTS) --random-order-bucket=global --cov-config .coveragerc --cov-report html --cov-report term-missing --cov=securedrop_client --cov-fail-under 100 $(TESTOPTS) $(TESTS)" ; \
if command -v xvfb-run > /dev/null; then \
xvfb-run $$TEST_CMD ; else \
xvfb-run -a $$TEST_CMD ; else \
$$TEST_CMD ; fi

.PHONY: test-functional
test-functional : ## Run the functional tests in random order.
@TEST_CMD="python -m pytest -v --random-order-bucket=global $(TESTOPTS) $(FTESTS)" ; \
if command -v xvfb-run > /dev/null; then \
xvfb-run -a $$TEST_CMD ; else \
$$TEST_CMD ; fi

.PHONY: lint
Expand All @@ -67,7 +75,7 @@ bandit: ## Run bandit with medium level excluding test-related folders
bandit -ll --recursive . --exclude ./tests,./.venv

.PHONY: check
check: clean bandit lint mypy test-random ## Run the full CI test suite
check: clean bandit lint mypy test-random test-functional ## Run the full CI test suite

.PHONY: update-pip-requirements
update-pip-requirements: ## Updates all Python requirements files via pip-compile.
Expand Down
2 changes: 1 addition & 1 deletion securedrop_client/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def show_login(self, error: str = ''):
self.login_dialog.reset()
if error:
self.login_dialog.error(error)
self.login_dialog.exec()
self.login_dialog.show()

def show_login_error(self, error):
"""
Expand Down
35 changes: 35 additions & 0 deletions tests/functional/cassettes/test_login_as_journalist.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
interactions:
- request:
body: '{"username": "journalist", "passphrase": "correct horse battery staple
profanity oil chewy", "one_time_code": "493941"}'
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
Content-Length:
- '119'
User-Agent:
- python-requests/2.20.0
method: POST
uri: http://localhost:8081/api/v1/token
response:
body:
string: "{\n \"expiration\": \"2020-02-17T21:45:20.569829Z\", \n \"journalist_first_name\":
null, \n \"journalist_last_name\": null, \n \"journalist_uuid\": \"1eff0bb5-289b-4105-b3c1-ec93d5704db6\",
\n \"token\": \"eyJhbGciOiJIUzI1NiIsImlhdCI6MTU4MTk0NzEyMCwiZXhwIjoxNTgxOTc1OTIwfQ.eyJpZCI6MX0.4h6pPAy4bl58Yc5Gim1LVdhvTYELvMfIWoZPlBR2ZZI\"\n}\n"
headers:
Content-Length:
- '317'
Content-Type:
- application/json
Date:
- Mon, 17 Feb 2020 13:45:20 GMT
Server:
- Werkzeug/0.16.0 Python/3.5.2
status:
code: 200
message: OK
version: 1
32 changes: 17 additions & 15 deletions tests/functional/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@
HOSTNAME = "http://localhost:8081/"
USERNAME = "journalist"
PASSWORD = "correct horse battery staple profanity oil chewy"
OTP = "783682"


def get_safe_tempdir():
return tempfile.TemporaryDirectory()


def get_test_context(sdc_home, mocker):
def get_test_context(sdc_home, qtbot):
"""
Returns a tuple containing a Window instance and a Controller instance that
have been correctly set up and isolated from any other instances of the
Expand All @@ -46,9 +45,10 @@ def get_test_context(sdc_home, mocker):
False, False)
# Link the gui and controller together.
gui.controller = controller
# Ensure Qt widgets are properly closed after test run.
qtbot.addWidget(gui)
# Et Voila...
with mocker.patch("securedrop_client.logic.sdclientapi") as mock_api:
return (gui, controller, mock_api)
return (gui, controller)


def create_dev_data(sdc_home, session_maker):
Expand Down Expand Up @@ -89,19 +89,21 @@ def test_login_ensure_errors_displayed(qtbot, mocker):
assert actual == expected


@pytest.mark.vcr()
def test_login_as_journalist(qtbot, mocker):
"""
The app is visible if the user logs in with apparently correct credentials.
"""
tempdir = get_safe_tempdir() # Once out of scope, is deleted.
gui, controller, mock_api = get_test_context(tempdir, mocker)
login_dialog = LoginDialog(gui)
login_dialog.setup(controller)
gui.login_dialog = login_dialog
login_dialog.show()
assert login_dialog.error_bar.error_status_bar.text() == ""
qtbot.keyClicks(login_dialog.username_field, USERNAME)
qtbot.keyClicks(login_dialog.password_field, PASSWORD)
qtbot.keyClicks(login_dialog.tfa_field, OTP)
qtbot.mouseClick(login_dialog.submit, Qt.LeftButton)
assert gui.isVisible()
gui, controller = get_test_context(tempdir, qtbot)
gui.setup(controller)
assert gui.login_dialog.error_bar.error_status_bar.text() == ""
qtbot.keyClicks(gui.login_dialog.username_field, USERNAME)
qtbot.keyClicks(gui.login_dialog.password_field, PASSWORD)
qtbot.keyClicks(gui.login_dialog.tfa_field, "493941")
with qtbot.waitSignal(controller.authentication_state, timeout=10000):
qtbot.mouseClick(gui.login_dialog.submit, Qt.LeftButton)
# The main window is visible (indicating a successful login).
assert gui.isVisible()
# The login box isn't visible.
assert gui.login_dialog is None
4 changes: 2 additions & 2 deletions tests/gui/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_show_login(mocker):

mock_ld.assert_called_once_with(w)
w.login_dialog.reset.assert_called_once_with()
w.login_dialog.exec.assert_called_once_with()
w.login_dialog.show.assert_called_once_with()


def test_show_login_with_error_message(mocker):
Expand All @@ -124,7 +124,7 @@ def test_show_login_with_error_message(mocker):

mock_ld.assert_called_once_with(w)
w.login_dialog.reset.assert_called_once_with()
w.login_dialog.exec.assert_called_once_with()
w.login_dialog.show.assert_called_once_with()
w.login_dialog.error.assert_called_once_with('this-is-an-error-message-to-show-on-login-window')


Expand Down

0 comments on commit c969282

Please sign in to comment.