2
0
forked from Wavyzz/dolibarr

Feature files created to login, add users and logout

Co-authored-by: Yamuna Adhikari <adhikariamuna4444@gmail.com>
This commit is contained in:
SwikritiT
2020-08-18 10:30:22 +05:45
parent ff739dcbd2
commit 1ef46d082c
17 changed files with 728 additions and 0 deletions

11
.gitignore vendored
View File

@@ -41,3 +41,14 @@ htdocs/includes/sebastian/
htdocs/includes/squizlabs/ htdocs/includes/squizlabs/
htdocs/includes/webmozart/ htdocs/includes/webmozart/
htdocs/.well-known/apple-developer-merchantid-domain-association htdocs/.well-known/apple-developer-merchantid-domain-association
# Node Modules
build/yarn-error.log
build/node_modules/
node_modules/
#yarn
yarn.lock
#package-lock
package-lock.json

29
nightwatch.conf.js Normal file
View File

@@ -0,0 +1,29 @@
const admin_username = process.env.ADMIN_USERNAME || 'dolibarr';
const admin_password = process.env.ADMIN_PASSWORD || 'password';
const launch_url = process.env.LAUNCH_URL || 'http://localhost/dolibarr/htdocs/';
const dol_api_key = process.env.DOLAPIKEY || 'superadminuser';
module.exports = {
page_objects_path : './test/acceptance/pageObjects/', // jshint ignore:line
src_folders : ['test'],
test_settings : {
default : {
selenium_host : '127.0.0.1',
launchUrl : launch_url,
globals : {
backend_url : launch_url,
adminUsername : admin_username,
adminPassword : admin_password,
dolApiKey : dol_api_key
},
desiredCapabilities : {
browserName : 'chrome',
javascriptEnabled : true,
chromeOptions : {
args : ['disable-gpu'],
w3c : false
}
}
}
}
};

13
package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"devDependencies": {
"cucumber": "^6.0.5",
"nightwatch": "^1.4.1",
"nightwatch-api": "^3.0.1"
},
"scripts": {
"test:e2e": "node_modules/cucumber/bin/cucumber-js --require test/acceptance/index.js --require test/acceptance/stepDefinitions"
},
"dependencies": {
"node-fetch": "^2.6.1"
}
}

View File

@@ -0,0 +1,86 @@
Feature: Add user
As an admin
I want to add users
So that the authorized access is possible
Background:
Given the administrator has logged in using the webUI
And the administrator has browsed to the new users page
Scenario: Admin adds user without permission
When the admin creates user with following details
| last name | Potter |
| login | harrypotter@gmail.com |
| password | password |
Then new user "Potter" should be created
And message "This user has no permissions defined" should be displayed in the webUI
Scenario Outline: Admin adds user with permission
When the admin creates user with following details
| last name | Potter |
| login | harrypotter@gmail.com |
| password | password |
| administrator | <administrator> |
| gender | <gender> |
Then message "This user has no permissions defined" <shouldOrShouldNot> be displayed in the webUI
And new user "Potter" should be created
Examples:
| administrator | gender | shouldOrShouldNot |
| No | | should |
| No | Man | should |
| No | Woman | should |
| Yes | | should not |
| Yes | Man | should not |
| Yes | Woman | should not |
Scenario Outline: Admin adds user with last name as special characters
When the admin creates user with following details
| last name | <last name> |
| login | harry |
| password | password |
Then message "This user has no permissions defined" should be displayed in the webUI
And new user "<last name>" should be created
Examples:
| last name |
| swi@ |
| g!!@%ui |
| swikriti@h |
| !@#$%^&*()-_+=[]{}:;,.<>?~ |
| $w!kr!t! |
| España§àôœ |
| |
| ि $%#?&@name.txt |
Scenario Outline: Admin adds user with incomplete essential credentials
When the admin creates user with following details
| last name | <last name> |
| login | <login> |
| password | <password> |
Then message "<message>" should be displayed in the webUI
And new user "<last name>" should not be created
Examples:
| last name | login | password | message |
| | | | Name is not defined.\nLogin is not defined. |
| Joseph | | | Login is not defined. |
| | john@gmail.com | | Name is not defined. |
| Joseph | | hihi | Login is not defined. |
Scenario: Admin adds user with incomplete essential credentials
When the admin creates user with following details
| last name | Doe |
| login | John |
| password | |
Then message "This user has no permissions defined" should be displayed in the webUI
And new user "Doe" should be created
Scenario: Admin tries to add user with pre-existing login credential
Given a user has been created with following details
| login | last name | password |
| Tyler | Joseph | pass1234 |
And the administrator has browsed to the new users page
When the admin creates user with following details
| last name | Dun |
| login | Tyler |
| password | pass1234 |
Then message "Login already exists." should be displayed in the webUI
And new user "Dun" should not be created

