Skip to content
This repository has been archived by the owner on Apr 24, 2024. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:ElektraInitiative/PermaplanT into…
Browse files Browse the repository at this point in the history
… #638_shade_layer_endpoint
  • Loading branch information
kitzbergerg committed Jul 31, 2023
2 parents ef57c22 + 2a06823 commit 765263f
Show file tree
Hide file tree
Showing 22 changed files with 419 additions and 136 deletions.
16 changes: 10 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ run-frontend: build-frontend ## Build & Run frontend.
run-backend: build-backend ## Build & Run backend.
cd backend && make run

.PHONY: run-reset-backend
run-reset-backend: build-backend reset-database ## Build & Run backend with a database reset.
cd backend && make run

.PHONY: run-mdbook
run-mdbook: build-mdbook ## Build & Run mdbook.
mdbook serve --open
Expand All @@ -24,7 +28,6 @@ run-storybook: build-storybook ## Build & Run storybook.
# TEST

.PHONY: test
test: test-frontend test-backend test-mdbook test-e2e ## Test everything.
test: test-frontend test-backend test-mdbook test-e2e ## Test everything.
pre-commit

Expand Down Expand Up @@ -75,8 +78,8 @@ build-storybook: install generate-api-types ## Build storybook.
scraper-start-full: ## Scrape and then insert scraped data into the database.
cd scraper && npm install && mkdir -p data && npm run start:full

.PHONY: scraper-insert
scraper-insert: ## Insert scraped data into the database.
.PHONY: insert-scraper
insert-scraper: ## Insert scraped data into the database.
cd scraper && npm install && mkdir -p data && npm run insert

.PHONY: migration
Expand All @@ -91,9 +94,10 @@ migration-redo: ## Run down.sql and then up.sql for most recent migrations.
migration-redo-a: ## Run down.sql and then up.sql for all migrations.
cd backend && make migration-redo-a

.PHONY: database-reset
database-reset: ## Reset diesel database.
cd backend && make database-reset
.PHONY: reset-database
reset-database: ## Reset diesel database AND insert data again.
cd backend && make reset-database
$(MAKE) insert-scraper

.PHONY: generate-type-doc
generate-type-doc: ## Generate typedoc.
Expand Down
4 changes: 2 additions & 2 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ migration-redo: ## Run down.sql and then up.sql for most recent migrations.
migration-redo-a: ## Run down.sql and then up.sql for all migration.
LC_ALL=C diesel migration redo -a

.PHONY: database-reset
database-reset: ## Reset diesel database.
.PHONY: reset-database
reset-database: ## Reset diesel database.
diesel database reset

.PHONY: clean
Expand Down
8 changes: 4 additions & 4 deletions doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Syntax: `- short text describing the change _(Your Name)_`
- _()_
- _()_
- _()_
- _()_
- basic baseLayerConfig validation - fixed bugfix/734-base-layer-image-gets-lost _(Samuel)_
- _()_
- _()_
- _()_
Expand All @@ -58,7 +58,7 @@ Syntax: `- short text describing the change _(Your Name)_`
- _()_
- _()_
- _()_
- _()_
- E2E: Add plant search to e2e tests _(4ydan)_
- Map: Remove Step/History info #745 _(4ydan)_
- CI: Add most of pre-commit hooks to sanity stage #736 _(4ydan)_
- _()_
Expand Down Expand Up @@ -88,8 +88,8 @@ Syntax: `- short text describing the change _(Your Name)_`
- CI: Optimized mdbook docker image #585 _(4ydan)_
- CI: Test `diesel` migrations in PR and master #134 _(4ydan)_
- CI: Remove cargo check, put clippy before doc #688 _(4ydan)_
- CI: login/logout e2e tests #625 _(4ydan)_
- CI: map creation e2e tests #707 _(4ydan)_
- E2E: login/logout e2e tests #625 _(4ydan)_
- E2E: map creation e2e tests #707 _(4ydan)_

## 0.2.3 - 18.07.2023 (165 commits)

Expand Down
2 changes: 2 additions & 0 deletions e2e/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 120
136 changes: 84 additions & 52 deletions e2e/ReadMe.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
# PermaplanT E2E tests with Playwright for python and pytest-bdd

## Guidelines

link TBD

## Directory structure

