Skip to content

Commit

Permalink
test: introduce cypress test suite and github workflow (#55)
Browse files Browse the repository at this point in the history
* chore: install and configure cypress and related deps

* test: create cypress test suite and tweak cypress setup

* test: use datatables in scenarios for improved readability

* chore: add e2e steps to workflow

* chore: format json files

* chore: provide dhis2BaseUrl

* chore: upgrade cypress tools and fixtures

* chore: upgrade cypress-commands & cypress-plugins and generate fixtures

* chore: add .prettierignore to avoid generated fixtures from being linted

* chore: updated fixtures after rebase

* test: add cypress tests for non-actionable approval statuses
  • Loading branch information
HendrikThePendric authored Aug 24, 2021
1 parent 36d07fa commit 5652efe
Show file tree
Hide file tree
Showing 24 changed files with 6,038 additions and 189 deletions.
3 changes: 3 additions & 0 deletions .cypress-cucumber-preprocessorrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"nonGlobalStepDefinitions": true
}
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ const { config } = require('@dhis2/cli-style')

module.exports = {
extends: [config.eslintReact],
globals: {
cy: 'readonly',
Cypress: 'readonly',
},
rules: {
'import/extensions': [2, 'ignorePackages'],
},
Expand Down
65 changes: 32 additions & 33 deletions .github/workflows/dhis2-verify-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,39 +73,38 @@ jobs:
- name: Test
run: yarn test

#e2e:
# runs-on: ubuntu-latest
# if: "!github.event.push.repository.fork && github.actor != 'dependabot[bot]'"
#
# strategy:
# fail-fast: false
# matrix:
# containers: [1, 2, 3, 4]
#
# steps:
# - name: Checkout
# uses: actions/checkout@v2
#
# - uses: actions/setup-node@v1
# with:
# node-version: 12.x
#
# - name: End-to-End tests
# uses: cypress-io/github-action@v2
# with:
# # This should be a command that serves the app.
# start: yarn d2-app-scripts start
# wait-on: 'http://localhost:3000'
# wait-on-timeout: 300
# record: true
# parallel: true
# env:
# BROWSER: none
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# CYPRESS_dhis2BaseUrl: https://debug.dhis2.org/dev
# CYPRESS_dhis2ApiVersion: 37
# CYPRESS_networkMode: stub
e2e:
runs-on: ubuntu-latest
if: "!github.event.push.repository.fork && github.actor != 'dependabot[bot]'"

strategy:
fail-fast: false
matrix:
containers: [1, 2, 3, 4]

steps:
- name: Checkout
uses: actions/checkout@v2

- uses: actions/setup-node@v1
with:
node-version: 12.x

- name: End-to-End tests
uses: cypress-io/github-action@v2
with:
start: yarn d2-app-scripts start
wait-on: 'http://localhost:3000'
wait-on-timeout: 300
record: true
parallel: true
env:
BROWSER: none
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_dhis2BaseUrl: http://localhost:8080
CYPRESS_dhis2ApiVersion: 37
CYPRESS_networkMode: stub

publish:
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
node_modules
.d2
src/locales
build
build
cypress.env.json
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.d2
src/locales
build
cypress/fixtures/network
12 changes: 12 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"baseUrl": "http://localhost:3000",
"video": false,
"projectId": "3wojdo",
"experimentalInteractiveRunEvents": true,
"env": {
"dhis2DataTestPrefix": "dhis2-approval",
"networkMode": "live",
"dhis2ApiVersion": "37"
},
"testFiles": "**/*.feature"
}
293 changes: 293 additions & 0 deletions cypress/fixtures/network/37/static_resources.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions cypress/fixtures/network/37/summary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"count": 333,
"totalResponseSize": 111388,
"duplicates": 227,
"nonDeterministicResponses": 11,
"apiVersion": 37,
"fixtureFiles": [
"static_resources.json",
"users_can_approve,_accept,_unapprove,_and_unaccept_data.json",
"users_can_view_data_set_reports_for_data_sets_connected_to_a_workflow.json"
]
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions cypress/integration/actions.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Feature: Users can approve, accept, unapprove, and unaccept data

