diff --git a/LICENSE.md b/LICENSE.md index 79810c848..656e4b794 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) Taylor Otwell +Copyright (c) Jonas Staudenmeir Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index e73af996e..8158e73d7 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,8 @@ ], "require": { "php": ">=7.1.0", + "ext-json": "*", + "ext-zip": "*", "facebook/webdriver": "^1.3", "nesbot/carbon": "^1.20|^2.0", "illuminate/console": "~5.7.0|~5.8.0|~5.9.0", diff --git a/src/Chrome/ChromeProcess.php b/src/Chrome/ChromeProcess.php index e07e2eb3b..db44e1633 100644 --- a/src/Chrome/ChromeProcess.php +++ b/src/Chrome/ChromeProcess.php @@ -3,7 +3,7 @@ namespace Laravel\Dusk\Chrome; use RuntimeException; -use Illuminate\Support\Str; +use Laravel\Dusk\OperatingSystem; use Symfony\Component\Process\Process; class ChromeProcess @@ -44,14 +44,12 @@ public function toProcess(array $arguments = []) if ($this->onWindows()) { $this->driver = realpath(__DIR__.'/../../bin/chromedriver-win.exe'); - - return $this->process($arguments); + } elseif ($this->onMac()) { + $this->driver = realpath(__DIR__.'/../../bin/chromedriver-mac'); + } else { + $this->driver = realpath(__DIR__.'/../../bin/chromedriver-linux'); } - $this->driver = $this->onMac() - ? realpath(__DIR__.'/../../bin/chromedriver-mac') - : realpath(__DIR__.'/../../bin/chromedriver-linux'); - return $this->process($arguments); } @@ -89,7 +87,7 @@ protected function chromeEnvironment() */ protected function onWindows() { - return PHP_OS === 'WINNT' || Str::contains(php_uname(), 'Microsoft'); + return OperatingSystem::onWindows(); } /** @@ -99,6 +97,6 @@ protected function onWindows() */ protected function onMac() { - return PHP_OS === 'Darwin'; + return OperatingSystem::onMac(); } } diff --git a/src/Console/ChromeDriverCommand.php b/src/Console/ChromeDriverCommand.php new file mode 100644 index 000000000..1784ebee6 --- /dev/null +++ b/src/Console/ChromeDriverCommand.php @@ -0,0 +1,220 @@ + 'linux64', + 'mac' => 'mac64', + 'win' => 'win32', + ]; + + /** + * The legacy versions for the ChromeDriver. + * + * @var array + */ + protected $legacyVersions = [ + 43 => '2.20', + 44 => '2.20', + 45 => '2.20', + 46 => '2.21', + 47 => '2.21', + 48 => '2.21', + 49 => '2.22', + 50 => '2.22', + 51 => '2.23', + 52 => '2.24', + 53 => '2.26', + 54 => '2.27', + 55 => '2.28', + 56 => '2.29', + 57 => '2.29', + 58 => '2.31', + 59 => '2.32', + 60 => '2.33', + 61 => '2.34', + 62 => '2.35', + 63 => '2.36', + 64 => '2.37', + 65 => '2.38', + 66 => '2.40', + 67 => '2.41', + 68 => '2.42', + 69 => '2.44', + ]; + + /** + * Path to the bin directory. + * + * @var string + */ + protected $directory = __DIR__.'/../../bin/'; + + /** + * Execute the console command. + * + * @return void + */ + public function handle() + { + $version = $this->version(); + $all = $this->option('all'); + $currentOS = OperatingSystem::id(); + + foreach ($this->slugs as $os => $slug) { + if ($all || ($os === $currentOS)) { + $archive = $this->download($version, $slug); + + $binary = $this->extract($archive); + + $this->rename($binary, $os); + } + } + + $message = 'ChromeDriver %s successfully installed for version %s.'; + + $this->info(sprintf($message, $all ? 'binaries' : 'binary', $version)); + } + + /** + * Get the desired ChromeDriver version. + * + * @return string + */ + protected function version() + { + $version = $this->argument('version'); + + if ($version) { + if (! ctype_digit($version)) { + return $version; + } + + $version = (int) $version; + + if ($version < 70) { + return $this->legacyVersions[$version]; + } + } else { + $version = $this->latestChromeVersion(); + } + + $url = sprintf($this->versionUrl, $version); + + return trim(file_get_contents($url)); + } + + /** + * Get the latest major Chrome version. + * + * @return int + */ + protected function latestChromeVersion() + { + $index = file_get_contents($this->indexUrl); + + preg_match('#.*LATEST_RELEASE_(\d+)#', $index, $matches); + + return (int) $matches[1]; + } + + /** + * Download the ChromeDriver archive. + * + * @param string $version + * @param string $slug + * @return string + */ + protected function download($version, $slug) + { + $archive = $this->directory.'chromedriver.zip'; + $url = sprintf($this->downloadUrl, $version, $slug); + + file_put_contents($archive, fopen($url, 'r')); + + return $archive; + } + + /** + * Extract the ChromeDriver binary from the archive and delete the archive. + * + * @param string $archive + * @return string + */ + protected function extract($archive) + { + $zip = new ZipArchive; + $zip->open($archive); + $zip->extractTo($this->directory); + + $binary = $zip->getNameIndex(0); + + $zip->close(); + + unlink($archive); + + return $binary; + } + + /** + * Rename the ChromeDriver binary and make it executable. + * + * @param string $binary + * @param string $os + * @return void + */ + protected function rename($binary, $os) + { + $newName = str_replace('chromedriver', 'chromedriver-'.$os, $binary); + + rename($this->directory.$binary, $this->directory.$newName); + + chmod($this->directory.$newName, 0755); + } +} diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 158cf66e2..dd672391d 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -20,16 +20,6 @@ class InstallCommand extends Command */ protected $description = 'Install Dusk into the application'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * @@ -67,6 +57,10 @@ public function handle() } $this->info('Dusk scaffolding installed successfully.'); + + $this->info('Installing ChromeDriver binaries...'); + + $this->call('dusk:chrome-driver', ['--all' => true]); } /** diff --git a/src/DuskServiceProvider.php b/src/DuskServiceProvider.php index c612201d9..156a4f305 100644 --- a/src/DuskServiceProvider.php +++ b/src/DuskServiceProvider.php @@ -35,7 +35,7 @@ public function boot() * Register any package services. * * @return void - * @throws Exception + * @throws \Exception */ public function register() { @@ -51,6 +51,7 @@ public function register() Console\MakeCommand::class, Console\PageCommand::class, Console\ComponentCommand::class, + Console\ChromeDriverCommand::class, ]); } } diff --git a/src/OperatingSystem.php b/src/OperatingSystem.php new file mode 100644 index 000000000..d32a99c84 --- /dev/null +++ b/src/OperatingSystem.php @@ -0,0 +1,38 @@ +