Repository: beyondcode/dusk-dashboard Branch: master Commit: b0a018627be0 Files: 36 Total size: 72.5 KB Directory structure: gitextract_fazur9k6/ ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .scrutinizer.yml ├── .styleci.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config/ │ └── dusk-dashboard.php ├── phpunit.xml.dist ├── resources/ │ └── views/ │ └── index.html └── src/ ├── Action.php ├── BrowserActionCollector.php ├── Console/ │ └── StartDashboardCommand.php ├── Dusk/ │ ├── Browser.php │ └── Concerns/ │ ├── InteractsWithAuthentication.php │ ├── InteractsWithCookies.php │ ├── InteractsWithElements.php │ ├── InteractsWithJavascript.php │ ├── InteractsWithMouse.php │ ├── MakesAssertions.php │ ├── MakesUrlAssertions.php │ └── WaitsForElements.php ├── DuskDashboardServiceProvider.php ├── DuskProcessFactory.php ├── Ratchet/ │ ├── Http/ │ │ ├── Controller.php │ │ ├── DashboardController.php │ │ └── EventController.php │ ├── Server/ │ │ ├── App.php │ │ └── HttpServer.php │ └── Socket.php ├── Testing/ │ └── TestCase.php └── Watcher.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ; This file is for unifying the coding style for different editors and IDEs. ; More information at http://editorconfig.org root = true [*] charset = utf-8 indent_size = 4 indent_style = space end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ # Path-based git attributes # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # Ignore all test and documentation with "export-ignore". /.gitattributes export-ignore /.gitignore export-ignore /.travis.yml export-ignore /phpunit.xml.dist export-ignore /.scrutinizer.yml export-ignore /tests export-ignore /.editorconfig export-ignore ================================================ FILE: .gitignore ================================================ build composer.lock docs vendor coverage ================================================ FILE: .scrutinizer.yml ================================================ filter: excluded_paths: [tests/*] checks: php: remove_extra_empty_lines: true remove_php_closing_tag: true remove_trailing_whitespace: true fix_use_statements: remove_unused: true preserve_multiple: false preserve_blanklines: true order_alphabetically: true fix_php_opening_tag: true fix_linefeed: true fix_line_ending: true fix_identation_4spaces: true fix_doc_comments: true ================================================ FILE: .styleci.yml ================================================ preset: laravel disabled: - single_class_element_per_statement ================================================ FILE: .travis.yml ================================================ language: php php: - 7.1 - 7.2 - 7.3 env: matrix: - COMPOSER_FLAGS="--prefer-lowest" - COMPOSER_FLAGS="" before_script: - travis_retry composer self-update - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source script: - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover after_script: - php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to `dusk-dashboard` will be documented in this file ## 1.0.0 - 201X-XX-XX - initial release ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Contributions are **welcome** and will be fully **credited**. Please read and understand the contribution guide before creating an issue or pull request. ## Etiquette This project is open source, and as such, the maintainers give their free time to build and maintain the source code held within. They make the code freely available in the hope that it will be of use to other developers. It would be extremely unfair for them to suffer abuse or anger for their hard work. Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the world that developers are civilized and selfless people. It's the duty of the maintainer to ensure that all submissions to the project are of sufficient quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. ## Viability When requesting or submitting new features, first consider whether it might be useful to others. Open source projects are used by many developers, who may have entirely different needs to your own. Think about whether or not your feature is likely to be used by other users of the project. ## Procedure Before filing an issue: - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. - Check to make sure your feature suggestion isn't already present within the project. - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. - Check the pull requests tab to ensure that the feature isn't already in progress. Before submitting a pull request: - Check the codebase to ensure that your feature doesn't already exist. - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. ## Requirements If the project maintainer has any additional requirements, you will find them listed here. - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). - **Add tests!** - Your patch won't be accepted if it doesn't have tests. - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. **Happy coding**! ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) Beyond Code GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Laravel Dusk Dashboard A beautiful dashboard for your Dusk test suites. [![Latest Version on Packagist](https://img.shields.io/packagist/v/beyondcode/dusk-dashboard.svg?style=flat-square)](https://packagist.org/packages/beyondcode/dusk-dashboard) [![Quality Score](https://img.shields.io/scrutinizer/g/beyondcode/dusk-dashboard.svg?style=flat-square)](https://scrutinizer-ci.com/g/beyondcode/dusk-dashboard) [![Total Downloads](https://img.shields.io/packagist/dt/beyondcode/dusk-dashboard.svg?style=flat-square)](https://packagist.org/packages/beyondcode/dusk-dashboard) ![](https://pociot.dev/storage/22/dusk-dashboard.gif) ## Installation You can install the package via composer: ```bash composer require beyondcode/dusk-dashboard --dev ``` Next up, you need to go to your `DuskTestCase.php` that was installed by Laravel Dusk. You can find this file in your `tests` directory: Find and replace this line: ```php use Laravel\Dusk\TestCase as BaseTestCase; ``` with: ```php use BeyondCode\DuskDashboard\Testing\TestCase as BaseTestCase; ``` ## Usage ``` php artisan dusk:dashboard ``` Check out the documentation [here](https://pociot.dev/8-introducing-laravel-dusk-dashboard). ### Handling Asset Paths Assets may not load or display properly when using relative paths due to port specification. Using Larvel's [Path Helpers](https://laravel.com/docs/5.7/helpers#available-methods) such as the `url()` and `asset()` helpers (Ex: `{{ url('path/to/asset.css') }}`) will help overcome these pathing issues. ### Testing ``` bash composer test ``` ### Changelog Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ### Security If you discover any security related issues, please email marcel@beyondco.de instead of using the issue tracker. ## Credits - [Marcel Pociot](https://github.com/mpociot) - [All Contributors](../../contributors) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. ================================================ FILE: composer.json ================================================ { "name": "beyondcode/dusk-dashboard", "description": "A beautiful dashboard for your Laravel Dusk tests", "keywords": [ "beyondcode", "dusk-dashboard" ], "homepage": "https://github.com/beyondcode/dusk-dashboard", "license": "MIT", "authors": [ { "name": "Marcel Pociot", "email": "marcel@beyondco.de", "homepage": "https://beyondcode.de", "role": "Developer" } ], "require": { "php": "^7.2", "cboden/ratchet": "^0.4.1", "clue/buzz-react": "^2.5", "guzzlehttp/guzzle": "^7.0.1", "illuminate/console": "5.6.*|5.7.*|5.8.*|6.*|7.*|8.*", "illuminate/support": "5.6.*|5.7.*|5.8.*|6.*|7.*|8.*", "laravel/dusk": "^4.0|^5.0|^6.0", "yosymfony/resource-watcher": "^2.0" }, "require-dev": { "larapack/dd": "^1.0", "phpunit/phpunit": "^7.0" }, "autoload": { "psr-4": { "BeyondCode\\DuskDashboard\\": "src" } }, "autoload-dev": { "psr-4": { "BeyondCode\\DuskDashboard\\Tests\\": "tests" } }, "scripts": { "test": "vendor/bin/phpunit", "test-coverage": "vendor/bin/phpunit --coverage-html coverage" }, "config": { "sort-packages": true }, "extra": { "laravel": { "providers": [ "BeyondCode\\DuskDashboard\\DuskDashboardServiceProvider" ] } } } ================================================ FILE: config/dusk-dashboard.php ================================================ env('DUSK_DASHBOARD_URL', env('APP_URL', 'http://localhost')), ]; ================================================ FILE: phpunit.xml.dist ================================================ tests src/ ================================================ FILE: resources/views/index.html ================================================ Dusk Dashboard