Background:
Given the admin user visits the app
Then the user has workflow "Mortality < 5 years" preselected
When the user selects period "Februari 2021"
When the user selects organisation unit "Badjia"

# These scenarios need to be executed in sequence, because the current
# available actions depend on the previous ones.

# In "Ready for approval" state the "Approve" action becomes available
Scenario: User approves data
Then the status tag shows the approval status "Ready for approval"
When the user clicks the Approve button
Then a modal confirmation dialog is displayed
When the confirmation button is clicked
Then a circular loader is rendered
And the following buttons are available
| label | available |
| Approve | |
| Accept | yes |
| Unapprove | yes |
| Unaccept | |

# In "Approved" state the "Accept" action becomes available
Scenario: User accepts data
Then the status tag shows the approval status "Approved"
When the user clicks the Accept button
Then a circular loader is rendered
And the status tag shows the approval status "Ready for approval — Accepted"
And the following buttons are available
| label | available |
| Approve | yes |
| Accept | |
| Unapprove | yes |
| Unaccept | yes |

# In "Ready for approval — Accepted" state the "Unaccept" action becomes available
Scenario: User unaccepts data
Then the status tag shows the approval status "Ready for approval — Accepted"
When the user clicks the Unaccept button
Then a circular loader is rendered
And the status tag shows the approval status "Approved"
And the following buttons are available
| label | available |
| Approve | |
| Accept | yes |
| Unapprove | yes |
| Unaccept | |

# After unaccepting the state jumps back to "Approved" and the "Unapprove" action becomes available
Scenario: User unapproves data
Then the status tag shows the approval status "Approved"
When the user clicks the Unapprove button
Then a circular loader is rendered
And the status tag shows the approval status "Ready for approval"
And the following buttons are available
| label | available |
| Approve | yes |
| Accept | |
| Unapprove | |
| Unaccept | |
57 changes: 57 additions & 0 deletions cypress/integration/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import '../common/index.js'
import {
When,
Then,
defineParameterType,
} from 'cypress-cucumber-preprocessor/steps'

const buttonLabels = ['Approve', 'Accept', 'Unapprove', 'Unaccept']
defineParameterType({
name: 'buttonLabel',
regexp: new RegExp(buttonLabels.join('|')),
})

When('the user clicks the {buttonLabel} button', buttonLabel => {
cy.get('[data-test="bottom-bar"]')
.find('button')
.contains(buttonLabel)
.click()
})

Then('the following buttons are available', dataTable => {
dataTable.hashes().forEach(({ label, available }) => {
if (available) {
cy.get('[data-test="bottom-bar"]')
.find('button')
.contains(label)
.should('be.visible')
.and('not.be.disabled')
} else {
cy.get('[data-test="bottom-bar"]')
.find('button')
.contains(label)
.should('not.exist')
}
})
})

Then('a circular loader is rendered', () => {
cy.get('[data-test="dhis2-uicore-circularloader"]').should('be.visible')
})

Then('a modal confirmation dialog is displayed', () => {
cy.get('[data-test="dhis2-uicore-modal"]').should('be.visible')
})

When('the confirmation button is clicked', () => {
cy.get('[data-test="dhis2-uicore-modal"]')
.find('button')
.contains('Approve')
.click()
})

Then('an alert bar is displayed with the text Approval saved', () => {
cy.get('[data-test="dhis2-uicore-alertbar"]')
.contains('Approval saved')
.should('be.visible')
})
53 changes: 53 additions & 0 deletions cypress/integration/common/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Given,
Then,
When,
defineParameterType,
} from 'cypress-cucumber-preprocessor/steps'

Given('the admin user visits the app', () => {
cy.visit('/')
})

Then('the user has workflow "Mortality < 5 years" preselected', () => {
cy.get('[data-test="workflow-context-select-button"]')
.find('[data-test="value"]')
.should('have.text', 'Mortality < 5 years')
})

When('the user selects period "Februari 2021"', () => {
cy.get('[data-test="period-context-select-button"]').click()
cy.get('[data-test="period-context-select-popover"]')
.contains('February 2021')
.click()
})

