Ver código fonte

Feature files created to login, add users and logout

Co-authored-by: Yamuna Adhikari <adhikariamuna4444@gmail.com>
SwikritiT 4 anos atrás
pai
commit
1ef46d082c

+ 11 - 0
.gitignore

@@ -41,3 +41,14 @@ htdocs/includes/sebastian/
 htdocs/includes/squizlabs/
 htdocs/includes/webmozart/
 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 - 0
nightwatch.conf.js

@@ -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 - 0
package.json

@@ -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"
+  }
+}

+ 86 - 0
test/acceptance/features/addUsers.feature

@@ -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

+ 29 - 0
test/acceptance/features/listUsers.feature

@@ -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

+ 27 - 0
test/acceptance/features/login.feature

@@ -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 |

+ 10 - 0
test/acceptance/features/logout.feature

@@ -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 - 0
test/acceptance/index.js

@@ -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();
+})

+ 128 - 0
test/acceptance/pageObjects/addUsersPage.js

@@ -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'
+		}
+	}
+};

+ 44 - 0
test/acceptance/pageObjects/homePage.js

@@ -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'
+		}
+	}
+};

+ 47 - 0
test/acceptance/pageObjects/listUsersPage.js

@@ -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'
+		}
+	}
+};

+ 83 - 0
test/acceptance/pageObjects/loginPage.js

@@ -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'
+		}
+	}
+};

+ 34 - 0
test/acceptance/pageObjects/logoutPage.js

@@ -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'
+		}
+	}
+};

+ 123 - 0
test/acceptance/stepDefinitions/addUsersContext.js

@@ -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);
+					}
+				});
+		}
+	}
+});

+ 14 - 0
test/acceptance/stepDefinitions/listUsersContext.js

@@ -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);
+});

+ 22 - 0
test/acceptance/stepDefinitions/loginContext.js

@@ -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);
+});

+ 14 - 0
test/acceptance/stepDefinitions/logoutContext.js

@@ -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();
+});