View File

@@ -0,0 +1,29 @@
Feature: list users
As an admin user
I want to view the list of users
So that I can manage users
Background:
Given the administrator has logged in using the webUI
Scenario: Admin user should be able to see list of created users when no new users are created
When the administrator browses to the list of users page using the webUI
Then following users should be displayed in the users list
| login | last name |
| dolibarr | SuperAdmin |
And the number of created users should be 1
Scenario: Admin user should be able to see number of created users
Given the admin has created the following users
| login | last name | password |
| Harry | Potter | hello123 |
| Hermoine | Granger | hello123 |
| Ron | Weasley | hello123 |
When the administrator browses to the list of users page using the webUI
Then following users should be displayed in the users list
| login | last name |
| dolibarr | SuperAdmin |
| Harry | Potter |
| Hermoine | Granger |
| Ron | Weasley |
And the number of created users should be 4

View File

@@ -0,0 +1,27 @@
Feature: user login
As a user/admin
I want to login to my account
So that I can have access to my functionality
Background:
Given the user has browsed to the login page
Scenario: Admin user should be able to login successfully
When user logs in with username "dolibarr" and password "password"
Then the user should be directed to the homepage
Scenario: Admin user with empty credentials should not be able to login
When user logs in with username "" and password ""
Then the user should not be able to login
Scenario Outline: user logs in with invalid credentials
When user logs in with username "<username>" and password "<password>"
Then the user should not be able to login
And error message "Bad value for login or password" should be displayed in the webUI
Examples:
| username | password |
| dolibar | pass |
| dolibarr | passw |
| dolibar | |
| dolibarr | |
| dolibar | password |

View File

@@ -0,0 +1,10 @@
Feature: user logs out
As a user
I want to log out of my account
So that I can protect my work, identity and be assured of my privacy
Scenario: User can logout
Given the administrator has logged in using the webUI
When the user opens the user profile using the webUI
And the user logs out using the webUI
Then the user should be logged out successfully

14
test/acceptance/index.js Normal file
View File

@@ -0,0 +1,14 @@
const { setDefaultTimeout, After, Before } = require('cucumber')
const { createSession, closeSession, startWebDriver, stopWebDriver } = require('nightwatch-api')
setDefaultTimeout(60000)
Before(async () => {
await startWebDriver();
await createSession();
})
After(async () => {
await closeSession();
await stopWebDriver();
})

View File

