BrowserExtension.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. namespace Tests;
  3. use Illuminate\Support\Arr;
  4. use Illuminate\Support\Str;
  5. use Laravel\Dusk\Browser;
  6. use Laravel\Dusk\Component;
  7. use PHPUnit\Framework\Assert as PHPUnit;
  8. trait BrowserExtension
  9. {
  10. public function extendBrowser()
  11. {
  12. $functions = [
  13. // 等待文本可见
  14. 'whenTextAvailable' => function ($text, $callbackOrSeconds = null, $seconds = null) {
  15. $callback = null;
  16. if (is_callable($callbackOrSeconds)) {
  17. $callback = $callbackOrSeconds;
  18. } elseif (is_numeric($callbackOrSeconds)) {
  19. $seconds = $callbackOrSeconds;
  20. }
  21. $text = Arr::wrap($text);
  22. $message = $this->formatTimeOutMessage('Waited %s seconds for text', implode("', '", $text));
  23. return $this->waitUsing($seconds, 100, function () use ($text, $callback) {
  24. $results = Str::contains($this->resolver->findOrFail('')->getText(), $text);
  25. if ($results) {
  26. $callback && $callback($this);
  27. }
  28. return $results;
  29. }, $message);
  30. },
  31. // 等待元素可见
  32. 'whenElementAvailable' => function ($selector, $callbackOrSeconds = null, $seconds = null) {
  33. $callback = null;
  34. if (is_callable($callbackOrSeconds)) {
  35. $callback = $callbackOrSeconds;
  36. } elseif (is_numeric($callbackOrSeconds)) {
  37. $seconds = $callbackOrSeconds;
  38. }
  39. return $this->whenAvailable($selector, function ($value) use ($callback) {
  40. $callback && $callback($value);
  41. }, $seconds);
  42. },
  43. // 判断input框是否存在
  44. 'hasInput' => function ($field) {
  45. /* @var \Facebook\WebDriver\Remote\RemoteWebElement $element */
  46. $this->resolver->resolveForTyping($field);
  47. return $this;
  48. },
  49. // 判断元素是否不可见
  50. 'assertHidden' => function ($selector) {
  51. $fullSelector = $this->resolver->format($selector);
  52. $isHidden = $this->script(
  53. <<<JS
  54. var display = $('{$fullSelector}').css('display');
  55. return display === 'none' || $('{$fullSelector}').is(':hidden');
  56. JS
  57. );
  58. PHPUnit::assertTrue(
  59. (bool) ($isHidden[0] ?? false),
  60. "Element [{$fullSelector}] is displayed."
  61. );
  62. return $this;
  63. },
  64. // 判断是否是给定组件
  65. 'is' => function (Component $component) {
  66. return $this->with($component, function () {
  67. });
  68. },
  69. // 判断文本是否存在,忽略大小写
  70. 'assertSeeTextIn' => function (?string $selector, ?string $text) {
  71. $fullSelector = $this->resolver->format($selector);
  72. $element = $this->resolver->findOrFail($selector);
  73. PHPUnit::assertTrue(
  74. Str::contains(strtolower($element->getText()), strtolower($text)),
  75. "Did not see expected text [{$text}] within element [{$fullSelector}]."
  76. );
  77. return $this;
  78. },
  79. // 判断文本是否存在,忽略大小写
  80. 'assertSeeText' => function (?string $text) {
  81. return $this->assertSeeTextIn('', $text);
  82. },
  83. // 判断全页面中是否存在文本
  84. 'assertSeeInBody' => function (?string $text) {
  85. $resolver = clone $this->resolver;
  86. $resolver->prefix = 'html';
  87. $element = $resolver->findOrFail('');
  88. PHPUnit::assertTrue(
  89. Str::contains(strtolower($element->getText()), strtolower($text)),
  90. "Did not see expected text [{$text}] within element [html]."
  91. );
  92. return $this;
  93. },
  94. // 等待全页面出现文本
  95. 'waitForTextInBody' => function ($text, $seconds = null) {
  96. $text = Arr::wrap($text);
  97. $message = $this->formatTimeOutMessage('Waited %s seconds for text', implode("', '", $text));
  98. $resolver = clone $this->resolver;
  99. $resolver->prefix = 'html';
  100. return $this->waitUsing($seconds, 100, function () use ($resolver, $text) {
  101. return Str::contains($resolver->findOrFail('')->getText(), $text);
  102. }, $message);
  103. },
  104. // 滚动到页面底部
  105. 'scrollToBottom' => function () {
  106. $this->script(
  107. <<<'JS'
  108. $(document).scrollTop($(document).height() - $(window).height());
  109. JS
  110. );
  111. return $this;
  112. },
  113. 'scrollToTop' => function () {
  114. $this->script(
  115. <<<'JS'
  116. $(document).scrollTop(0);
  117. JS
  118. );
  119. return $this;
  120. },
  121. ];
  122. foreach ($functions as $method => $callback) {
  123. Browser::macro($method, $callback);
  124. }
  125. }
  126. public function makeDelayBrowser($browser)
  127. {
  128. return new class($browser)
  129. {
  130. protected $browser;
  131. protected $callbacks = [];
  132. public function __construct(Browser $browser)
  133. {
  134. $this->browser = $browser;
  135. }
  136. public function __call($method, $arguments = [])
  137. {
  138. $this->callbacks[] = [
  139. 'method' => $method,
  140. 'arguments' => $arguments,
  141. ];
  142. return $this;
  143. }
  144. public function __invoke()
  145. {
  146. $browser = $this->browser;
  147. foreach ($this->callbacks as $value) {
  148. $method = $value['method'];
  149. $browser = $browser->{$method}(...$value['arguments']);
  150. }
  151. return $browser;
  152. }
  153. };
  154. }
  155. }