Skip to content

Commit

Permalink
feat(bdd smoke testing): introduce multi-user playwright bdd tests
Browse files Browse the repository at this point in the history
ocis and oc10 bdd smoke tests are now able to with playwright and in parallel,
this means running tests with many users at the same time are now possible.
Sessions won't conflict, this introduces a whole new level of test possibilities and speed
  • Loading branch information
fschade committed Aug 19, 2021
1 parent a7d2068 commit 962ccfc
Show file tree
Hide file tree
Showing 55 changed files with 1,719 additions and 173 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
*/
'require-await': 'warn',
'no-new': 'off',
'jest/no-standalone-expect': 'off',
'node/no-callback-literal': 'off'
},
globals: {
Expand Down
12 changes: 12 additions & 0 deletions cucumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
smoke: `
--require ./tests/smoke/**/*.ts
--require-module ts-node/register
--format @cucumber/pretty-formatter
--publish-quiet
--format-options ${JSON.stringify({
snippetInterface: 'async-await',
snippetSyntax: './tests/smoke/support/cucumber/snippets-syntax.js'
})}
`
}
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
"depcheck": "depcheck",
"lint": "eslint '{packages,tests}/**/*.{js,ts,vue}' --color",
"serve": "SERVER=true yarn build:w",
"test:acceptance:drone": "cucumber-js --retry 1 --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format node_modules/cucumber-pretty",
"test:acceptance:oc10": "cucumber-js --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format node_modules/cucumber-pretty -t \"${TEST_TAGS:-not @skip and not @skipOnOC10}\" -f json:tests/report/cucumber_report.json",
"test:acceptance:ocis": "NODE_TLS_REJECT_UNAUTHORIZED=0 RUN_ON_OCIS=true cucumber-js --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format node_modules/cucumber-pretty -t \"${TEST_TAGS:-not @skip and not @skipOnOCIS and not @notToImplementOnOCIS}\" -f json:tests/report/cucumber_report.json",
"test:acceptance:drone": "cucumber-js --retry 1 --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format @cucumber/pretty-formatter",
"test:acceptance:oc10": "cucumber-js --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format @cucumber/pretty-formatter -t \"${TEST_TAGS:-not @skip and not @skipOnOC10}\" -f json:tests/report/cucumber_report.json",
"test:acceptance:ocis": "NODE_TLS_REJECT_UNAUTHORIZED=0 RUN_ON_OCIS=true cucumber-js --require-module @babel/register --require-module @babel/polyfill --require tests/acceptance/setup.js --require tests/acceptance/stepDefinitions --format @cucumber/pretty-formatter -t \"${TEST_TAGS:-not @skip and not @skipOnOCIS and not @notToImplementOnOCIS}\" -f json:tests/report/cucumber_report.json",
"test:smoke:experimental": "NODE_TLS_REJECT_UNAUTHORIZED=0 cucumber-js --profile=smoke",
"test:unit": "jest --coverage --config ./tests/unit/config/jest.config.js",
"test:integration": "jest --config ./tests/integration/config/jest.config.js"
},
Expand All @@ -37,16 +38,21 @@
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.14.7",
"@babel/register": "^7.13.16",
"@cucumber/cucumber": "^7.3.1",
"@cucumber/pretty-formatter": "^1.0.0-alpha.1",
"@erquhart/rollup-plugin-node-builtins": "^2.1.5",
"@playwright/test": "^1.14.0",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-html": "^0.2.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-typescript": "^8.2.5",
"@testing-library/jest-dom": "^5.13.0",
"@testing-library/vue": "^5.6.2",
"@types/cucumber": "^7.0.0",
"@types/jest": "^26.0.23",
"@types/jest-axe": "^3.5.2",
"@types/lodash-es": "^4.17.4",
"@types/node-fetch": "^2.5.12",
"@types/vue": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
Expand All @@ -60,9 +66,7 @@
"chromedriver": "^89.0.0",
"commander": "^8.1.0",
"core-js": "^3.15.2",
"cucumber": ">=6.0.5",
"cucumber-html-reporter": "^5.4.0",
"cucumber-pretty": "^6.0.0",
"depcheck": "^1.3.1",
"ejs": "^3.1.5",
"eslint": "^7.30.0",
Expand Down Expand Up @@ -94,6 +98,7 @@
"nightwatch-vrt": "^0.2.10",
"node-fetch": "^2.6.1",
"p-limit": "^3.1.0",
"playwright": "^1.14.0",
"postcss": "^8.2.15",
"regenerator-runtime": "^0.13.7",
"requirejs": "^2.3.6",
Expand All @@ -115,6 +120,7 @@
"rollup-plugin-vue": "^5.1.4",
"sync-fetch": "^0.3.0",
"ts-jest": "^26.5.6",
"ts-node": "^10.2.0",
"tslib": "^2.2.0",
"typescript": "^4.3.2",
"url-search-params-polyfill": "^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { setDefaultTimeout, After, Before, defineParameterType } from 'cucumber'
import { setDefaultTimeout, After, Before, defineParameterType } from '@cucumber/cucumber'
import { createSession, closeSession, client, startWebDriver, stopWebDriver } from 'nightwatch-api'
import { rollbackConfigs, cacheConfigs } from './helpers/config'
import { getAllLogsWithDateTime } from './helpers/browserConsole.js'
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/accountContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When, Then } = require('cucumber')
const { When, Then } = require('@cucumber/cucumber')
const assert = require('assert')
const _ = require('lodash')

Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/filesContext.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-unused-expressions */
const { client } = require('nightwatch-api')
const assert = require('assert')
const { Given, When, Then, Before } = require('cucumber')
const { Given, When, Then, Before } = require('@cucumber/cucumber')
const webdav = require('../helpers/webdavHelper')
const _ = require('lodash')
const loginHelper = require('../helpers/loginHelper')
Expand Down
9 changes: 5 additions & 4 deletions tests/acceptance/stepDefinitions/generalContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { After, Before, Given, Then, When } = require('cucumber')
const { After, Before, Given, Then, When } = require('@cucumber/cucumber')
const webdavHelper = require('../helpers/webdavHelper')
const httpHelper = require('../helpers/httpHelper')
const backendHelper = require('../helpers/backendHelper')
Expand All @@ -8,7 +8,7 @@ const fs = require('fs')
const occHelper = require('../helpers/occHelper')