@@ -0,0 +1,128 @@
const util = require('util');
module.exports = {
url: function () {
return this.api.launchUrl + 'user/card.php?leftmenu=users&action=create';
},
commands: [
{
adminCreatesUser: async function (dataTable) {
const userDetails = dataTable.rowsHash();
let administrator = userDetails['administrator'];
let gender = userDetails['gender'];
await this.waitForElementVisible('@newUserAddOption')
.useXpath()
.waitForElementVisible('@lastnameField')
.clearValue('@lastnameField')
.setValue('@lastnameField', userDetails['last name'])
.waitForElementVisible('@loginField')
.clearValue('@loginField')
.setValue('@loginField', userDetails['login'])
.waitForElementVisible('@newUserPasswordField')
.clearValue('@newUserPasswordField')
.setValue('@newUserPasswordField', userDetails['password']);
if (userDetails['administrator']) {
const admin = util.format(this.elements.administratorSelectOption.selector, administrator);
await this.waitForElementVisible('@administratorField')
.click('@administratorField')
.waitForElementVisible(admin)
.click(admin);
}
if (userDetails['gender']) {
const genderValue = util.format(this.elements.genderSelectOption.selector, gender)
await this.waitForElementVisible('@genderField')
.click('@genderField')
.waitForElementVisible(genderValue)
.click(genderValue);
}
return this.waitForElementVisible('@submitButton')
.click('@submitButton')
.useCss();
},
noPermissionMessage: async function (message) {
await this.useXpath()
.waitForElementVisible('@noPermissionDefinedMessage')
.expect.element('@noPermissionDefinedMessage')
.text.to.equal(message);
return this.useCss();
},
newUserShouldBeCreated: async function (lastname) {
await this.useXpath()
.waitForElementVisible('@newUserCreated')
.expect.element('@newUserCreated')
.text.to.equal(lastname);
return this.useCss();
},
noPermissionDefinedMessageNotShown: function (message) {
return this.useXpath()
.waitForElementNotPresent('@noPermissionDefinedMessage')
.useCss();
},
userNotCreated: function (lastname) {
return this.waitForElementVisible('@newUserAddOption');
}
}
],
elements: {
newUserAddOption: {
selector: '.fiche'
},
lastnameField: {
selector: '//table[@class="border centpercent"]/tbody/tr/td//input[@id="lastname"]',
locateStrategy: 'xpath'
},
loginField: {
selector: '//table[@class="border centpercent"]/tbody/tr/td//input[@name="login"]',
locateStrategy: 'xpath'
},
newUserPasswordField: {
selector: '//table[@class="border centpercent"]/tbody/tr/td//input[@name="password"]',
locateStrategy: 'xpath'
},
submitButton: {
selector: '//div[@class="center"]/input[@class="button"]',
locateStrategy: 'xpath'
},
administratorField: {
selector: '//table[@class="border centpercent"]/tbody/tr/td//select[@id="admin"]',
locateStrategy: 'xpath'
},
administratorSelectOption: {
selector: '//select[@id="admin"]/option[.="%s"]',
locateStrategy: 'xpath'
},
genderField: {
selector: '//table[@class="border centpercent"]/tbody/tr/td//select[@id="gender"]',
locateStrategy: 'xpath'
},
genderSelectOption: {
selector: '//select[@id="gender"]/option[.="%s"]',
locateStrategy: 'xpath'
},
noPermissionDefinedMessage: {
selector: '//div[@class="jnotify-message"]',
locateStrategy: 'xpath'
},
newUserCreated: {
selector: '//div[contains(@class,"valignmiddle")]//div[contains(@class,"inline-block floatleft valignmiddle")]',
locateStrategy: 'xpath'
}
}
};

View File

@@ -0,0 +1,44 @@
module.exports = {
url: function () {
return this.api.launchUrl + 'admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete';
},
commands: [
{
browsedToNewUserPage: function () {
return this.useXpath()
.waitForElementVisible('@usersAndGroups')
.click('@usersAndGroups')
.waitForElementVisible('@newUser')
.click('@newUser')
.useCss();
},
browsedToListOfUsers: function () {
return this.useXpath()
.waitForElementVisible('@usersAndGroups')
.click('@usersAndGroups')
.waitForElementVisible('@listOfUsers')
.click('@listOfUsers')
.useCss();
}
}
],
elements: {
usersAndGroups: {
selector: '//div[@class="menu_titre"]/a[@title="Users & Groups"]',
locateStrategy: 'xpath'
},
newUser: {
selector: '//div[@class="menu_contenu menu_contenu_user_card"]/a[@title="New user"]',
locateStrategy: 'xpath'
},
listOfUsers: {
selector: '//a[@class="vsmenu"][@title="List of users"]',
locateStrategy: 'xpath'
}
}
};

View File

