456 lines
10 KiB
PHP
456 lines
10 KiB
PHP
<?php
|
|
|
|
namespace Laravel\Dusk\Concerns;
|
|
|
|
use Facebook\WebDriver\Interactions\WebDriverActions;
|
|
use Facebook\WebDriver\Remote\LocalFileDetector;
|
|
use Facebook\WebDriver\WebDriverBy;
|
|
use Facebook\WebDriver\WebDriverSelect;
|
|
use Illuminate\Support\Arr;
|
|
|
|
trait InteractsWithElements
|
|
{
|
|
use InteractsWithKeyboard;
|
|
|
|
/**
|
|
* Get all of the elements matching the given selector.
|
|
*
|
|
* @param string $selector
|
|
* @return \Facebook\WebDriver\Remote\RemoteWebElement[]
|
|
*/
|
|
public function elements($selector)
|
|
{
|
|
return $this->resolver->all($selector);
|
|
}
|
|
|
|
/**
|
|
* Get the element matching the given selector.
|
|
*
|
|
* @param string $selector
|
|
* @return \Facebook\WebDriver\Remote\RemoteWebElement|null
|
|
*/
|
|
public function element($selector)
|
|
{
|
|
return $this->resolver->find($selector);
|
|
}
|
|
|
|
/**
|
|
* Click the link with the given text.
|
|
*
|
|
* @param string $link
|
|
* @param string $element
|
|
* @return $this
|
|
*/
|
|
public function clickLink($link, $element = 'a')
|
|
{
|
|
$this->ensurejQueryIsAvailable();
|
|
|
|
$selector = addslashes(trim($this->resolver->format("{$element}")));
|
|
|
|
$link = str_replace("'", "\\\\'", $link);
|
|
|
|
$this->driver->executeScript("jQuery.find(`{$selector}:contains('{$link}'):visible`)[0].click();");
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Directly get or set the value attribute of an input field.
|
|
*
|
|
* @param string $selector
|
|
* @param string|null $value
|
|
* @return $this
|
|
*/
|
|
public function value($selector, $value = null)
|
|
{
|
|
if (is_null($value)) {
|
|
return $this->resolver->findOrFail($selector)->getAttribute('value');
|
|
}
|
|
|
|
$selector = $this->resolver->format($selector);
|
|
|
|
$this->driver->executeScript(
|
|
'document.querySelector('.json_encode($selector).').value = '.json_encode($value).';'
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the text of the element matching the given selector.
|
|
*
|
|
* @param string $selector
|
|
* @return string
|
|
*/
|
|
public function text($selector)
|
|
{
|
|
return $this->resolver->findOrFail($selector)->getText();
|
|
}
|
|
|
|
/**
|
|
* Get the given attribute from the element matching the given selector.
|
|
*
|
|
* @param string $selector
|
|
* @param string $attribute
|
|
* @return string
|
|
*/
|
|
public function attribute($selector, $attribute)
|
|
{
|
|
return $this->resolver->findOrFail($selector)->getAttribute($attribute);
|
|
}
|
|
|
|
/**
|
|
* Send the given keys to the element matching the given selector.
|
|
*
|
|
* @param string $selector
|
|
* @param mixed $keys
|
|
* @return $this
|
|
*/
|
|
public function keys($selector, ...$keys)
|
|
{
|
|
$this->resolver->findOrFail($selector)->sendKeys($this->parseKeys($keys));
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Type the given value in the given field.
|
|
*
|
|
* @param string $field
|
|
* @param string $value
|
|
* @return $this
|
|
*/
|
|
public function type($field, $value)
|
|
{
|
|
$this->resolver->resolveForTyping($field)->clear()->sendKeys($value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Type the given value in the given field slowly.
|
|
*
|
|
* @param string $field
|
|
* @param string $value
|
|
* @param int $pause
|
|
* @return $this
|
|
*/
|
|
public function typeSlowly($field, $value, $pause = 100)
|
|
{
|
|
$this->clear($field)->appendSlowly($field, $value, $pause);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Type the given value in the given field without clearing it.
|
|
*
|
|
* @param string $field
|
|
* @param string $value
|
|
* @return $this
|
|
*/
|
|
public function append($field, $value)
|
|
{
|
|
$this->resolver->resolveForTyping($field)->sendKeys($value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Type the given value in the given field slowly without clearing it.
|
|
*
|
|
* @param string $field
|
|
* @param string $value
|
|
* @param int $pause
|
|
* @return $this
|
|
*/
|
|
public function appendSlowly($field, $value, $pause = 100)
|
|
{
|
|
$characters = preg_split('//u', $value, -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
if (is_array($characters)) {
|
|
foreach ($characters as $character) {
|
|
$this->append($field, $character)->pause($pause);
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Clear the given field.
|
|
*
|
|
* @param string $field
|
|
* @return $this
|
|
*/
|
|
public function clear($field)
|
|
{
|
|
$this->resolver->resolveForTyping($field)->clear();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Select the given value or random value of a drop-down field.
|
|
*
|
|
* @param string $field
|
|
* @param string|array|null $value
|
|
* @return $this
|
|
*/
|
|
public function select($field, $value = null)
|
|
{
|
|
$element = $this->resolver->resolveForSelection($field);
|
|
|
|
$options = $element->findElements(WebDriverBy::cssSelector('option:not([disabled])'));
|
|
|
|
$select = $element->getTagName() === 'select' ? new WebDriverSelect($element) : null;
|
|
|
|
$isMultiple = false;
|
|
|
|
if (! is_null($select)) {
|
|
if ($isMultiple = $select->isMultiple()) {
|
|
$select->deselectAll();
|
|
}
|
|
}
|
|
|
|
if (func_num_args() === 1) {
|
|
$options[array_rand($options)]->click();
|
|
} else {
|
|
$value = collect(Arr::wrap($value))->transform(function ($value) {
|
|
if (is_bool($value)) {
|
|
return $value ? '1' : '0';
|
|
}
|
|
|
|
return (string) $value;
|
|
})->all();
|
|
|
|
foreach ($options as $option) {
|
|
if (in_array((string) $option->getAttribute('value'), $value)) {
|
|
$option->click();
|
|
|
|
if (! $isMultiple) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Select the given value of a radio button field.
|
|
*
|
|
* @param string $field
|
|
* @param string $value
|
|
* @return $this
|
|
*/
|
|
public function radio($field, $value)
|
|
{
|
|
$this->resolver->resolveForRadioSelection($field, $value)->click();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Check the given checkbox.
|
|
*
|
|
* @param string $field
|
|
* @param string|null $value
|
|
* @return $this
|
|
*/
|
|
public function check($field, $value = null)
|
|
{
|
|
$element = $this->resolver->resolveForChecking($field, $value);
|
|
|
|
if (! $element->isSelected()) {
|
|
$element->click();
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Uncheck the given checkbox.
|
|
*
|
|
* @param string $field
|
|
* @param string|null $value
|
|
* @return $this
|
|
*/
|
|
public function uncheck($field, $value = null)
|
|
{
|
|
$element = $this->resolver->resolveForChecking($field, $value);
|
|
|
|
if ($element->isSelected()) {
|
|
$element->click();
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Attach the given file to the field.
|
|
*
|
|
* @param string $field
|
|
* @param string $path
|
|
* @return $this
|
|
*/
|
|
public function attach($field, $path)
|
|
{
|
|
$element = $this->resolver->resolveForAttachment($field);
|
|
|
|
$element->setFileDetector(new LocalFileDetector)->sendKeys($path);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Press the button with the given text or name.
|
|
*
|
|
* @param string $button
|
|
* @return $this
|
|
*/
|
|
public function press($button)
|
|
{
|
|
$this->resolver->resolveForButtonPress($button)->click();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Press the button with the given text or name.
|
|
*
|
|
* @param string $button
|
|
* @param int $seconds
|
|
* @return $this
|
|
*/
|
|
public function pressAndWaitFor($button, $seconds = 5)
|
|
{
|
|
$element = $this->resolver->resolveForButtonPress($button);
|
|
|
|
$element->click();
|
|
|
|
return $this->waitUsing($seconds, 100, function () use ($element) {
|
|
return $element->isEnabled();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Drag an element to another element using selectors.
|
|
*
|
|
* @param string $from
|
|
* @param string $to
|
|
* @return $this
|
|
*/
|
|
public function drag($from, $to)
|
|
{
|
|
(new WebDriverActions($this->driver))->dragAndDrop(
|
|
$this->resolver->findOrFail($from), $this->resolver->findOrFail($to)
|
|
)->perform();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Drag an element up.
|
|
*
|
|
* @param string $selector
|
|
* @param int $offset
|
|
* @return $this
|
|
*/
|
|
public function dragUp($selector, $offset)
|
|
{
|
|
return $this->dragOffset($selector, 0, -$offset);
|
|
}
|
|
|
|
/**
|
|
* Drag an element down.
|
|
*
|
|
* @param string $selector
|
|
* @param int $offset
|
|
* @return $this
|
|
*/
|
|
public function dragDown($selector, $offset)
|
|
{
|
|
return $this->dragOffset($selector, 0, $offset);
|
|
}
|
|
|
|
/**
|
|
* Drag an element to the left.
|
|
*
|
|
* @param string $selector
|
|
* @param int $offset
|
|
* @return $this
|
|
*/
|
|
public function dragLeft($selector, $offset)
|
|
{
|
|
return $this->dragOffset($selector, -$offset, 0);
|
|
}
|
|
|
|
/**
|
|
* Drag an element to the right.
|
|
*
|
|
* @param string $selector
|
|
* @param int $offset
|
|
* @return $this
|
|
*/
|
|
public function dragRight($selector, $offset)
|
|
{
|
|
return $this->dragOffset($selector, $offset, 0);
|
|
}
|
|
|
|
/**
|
|
* Drag an element by the given offset.
|
|
*
|
|
* @param string $selector
|
|
* @param int $x
|
|
* @param int $y
|
|
* @return $this
|
|
*/
|
|
public function dragOffset($selector, $x = 0, $y = 0)
|
|
{
|
|
(new WebDriverActions($this->driver))->dragAndDropBy(
|
|
$this->resolver->findOrFail($selector), $x, $y
|
|
)->perform();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Accept a JavaScript dialog.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function acceptDialog()
|
|
{
|
|
$this->driver->switchTo()->alert()->accept();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Type the given value in an open JavaScript prompt dialog.
|
|
*
|
|
* @param string $value
|
|
* @return $this
|
|
*/
|
|
public function typeInDialog($value)
|
|
{
|
|
$this->driver->switchTo()->alert()->sendKeys($value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Dismiss a JavaScript dialog.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function dismissDialog()
|
|
{
|
|
$this->driver->switchTo()->alert()->dismiss();
|
|
|
|
return $this;
|
|
}
|
|
}
|