When('the user selects organisation unit "Badjia"', () => {
cy.get('[data-test="org-unit-context-select-button"]').click()

cy.openOrgUnitNodeByName('Sierra Leone')
cy.openOrgUnitNodeByName('Bo')

cy.get('[data-test="org-unit-context-select-popover"]')
.contains('Badjia')
.click()
})

const statuses = [
'Ready for approval',
'Approved',
'Ready for approval — Accepted',
'Cannot be approved',
'Waiting for lower level approval',
'Waiting for higher level approval',
]
defineParameterType({
name: 'status',
regexp: new RegExp(statuses.join('|')),
})

Then('the status tag shows the approval status "{status}"', status => {
cy.get('[data-test="bottom-bar"]')
.find('[data-test="dhis2-uicore-tag-text"]')
.should('have.text', status)
})
30 changes: 30 additions & 0 deletions cypress/integration/displaying-data.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Feature: Users can view data set reports for data sets connected to a workflow

Background:
Given the admin user visits the app
Then the user has workflow "Mortality < 5 years" preselected
When the user selects period "Februari 2021"

Scenario: User views approvable data
When the user selects organisation unit "Badjia"
Then the status tag shows the approval status "Ready for approval"
Then a tab bar is displayed with a single tab "Mortality < 5 years"
And a single table with data is displayed

Scenario: User views unapprovable data
When the user selects organisation unit "Sierra Leone"
Then the status tag shows the approval status "Cannot be approved"
Then a tab bar is displayed with a single tab "Mortality < 5 years"
And a single table with data is displayed

Scenario: User views data waiting for lower level approval
When the user selects organisation unit "Bo"
Then the status tag shows the approval status "Waiting for lower level approval"
Then a tab bar is displayed with a single tab "Mortality < 5 years"
And a single table with data is displayed

Scenario: User views data waiting for higher level approval
When the user selects organisation unit "Baoma Station CHP"
Then the status tag shows the approval status "Waiting for higher level approval"
Then a tab bar is displayed with a single tab "Mortality < 5 years"
And a single table with data is displayed
40 changes: 40 additions & 0 deletions cypress/integration/displaying-data/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import '../common/index.js'
import { Then, When } from 'cypress-cucumber-preprocessor/steps'

Then('a tab bar is displayed with a single tab "Mortality < 5 years"', () => {
cy.get('[data-test="dhis2-uicore-tabbar"]').should('have.length', 1)
cy.get('[data-test="dhis2-uicore-tab"]').should('have.length', 1)
cy.get('[data-test="dhis2-uicore-tab"]').should(
'have.text',
'Mortality < 5 years'
)
})

Then('a single table with data is displayed', () => {
cy.get('[data-test="dhis2-uicore-datatable"]').should('have.length', 1)
})

When('the user selects organisation unit "Sierra Leone"', () => {
cy.get('[data-test="org-unit-context-select-button"]').click()
cy.get('[data-test="org-unit-context-select-popover"]')
.contains('Sierra Leone')
.click()
})

When('the user selects organisation unit "Bo"', () => {
cy.get('[data-test="org-unit-context-select-button"]').click()
cy.openOrgUnitNodeByName('Sierra Leone')
cy.get('[data-test="org-unit-context-select-popover"]')
.contains('Bo')
.click()
})

When('the user selects organisation unit "Baoma Station CHP"', () => {
cy.get('[data-test="org-unit-context-select-button"]').click()
cy.openOrgUnitNodeByName('Sierra Leone')
cy.openOrgUnitNodeByName('Bo')
cy.openOrgUnitNodeByName('Baoma')
cy.get('[data-test="org-unit-context-select-popover"]')
.contains('Baoma Station CHP')
.click()
})
11 changes: 11 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const {
networkShim,
chromeAllowXSiteCookies,
cucumberPreprocessor,
} = require('@dhis2/cypress-plugins')

module.exports = (on, config) => {
networkShim(on)
chromeAllowXSiteCookies(on)
cucumberPreprocessor(on, config)
}
Loading

0 comments on commit 5652efe

Please sign in to comment.