Skip to content

Commit

Permalink
Add some initial structure for test cases (#989)
Browse files Browse the repository at this point in the history
* add some initial structure for test cases

* convert fetch to xhr file to typescript

* add tooling for manipulating redux directly

* use playwright to run e2e

* make tests run in parallel

* debug

* remove debug

* allow empty test directories

* add temporary build trigger from this branch

* disable rate limiting in development

* implement technical helper for logging in

* also build docker image temporarily from this branch

* add new branch prefix e2e/ for triggering pipelines

* add example of birth event test creation

* add a new plain e2e pipeline that triggers from e2e/ branches

* update e2e command to use playwright

* update commands to deprecate cypress
  • Loading branch information
rikukissa authored May 7, 2024
1 parent 0412d63 commit 3cf683e
Show file tree
Hide file tree
Showing 20 changed files with 448 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: E2E
name: Deploy & run E2E
run-name: Deploy to ${{ github.event.inputs.environment }} and E2E
on:
repository_dispatch:
Expand Down Expand Up @@ -30,6 +30,7 @@ concurrency:

jobs:
get-core-commit:
name: Resolve latest core tag
runs-on: ubuntu-latest
outputs:
latest_commit_sha: ${{ steps.get_latest_commit.outputs.commit_sha }}
Expand All @@ -46,6 +47,7 @@ jobs:
run: echo "::set-output name=commit_sha::$(git rev-parse HEAD | cut -c 1-7)"

get-country-config-commit:
name: Resolve latest Farajaland tag
runs-on: ubuntu-latest
needs: get-core-commit
outputs:
Expand All @@ -69,33 +71,63 @@ jobs:
reset: 'true'
secrets: inherit

discover-tests:
name: Discover test directories
runs-on: ubuntu-22.04
outputs:
test_matrix: ${{ steps.list-tests.outputs.test_matrix }}
steps:
- name: Check out code
uses: actions/checkout@v4

- name: List Test Directories
id: list-tests
run: |
test_dirs=$(find ./e2e/testcases -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | jq -R -s -c 'split("\n")[:-1]')
echo "Test directories: $test_dirs"
echo "test_matrix=$test_dirs" >> $GITHUB_OUTPUT
echo "test_matrix=$test_dirs"
test:
needs: deploy
runs-on: ubuntu-20.04
needs: [deploy, discover-tests]
runs-on: ubuntu-22.04
environment: ${{ github.event.inputs.environment || 'development' }}
strategy:
fail-fast: false
matrix:
test_dir: ${{ fromJson(needs.discover-tests.outputs.test_matrix) }}
steps:
- name: Checking out git repo
uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
fetch-depth: 1
node-version: 18

- name: Use Node.js 18.19
uses: actions/setup-node@v2
with:
node-version: '18.19'
cache: 'npm'
- name: Check for Spec Files
id: check-specs
run: |
if ls ./e2e/testcases/${{ matrix.test_dir }}/*.spec.ts > /dev/null 2>&1; then
echo "::set-output name=has_spec_files::true"
else
echo "::set-output name=has_spec_files::false"
fi
- name: Runs dependency installation
- name: Install Dependencies
if: steps.check-specs.outputs.has_spec_files == 'true'
run: yarn

- name: Run tests
uses: cypress-io/[email protected]
- name: Install Playwright Browsers
if: steps.check-specs.outputs.has_spec_files == 'true'
run: npx playwright install --with-deps

- name: Run Playwright Tests
if: steps.check-specs.outputs.has_spec_files == 'true'
run: npx playwright test ./e2e/testcases/${{ matrix.test_dir }}
env:
CYPRESS_RECORD_KEY: '${{ secrets.CYPRESS_RECORD_KEY }}'
CYPRESS_COUNTRYCONFIG_URL: 'https://countryconfig.${{ vars.DOMAIN }}/'
CYPRESS_GATEWAY_URL: 'https://gateway.${{ vars.DOMAIN }}/'
CYPRESS_LOGIN_URL: 'https://login.${{ vars.DOMAIN }}/'
CYPRESS_CLIENT_URL: 'https://register.${{ vars.DOMAIN }}/'
CYPRESS_AUTH_URL: 'https://auth.${{ vars.DOMAIN }}/'
DOMAIN: '${{ vars.DOMAIN }}'

- uses: actions/upload-artifact@v4
if: steps.check-specs.outputs.has_spec_files == 'true'
with:
record: true
name: playwright-report-${{ matrix.test_dir }}
path: playwright-report/
retention-days: 30
77 changes: 77 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: E2E
run-name: Deploy to ${{ github.event.inputs.environment }} and E2E
on:
push:
branches:
- e2e/*
workflow_dispatch:
inputs:
environment:
type: choice
description: Environment to deploy to
required: true
default: 'development'
options:
- development

jobs:
discover-tests:
name: Discover test directories
runs-on: ubuntu-22.04
outputs:
test_matrix: ${{ steps.list-tests.outputs.test_matrix }}
steps:
- name: Check out code
uses: actions/checkout@v4

- name: List Test Directories
id: list-tests
run: |
test_dirs=$(find ./e2e/testcases -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | jq -R -s -c 'split("\n")[:-1]')
echo "Test directories: $test_dirs"
echo "test_matrix=$test_dirs" >> $GITHUB_OUTPUT
echo "test_matrix=$test_dirs"
test:
needs: [discover-tests]
runs-on: ubuntu-22.04
environment: ${{ github.event.inputs.environment || 'development' }}
strategy:
fail-fast: false
matrix:
test_dir: ${{ fromJson(needs.discover-tests.outputs.test_matrix) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18

- name: Check for Spec Files
id: check-specs
run: |
if ls ./e2e/testcases/${{ matrix.test_dir }}/*.spec.ts > /dev/null 2>&1; then
echo "::set-output name=has_spec_files::true"
else
echo "::set-output name=has_spec_files::false"
fi
- name: Install Dependencies
if: steps.check-specs.outputs.has_spec_files == 'true'
run: yarn

- name: Install Playwright Browsers
if: steps.check-specs.outputs.has_spec_files == 'true'
run: npx playwright install --with-deps

- name: Run Playwright Tests
if: steps.check-specs.outputs.has_spec_files == 'true'
run: npx playwright test ./e2e/testcases/${{ matrix.test_dir }}
env:
DOMAIN: '${{ vars.DOMAIN }}'

- uses: actions/upload-artifact@v4
if: steps.check-specs.outputs.has_spec_files == 'true'
with:
name: playwright-report-${{ matrix.test_dir }}
path: playwright-report/
retention-days: 30
1 change: 1 addition & 0 deletions .github/workflows/publish-to-dockerhub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- master
- develop
- main
- e2e/*
workflow_dispatch:
inputs:
branch_name:
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ cypress/videos
graphql.schema.json
*.tar.gz
.secrets
.env*
.env*
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
4 changes: 4 additions & 0 deletions cypress/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ declare namespace Cypress {
enterDeathMaximumInput: (options?: DeclarationOptions) => void
enterDeathMinimumInput: (options?: DeclarationOptions) => void
someoneElseJourney: () => void
getReduxStore: () => Cypress.Chainable<{
getState: () => any
dispatch: (action: any) => void
}>
}
}
/* eslint-enable no-unused-vars */
19 changes: 19 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,22 @@ Cypress.Commands.add('someoneElseJourney', () => {
// DOCUMENTS
goToNextFormSection()
})

Cypress.Commands.add('getReduxStore', () => {
return cy.window().then((win) => {
const container = Object.entries(win.document.getElementById('root')).find(
([x, y]) => x.includes('reactContainer')
)[1]

if (!container) {
throw new Error('React container not found')
}

const store = container.memoizedState?.element?.props?.store
if (!store) {
throw new Error('Redux store not found')
}

return store
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ function fetchToXhr() {

before(() => {
cy.readFile('node_modules/whatwg-fetch/dist/fetch.umd.js').then(
contents => (polyfill = contents)
(contents) => (polyfill = contents)
)
Cypress.on('window:before:load', win => {
Cypress.on('window:before:load', (win) => {
delete win.fetch
win.eval(polyfill)
})
Expand Down
4 changes: 4 additions & 0 deletions e2e/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const DOMAIN = process.env.DOMAIN || 'farajaland-dev.opencrvs.org'
export const LOGIN_URL = 'https://login.' + DOMAIN
export const AUTH_URL = 'https://auth.' + DOMAIN
export const CLIENT_URL = 'https://register.' + DOMAIN
72 changes: 72 additions & 0 deletions e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Page, expect } from '@playwright/test'
import { AUTH_URL, CLIENT_URL } from './constants'
type Store = {
getState: () => any
dispatch: (action: any) => void
}

export function dispatchAction(page: Page, reduxAction: any) {
return page.evaluate<Store>((reduxAction) => {
const root = window.document.getElementById('root')!
const container = Object.entries(root).find(([x]) =>
x.includes('reactContainer')
)![1]

if (!container) {
throw new Error('React container not found')
}

const store = container.memoizedState?.element?.props?.store

if (!store) {
throw new Error('Redux store not found')
}

store.dispatch(reduxAction)
return store
}, reduxAction)
}

export async function login(page: Page, username: string, password: string) {
const token = await getToken(username, password)
await page.goto(`${CLIENT_URL}?token=${token}`)
await expect(page.locator('#appSpinner')).toBeVisible()
}

export async function createPIN(page: Page) {
await page.click('#pin-input')
for (let i = 1; i <= 8; i++) {
await page.type('#pin-input', `${i % 2}`)
}
}

async function getToken(username: string, password: string) {
const authUrl = `${AUTH_URL}/authenticate`
const verifyUrl = `${AUTH_URL}/verifyCode`

const authResponse = await fetch(authUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password
})
})

const authBody = await authResponse.json()
const verifyResponse = await fetch(verifyUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
nonce: authBody.nonce,
code: '000000'
})
})

const verifyBody = await verifyResponse.json()
return verifyBody.token
}
Empty file.
Empty file added e2e/testcases/birth/.gitkeep
Empty file.
54 changes: 54 additions & 0 deletions e2e/testcases/birth/1-birth-event-declaration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { expect, test } from '@playwright/test'
import { createPIN, login } from '../../helpers'

test.describe('1. Birth event declaration', () => {
test.beforeEach(async ({ page }) => {
await login(page, 'k.mweene', 'test')
await createPIN(page)
})

test('1.1. Navigate to the birth event declaration page', async ({
page
}) => {
await page.click('#header_new_event')
await page.waitForSelector('#continue')
})

test('1.2. Validate event selection page', async ({ page }) => {
await page.click('#header_new_event')

await test.step('1.2.1 Validate the contents of the event type page', async () => {
/*
* Expected result: should show
* - Radio buttons of the events
* - Continue button
* - Exit button
*/
await expect(page.locator('#select_birth_event')).toBeVisible()
await expect(page.locator('#select_death_event')).toBeVisible()
await expect(page.locator('#select_marriage_event')).toBeVisible()
await expect(page.locator('#goBack')).toBeVisible()
await expect(page.locator('#continue')).toBeVisible()
})

await test.step('1.2.2 Click the "Continue" button without selecting any event', async () => {
await page.click('#continue')
/*
* Expected result: should throw an error as below:
* "Please select the type of event"
*/
await expect(page.locator('#require-error')).toBeVisible()
})

await test.step('1.2.3 Select the "Birth" event and click "Continue" button', async () => {
await page.click('#select_birth_event')
await page.click('#continue')
/*
* Expected result: User should navigate to the "Introduction" page
*/
await expect(
page.locator('#form_section_id_information-group')
).toBeVisible()
})
})
})
Empty file added e2e/testcases/death/.gitkeep
Empty file.
Loading

0 comments on commit 3cf683e

Please sign in to comment.