MOBILE-3738 behat: Implement navigation tests

main
Noel De Martin 2021-04-29 13:40:42 +02:00
parent 17006dcc4e
commit 778298a455
3 changed files with 257 additions and 34 deletions

View File

@ -194,6 +194,24 @@
return isElementVisible(element.parentElement, container);
};
/**
* Check if an element is selected.
*
* @param {HTMLElement} element Element
* @param {HTMLElement} container Container
* @returns {boolean} Whether the element is selected or not
*/
var isElementSelected = (element, container) => {
const ariaCurrent = element.getAttribute('aria-current');
if (ariaCurrent && ariaCurrent !== 'false')
return true;
if (!element.parentElement || element.parentElement === container)
return false;
return isElementSelected(element.parentElement, container);
};
/**
* Generic shared function to find possible xpath matches within the document, that are visible,
* and then process them using a callback function.
@ -330,43 +348,61 @@
*/
var behatPressStandard = function(button) {
log('Action - Click standard button: ' + button);
var selector;
switch (button) {
case 'back' :
selector = 'ion-navbar > button.back-button-md';
break;
case 'main menu' :
// Change in app version 3.8.
selector = 'page-core-mainmenu .tab-button > ion-icon[aria-label=more], ' +
'page-core-mainmenu .tab-button > ion-icon[aria-label=menu]';
break;
case 'page menu' :
// This lang string was changed in app version 3.6.
selector = 'core-context-menu > button[aria-label=Info], ' +
'core-context-menu > button[aria-label=Information], ' +
'core-context-menu > button[aria-label="Display options"]';
break;
default:
return 'ERROR: Unsupported standard button type';
}
var buttons = Array.from(document.querySelectorAll(selector));
// Find button
var foundButton = null;
var tooMany = false;
buttons.forEach(function(button) {
if (button.offsetParent) {
if (foundButton === null) {
foundButton = button;
} else {
tooMany = true;
}
if (window.BehatMoodleAppLegacy) {
var selector;
switch (button) {
case 'back' :
selector = 'ion-navbar > button.back-button-md';
break;
case 'main menu' :
// Change in app version 3.8.
selector = 'page-core-mainmenu .tab-button > ion-icon[aria-label=more], ' +
'page-core-mainmenu .tab-button > ion-icon[aria-label=menu]';
break;
case 'page menu' :
// This lang string was changed in app version 3.6.
selector = 'core-context-menu > button[aria-label=Info], ' +
'core-context-menu > button[aria-label=Information], ' +
'core-context-menu > button[aria-label="Display options"]';
break;
default:
return 'ERROR: Unsupported standard button type';
}
var buttons = Array.from(document.querySelectorAll(selector));
var tooMany = false;
buttons.forEach(function(button) {
if (button.offsetParent) {
if (foundButton === null) {
foundButton = button;
} else {
tooMany = true;
}
}
});
if (!foundButton) {
return 'ERROR: Could not find button';
}
if (tooMany) {
return 'ERROR: Found too many buttons';
}
} else {
switch (button) {
case 'back':
foundButton = findElementBasedOnText('Back');
break;
case 'main menu':
foundButton = findElementBasedOnText('more', 'Notifications');
break;
default:
return 'ERROR: Unsupported standard button type';
}
});
if (!foundButton) {
return 'ERROR: Could not find button';
}
if (tooMany) {
return 'ERROR: Found too many buttons';
}
// Click button
foundButton.click();
// Mark busy until the button click finishes processing.
@ -449,6 +485,25 @@
return window.appProvider.appCtrl.getRootNavs()[0];
};
/**
* Check whether an item is selected or not.
*
* @param {string} text Text (full or partial)
* @param {string} near Optional 'near' text
* @return {string} YES or NO if successful, or ERROR: followed by message
*/
var behatIsSelected = function(text, near) {
log(`Action - Is Selected: "${text}"${near ? ` near "${near}"`: ''}`);
try {
const element = findElementBasedOnText(text, near);
return isElementSelected(element, document.body) ? 'YES' : 'NO';
} catch (error) {
return 'ERROR: ' + error.message;
}
}
/**
* Function to press arbitrary item based on its text or Aria label.
*
@ -639,6 +694,7 @@
pressStandard : behatPressStandard,
closePopup : behatClosePopup,
find : behatFind,
isSelected : behatIsSelected,
press : behatPress,
setField : behatSetField,
getHeader : behatGetHeader,

View File

@ -120,6 +120,40 @@ class behat_app extends behat_base {
$this->wait_for_pending_js();
}
/**
* Check if elements are selected in the app.
*
* @Then /^"(?P<text_string>(?:[^"]|\\")*)"(?: near "(?P<near_string>(?:[^"]|\\")*)")? should(?P<not_boolean> not)? be selected in the app$/
* @param string $text
*/
public function be_selected_in_the_app($text, $near='', $not='') {
$not = !empty($not);
$text = addslashes_js($text);
$near = addslashes_js($near);
$this->spin(function() use ($not, $text, $near) {
$result = $this->evaluate_script("return window.behat.isSelected(\"$text\", \"$near\");");
switch ($result) {
case 'YES':
if ($not) {
throw new ExpectationException("Item was selected and shouldn't have", $this->getSession()->getDriver());
}
break;
case 'NO':
if (!$not) {
throw new ExpectationException("Item wasn't selected and should have", $this->getSession()->getDriver());
}
break;
default:
throw new DriverException('Error finding item - ' . $result);
}
return true;
});
$this->wait_for_pending_js();
}
/**
* Checks the Behat setup - tags and configuration.
*

View File

@ -0,0 +1,133 @@
@app @javascript
Feature: It navigates properly between pages.
Background:
Given the following "users" exist:
| username |
| student1 |
Given the following "courses" exist:
| fullname | shortname |
| Course 2 | C2 |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
And the following "grade categories" exist:
| fullname | course |
| Grade category C1 | C1 |
| Grade category C2 | C2 |
And the following "grade items" exist:
| gradecategory | itemname | grademin | grademax | course |
| Grade category C1 | Grade item C1 | 20 | 40 | C1 |
| Grade category C2 | Grade item C2 | 60 | 80 | C2 |
Scenario: Navigate between split-view items in mobiles
# Open more tab
Given I enter the app
And I log in as "student1"
And I press the main menu button in the app
# Open grades tab
When I press "Grades" in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
# Open C1 course grades
When I press "Course 1" in the app
Then the header should be "Grades" in the app
And I should find "Grade category C1" in the app
# Open C1 grade item
When I press "Grade item C1" in the app
Then the header should be "Grade" in the app
And I should find "20" near "Range" in the app
And I should find "40" near "Range" in the app
# Go back to course grades
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Grade category C1" in the app
# Go back to grades tab
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
# Open C2 course grades
When I press "Course 2" in the app
Then the header should be "Grades" in the app
And I should find "Grade category C2" in the app
# Open C2 grade item
When I press "Grade item C2" in the app
Then the header should be "Grade" in the app
And I should find "60" near "Range" in the app
And I should find "80" near "Range" in the app
# Go back to course grades
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Grade category C2" in the app
# Go back to grades tab
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
# Go back to more tab
When I press the back button in the app
Then I should find "Grades" in the app
And I should find "App settings" in the app
But I should not find "Back" in the app
Scenario: Navigate between split-view items in tablets
# Open more tab
Given I enter the app
And I change viewport size to "1200x640"
And I log in as "student1"
# Open grades tab
When I press "Grades" in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
And I should find "Grade category C1" in the app
# Open C1 course grades
When I press "Grade item C1" in the app
Then the header should be "Grades" in the app
And I should find "Grade category C1" in the app
And I should find "20" near "Range" in the app
And I should find "40" near "Range" in the app
# Go back to grades tab
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
# Select C2 course
When I press "Course 2" in the app
Then the header should be "Grades" in the app
And "Course 2" should be selected in the app
And I should find "Grade category C2" in the app
# Open C2 course grades
When I press "Grade item C2" in the app
Then the header should be "Grades" in the app
And I should find "Grade category C2" in the app
And I should find "60" near "Range" in the app
And I should find "80" near "Range" in the app
# Go back to grades tab
When I press the back button in the app
Then the header should be "Grades" in the app
And I should find "Course 1" in the app
And I should find "Course 2" in the app
But I should not find "Back" in the app