@@ -0,0 +1,47 @@
const util = require('util');
module.exports = {
url: function () {
return this.api.launchUrl + 'user/list.php?leftmenu=users';
},
commands: [
{
listOfUsersDisplayed: async function (dataTable) {
const usersList = dataTable.hashes();
this.useXpath();
for (const row of usersList) {
let login = row['login'];
let lastName = row['last name'];
const userDetail = util.format(this.elements.userList.selector, login, lastName);
await this.waitForElementVisible('@userRow')
.waitForElementVisible(userDetail);
}
return this.useCss();
},
numberOfUsersDisplayed: async function (number) {
const userCount = util.format(this.elements.numberOfUsers.selector, number);
await this.useXpath()
.waitForElementVisible(userCount);
return this.useCss();
}
}
],
elements: {
userRow: {
selector: '//table[contains(@class,"tagtable liste")]/tbody/tr[position()>2]',
locateStrategy: 'xpath'
},
numberOfUsers: {
selector: '//div[contains(@class, "titre inline-block") and contains(., "List of users")]/span[.="(%d)"]',
locateStrategy: 'xpath'
},
userList: {
selector: '//table[contains(@class,"tagtable liste")]/tbody/tr[position()>2]/td/a//span[normalize-space(@class="nopadding usertext")][.="%s"]/../../following-sibling::td[.="%s"]',
locateStrategy: 'xpath'
}
}
};

View File

@@ -0,0 +1,83 @@
module.exports = {
url: function () {
return this.api.launchUrl;
},
commands: [
{
waitForLoginPage: function () {
return this.waitForElementVisible('@loginTable');
},
userLogsInWithUsernameAndPassword: function (username, password) {
return this.waitForElementVisible('@userNameField')
.setValue('@userNameField', username)
.waitForElementVisible('@passwordField')
.setValue('@passwordField', password)
.useXpath()
.waitForElementVisible('@loginButton')
.click('@loginButton')
.useCss();
},
successfulLogin: function () {
return this.waitForElementNotPresent('@loginTable')
.waitForElementVisible('@userProfileDropdown');
},
userIsLoggedIn: async function (login) {
await this.useXpath()
.waitForElementVisible('@userLogin')
.expect.element('@userLogin')
.text.to.equal(login);
return this.useCss();
},
unsuccessfulLogin: function () {
return this.waitForElementVisible('@loginTable')
.waitForElementNotPresent('@userProfileDropdown');
},
loginErrorDisplayed: async function (errorMessage) {
await this.useXpath()
.waitForElementVisible('@loginError')
.expect.element('@loginError')
.text.to.equal(errorMessage);
return this.useCss();
}
}
],
elements: {
loginButton: {
selector: '//div[@id="login-submit-wrapper"]/input[@type="submit"]',
locateStrategy: 'xpath'
},
userNameField: {
selector: '#username'
},
passwordField: {
selector: '#password'
},
loginTable: {
selector: '.login_table'
},
userProfileDropdown: {
selector: '#topmenu-login-dropdown'
},
userLogin: {
selector: '//div[@id="topmenu-login-dropdown"]/a//span[contains(@class,"atoploginusername")]',
locateStrategy: 'xpath'
},
loginError: {
selector: '//div[@class="center login_main_message"]/div[@class="error"]',
locateStrategy: 'xpath'
}
}
};

View File

@@ -0,0 +1,34 @@
module.exports = {
url: function () {
return this.api.launchUrl + 'admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete';
},
commands:
[
{
userOpensProfile: async function () {
await this.useXpath()
.waitForElementVisible('@userProfileDropdown')
.click('@userProfileDropdown')
return this.useCss();
},
userLogsOut: function () {
return this.waitForElementVisible('@logoutButton')
.click('@logoutButton');
}
}
],
elements: {
logoutButton: {
selector: '.pull-right'
},
userProfileDropdown: {
selector: '//div[@id="topmenu-login-dropdown"]',
locateStrategy: 'xpath'
}
}
};

View File