```sh
├── features Bdd features
├── features Gherkin features
├── pages Page object models
├── steps The actual tests
```
Expand Down Expand Up @@ -44,7 +40,7 @@ To run all tests with as many processes as your computer has CPU cores
python3 -m pytest --numprocesses auto
```

To run retries on your tests
To perform retries on your tests

```sh
python3 -m pytest --retries 3
Expand All @@ -62,54 +58,77 @@ To capture video when testing
python3 -m pytest --video on
```

To capture video only on failure
To capture video only on test failures

```sh
python3 -m pytest --video retain-on-failure
```

If there is something suspicious going on.
If there is something suspicious going on

```sh
set -e; for i in `seq 10`;do echo "Running iteration $i"; python -m pytest -n auto; done
```

### Cleanup

Currently you need to reset your database after running the tests once, since the tests do not cleanup after execution
and many maps can't be recreated.

## ENV Variables

### TEST_URL

The url under which the tests look for the website.
Defaults to `localhost:5173`

### USERNAME

The username for loging into the website
Defaults to `Adi`

### PASSWORD

The password for loging into the website
Defaults to `1234`

## How to write tests

### Before we start
### Guidelines

Before developing E2E tests make sure you have read the [guidelines](https://github.com/ElektraInitiative/PermaplanT/doc/guidelines/frontend-testing.md).

### General rules

- Be consistent and minimalistic.
- Use the same vocabulary as playwright (click, visible, etc.)
- Avoid using multiple different verbs for the same actions, keep your vocabulary small and precise.
Use Playwrights vocabulary.
This means to prefix methods with the actions from playwright (e.g when calling `xyz.click()` from playwright inside that method, name the method `click_xyz()` not a mix of press, click, push etc.)
- Dont indent more than one time.
- Dont make a complicated call stack higher than two from a page object.
- Every test should be independent from other tests (concurrency).
- Name inputs on the page SUT (System under Test).
- Use the same vocabulary as playwright (click, visible, etc.)
- Avoid using multiple different verbs for the same actions, keep your vocabulary small and precise.
- Name inputs or objects you create SUT (System under Test) so they are clearly marked as test artifacts.

### A typical workflow

The usual workflow consists of:
The usual workflow consists of three steps:

- write a feature inside `/features` with one or multiple scenarios.
- implement the page object if it doesn't exist yet in `/pages`.
- write the steps that are connected to the gherkin syntax with pytest-bdd inside `/steps`.
1. (`/features`) write a feature inside with one or multiple scenarios.
2. (`/pages`) implement the page object (if it doesn't already exist) in .
3. (`/steps`) create a function for each scenario step using the page objects.

Lets go over these steps one by one.
Lets go over these steps in more detail.

### Writing .feature files

This is an optional step, since it is possible that someone else has already written it for you.
If not, make sure to have a good understanding about Gherkin.
Make sure to have a solid understanding about the Gherkin syntax, so you dont fall into common pitfalls.
Usually the syntax is not very strict but poor Gherkin will cascade into the later processes of testing and make everything more complicated.

Avoid the following:

- Multiple Given, When or Thens
- Too verbose/detailed
- Multiple Given, When or Thens in one scenario
- Too verbose/detailed sentences
- Describing the implementation (press, enter, type etc.)

If you are using more than one "Given-When-Then" You should probably split up your scenario.
Expand All @@ -122,58 +141,71 @@ Here are some good guidelines.

- https://cucumber.io/docs/bdd/better-gherkin/

### Implementing a pageobject
### Implementing Page Objects

The tests are performed with pageobjects, where the selectors and functions are inside a class.
Have a look [here](https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models/) for more information about page object models.
The Pagefactory does not exist yet.
ALL tests are performed exclusively with pageobjects, where the selectors and functions are defined inside a class.
If you are not familiar with the page object model look [here](https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models/) if you are familiar with the page object model, still have a look : ).
The pages are all instantiated globally inside `conftest.py` and injected into the step functions as arguments.
When using Playwright for python we are interacting with Playwrights [page class](https://playwright.dev/docs/api/class-page), which is basically representing a tab.
This Playwright page class is then instantiated as a singleton and used globally across all OUR page objects to perform actions on the webapp.
It is crucial to pay close attention what the side effects of OUR page object methods are as some methods will change the webpage and you will have to use a different page object after.

### Creating the steps with pytest-bdd
**The Pagefactory does not exist yet** due to implementation difficulty and for flexible design at the start.
Just use the fixtures instead as they basically do the same.

TBD
### Creating steps with pytest-bdd

### Helpful tools
When implementing the test steps you should ONLY be invoking methods of page objects to reach your goal.
Implementing complex functionalities inside tests should be STRICTLY avoided and implemented into the corresponding page objects.

#### Playwright Test generator
Following the [testing strategy](https://github.com/ElektraInitiative/PermaplanT/tree/master/doc/tests) from PermaplanT:

This is the fastest way to generate some prototypes and get some locators.
Launch the playwright codegen from the terminal and create code that you can later refactor into your pageobjects.
- Given should ARRANGE
- When should ACT
- Then should ASSERT

```sh
playwright codegen http://localhost:5173/
```
This will ensure the tests are simple and don't perform too much magic all over the place.

More information [here](https://playwright.dev/python/docs/codegen).
### Small note on pytest-xdist

## Other Documentations
[Pytest-xdist](https://pytest-xdist.readthedocs.io/en/latest/distribution.html) is used to parallelize the tests.
This is done to reduce the pipeline time, since many tests could make this stage take a long time at the end.
When developing tests always keep in mind that each scenario is running on a separate core and should not depend on results of other scenarios.
A scenario outlet will also start each scenario with one core.
Try to avoid too complex parallelization and we also probably dont need to assign and manage worker groups with additional xdist syntax.

### Playwright for Python Documentation
### Helpful tools

[https://playwright.dev/python/docs/intro](https://playwright.dev/python/docs/intro)
#### [Playwright Test generator](https://playwright.dev/python/docs/codegen)

### pytest-bdd Documentation
This is the fastest way to get some locators and see how playwright would write the tests.
This should under no circumstances be copy pasted and put into a test but rather help you write the page objects.

[https://pypi.org/project/pytest-bdd/](https://pypi.org/project/pytest-bdd/)
Launch the playwright codegen from the terminal and create code that you can later refactor into your pageobjects.

## Features
```sh
playwright codegen http://localhost:5173/
```

### Video capturing
#### Reporting

- python3 -m pytest e2e/ --video on
- python3 -m pytest --cucumberjson=./cucumber.json
- python3 -m pytest --gherkin-terminal-reporter

only on failure
#### Pytest-bdd Test generator

- python3 -m pytest e2e/ --video retain-on-failure
- pytest-bdd generate features/login_logout.feature > steps/test_some_feature.py

### Reporting
or only missing stuff

- python3 -m pytest --cucumberjson=./test.json
- python3 -m pytest --gherkin-terminal-reporter
- pytest --generate-missing --feature features steps/

### Testcode generator
## Other Documentations

- pytest-bdd generate features/login_logout.feature > tests/test_some.py
### Playwright for Python Documentation

or only missing stuff
[https://playwright.dev/python/docs/intro](https://playwright.dev/python/docs/intro)

### pytest-bdd Documentation

- pytest --generate-missing --feature features tests/
[https://pypi.org/project/pytest-bdd/](https://pypi.org/project/pytest-bdd/)
57 changes: 52 additions & 5 deletions e2e/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
import os
import pytest
from .pages.home import HomePage
from .pages.login import LoginPage
from .pages.maps.management import MapManagementPage
from .pages.maps.create import MapCreatePage
from .pages.maps.edit import MapEditPage
from playwright.sync_api import Page
from e2e.pages.home import HomePage
from e2e.pages.login import LoginPage
from e2e.pages.maps.management import MapManagementPage
from e2e.pages.maps.create import MapCreatePage
from e2e.pages.maps.edit import MapEditPage
from e2e.pages.maps.planting import MapPlantingPage


"""
Commonly used workflows and util
"""


def worker_id():
"""
When executing tests in parallel, for example with
`pytest -n auto`, each worker has an id that can be
used in tests. If executed with one core the id is
an empty string.
"""
worker_id = ""
if "PYTEST_XDIST_WORKER" in os.environ:
# Get the value of the environment variable
worker_id = os.environ["PYTEST_XDIST_WORKER"]
return worker_id


def login(hp: HomePage, lp: LoginPage) -> HomePage:
"""
Login to permaplant and close the login notification.
Returns a homepage object
"""
hp.load()
hp.login_button_is_visible()
hp.click_login_button()
lp.fill_username()
lp.fill_password()
lp.click_sign_in()
hp.verify()
hp.close_alert()
return hp


"""
Global page objects pytest fixtures
"""


@pytest.fixture
Expand All @@ -30,3 +72,8 @@ def mcp(page: Page) -> MapCreatePage:
@pytest.fixture
def mep(page: Page) -> MapEditPage:
return MapEditPage(page)


@pytest.fixture
def mpp(page: Page) -> MapPlantingPage:
return MapPlantingPage(page)
5 changes: 2 additions & 3 deletions e2e/features/login_logout.feature
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
Feature: PermaplanT Login and Logout Functionality
As a user
I want to be able to login and logout successfully
As a user I want to be able to login and logout successfully

Scenario: Successful Login
Given I am on the PermaplanT homepage
When I click the login button
And I enter valid credentials Adi 1234
And I enter valid credentials
And I click the submit button
Then I should be redirected back to the homepage
And I should see a welcome message
Expand Down
Loading

0 comments on commit 765263f

Please sign in to comment.