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

add create, delete book scenario #1

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
tmp
# Logs
logs
*.log
Expand Down
143 changes: 143 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
async function goHome() {
await browser.goTo('#Shell-home');
await browser.waitForUI5();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where are goTo and waitForUI5() injected into the browser object?
a comment here might help the inclined developer :)
plus: if this is primarily targeting wdi5, the wdi5.goTo() seems more appropriate.
plus: we should refrain from any manual waitForXX APIs and rather leave it to the test framework to sync app lifecyle with test lifecycle. E.g. with wdi5, no waiting for any ops to finish is necessary as the underlying RecordReplay-API takes care of this and is accounted for in wdi5

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switched to wdi5.goTo()

}

async function pressTile(name) {
const control = await browser.asControl({
selector: {
controlType: "sap.m.GenericTile",
properties: {
header: name
}
}
});
await control.press()
}

async function searchFor(text) {
await browser.asControl({
selector: {
controlType: "sap.m.SearchField",
interaction: {
idSuffix: "I"
}
}
}).enterText(text)
}

async function performStandardAction( name ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespacing btw method and parenthesis → please introduce a formatter (e.g. prettier) and optionally a linter (e.g. eslint) for avoiding style issues.
I'd suggest to align with wdi5 with that: https://github.com/ui5-community/wdi5/blob/main/.prettierrc

Copy link
Collaborator Author

@vl-leon vl-leon Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added prettier as suggested, eslint also

const control = await browser.asControl({
selector: {
controlType: "sap.m.Button",
interactable: true,
id: new RegExp(`.*\:\:StandardAction\:\:${name}\$`)
}
});
await control.press();
}

async function setFieldValue( name, value ) {
const control = await browser.asControl({
selector: {
controlType: "sap.m.Input",
interaction: "root",
labelFor: {
text: name
}
}
});
await control.setValue(value);
}

async function openValueHelpForField( name ) {
const control = await browser.asControl({
selector: {
controlType: "sap.m.Input",
labelFor: {
text: name
}
}
});
await control.press();
}

async function _findTableWithTitle(title, controlType, opt={}) {
let { searchOpenDialogs } = opt;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about your intended usage of let and const here and this file in general. Personally, I prefer const over let for immutable var assignments, but that's just me. Please just be aware of it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed that: let it be const ;)

return await browser.asControl({
selector: {
controlType,
searchOpenDialogs,
interaction: "root",
descendant: {
controlType: "sap.m.Title",
properties: {
text: {
regex: {
source: `^${title}`
}
}
}
}
}
})
}

async function chooseRowInValueHelpDialogTable( tableTitle, rowNumber ) {
let table = await _findTableWithTitle(tableTitle, "sap.ui.table.Table", {searchOpenDialogs:true});
//let rows = await table.getAggregation('rows');
//let cells = await rows[rowNumber].getAggregation('cells');
//let id = await cells[0].getId();
let id = await table.getId()+`-rows-row${rowNumber}-col0`;
await tapOnId(id);
}

async function selectRowInTable( tableTitle, targetRow ) {
let table = await _findTableWithTitle(tableTitle, "sap.m.Table");
let items = await table.getItems();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has proven to be a potential drawback: if the table in question has growing turned off and at the same time many entries, the test will get flaky here with looong runtimes. Don't have an immediate suggestion for mitigation, just be aware and eventually leave a comment here à la TODO or REVISIT

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment

let checkbox = await items[targetRow].getMultiSelectControl();
await checkbox.fireSelect({selected:true});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please refrain from using any fire* methods. When using standalone, they have unintended side effects such as not triggering any other handlers regisitered with the control. Instead, please use press()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to press - worked fine

}

async function navigateBack() {
const control = await browser.asControl({
selector: {
controlType: 'sap.ushell.ui.shell.ShellHeadItem',
interactable: true,
id: 'backBtn'
}
});
await control.press();
}

async function pressButtonInDialog( text ) {
const control = await browser.asControl({
selector: {
controlType: "sap.m.Button",
searchOpenDialogs: true,
interactable: true,
properties: {
text
}
}
});
await control.press();
}

async function tapOnId(id) {
await browser.executeScript('$("#"+$.escapeSelector(arguments[0])).trigger("tap")',[id]);
await browser.waitForUI5();
}

