Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[shopsys] acceptance tests now use test prefix for css classes #2179

Merged
merged 5 commits into from Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,26 @@
# Best Practices for Writing Acceptance Tests
grossmannmartin marked this conversation as resolved.
Show resolved Hide resolved

## Use prepared methods from `StrictWebDriver` that are available via `AcceptanceTester`
We have prepared many useful methods like `fillFieldByName` that actually do multiple actions to improve acceptance tests dependability.
These methods find the element on the page, scroll to it, move mouse over it and then do appropriate action.
They are intended to imitate human behaviour as much as possible.

## Separate common behaviour to page objects
Use page object that extends from `AbstractPage` for common actions.
For example: we do test login in several tests.
Without `LoginPage` its logic would be duplicated in several tests, and it would be much harder to maintain any changes.

## Use test prefix for CSS classes
Test prefix helps to differentiate common CSS classes used by frontend developers from those used for tests, so they are not accidentally removed during design changes.

## Use translations for testing text appearance
Texts on a frontend can be changed from time to time or language can be changed during development.
Such change would lead to many errors reported by acceptance tests.
We are preventing such errors by using prepared methods in `AcceptanceTester` like `seeTranslationFrontend` or `seeTranslationAdminInCss`.

## Use calculation of price by exchange rate for testing prices
Testing price is not common in acceptance tests, but it can be required from time to time.
By default, there are several tests checking right price displayed in popup window after products is added to cart.
As currency can be easily changed on a domain, we wanted to prevent error caused by such change.
`AcceptanceTester` includes two useful methods `getPriceWithVatConvertedToDomainDefaultCurrency` and `getFormattedPriceWithCurrencySymbolRoundedByCurrencyOnFrontend` for such cases.
See `CartBoxPage` to get some inspiration.
TomasLudvik marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions docs/automated-testing/index.md
@@ -0,0 +1,5 @@
# Automated Testing

* [Introduction to Automated Testing](../automated-testing/introduction-to-automated-testing.md)
* [Running Acceptance Tests](../automated-testing/running-acceptance-tests.md)
* [Best Practices for Writing Acceptance Tests](../automated-testing/best-practices-for-writing-acceptance-tests.md)
@@ -1,4 +1,4 @@
# Automated Testing
# Introduction to Automated Testing

Testing is a crucial part of development and maintenance of reliable software.
For this reason Shopsys Framework comes with 5 types of automated tests:
Expand Down Expand Up @@ -82,7 +82,7 @@ Notice that test method names describe the tested scenario. Also, notice that ea
When a test fails it provides detailed feedback to the developer.

