MDL-70148 behat: Add steps to send keys without an element

This commit is contained in:
Andrew Nicols
2020-11-17 15:14:43 +08:00
parent 30b0cfafc3
commit 8aadb679dc
3 changed files with 237 additions and 6 deletions
@@ -0,0 +1,52 @@
@tool_behat
Feature: Verify that keyboard steps work as expected
In order to use behat step definitions
As a test writer
I need to verify that the keyboard steps work as expected
@javascript
Scenario: Typing keys into a field causes them to be input
Given the following "users" exist:
| username | email | firstname | lastname | password |
| saffronr | saffron.rutledge@example.com | Saffron | Rutledge | flowerpower |
Given I click on "Log in" "link"
And I click on "Username" "field"
When I type "saffronr"
And I press the tab key
And I type "flowerpower"
And I press enter
Then I should see "You are logged in as Saffron Rutledge"
@javascript
Scenario: Using tab changes focus to the next or previous field
Given I click on "Log in" "link"
And I click on "Username" "field"
And the focused element is "Username" "field"
When I press the tab key
Then the focused element is "Password" "field"
And I press the shift tab key
And the focused element is "Username" "field"
@javascript
Scenario: Using the arrow keys allows me to navigate through menus
Given the following "users" exist:
| username | email | firstname | lastname |
| saffronr | saffron.rutledge@example.com | Saffron | Rutledge |
And I log in as "saffronr"
And I click on "Saffron Rutledge" "link" in the ".usermenu" "css_element"
When I press the up key
Then the focused element is "Log out" "link"
@javascript
Scenario: The escape key can be used to close a dialogue
Given the following "course" exists:
| fullname | C1|
| shortname | C1 |
And I log in as "admin"
And I am on "C1" course homepage
And I navigate to course participants
And I press "Enrol users"
And "Enrol users" "dialogue" should be visible
When I press the escape key
Then "Enrol users" "dialogue" should not be visible
+42
View File
@@ -38,6 +38,10 @@ use Behat\Mink\Session;
require_once(__DIR__ . '/classes/component_named_selector.php');
require_once(__DIR__ . '/classes/component_named_replacement.php');
// Alias the WebDriver\Key class to behat_keys to make future transition to a different WebDriver implementation
// easier.
class_alias('WebDriver\\Key', 'behat_keys');
/**
* Steps definitions base class.
*
@@ -254,6 +258,44 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
];
}
/**
* Send key presses straight to the currently active element.
*
* The `$keys` array contains a list of key values to send to the session as defined in the WebDriver and JsonWire
* specifications:
* - JsonWire: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidkeys
* - W3C WebDriver: https://www.w3.org/TR/webdriver/#keyboard-actions
*
* This may be a combination of typable characters, modifier keys, and other supported keypoints.
*
* The NULL_KEY should be used to release modifier keys. If the NULL_KEY is not used then modifier keys will remain
* in the pressed state.
*
* Example usage:
*
* behat_base::type_keys($this->getSession(), [behat_keys::SHIFT, behat_keys::TAB, behat_keys::NULL_KEY]);
* behat_base::type_keys($this->getSession(), [behat_keys::ENTER, behat_keys::NULL_KEY]);
* behat_base::type_keys($this->getSession(), [behat_keys::ESCAPE, behat_keys::NULL_KEY]);
*
* It can also be used to send text input, for example:
*
* behat_base::type_keys(
* $this->getSession(),
* ['D', 'o', ' ', 'y', 'o', 'u', ' ', 'p', 'l', 'a' 'y', ' ', 'G', 'o', '?', behat_base::NULL_KEY]
* );
*
*
* Please note: This function does not use the element/sendKeys variants but sends keys straight to the browser.
*
* @param Session $session
* @param string[] $keys
*/
public static function type_keys(Session $session, array $keys) {
$session->getDriver()->getWebDriverSession()->keys([
'value' => $keys,
]);
}
/**
* Finds DOM nodes in the page using named selectors.
*
+143 -6
View File
@@ -27,12 +27,12 @@
require_once(__DIR__ . '/../../behat/behat_base.php');
use Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
Behat\Mink\Exception\DriverException as DriverException,
WebDriver\Exception\NoSuchElement as NoSuchElement,
WebDriver\Exception\StaleElementReference as StaleElementReference,
Behat\Gherkin\Node\TableNode as TableNode;
use Behat\Gherkin\Node\TableNode as TableNode;
use Behat\Mink\Exception\DriverException as DriverException;
use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
use Behat\Mink\Exception\ExpectationException as ExpectationException;
use WebDriver\Exception\NoSuchElement as NoSuchElement;
use WebDriver\Exception\StaleElementReference as StaleElementReference;
/**
* Cross component steps definitions.
@@ -1618,6 +1618,143 @@ EOF;
}
}
/**
* Send key presses to the browser without first changing focusing, or applying the key presses to a specific
* element.
*
* Example usage of this step:
* When I type "Penguin"
*
* @When I type :keys
* @param string $keys The key, or list of keys, to type
*/
public function i_type(string $keys) {
behat_base::type_keys($this->getSession(), str_split($keys));
}
/**
* Press a named key with an optional set of modifiers.
*
* Supported named keys are:
* - up
* - down
* - left
* - right
* - pageup|page_up
* - pagedown|page_down
* - home
* - end
* - insert
* - delete
* - backspace
* - escape
* - enter
* - tab
*
* Supported moderators are:
* - shift
* - ctrl
* - alt
* - meta
*
* Example usage of this new step:
* When I press the up key
* When I press the space key
* When I press the shift tab key
*
* Multiple moderator keys can be combined using the '+' operator, for example:
* When I press the ctrl+shift enter key
* When I press the ctrl + shift enter key
*
* @When /^I press the (?P<modifiers_string>.* )?(?P<key_string>.*) key$/
* @param string $modifiers A list of keyboard modifiers, separated by the `+` character
* @param string $key The name of the key to press
*/
public function i_press_named_key(string $modifiers, string $key) {
behat_base::require_javascript_in_session($this->getSession());
$keys = [];
foreach (explode('+', $modifiers) as $modifier) {
switch (strtoupper(trim($modifier))) {
case '':
break;
case 'SHIFT':
$keys[] = behat_keys::SHIFT;
break;
case 'CTRL':
$keys[] = behat_keys::CONTROL;
break;
case 'ALT':
$keys[] = behat_keys::ALT;
break;
case 'META':
$keys[] = behat_keys::META;
break;
default:
throw new \coding_exception("Unknown modifier key '$modifier'}");
}
}
$modifier = trim($key);
switch (strtoupper($key)) {
case 'UP':
$keys[] = behat_keys::UP_ARROW;
break;
case 'DOWN':
$keys[] = behat_keys::DOWN_ARROW;
break;
case 'LEFT':
$keys[] = behat_keys::LEFT_ARROW;
break;
case 'RIGHT':
$keys[] = behat_keys::RIGHT_ARROW;
break;
case 'HOME':
$keys[] = behat_keys::HOME;
break;
case 'END':
$keys[] = behat_keys::END;
break;
case 'INSERT':
$keys[] = behat_keys::INSERT;
break;
case 'BACKSPACE':
$keys[] = behat_keys::BACKSPACE;
break;
case 'DELETE':
$keys[] = behat_keys::DELETE;
break;
case 'PAGEUP':
case 'PAGE_UP':
$keys[] = behat_keys::PAGE_UP;
break;
case 'PAGEDOWN':
case 'PAGE_DOWN':
$keys[] = behat_keys::PAGE_DOWN;
break;
case 'ESCAPE':
$keys[] = behat_keys::ESCAPE;
break;
case 'ENTER':
$keys[] = behat_keys::ENTER;
break;
case 'TAB':
$keys[] = behat_keys::TAB;
break;
case 'SPACE':
$keys[] = behat_keys::SPACE;
break;
default:
throw new \coding_exception("Unknown key '$key'}");
}
// Always send the NULL key as the last key.
$keys[] = behat_keys::NULL_KEY;
behat_base::type_keys($this->getSession(), $keys);
}
/**
* Trigger a keydown event for a key on a specific element.
*