Laravel Dusk Dashboard

  • {{ test }}

    • {{ key+1 }}
      {{ event.name }} {{ event.arguments[0] || '' }}
      {{ argument }}
      {{ event.failure }}
DOM Snapshot Before After
================================================ FILE: src/Action.php ================================================ name = $name; $this->arguments = $arguments; $this->html = $html; $this->path = $path; } public function getName() { return $this->name; } public function getHtml() { return $this->html; } public function setPreviousHtml(?string $html) { $this->previousHtml = $html; } public function getPreviousHtml() { return $this->previousHtml; } public function getArguments() { return $this->arguments; } public function getPath() { return $this->path; } } ================================================ FILE: src/BrowserActionCollector.php ================================================ testName = $testName; $this->client = new Client(); } public function collect(string $action, array $arguments, Browser $browser, string $previousHtml = null) { $path = parse_url($browser->driver->getCurrentURL(), PHP_URL_PATH) ?? ''; $action = new Action($action, $arguments, $browser->getCurrentPageSource(), $path); $action->setPreviousHtml($previousHtml); $this->pushAction('dusk-event', [ 'test' => $this->testName, 'path' => $action->getPath(), 'name' => $action->getName(), 'arguments' => $action->getArguments(), 'before' => $action->getPreviousHtml(), 'html' => $action->getHtml(), ]); $this->processPerformanceLog($browser); } protected function processPerformanceLog(Browser $browser) { $logs = collect([]); try { $logs = collect($browser->driver->manage()->getLog('performance')); } catch (\Exception $e) { // performance logging might be disabled. } $allowedMethods = [ 'Network.requestWillBeSent', 'Network.responseReceived', ]; $logs ->map(function ($log) { return json_decode($log['message']); }) ->filter(function ($log) use ($allowedMethods) { $method = data_get($log, 'message.method'); $type = data_get($log, 'message.params.type'); return in_array($method, $allowedMethods) && $type === 'XHR'; })->groupBy(function ($log) { if (data_get($log, 'message.method') === 'Network.requestWillBeSent') { return data_get($log, 'message.requestId'); } return data_get($log, 'params.requestId'); })->map(function ($log) use ($browser) { $this->pushPerformanceLog($log->toArray(), $browser); }); } protected function pushAction(string $name, array $payload) { try { $this->client->post('http://127.0.0.1:'.StartDashboardCommand::PORT.'/events', [ RequestOptions::JSON => [ 'channel' => 'dusk-dashboard', 'name' => $name, 'data' => $payload, ], ]); } catch (\Exception $e) { // Dusk-Dashboard Server might be turned off. No need to panic! } } protected function pushPerformanceLog(array $log, Browser $browser) { $request = $log[0]; $response = $log[1]; $url = parse_url(data_get($request, 'message.params.request.url')); $this->pushAction('dusk-event', [ 'test' => $this->testName, 'name' => 'XHR', 'arguments' => [ data_get($request, 'message.params.request.method').' '. $url['path'].' '. data_get($response, 'message.params.response.status').' '. data_get($response, 'message.params.response.statusText'), ], 'html' => $browser->getCurrentPageSource(), 'logs' => $log, ]); } } ================================================ FILE: src/Console/StartDashboardCommand.php ================================================ ignoreValidationErrors(); } /** @var App */ protected $app; /** @var LoopInterface */ protected $loop; public function handle() { $url = parse_url(config('dusk-dashboard.host', config('app.url'))); $this->loop = LoopFactory::create(); $this->loop->futureTick(function () use ($url) { $dashboardUrl = 'http://'.$url['host'].':'.self::PORT.'/dashboard'; $this->info('Started Dusk Dashboard on port '.self::PORT); $this->info('Your Dusk tests are now being watched.'); $this->info('If the dashboard does not automatically open, visit: '.$dashboardUrl); $this->tryToOpenInBrowser($dashboardUrl); }); $this->createTestWatcher(); $this->createApp($url); } protected function addRoutes() { $eventRoute = new Route('/events', ['_controller' => new EventController()], [], [], null, [], ['POST']); $this->app->routes->add('events', $eventRoute); $dashboardRoute = new Route('/dashboard', ['_controller' => new DashboardController()], [], [], null, [], ['GET']); $this->app->routes->add('dashboard', $dashboardRoute); } protected function createTestWatcher() { $finder = (new Finder) ->name('*.php') ->files() ->in($this->getTestSuitePath()); (new Watcher($finder, $this->loop))->startWatching(function () { $client = new Browser($this->loop); $client->post('http://127.0.0.1:'.self::PORT.'/events', [ 'Content-Type' => 'application/json', ], json_encode([ 'channel' => 'dusk-dashboard', 'name' => 'dusk-reset', 'data' => [], ]) ); $process = DuskProcessFactory::make(); $process->start(); }); } protected function getTestSuitePath() { $directories = []; if (file_exists(base_path('phpunit.dusk.xml'))) { $xml = simplexml_load_file(base_path('phpunit.dusk.xml')); foreach ($xml->testsuites->testsuite as $testsuite) { $directories[] = (string) $testsuite->directory; } } else { $directories[] = base_path('tests/Browser'); } return $directories; } protected function createApp(array $url) { $socket = new Socket(); $this->app = new App($url['host'], self::PORT, '0.0.0.0', $this->loop); $this->app->route('/socket', new WsServer($socket), ['*']); $this->addRoutes(); $this->app->run(); } protected function tryToOpenInBrowser($url) { if (PHP_OS === 'Darwin') { exec('open '.$url); } } } ================================================ FILE: src/Dusk/Browser.php ================================================ actionCollector = $collector; } /** * @return BrowserActionCollector|null */ public function getActionCollector() { return $this->actionCollector; } /** {@inheritdoc} */ public function visit($url) { $browser = parent::visit($url); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function visitRoute($route, $parameters = []) { $browser = parent::visitRoute($route, $parameters); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function refresh() { $browser = parent::refresh(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } public function getCurrentPageSource() { $this->ensurejQueryIsAvailable(); $this->restoreHtml(); return $this->driver->executeScript('return document.documentElement.innerHTML;'); } protected function restoreHtml() { $this->driver->executeScript("jQuery('input').attr('value', function() { return jQuery(this).val(); });"); $this->driver->executeScript("jQuery('input[type=checkbox]').each(function() { jQuery(this).attr('checked', jQuery(this).prop(\"checked\")); });"); $this->driver->executeScript("jQuery('textarea').each(function() { jQuery(this).html(jQuery(this).val()); });"); $this->driver->executeScript("jQuery('input[type=radio]').each(function() { jQuery(this).attr('checked', this.checked); });"); $this->driver->executeScript("jQuery('select option').each(function() { jQuery(this).attr('selected', this.selected); });"); } } ================================================ FILE: src/Dusk/Concerns/InteractsWithAuthentication.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function loginAs($userId, $guard = null) { $browser = parent::loginAs($userId, $guard); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function logout($guard = null) { $browser = parent::logout($guard); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function assertAuthenticated($guard = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertAuthenticated($guard); } /** {@inheritdoc} */ public function assertGuest($guard = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertGuest($guard); } /** {@inheritdoc} */ public function assertAuthenticatedAs($user, $guard = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertAuthenticatedAs($user, $guard); } } ================================================ FILE: src/Dusk/Concerns/InteractsWithCookies.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::cookie($name, $value, $expiry, $options); } /** {@inheritdoc} */ public function plainCookie($name, $value = null, $expiry = null, array $options = []) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::plainCookie($name, $value, $expiry, $options); } /** {@inheritdoc} */ public function addCookie($name, $value, $expiry = null, array $options = [], $encrypt = true) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::addCookie($name, $value, $expiry, $options, $encrypt); } /** {@inheritdoc} */ public function deleteCookie($name) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::deleteCookie($name); } } ================================================ FILE: src/Dusk/Concerns/InteractsWithElements.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::elements($selector); } /** {@inheritdoc} */ public function element($selector) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::element($selector); } /** {@inheritdoc} */ public function clickLink($link, $element = 'a') { $previousHtml = $this->getCurrentPageSource(); $browser = parent::clickLink($link, $element); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function value($selector, $value = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::value($selector, $value); } /** {@inheritdoc} */ public function text($selector) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::text($selector); } /** {@inheritdoc} */ public function attribute($selector, $attribute) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::attribute($selector, $attribute); } /** {@inheritdoc} */ public function keys($selector, ...$keys) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::keys($selector, $keys); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function type($field, $value) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::type($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function append($field, $value) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::append($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function clear($field) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::clear($field); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function select($field, $value = null) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::select($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function radio($field, $value) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::radio($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function check($field, $value = null) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::check($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function uncheck($field, $value = null) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::uncheck($field, $value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function attach($field, $path) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::attach($field, $path); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function press($button) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::press($button); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function pressAndWaitFor($button, $seconds = 5) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::pressAndWaitFor($button, $seconds); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function drag($from, $to) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::drag($from, $to); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function dragUp($selector, $offset) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::dragUp($selector, $offset); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function dragDown($selector, $offset) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::dragDown($selector, $offset); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function dragLeft($selector, $offset) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::dragLeft($selector, $offset); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function dragRight($selector, $offset) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::dragRight($selector, $offset); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function dragOffset($selector, $x = 0, $y = 0) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::dragOffset($selector, $x, $y); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function acceptDialog() { $browser = parent::acceptDialog(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function typeInDialog($value) { $browser = parent::typeInDialog($value); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } /** {@inheritdoc} */ public function dismissDialog() { $browser = parent::dismissDialog(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $browser; } } ================================================ FILE: src/Dusk/Concerns/InteractsWithJavascript.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return $result; } } ================================================ FILE: src/Dusk/Concerns/InteractsWithMouse.php ================================================ getCurrentPageSource(); $browser = parent::moveMouse($xOffset, $yOffset); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function mouseover($selector) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::mouseover($selector); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function click($selector = null) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::click($selector); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function clickAndHold() { $previousHtml = $this->getCurrentPageSource(); $browser = parent::clickAndHold(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } public function doubleClick() { $previousHtml = $this->getCurrentPageSource(); $browser = parent::doubleClick(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function rightClick($selector = null) { $previousHtml = $this->getCurrentPageSource(); $browser = parent::rightClick($selector); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } /** {@inheritdoc} */ public function releaseMouse() { $previousHtml = $this->getCurrentPageSource(); $browser = parent::releaseMouse(); $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this, $previousHtml); return $browser; } } ================================================ FILE: src/Dusk/Concerns/MakesAssertions.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertTitle($title); } /** {@inheritdoc} */ public function assertTitleContains($title) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertTitleContains($title); } /** {@inheritdoc} */ public function assertHasCookie($name, $decrypt = true) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertHasCookie($name, $decrypt); } /** {@inheritdoc} */ public function assertHasPlainCookie($name) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertHasPlainCookie($name); } /** {@inheritdoc} */ public function assertCookieMissing($name, $decrypt = true) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertCookieMissing($name, $decrypt); } /** {@inheritdoc} */ public function assertPlainCookieMissing($name) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPlainCookieMissing($name); } /** {@inheritdoc} */ public function assertCookieValue($name, $value, $decrypt = true) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertCookieValue($name, $value, $decrypt); } /** {@inheritdoc} */ public function assertPlainCookieValue($name, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPlainCookieValue($name, $value); } /** {@inheritdoc} */ public function assertSee($text) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSee($text); } /** {@inheritdoc} */ public function assertSeeIn($selector, $text) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSeeIn($selector, $text); } /** {@inheritdoc} */ public function assertDontSeeIn($selector, $text) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertDontSeeIn($selector, $text); } /** {@inheritdoc} */ public function assertSourceHas($code) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSourceHas($code); } /** {@inheritdoc} */ public function assertSourceMissing($code) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSourceMissing($code); } /** {@inheritdoc} */ public function assertSeeLink($link) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSeeLink($link); } /** {@inheritdoc} */ public function assertDontSeeLink($link) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertDontSeeLink($link); } /** {@inheritdoc} */ public function seeLink($link) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::seeLink($link); } /** {@inheritdoc} */ public function assertInputValue($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertInputValue($field, $value); } /** {@inheritdoc} */ public function assertInputValueIsNot($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertInputValueIsNot($field, $value); } /** {@inheritdoc} */ public function inputValue($field) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::inputValue($field); } /** {@inheritdoc} */ public function assertChecked($field, $value = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertChecked($field, $value); } /** {@inheritdoc} */ public function assertNotChecked($field, $value = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertNotChecked($field, $value); } /** {@inheritdoc} */ public function assertRadioSelected($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertRadioSelected($field, $value); } /** {@inheritdoc} */ public function assertRadioNotSelected($field, $value = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertRadioNotSelected($field, $value); } /** {@inheritdoc} */ public function assertSelected($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSelectHasOption($field, $value); } /** {@inheritdoc} */ public function assertNotSelected($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertNotSelected($field, $value); } /** {@inheritdoc} */ public function assertSelectHasOptions($field, array $values) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSelectHasOptions($field, $values); } /** {@inheritdoc} */ public function assertSelectMissingOptions($field, array $values) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSelectMissingOptions($field, $values); } /** {@inheritdoc} */ public function assertSelectHasOption($field, $value) { return $this->assertSelectHasOptions($field, [$value]); } /** {@inheritdoc} */ public function assertSelectMissingOption($field, $value) { return $this->assertSelectMissingOptions($field, [$value]); } /** {@inheritdoc} */ public function selected($field, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::select($field, $value); } /** {@inheritdoc} */ public function assertValue($selector, $value) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertValue($field, $value); } /** {@inheritdoc} */ public function assertVisible($selector) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertVisible($selector); } /** {@inheritdoc} */ public function assertPresent($selector) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPresent($selector); } /** {@inheritdoc} */ public function assertMissing($selector) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertMissing($selector); } /** {@inheritdoc} */ public function assertDialogOpened($message) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertDialogOpened($message); } /** {@inheritdoc} */ public function assertEnabled($field) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertEnabled($field); } /** {@inheritdoc} */ public function assertDisabled($field) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertDisabled($field); } /** {@inheritdoc} */ public function assertFocused($field) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertFocused($field); } /** {@inheritdoc} */ public function assertNotFocused($field) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertNotFocused($field); } /** {@inheritdoc} */ public function assertVue($key, $value, $componentSelector = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertVue($key, $value, $componentSelector); } /** {@inheritdoc} */ public function assertVueIsNot($key, $value, $componentSelector = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertVueIsNot($key, $value, $componentSelector); } /** {@inheritdoc} */ public function assertVueContains($key, $value, $componentSelector = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertVueContains($key, $value, $componentSelector); } /** {@inheritdoc} */ public function assertVueDoesNotContain($key, $value, $componentSelector = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertVueDoesNotContain($key, $value, $componentSelector); } /** {@inheritdoc} */ public function vueAttribute($componentSelector, $key) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::vueAttribute($componentSelector, $key); } } ================================================ FILE: src/Dusk/Concerns/MakesUrlAssertions.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertUrlIs($url); } /** {@inheritdoc} */ public function assertSchemeIs($scheme) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSchemeIs($scheme); } /** {@inheritdoc} */ public function assertSchemeIsNot($scheme) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertSchemeIsNot($scheme); } /** {@inheritdoc} */ public function assertHostIs($host) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertHostIs($host); } /** {@inheritdoc} */ public function assertHostIsNot($host) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertHostIsNot($host); } /** {@inheritdoc} */ public function assertPortIs($port) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPortIs($port); } /** {@inheritdoc} */ public function assertPortIsNot($port) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPortIsNot($port); } /** {@inheritdoc} */ public function assertPathIs($path) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPathIs($path); } /** {@inheritdoc} */ public function assertPathBeginsWith($path) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPathBeginsWith($path); } /** {@inheritdoc} */ public function assertPathIsNot($path) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertPathIsNot($path); } /** {@inheritdoc} */ public function assertFragmentIs($fragment) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertFragmentIs($fragment); } /** {@inheritdoc} */ public function assertFragmentBeginsWith($fragment) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertFragmentBeginsWith($fragment); } /** {@inheritdoc} */ public function assertFragmentIsNot($fragment) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertFragmentIsNot($fragment); } /** {@inheritdoc} */ public function assertRouteIs($route, $parameters = []) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertRouteIs($route, $parameters); } /** {@inheritdoc} */ public function assertQueryStringHas($name, $value = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertQueryStringHas($name, $value); } /** {@inheritdoc} */ public function assertQueryStringMissing($name) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertQueryStringMissing($name); } /** {@inheritdoc} */ protected function assertHasQueryStringParameter($name) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::assertHasQueryStringParameter($name); } } ================================================ FILE: src/Dusk/Concerns/WaitsForElements.php ================================================ actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::whenAvailable($selector, $callback, $seconds); } /** {@inheritdoc} */ public function waitFor($selector, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitFor($selector, $seconds); } /** {@inheritdoc} */ public function waitUntilMissing($selector, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitUntilMissing($selector, $seconds); } /** {@inheritdoc} */ public function waitForText($text, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForText($text, $seconds); } /** {@inheritdoc} */ public function waitForLink($link, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForLink($link, $seconds); } /** {@inheritdoc} */ public function waitForLocation($path, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForLocation($path, $seconds); } /** {@inheritdoc} */ public function waitForRoute($route, $parameters = [], $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForRoute($route, $parameters, $seconds); } /** {@inheritdoc} */ public function waitUntil($script, $seconds = null, $message = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitUntil($script, $seconds, $message); } /** {@inheritdoc} */ public function waitForDialog($seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForDialog($seconds); } /** {@inheritdoc} */ public function waitForReload($callback = null, $seconds = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitForReload($callback, $seconds); } /** {@inheritdoc} */ public function waitUsing($seconds, $interval, Closure $callback, $message = null) { $this->actionCollector->collect(__FUNCTION__, func_get_args(), $this); return parent::waitUsing($seconds, $interval, $callback, $message); } } ================================================ FILE: src/DuskDashboardServiceProvider.php ================================================ commands([ Console\StartDashboardCommand::class, ]); } } ================================================ FILE: src/DuskProcessFactory.php ================================================ send( str(new Response( 200, ['Content-Type' => 'text/html'], file_get_contents(__DIR__.'/../../../resources/views/index.html') )) ); $connection->close(); } } ================================================ FILE: src/Ratchet/Http/EventController.php ================================================ send($request->getBody()); } $conn->send(str(new Response(200))); } catch (Exception $e) { $conn->send(str(new Response(500, [], $e->getMessage()))); } $conn->close(); } } ================================================ FILE: src/Ratchet/Server/App.php ================================================ httpHost = $httpHost; $this->port = $port; $socket = new Reactor($address.':'.$port, $loop); $this->routes = new RouteCollection; $urlMatcher = new UrlMatcher($this->routes, new RequestContext); $router = new Router($urlMatcher); $httpServer = new HttpServer($router); $this->_server = new IoServer($httpServer, $socket, $loop); } } ================================================ FILE: src/Ratchet/Server/HttpServer.php ================================================ _reqParser->maxSize = 5242880; } } ================================================ FILE: src/Ratchet/Socket.php ================================================ method === 'startTests') { $process = DuskProcessFactory::make(); $process->start(); } } public function onClose(ConnectionInterface $connection) { } public function onError(ConnectionInterface $connection, \Exception $e) { } } ================================================ FILE: src/Testing/TestCase.php ================================================ setActionCollector(new BrowserActionCollector($this->getTestName())); } static::$browsers = $browsers; return static::$browsers; } protected function getTestName() { return class_basename(static::class).'::'.$this->getName(); } protected function enableNetworkLogging(DesiredCapabilities $capabilities): DesiredCapabilities { $chromeOptions = $capabilities->getCapability(ChromeOptions::CAPABILITY); $perfLoggingPrefs = new \stdClass(); $perfLoggingPrefs->enableNetwork = true; $chromeOptions->setExperimentalOption('perfLoggingPrefs', $perfLoggingPrefs); $capabilities->setCapability(ChromeOptions::CAPABILITY, $chromeOptions); $loggingPrefs = new \stdClass(); $loggingPrefs->browser = 'ALL'; $loggingPrefs->performance = 'ALL'; $capabilities->setCapability('loggingPrefs', $loggingPrefs); return $capabilities; } protected function onNotSuccessfulTest(Throwable $t): void { try { (new Client())->post('http://127.0.0.1:'.StartDashboardCommand::PORT.'/events', [ RequestOptions::JSON => [ 'channel' => 'dusk-dashboard', 'name' => 'dusk-failure', 'data' => [ 'message' => $t->getMessage(), ], ], ]); } catch (\Exception $e) { // Dashboard is offline } finally { parent::onNotSuccessfulTest($t); } } } ================================================ FILE: src/Watcher.php ================================================ finder = $finder; $this->loop = $loop; } public function startWatching(Closure $callback) { $hashContent = new Crc32ContentHash(); $watcher = new ResourceWatcher(new ResourceCacheMemory(), $this->finder, $hashContent); $this->loop->addPeriodicTimer(1 / 2, function () use ($watcher, $callback) { $watcher_result = $watcher->findChanges(); if ($watcher_result->hasChanges()) { call_user_func($callback); } }); } }