let initialConfigJsonSettings
let createdFiles = []
const createdFiles = []

Given(
'a file with the size of {string} bytes and the name {string} has been created locally',
Expand Down Expand Up @@ -245,7 +245,7 @@ After(async function(testCase) {
})

Before(function(testCase) {
createdFiles = []
testCase.pickle.createdFiles = []
if (
typeof process.env.SCREEN_RESOLUTION !== 'undefined' &&
process.env.SCREEN_RESOLUTION.trim() !== ''
Expand All @@ -265,7 +265,8 @@ Before(function(testCase) {
} else {
client.maximizeWindow()
}
console.log(' ' + testCase.sourceLocation.uri + ':' + testCase.sourceLocation.line + '\n')
// todo
// console.log(' ' + testCase.sourceLocation.uri + ':' + testCase.sourceLocation.line + '\n')
})

After(async function(testCase) {
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/loginContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { Given, Then, When } = require('cucumber')
const { Given, Then, When } = require('@cucumber/cucumber')
const loginHelper = require('../helpers/loginHelper')
const assert = require('assert')

Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/markdownEditorContext.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { client } = require('nightwatch-api')
const assert = require('assert')
const _ = require('lodash')
const { Given, When, Then } = require('cucumber')
const { Given, When, Then } = require('@cucumber/cucumber')

const markdownEditor = client.page.markdownEditorPage()
const filesList = client.page.FilesPageElement.filesList()
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/messagesContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When } = require('cucumber')
const { When } = require('@cucumber/cucumber')

When('the user closes the message', function() {
return client.page.webPage().closeMessage()
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/notificationsContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { Given, When, Then } = require('cucumber')
const { Given, When, Then } = require('@cucumber/cucumber')
const httpHelper = require('../helpers/httpHelper')
const codify = require('../helpers/codify')
const assert = require('assert')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/previewContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { Given, When, Then } = require('cucumber')
const { Given, When, Then } = require('@cucumber/cucumber')
const mediaViewerPage = client.page.FilesPageElement.mediaViewerPage()
const filesList = client.page.FilesPageElement.filesList()
const assert = require('assert')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/privateLinksContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When } = require('cucumber')
const { When } = require('@cucumber/cucumber')
const webdav = require('../helpers/webdavHelper')

When(
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/provisioningContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { Given, After } = require('cucumber')
const { Given, After } = require('@cucumber/cucumber')
const fs = require('fs-extra')
const assert = require('assert')
require('url-search-params-polyfill')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/publicLinkContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When, Then } = require('cucumber')
const { When, Then } = require('@cucumber/cucumber')
require('url-search-params-polyfill')
const sharingHelper = require('../helpers/sharingHelper')
const assert = require('assert')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/searchContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When } = require('cucumber')
const { When } = require('@cucumber/cucumber')

When('the user searches for {string} using the webUI', function(searchTerm) {
return client.page.webPage().search(searchTerm)
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/sharingContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { client } = require('nightwatch-api')
const { When, Given, Then } = require('cucumber')
const { When, Given, Then } = require('@cucumber/cucumber')
const assert = require('assert')
const { URLSearchParams } = require('url')
require('url-search-params-polyfill')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/visualContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { Then } = require('cucumber')
const { Then } = require('@cucumber/cucumber')
const { client } = require('nightwatch-api')
const _ = require('lodash')
const path = require('path')
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance/stepDefinitions/webdavContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { Given, Then } = require('cucumber')
const { Given, Then } = require('@cucumber/cucumber')
require('url-search-params-polyfill')
const httpHelper = require('../helpers/httpHelper')
const backendHelper = require('../helpers/backendHelper')
Expand Down
100 changes: 100 additions & 0 deletions tests/smoke/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# WEB-Smoke-Tests (EXPERIMENTAL POC)

Use with care, the smoke-test package introduces many new concepts and is still under heavy development.

## Why smoke tests

Before we release a new web version, we always take care that nothing breaks and the core features are working as
expected. Till now this was a manual process which consumed a lot of time and effort.

Manual steps are error-prone by nature, on the one hand they can bring up unknown issues, but on the other it's easy to
forget some test steps. The main problem we faced, was that it's never a good thing to manually test your own code.

To get closer to the point where we are able to release faster and more often, we started to develop the WEB-Smoke-Tests
package.

## Why not as part of the tests/acceptance package

The tests/acceptance package has a much broader test scope and is here to test every little element // interaction in
ci. To guarantee that process, a lot of steps are required which takes a lot of time and isn't the perfect fit for local
development.

The WEB-Smoke-Tests are much smaller and are still in an early phase. Many things are still not there compared to the
mature tests/acceptance package.

**The main reasons why we decided to split out the smoke tests are:**

* we wanted to test on a persona // use-case level
* testing should be easy, fast and reliable
* no external dependencies expect a backend
* strongly typed page-objects, steps and helpers
* develop, test manually, extend automatic tests, release, repeat

## Run a smoke test

Please make sure to point http://host.docker.internal/ to 127.0.0.1 by adding it to your hosts.

```shell
$ yarn && yarn build:w
$ docker-compose up oc10 ocis
$ yarn test:smoke:experimental tests/smoke/features/kindergarten.feature
```

## Available options

To run the tests with below options, you have to set them as environment variable in you shell.

```shell
$ OPTION_1=foo OPTION_2=bar yarn test:smoke:experimental ...
```

* **OCIS=boolean**
* defines if the tests should use ocis as backend
* **default**: false
* **SLOW_MO=time-in-ms**
* run the tests with a timeout between every interaction
* **default**: 0
* **HEADLESS=boolean**
* run the tests without opening a browser
* **default**: false
* **BROWSER=string**
* define which browser is used to run the tests
* **default**: chrome
* **values**: firefox | webkit | chrome | chromium

## Package structure

This package is still in an early stage, design could change over time.

### Features
`./tests/smoke/features`
[Gherkin features](https://cucumber.io/docs/gherkin/reference/) and corresponding [Cucumber step definitions](https://cucumber.io/docs/cucumber/step-definitions/) placed in there, it follows

### Support
`./tests/smoke/support`
everything that is needed to run the tests organize as package

`./tests/smoke/support/api`
Every call that is used to provision, configure or even request data from backend will be placed here

`./tests/smoke/support/cta`
(Call to action) - Snippets that can be used across the entire livecycle of a test

`./tests/smoke/support/cucumber`
Mainly all cucumber related things, for now only setup is done here

`./tests/smoke/support/page`
With the option in mind that the smoke-test package could grow, we decided to already group interactions inside page
objects. To get more background what page objects are, you can read
the [introduction here](https://playwright.dev/docs/pom/)

`./tests/smoke/support/store`
Simple store to persist data for the lifetime of a test

`./tests/smoke/support/world`
Custom world that is used across the tests, global objects like services get initialized there. [Read more](https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/world.md)

## Packages we use

* [cucumber-js](https://github.com/cucumber/cucumber-js)
* [playwright](https://github.com/microsoft/playwright)
68 changes: 68 additions & 0 deletions tests/smoke/features/kindergarten.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Feature: Kindergarten can use web to organize a day

As a kindergarten operator named Alice
I want to manage all file related operations by using ownCloud WEB
So that i'm sure all parents are informed and have the latest information in a easy and secure way

Background:
Given following users have been created
| Alice |
| Brian |
| Carol |

Scenario: Alice can share this weeks meal plan with all parents
Given "Alice" has logged in
Then "Alice" opens the "files" app
Then "Alice" navigates to the files page
Then "Alice" creates following folders
| groups/Kindergarten Koalas/meal plan |
| groups/Pre-Schools Pirates/meal plan |
| groups/Teddy Bear Daycare/meal plan |
And "Alice" uploads following resources
| resource | to |
| PARENT/parent.txt | groups/Kindergarten Koalas/meal plan |
| lorem.txt | groups/Kindergarten Koalas/meal plan |
| lorem-big.txt | groups/Kindergarten Koalas/meal plan |
| data.zip | groups/Pre-Schools Pirates/meal plan |
| lorem.txt | groups/Pre-Schools Pirates/meal plan |
| lorem-big.txt | groups/Pre-Schools Pirates/meal plan |
| data.zip | groups/Teddy Bear Daycare/meal plan |
| lorem.txt | groups/Teddy Bear Daycare/meal plan |
| lorem-big.txt | groups/Teddy Bear Daycare/meal plan |
Then "Alice" shares following resources
| resource | user |
| groups/Pre-Schools Pirates/meal plan | Brian |
| groups/Pre-Schools Pirates/meal plan | Carol |
Given "Brian" has logged in
Then "Brian" opens the "files" app
Then "Brian" navigates to the shared with me page
Then "Brian" downloads following files
| resource | from |
| data.zip | meal plan |
Given "Carol" has logged in
Then "Carol" opens the "files" app
Then "Carol" navigates to the shared with me page
Then "Carol" downloads following files
| resource | from |
| data.zip | meal plan |
| lorem.txt | meal plan |
| lorem-big.txt | meal plan |
Then "Carol" has logged out
Then "Brian" downloads following files
| resource | from |
| lorem.txt | meal plan |
| lorem-big.txt | meal plan |
Then "Brian" has logged out
Then "Alice" downloads following files
| resource | from |
| parent.txt | groups/Kindergarten Koalas/meal plan |
| lorem.txt | groups/Kindergarten Koalas/meal plan |
| lorem-big.txt | groups/Kindergarten Koalas/meal plan |
| data.zip | groups/Pre-Schools Pirates/meal plan |
| lorem.txt | groups/Pre-Schools Pirates/meal plan |
| lorem-big.txt | groups/Pre-Schools Pirates/meal plan |
| data.zip | groups/Teddy Bear Daycare/meal plan |
| lorem.txt | groups/Teddy Bear Daycare/meal plan |
| lorem-big.txt | groups/Teddy Bear Daycare/meal plan |
Then "Alice" has logged out

Loading

0 comments on commit 962ccfc

Please sign in to comment.