--- /dev/null
+@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
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.
*
];
}
+ /**
+ * 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): void {
+ $session->getDriver()->getWebDriverSession()->keys([
+ 'value' => $keys,
+ ]);
+ }
+
/**
* Finds DOM nodes in the page using named selectors.
*
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.
}
}
+ /**
+ * 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): void {
+ 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): void {
+ 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.
*