Completed
Push — master ( 97f2e9...a6bde0 )
by Marcel
02:00
created

StartDashboardCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace BeyondCode\DuskDashboard\Console;
4
5
use Clue\React\Buzz\Browser;
6
use Illuminate\Console\Command;
7
use Ratchet\WebSocket\WsServer;
8
use React\EventLoop\LoopInterface;
9
use Symfony\Component\Finder\Finder;
10
use Symfony\Component\Routing\Route;
11
use BeyondCode\DuskDashboard\Watcher;
12
use React\EventLoop\Factory as LoopFactory;
13
use BeyondCode\DuskDashboard\Ratchet\Socket;
14
use BeyondCode\DuskDashboard\DuskProcessFactory;
15
use BeyondCode\DuskDashboard\Ratchet\Server\App;
16
use BeyondCode\DuskDashboard\Ratchet\Http\EventController;
17
use BeyondCode\DuskDashboard\Ratchet\Http\DashboardController;
18
19
class StartDashboardCommand extends Command
20
{
21
    const PORT = 9773;
22
23
    protected $signature = 'dusk:dashboard';
24
25
    protected $description = 'Start the Laravel Dusk Dashboard';
26
27
    /**
28
     * Create a new command instance.
29
     *
30
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
31
     */
32
    public function __construct()
33
    {
34
        parent::__construct();
35
36
        $this->ignoreValidationErrors();
37
    }
38
39
    /** @var App */
40
    protected $app;
41
42
    /** @var LoopInterface */
43
    protected $loop;
44
45
    public function handle()
46
    {
47
        $url = parse_url(config('dusk-dashboard.host', config('app.url')));
48
49
        $this->loop = LoopFactory::create();
50
51
        $this->loop->futureTick(function () use ($url) {
52
            $dashboardUrl = 'http://'.$url['host'].':'.self::PORT.'/dashboard';
53
54
            $this->info('Started Dusk Dashboard on port '.self::PORT);
55
56
            $this->info('Your Dusk tests are now being watched.');
57
58
            $this->info('If the dashboard does not automatically open, visit: '.$dashboardUrl);
59
60
            $this->tryToOpenInBrowser($dashboardUrl);
61
        });
62
63
        $this->createTestWatcher();
64
65
        $this->createApp($url);
0 ignored issues
show
Security Bug introduced by
It seems like $url defined by parse_url(config('dusk-d...t', config('app.url'))) on line 47 can also be of type false; however, BeyondCode\DuskDashboard...ardCommand::createApp() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
66
    }
67
68
    protected function addRoutes()
69
    {
70
        $eventRoute = new Route('/events', ['_controller' => new EventController()], [], [], null, [], ['POST']);
71
72
        $this->app->routes->add('events', $eventRoute);
73
74
        $dashboardRoute = new Route('/dashboard', ['_controller' => new DashboardController()], [], [], null, [], ['GET']);
75
76
        $this->app->routes->add('dashboard', $dashboardRoute);
77
    }
78
79
    protected function createTestWatcher()
80
    {
81
        $finder = (new Finder)
82
            ->name('*.php')
83
            ->files()
84
            ->in($this->getTestSuitePath());
85
86
        (new Watcher($finder, $this->loop))->startWatching(function () {
87
            $client = new Browser($this->loop);
88
89
            $client->post('http://127.0.0.1:'.StartDashboardCommand::PORT.'/events', [
90
                'Content-Type' => 'application/json',
91
            ], json_encode([
92
                    'channel' => 'dusk-dashboard',
93
                    'name' => 'dusk-reset',
94
                    'data' => [],
95
                ])
96
            );
97
98
            $process = DuskProcessFactory::make();
99
100
            $process->start();
101
        });
102
    }
103
104
    protected function getTestSuitePath()
105
    {
106
        $directories = [];
107
108
        if (file_exists(base_path('phpunit.dusk.xml'))) {
109
            $xml = simplexml_load_file(base_path('phpunit.dusk.xml'));
110
111
            foreach ($xml->testsuites->testsuite as $testsuite) {
112
                $directories[] = (string) $testsuite->directory;
113
            }
114
        } else {
115
            $directories[] = base_path('tests/Browser');
116
        }
117
118
        return $directories;
119
    }
120
121
    protected function createApp(array $url)
122
    {
123
        $socket = new Socket();
124
125
        $this->app = new App($url['host'], self::PORT, '0.0.0.0', $this->loop);
126
127
        $this->app->route('/socket', new WsServer($socket), ['*']);
128
129
        $this->addRoutes();
130
131
        $this->app->run();
132
    }
133
134
    protected function tryToOpenInBrowser($url)
135
    {
136
        if (PHP_OS === 'Darwin') {
137
            exec('open '.$url);
138
        }
139
    }
140
}
141