@@ -0,0 +1,123 @@
const { Before, Given, When, Then, After } = require('cucumber');
const { client } = require('nightwatch-api');
const fetch = require('node-fetch');
let initialUsers = {};
Given('the administrator has logged in using the webUI', async function () {
await client.page.loginPage().navigate().waitForLoginPage();
await client.page.loginPage().userLogsInWithUsernameAndPassword(client.globals.adminUsername, client.globals.adminPassword);
return client.page.loginPage().userIsLoggedIn(client.globals.adminUsername);
});
Given('the administrator has browsed to the new users page', function () {
return client.page.homePage().browsedToNewUserPage();
});
When('the admin creates user with following details', function (datatable) {
return client.page.addUsersPage().adminCreatesUser(datatable);
});
Then('new user {string} should be created', function (lastname) {
return client.page.addUsersPage().newUserShouldBeCreated(lastname);
});
Then('message {string} should be displayed in the webUI', function (message) {
return client.page.addUsersPage().noPermissionMessage(message);
});
Then('message {string} should not be displayed in the webUI', function (message) {
return client.page.addUsersPage().noPermissionDefinedMessageNotShown(message);
});
Then('new user {string} should not be created', function (lastname) {
return client.page.addUsersPage().userNotCreated(lastname);
});
Given('a user has been created with following details', function (dataTable) {
return adminHasCreatedUser(dataTable);
});
Given('the admin has created the following users', function (dataTable) {
return adminHasCreatedUser(dataTable);
});
const getUsers = async function () {
const header = {};
const url = client.globals.backend_url + 'api/index.php/users';
const users = {};
header['Accept'] = 'application/json';
header['DOLAPIKEY'] = client.globals.dolApiKey;
await fetch(url, {
method: 'GET',
headers: header
})
.then(async (response) => {
const json_response = await response.json();
for (const user of json_response) {
users[user.id] = user.id;
}
});
return users;
};
const adminHasCreatedUser = async function (dataTable) {
const header = {};
const url = client.globals.backend_url + 'api/index.php/users';
header['Accept'] = 'application/json';
header['DOLAPIKEY'] = client.globals.dolApiKey;
header['Content-Type'] = 'application/json';
const userDetails = dataTable.hashes();
for (const user of userDetails) {
await fetch(url, {
method: 'POST',
headers: header,
body: JSON.stringify(
{
login: user['login'],
lastname: user['last name'],
pass: user['password']
}
)
})
.then((response) => {
if (response.status < 200 || response.status >= 400) {
throw new Error('Failed to create user: ' + user['login'] +
' ' + response.statusText);
}
return response.text();
});
}
};
Before(async () => {
initialUsers = await getUsers();
});
After(async () => {
const finalUsers = await getUsers();
const header = {};
const url = client.globals.backend_url + 'api/index.php/users/';
header['Accept'] = 'application/json';
header['DOLAPIKEY'] = client.globals.dolApiKey;
let found;
for (const finaluser in finalUsers) {
for (const initialuser in initialUsers) {
found = false;
if (initialuser === finaluser) {
found = true;
break;
}
}
if (!found) {
await fetch(url + finaluser, {
method: 'DELETE',
headers: header
})
.then(res => {
if (res.status < 200 || res.status >= 400) {
throw new Error("Failed to delete user: " + res.statusText);
}
});
}
}
});

View File

@@ -0,0 +1,14 @@
const { When, Then } = require('cucumber');
const { client } = require('nightwatch-api');
When('the administrator browses to the list of users page using the webUI', function () {
return client.page.homePage().browsedToListOfUsers();
});
Then('following users should be displayed in the users list', function (dataTable) {
return client.page.listUsersPage().listOfUsersDisplayed(dataTable);
});
Then('the number of created users should be {int}', function (number) {
return client.page.listUsersPage().numberOfUsersDisplayed(number);
});

View File

@@ -0,0 +1,22 @@
const { Given, When, Then } = require('cucumber')
const { client } = require('nightwatch-api')
Given('the user has browsed to the login page', function () {
return client.page.loginPage().navigate();
});
When('user logs in with username {string} and password {string}', function (username, password) {
return client.page.loginPage().userLogsInWithUsernameAndPassword(username, password);
});
Then('the user should be directed to the homepage', function () {
return client.page.loginPage().successfulLogin();
});
Then('the user should not be able to login', function () {
return client.page.loginPage().unsuccessfulLogin();
});
Then('error message {string} should be displayed in the webUI', function (errormessage) {
return client.page.loginPage().loginErrorDisplayed(errormessage);
});

View File

@@ -0,0 +1,14 @@
const { When, Then } = require('cucumber');
const { client } = require('nightwatch-api');
When('the user opens the user profile using the webUI', function () {
return client.page.logoutPage().userOpensProfile();
});
When('the user logs out using the webUI', function () {
return client.page.logoutPage().userLogsOut();
});
Then('the user should be logged out successfully', function () {
return client.page.loginPage().waitForLoginPage();
});