module.exports = {
goHome,
pressTile,
searchFor,
performStandardAction,
setFieldValue,
openValueHelpForField,
chooseRowInValueHelpDialogTable,
selectRowInTable,
navigateBack,
pressButtonInDialog,
}
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "wdi5-fe-selectors",
"description": "wdi5 selectors for Fiori Elements",
"version": "1.0.0",
"main": "lib/index.js",
"files": [
"lib"
],
"scripts": {
"test": "bash ./test/testCAPBookshop.sh"
}
}
45 changes: 45 additions & 0 deletions test/CAPBookshop.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { wdi5 } = require('wdio-ui5-service');
const lib = require('wdi5-fe-selectors');

describe('samples', () => {

it('should log', () => {
const logger = wdi5.getLogger();
logger.log('hello world!');
})

it('go home', async () => {
await lib.goHome();
await _sleep(2 * 1000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why this is actually required - I get that FE is slow in general (aka resource-intensive), but waiting 2 secs is just too long IMO.
Best try to get rid of any manual wait operations and leave it to the underlying framework such as wdi5 to sync app- and test-runtime

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed as it is not needed

})

it('new book', async () => {
await lib.goHome();
await lib.pressTile('Manage Books');
await lib.performStandardAction('Create');
await lib.setFieldValue('Title', 'WDI5');
await lib.openValueHelpForField('Author');
await lib.chooseRowInValueHelpDialogTable('Items', 1);
await lib.performStandardAction('Save');
await lib.navigateBack();
await lib.searchFor('WDI5');
await _sleep(2 * 1000)
})

it('delete book', async () => {
await lib.goHome();
await lib.pressTile('Manage Books');
await lib.selectRowInTable('Books', 0 );
await lib.selectRowInTable('Books', 1 );
await lib.performStandardAction('Delete');
await lib.pressButtonInDialog('Delete');
await _sleep(2 * 1000)
})

})

function _sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
63 changes: 63 additions & 0 deletions test/testCAPBookshop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/env bash

set -x
set -e

ROOT_DIR=`pwd`
WORK_DIR=tmp/testCAPBookshop

if [ -d ${WORK_DIR} ]; then

cd ${WORK_DIR}
cd service

else

mkdir -p ${WORK_DIR}
cd ${WORK_DIR}

npm init -y

npm init -w dk -y
npm add -w dk @sap/cds-dk

npm init -w service -y

cd service
rm package.json

npx cds init
npx cds add sample

npm add ${ROOT_DIR} || true

npm init wdi5@latest

fi

rm webapp/test/e2e/*.js
cp ${ROOT_DIR}/test/*.js webapp/test/e2e

PORT=8080 ../node_modules/.bin/cds run &
CDSPID=$!
echo "Started process with PID ${CDSPID}"

export wdi5_username="alice"
export wdi5_password=""

set +e

npm run wdi5
#../node_modules/.bin/wdio run ./webapp/test/e2e/wdio.conf.js --headless
RC=$?

# clean up
echo Terminate process with PID $CDSPID
kill -s SIGUSR2 $CDSPID

if [ $RC -ne 0 ]; then
echo "Test failed, rc:${RC}"
exit $RC
else
echo "Test succeeded, rc:${RC}"
fi
41 changes: 41 additions & 0 deletions test/wdio.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
let args = ["--window-size=1920,1080"];

if(process.argv.indexOf("--headless") > -1)
args.push("--headless=new");
if(process.argv.indexOf("--debug") > -1)
args.push("--auto-open-devtools-for-tabs");

exports.config = {
specs: ["./**/*.test.js"],
exclude: [
],
maxInstances: 10,
capabilities: [
{
maxInstances: 5,
//
browserName: "chrome",
"goog:chromeOptions": { args },
acceptInsecureCerts: true,
"wdi5:authentication": {
provider: "BasicAuth",
basicAuthUrls: [`http://localhost:8080/odata/v4/admin/Books`]
}
}
],
logLevel: "error",
bail: 0,
baseUrl: "http://localhost:8080/",
waitforTimeout: 10000,
connectionRetryTimeout: process.argv.indexOf("--debug") > -1 ? 1200000 : 120000,
connectionRetryCount: 3,
services: [
"ui5"
],
framework: "mocha",
reporters: ["spec"],
mochaOpts: {
ui: "bdd",
timeout: process.argv.indexOf("--debug") > -1 ? 600000 : 60000
}
}