You can create similar unit tests anywhere in your directory `tests/Unit/`.
If they are named with a prefix `Test` and are extending `\PHPUnit\Framework\TestCase` they will be executed during the [`tests` Phing target](./console-commands-for-application-management-phing-targets.md#tests).
If they are named with a prefix `Test` and are extending `\PHPUnit\Framework\TestCase` they will be executed during the [`tests` Phing target](../introduction/console-commands-for-application-management-phing-targets.md#tests).

### Functional tests
Even when all parts are working it is not guaranteed they work well together. Mocking can still be used for isolation when appropriate.
Expand Down
5 changes: 5 additions & 0 deletions docs/automated-testing/navigation.yml
@@ -0,0 +1,5 @@
arrange:
- index.md
- introduction-to-automated-testing.md
- running-acceptance-tests.md
- best-practices-for-writing-acceptance-tests.md
2 changes: 2 additions & 0 deletions docs/index.md
Expand Up @@ -34,6 +34,8 @@ If you are struggling with Docker, [Docker Troubleshooting](./docker/docker-trou
* Information about the frontend GraphQL API dedicated for connecting external storefront or mobile app.
* [Extensibility](./extensibility/index.md)
* How to customize the behavior of Shopsys Framework to suit your needs.
* [Automated Testing](./automated-testing/index.md)
* Information about available types of tests and how to run them.
* [Orchestration](./kubernetes/index.md)
* Orchestration, Google Cloud, and Kubernetes-related articles and guides.
* [Contributing](./contributing/index.md)
Expand Down
Expand Up @@ -213,7 +213,7 @@ Creates a new test database with demo data and runs all tests except acceptance
#### tests-acceptance
Runs acceptance tests. Running Selenium server is required.

More on this topic can be found in [Running Acceptance Tests](./running-acceptance-tests.md).
More on this topic can be found in [Running Acceptance Tests](../automated-testing/running-acceptance-tests.md).

#### tests-acceptance-single
Runs single acceptance test. Fastest way to run test without need of running whole acceptance suit
Expand Down
2 changes: 1 addition & 1 deletion docs/introduction/faq-and-common-issues.md
Expand Up @@ -150,7 +150,7 @@ Yes we have, you can easily use [`djfarrelly/MailDev`](https://github.com/djfarr
See [Outgoing emails](https://github.com/djfarrelly/MailDev#outgoing-email) in the documentation of the library for more information.*

## Can I see what is really happening in the Codeception acceptance tests when using Docker?
Yes, you can! Check [the quick guide](./running-acceptance-tests.md#how-to-watch-what-is-going-on-in-the-selenium-browser).
Yes, you can! Check [the quick guide](../automated-testing/running-acceptance-tests.md#how-to-watch-what-is-going-on-in-the-selenium-browser).

## Why is there a faked PHP 7.4.1 platform in the Composer config?
As a general rule, packages and libraries that depend on PHP 7.4.1 will work as expected even on any higher 7.x version, but not vice versa.
Expand Down
1 change: 0 additions & 1 deletion docs/introduction/index.md
@@ -1,7 +1,6 @@
# Introduction

* [Console Commands for Application Management (Phing Targets)](./console-commands-for-application-management-phing-targets.md)
* [Automated Testing](./automated-testing.md)
* [Basics About Package Architecture](./basics-about-package-architecture.md)
* [Basic and Demo Data During Application Installation](./basic-and-demo-data-during-application-installation.md)
* [Database Migrations](./database-migrations.md)
Expand Down
2 changes: 0 additions & 2 deletions docs/introduction/navigation.yml
Expand Up @@ -2,7 +2,6 @@ arrange:
- index.md
- start-building-your-application.md
- console-commands-for-application-management-phing-targets.md
- automated-testing.md
- basics-about-package-architecture.md
- basic-and-demo-data-during-application-installation.md
- database-migrations.md
Expand All @@ -20,7 +19,6 @@ arrange:
- friendly-url.md
- front-end-breadcrumb-navigation.md
- autocompletion-for-phing-targets.md
- running-acceptance-tests.md
- required-php-configuration.md
- shopsys-framework-on-docker.md
- monorepo.md
Expand Down
1 change: 1 addition & 0 deletions docs/navigation.yml
Expand Up @@ -10,5 +10,6 @@ arrange:
- backend-api
- frontend-api
- extensibility
- automated-testing
- kubernetes
- contributing
4 changes: 2 additions & 2 deletions packages/framework/assets/js/admin/utils/Window.js
Expand Up @@ -61,7 +61,7 @@ export default class Window {

this.$window.append($windowContent);
if (this.options.buttonClose) {
const $windowButtonClose = $('<a href="#" class="window-button-close window__close js-window-button-close" title="' + Translator.trans('Close (Esc)') + '">X</a>');
const $windowButtonClose = $('<a href="#" class="window-button-close window__close test-window-button-close" title="' + Translator.trans('Close (Esc)') + '">X</a>');
$windowButtonClose
.on('click.window', _this.options.eventClose)
.on('click.windowClose', function () {
Expand Down Expand Up @@ -93,7 +93,7 @@ export default class Window {
}

if (this.options.buttonContinue) {
const $windowButtonContinue = $('<a href="" class="window__actions__btn window-button-continue btn"></a>');
const $windowButtonContinue = $('<a href="" class="window__actions__btn window-button-continue btn test-window-button-continue"></a>');
$windowButtonContinue
.text(this.options.textContinue)
.attr('href', this.options.urlContinue)
Expand Down
Expand Up @@ -8,15 +8,15 @@
>

<div class="box-advanced-search__item__content">
{{ form_widget(ruleForm.subject, { attr: { class: 'js-advanced-search-rule-subject' }, isSimple: true}) }}
{{ form_widget(ruleForm.subject, { attr: { class: 'js-advanced-search-rule-subject test-advanced-search-rule-subject' }, isSimple: true}) }}
</div>

<div class="box-advanced-search__item__content">
{{ form_widget(ruleForm.operator, { attr: { class: 'js-advanced-search-rule-operator' }, isSimple: true}) }}
</div>

<div class="box-advanced-search__item__content">
<span class="js-advanced-search-rule-value">
<span class="js-advanced-search-rule-value test-advanced-search-rule-value">
{{ form_widget(ruleForm.value, { isSimple: true }) }}
</span>
</div>
Expand Down
Expand Up @@ -6,7 +6,7 @@

{% block grid_inline_edit_add_button %}
<div class="wrap-bar">
<a href="#" class="btn btn--primary btn--plus wrap-bar__btn js-inline-edit-add">
<a href="#" class="btn btn--primary btn--plus wrap-bar__btn js-inline-edit-add test-inline-edit-add">
<i class="btn__icon">+</i>
{{ 'Create pricing group'|trans }}
</a>
Expand Down
Expand Up @@ -7,15 +7,15 @@
{{- isTemplate ? ' id="js-advanced-search-rule-template"' : '' -}}
>
<div class="box-advanced-search__item__content">
{{ form_widget(ruleForm.subject, { attr: { class: 'js-advanced-search-rule-subject' }, isSimple: true} ) }}
{{ form_widget(ruleForm.subject, { attr: { class: 'js-advanced-search-rule-subject test-advanced-search-rule-subject' }, isSimple: true} ) }}
</div>

<div class="box-advanced-search__item__content">
{{ form_widget(ruleForm.operator, { attr: { class: 'js-advanced-search-rule-operator' }, isSimple: true} ) }}
</div>

<div class="box-advanced-search__item__content">
<span class="js-advanced-search-rule-value">
<span class="js-advanced-search-rule-value test-advanced-search-rule-value">
{{ form_widget(ruleForm.value, { isSimple: true }) }}
</span>
</div>
Expand Down
10 changes: 5 additions & 5 deletions packages/framework/src/Resources/views/Admin/Grid/Grid.html.twig
Expand Up @@ -143,7 +143,7 @@
{% endblock %}

{% block grid_row %}
<tr class="table-grid__row {{ cycle(['odd', 'even'], loopIndex) }} js-grid-row"
<tr class="table-grid__row {{ cycle(['odd', 'even'], loopIndex) }} js-grid-row test-grid-row"
{% if grid.inlineEdit and row is not null%} data-inline-edit-row-id="{{ grid.getRowId(row)|json_encode() }}"{% endif %}
{% if (grid.dragAndDrop or grid.multipleDragAndDrop) and row is not null %} data-drag-and-drop-grid-row-id="{{ grid.getRowId(row)|json_encode() }}"{% endif %}
>
Expand All @@ -167,7 +167,7 @@
</td>
{% endif %}
{% for column in grid.columnsById %}
<td class="table-grid__cell js-grid-column-{{ column.id }}{% if column.classAttribute %} {{ column.classAttribute }}{% endif %}">
<td class="table-grid__cell js-grid-column-{{ column.id }} test-grid-column-{{ column.id }}{% if column.classAttribute %} {{ column.classAttribute }}{% endif %}">
{{ gridView.renderCell(column, row, form|default(null)) }}
</td>
{% endfor %}
Expand All @@ -176,7 +176,7 @@
{% if grid.inlineEdit and form is defined %}
<div class="form-inline-edit js-inline-edit-buttons">
<div class="form-inline-edit__item">
<a href="#" class="js-inline-edit-save in-icon in-icon--save svg svg-checked" title="{{ 'Save changes'|trans }}"></a>
<a href="#" class="js-inline-edit-save test-inline-edit-save in-icon in-icon--save svg svg-checked" title="{{ 'Save changes'|trans }}"></a>
</div>
<div class="form-inline-edit__item">
<a href="#" class="js-inline-edit-cancel in-icon in-icon--cancel svg svg-delete" title="{{ 'Discard changes'|trans }}"> </a>
Expand All @@ -187,7 +187,7 @@
</span>
{% else %}
{% if grid.inlineEdit %}
<a href="#" class="js-inline-edit-edit svg svg-pencil in-icon in-icon--edit" title="{{ 'Edit'|trans }}"></a>
<a href="#" class="js-inline-edit-edit test-inline-edit-edit svg svg-pencil in-icon in-icon--edit" title="{{ 'Edit'|trans }}"></a>
{% endif %}
{% for actionColumn in grid.actionColumns %}
{{ gridView.renderActionCell(actionColumn, row)|raw }}
Expand Down Expand Up @@ -396,7 +396,7 @@

{% block grid_inline_edit_add_button %}
<div class="wrap-bar">
<a href="#" class="btn btn--primary btn--plus wrap-bar__btn js-inline-edit-add">
<a href="#" class="btn btn--primary btn--plus wrap-bar__btn js-inline-edit-add test-inline-edit-add">
<i class="btn__icon">+</i>
{{ addEntity|default('Create record'|trans) }}
</a>
Expand Down
Expand Up @@ -11,7 +11,7 @@
</a>
<a
href="{{ url(route, routeParams) }}"
class="window__actions__btn window-button-continue btn js-confirm-delete-direct-link"
class="window__actions__btn window-button-continue btn js-confirm-delete-direct-link test-window-button-continue"
>
{{ 'Yes'|trans }}
</a>
Expand Down
4 changes: 2 additions & 2 deletions project-base/assets/js/frontend/utils/Window.js
Expand Up @@ -88,7 +88,7 @@ export default class Window {
this.$window.append($windowContent);

if (this.options.buttonClose) {
const $windowButtonClose = $('<a href="#" class="window-button-close window-popup__close js-window-button-close" title="' + Translator.trans('Close (Esc)') + '"><i class="svg svg-remove-thin"></i></a>');
const $windowButtonClose = $('<a href="#" class="window-button-close window-popup__close test-window-button-close" title="' + Translator.trans('Close (Esc)') + '"><i class="svg svg-remove-thin"></i></a>');
$windowButtonClose
.bind('click.window', this.options.eventClose)
.bind('click.windowClose', function () {
Expand All @@ -111,7 +111,7 @@ export default class Window {
}

if (this.options.buttonContinue) {
const $windowButtonContinue = $('<a href="" class="window-popup__actions__btn window-popup__actions__btn--continue window-button-continue btn"><i class="svg svg-arrow"></i></a>');
const $windowButtonContinue = $('<a href="" class="window-popup__actions__btn window-popup__actions__btn--continue window-button-continue btn test-window-button-continue"><i class="svg svg-arrow"></i></a>');
$windowButtonContinue
.append(document.createTextNode(this.options.textContinue))
.addClass(this.options.cssClassContinue)
Expand Down
12 changes: 6 additions & 6 deletions project-base/templates/Front/Content/Cart/index.html.twig
Expand Up @@ -38,14 +38,14 @@
{% for index, cartItem in cartItems %}
{% set cartItemPrice = cartItemPrices[index] %}
{% set cartItemDiscount = cartItemDiscounts[index] %}
<tr class="table-cart__row js-cart-item">
<tr class="table-cart__row js-cart-item test-cart-item">
<td class="table-cart__cell table-cart__cell--image">
<a href="{{ url('front_product_detail', {id: cartItem.product.id}) }}" title="{{ cartItem.product.name }}" class="table-cart__cell__image">
{{ image(cartItem.product, { size: 'thumbnail' }) }}
</a>
</td>

<td class="table-cart__cell table-cart__cell--name js-cart-item-name">
<td class="table-cart__cell table-cart__cell--name test-cart-item-name">
<a href="{{ url('front_product_detail', {id: cartItem.product.id}) }}">
{{ cartItem.name }}
</a>
Expand All @@ -60,7 +60,7 @@
</div>
</td>

<td class="table-cart__cell table-cart__cell--price js-cart-item-price">
<td class="table-cart__cell table-cart__cell--price test-cart-item-price">
{{ cartItemPrice.unitPrice.priceWithVat|price }}
</td>

Expand All @@ -76,7 +76,7 @@
</span>
</td>

<td class="table-cart__cell table-cart__cell--price js-cart-item-total-price">
<td class="table-cart__cell table-cart__cell--price test-cart-item-total-price">
{{ cartItemPrice.totalPrice.priceWithVat|price }}
{% if cartItemDiscount is not null %}
<div>
Expand All @@ -89,7 +89,7 @@
<td class="table-cart__cell table-cart__cell--action">
<a
href="{{ url('front_cart_delete', {cartItemId: cartItem.id, _token: csrf_token('front_cart_delete_' ~ cartItem.id)}) }}"
class="js-cart-item-remove-button"
class="test-cart-item-remove-button"
>
<i class="svg svg-remove"></i>
</a>
Expand All @@ -103,7 +103,7 @@
<td class="table-cart__cell table-cart__cell--image">&nbsp;</td>
<td class="table-cart__cell table-cart__cell--name">&nbsp;</td>
<td class="table-cart__cell table-cart__cell--price">&nbsp;</td>
<td class="table-cart__cell table-cart__cell--total-price js-cart-total-price" colspan="3">
<td class="table-cart__cell table-cart__cell--total-price test-cart-total-price" colspan="3">
{{ 'Total price including VAT'|trans }}:
<span>
{{ productsPrice.priceWithVat|price }}
Expand Down
Expand Up @@ -69,7 +69,7 @@
</div>

{% if not productView.isMainVariant %}
<div class="js-product-detail-main-add-to-cart-wrapper box-detail-add">
<div class="test-product-detail-main-add-to-cart-wrapper box-detail-add">
{% if not productView.isSellingDenied %}
<div itemprop="offers"
itemscope
Expand Down