From c4e68e8aa6a03401d63cf76830bbb12ac23fe0bb Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Fri, 16 Feb 2024 22:46:11 +0100 Subject: [PATCH 1/9] Feature: Introduced a new DownloadUrlResolver, which can be used to find the specific download url for a version --- .../ChromeDriver/DownloadUrlResolver.php | 59 +++++++++++++++++++ src/Driver/ChromeDriver/VersionResolver.php | 5 ++ src/Driver/DownloadUrlResolver.php | 10 ++++ .../ChromeDriver/VersionResolverTest.php | 6 ++ 4 files changed, 80 insertions(+) create mode 100644 src/Driver/ChromeDriver/DownloadUrlResolver.php create mode 100644 src/Driver/DownloadUrlResolver.php diff --git a/src/Driver/ChromeDriver/DownloadUrlResolver.php b/src/Driver/ChromeDriver/DownloadUrlResolver.php new file mode 100644 index 0000000..61803c7 --- /dev/null +++ b/src/Driver/ChromeDriver/DownloadUrlResolver.php @@ -0,0 +1,59 @@ +httpClient = $httpClient; + } + + public function byDriver(Driver $driver, string $binaryName): string + { + if (! VersionResolver::isJsonVersion($driver->version())) { + return sprintf( + '%s/%s/%s.zip', + self::LEGACY_DOWNLOAD_ENDPOINT, + $driver->version()->toBuildString(), + $binaryName + ); + } + + $response = $this->httpClient->request( + 'GET', + self::LATEST_PATCH_WITH_DOWNLOAD_ENDPOINT, + ); + + $versions = $response->toArray(); + if (! isset($versions['builds'][$driver->version()->toString()]['downloads']['chromedriver'])) { + throw new UnexpectedValueException(sprintf('Could not find the chromedriver downloads for version %s', $driver->version()->toString())); + } + + $downloads = $versions['builds'][$driver->version()->toString()]['downloads']['chromedriver']; + foreach ($downloads as $download) { + if ($download['platform'] === $binaryName) { + return (string) $download['url']; + } + } + + $operatingSystem = $driver->operatingSystem(); + + throw NotImplemented::feature(sprintf('Downloading %s for %s', $driver->name()->getValue(), $operatingSystem->getValue())); + } +} diff --git a/src/Driver/ChromeDriver/VersionResolver.php b/src/Driver/ChromeDriver/VersionResolver.php index 92f0461..a84f2f0 100644 --- a/src/Driver/ChromeDriver/VersionResolver.php +++ b/src/Driver/ChromeDriver/VersionResolver.php @@ -28,6 +28,11 @@ final class VersionResolver implements VersionResolverInterface private HttpClientInterface $httpClient; + public static function isJsonVersion(Version $version): bool + { + return $version->major() >= self::MAJOR_VERSION_ENDPOINT_BREAKPOINT; + } + public function __construct(HttpClientInterface $httpClient) { $this->httpClient = $httpClient; diff --git a/src/Driver/DownloadUrlResolver.php b/src/Driver/DownloadUrlResolver.php new file mode 100644 index 0000000..a767c23 --- /dev/null +++ b/src/Driver/DownloadUrlResolver.php @@ -0,0 +1,10 @@ +versionResolver->latest()); } + public function testIsJsonVersion(): void + { + self::assertFalse(VersionResolver::isJsonVersion(Version::fromString('114.0.5735.90'))); + self::assertTrue(VersionResolver::isJsonVersion(Version::fromString('115.0.5751'))); + } + protected function setUp(): void { $httpClientMock = new MockHttpClient( From efdfec9d2ffd124457f090756da29f63ec4b33d6 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Fri, 16 Feb 2024 22:52:54 +0100 Subject: [PATCH 2/9] Feature: Implement the new url resolver in the chromedriver downloader --- bdi | 4 +- src/Driver/ChromeDriver/Downloader.php | 62 +++++++------------------- 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/bdi b/bdi index 79f1084..d636fab 100755 --- a/bdi +++ b/bdi @@ -49,8 +49,10 @@ $geckoDriverVersionResolver = new GeckoDriver\VersionResolver($httpClient); $driverVersionResolverFactory->register($chromeDriverVersionResolver); $driverVersionResolverFactory->register($geckoDriverVersionResolver); +$chromeDriverDownloadUrlResolver = new ChromeDriver\DownloadUrlResolver($httpClient); + $driverDownloaderFactory = new Driver\DownloaderFactory(); -$driverDownloaderFactory->register(new ChromeDriver\Downloader($filesystem, $httpClient, $multiExtractor)); +$driverDownloaderFactory->register(new ChromeDriver\Downloader($filesystem, $httpClient, $multiExtractor, $chromeDriverDownloadUrlResolver)); $driverDownloaderFactory->register(new GeckoDriver\Downloader($filesystem, $httpClient, $multiExtractor)); $browserFactory = new Browser\BrowserFactory($browserPathResolverFactory, $browserVersionResolverFactory); diff --git a/src/Driver/ChromeDriver/Downloader.php b/src/Driver/ChromeDriver/Downloader.php index d283be6..423624e 100644 --- a/src/Driver/ChromeDriver/Downloader.php +++ b/src/Driver/ChromeDriver/Downloader.php @@ -6,6 +6,7 @@ use DBrekelmans\BrowserDriverInstaller\Archive\Extractor; use DBrekelmans\BrowserDriverInstaller\Driver\Downloader as DownloaderInterface; +use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver; use DBrekelmans\BrowserDriverInstaller\Driver\Driver; use DBrekelmans\BrowserDriverInstaller\Driver\DriverName; use DBrekelmans\BrowserDriverInstaller\Exception\NotImplemented; @@ -31,17 +32,12 @@ final class Downloader implements DownloaderInterface { - private const DOWNLOAD_ENDPOINT = 'https://chromedriver.storage.googleapis.com'; private const BINARY_LINUX = 'chromedriver_linux64'; private const BINARY_MAC = 'chromedriver_mac64'; private const BINARY_WINDOWS = 'chromedriver_win32'; - private const DOWNLOAD_ENDPOINT_JSON = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'; - private const DOWNLOAD_ENDPOINT_JSON_NEW = 'https://storage.googleapis.com/chrome-for-testing-public'; private const BINARY_LINUX_JSON = 'chromedriver-linux64'; private const BINARY_MAC_JSON = 'chromedriver-mac-x64'; private const BINARY_WINDOWS_JSON = 'chromedriver-win32'; - private const NEW_JSON_API_ENDPOINT_MAJOR = 121; - private const NEW_JSON_API_ENDPOINT_PATCH = 6167; private Filesystem $filesystem; @@ -52,11 +48,18 @@ final class Downloader implements DownloaderInterface private string $tempDir; - public function __construct(Filesystem $filesystem, HttpClientInterface $httpClient, Extractor $archiveExtractor) - { + private DownloadUrlResolver $downloadUrlResolver; + + public function __construct( + Filesystem $filesystem, + HttpClientInterface $httpClient, + Extractor $archiveExtractor, + DownloadUrlResolver $downloadUrlResolver + ) { $this->filesystem = $filesystem; $this->httpClient = $httpClient; $this->archiveExtractor = $archiveExtractor; + $this->downloadUrlResolver = $downloadUrlResolver; $this->tempDir = sys_get_temp_dir(); } @@ -124,12 +127,7 @@ private function downloadArchive(Driver $driver): string $response = $this->httpClient->request( 'GET', - sprintf( - '%s/%s/%s.zip', - $this->getDownloadEndpoint($driver), - $driver->version()->toBuildString(), - $this->getBinaryName($driver) - ) + $this->downloadUrlResolver->byDriver($driver, $this->getBinaryName($driver)), ); $fileHandler = fopen($temporaryFile, 'wb'); @@ -153,17 +151,17 @@ private function downloadArchive(Driver $driver): string private function getBinaryName(Driver $driver): string { $operatingSystem = $driver->operatingSystem(); - if ($this->isJsonVersion($driver)) { + if (VersionResolver::isJsonVersion($driver->version())) { if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { - return 'win32/' . self::BINARY_WINDOWS_JSON; + return 'win32'; } if ($operatingSystem->equals(OperatingSystem::MACOS())) { - return 'mac-x64/' . self::BINARY_MAC_JSON; + return 'mac-x64'; } if ($operatingSystem->equals(OperatingSystem::LINUX())) { - return 'linux64/' . self::BINARY_LINUX_JSON; + return 'linux64'; } } else { if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { @@ -192,7 +190,7 @@ private function extractArchive(string $archive, Driver $driver): string { $unzipLocation = $this->tempDir . DIRECTORY_SEPARATOR . 'chromedriver'; $extractedFiles = $this->archiveExtractor->extract($archive, $unzipLocation); - if ($this->isJsonVersion($driver)) { + if (VersionResolver::isJsonVersion($driver->version())) { $extractedFiles = $this->cleanArchiveStructure($driver, $unzipLocation, $extractedFiles); } @@ -234,34 +232,6 @@ private function getFileName(OperatingSystem $operatingSystem): string return $fileName; } - private function isJsonVersion(Driver $driver): bool - { - return $driver->version()->major() >= VersionResolver::MAJOR_VERSION_ENDPOINT_BREAKPOINT; - } - - private function getDownloadEndpoint(Driver $driver): string - { - if ($this->isJsonVersion($driver)) { - return $this->resolveJsonVersionEndpoint($driver); - } - - return self::DOWNLOAD_ENDPOINT; - } - - private function resolveJsonVersionEndpoint(Driver $driver): string - { - $version = $driver->version(); - if ((int) $version->major() < self::NEW_JSON_API_ENDPOINT_MAJOR) { - return self::DOWNLOAD_ENDPOINT_JSON; - } - - if ((int) $version->major() === self::NEW_JSON_API_ENDPOINT_MAJOR && (int) $version->patch() < self::NEW_JSON_API_ENDPOINT_PATCH) { - return self::DOWNLOAD_ENDPOINT_JSON; - } - - return self::DOWNLOAD_ENDPOINT_JSON_NEW; - } - /** * @param string[] $extractedFiles * From 6426d6ffb3f2c846bc1a49a1f88d2df9f2ae52f3 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Fri, 16 Feb 2024 22:52:54 +0100 Subject: [PATCH 3/9] Feature: Implement the new url resolver in the chromedriver downloader --- src/Driver/ChromeDriver/Downloader.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Driver/ChromeDriver/Downloader.php b/src/Driver/ChromeDriver/Downloader.php index 423624e..a2e083f 100644 --- a/src/Driver/ChromeDriver/Downloader.php +++ b/src/Driver/ChromeDriver/Downloader.php @@ -32,12 +32,12 @@ final class Downloader implements DownloaderInterface { - private const BINARY_LINUX = 'chromedriver_linux64'; - private const BINARY_MAC = 'chromedriver_mac64'; - private const BINARY_WINDOWS = 'chromedriver_win32'; - private const BINARY_LINUX_JSON = 'chromedriver-linux64'; - private const BINARY_MAC_JSON = 'chromedriver-mac-x64'; - private const BINARY_WINDOWS_JSON = 'chromedriver-win32'; + private const BINARY_LINUX = 'chromedriver_linux64'; + private const BINARY_MAC = 'chromedriver_mac64'; + private const BINARY_WINDOWS = 'chromedriver_win32'; + private const BINARY_LINUX_JSON = 'chromedriver-linux64'; + private const BINARY_MAC_JSON = 'chromedriver-mac-x64'; + private const BINARY_WINDOWS_JSON = 'chromedriver-win32'; private Filesystem $filesystem; @@ -56,11 +56,11 @@ public function __construct( Extractor $archiveExtractor, DownloadUrlResolver $downloadUrlResolver ) { - $this->filesystem = $filesystem; - $this->httpClient = $httpClient; - $this->archiveExtractor = $archiveExtractor; + $this->filesystem = $filesystem; + $this->httpClient = $httpClient; + $this->archiveExtractor = $archiveExtractor; $this->downloadUrlResolver = $downloadUrlResolver; - $this->tempDir = sys_get_temp_dir(); + $this->tempDir = sys_get_temp_dir(); } public function supports(Driver $driver): bool From 9b743c6833b87c07db2268135df1474aeed170c2 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Fri, 16 Feb 2024 23:34:19 +0100 Subject: [PATCH 4/9] Feature: Update the DownloaderTest --- tests/Driver/ChromeDriver/DownloaderTest.php | 112 ++++++++++--------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/tests/Driver/ChromeDriver/DownloaderTest.php b/tests/Driver/ChromeDriver/DownloaderTest.php index 6f38dac..1aa1426 100644 --- a/tests/Driver/ChromeDriver/DownloaderTest.php +++ b/tests/Driver/ChromeDriver/DownloaderTest.php @@ -6,6 +6,7 @@ use DBrekelmans\BrowserDriverInstaller\Archive\Extractor; use DBrekelmans\BrowserDriverInstaller\Driver\ChromeDriver\Downloader; +use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver; use DBrekelmans\BrowserDriverInstaller\Driver\Driver; use DBrekelmans\BrowserDriverInstaller\Driver\DriverName; use DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem; @@ -30,6 +31,9 @@ class DownloaderTest extends TestCase /** @var Stub&Extractor */ private $archiveExtractor; + /** @var MockObject&DownloadUrlResolver */ + private $downloadUrlResolver; + /** @var MockObject&HttpClientInterface */ private $httpClient; @@ -49,13 +53,20 @@ public function testDownloadMac(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::MACOS()); + $chromeDriverMac = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::MACOS()); + + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverMac, 'chromedriver_mac64') + ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_mac64.zip'); + $this->httpClient ->expects(self::atLeastOnce()) ->method('request') ->with('GET', 'https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_mac64.zip'); - $chromeDriverMac = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::MACOS()); - $filePath = $this->downloader->download($chromeDriverMac, '.'); + $filePath = $this->downloader->download($chromeDriverMac, '.'); self::assertEquals('./chromedriver', $filePath); } @@ -64,28 +75,20 @@ public function testDownloadMacJson(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::MACOS(), true); - $this->httpClient - ->expects(self::atLeastOnce()) - ->method('request') - ->with('GET', 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5790.170/mac-x64/chromedriver-mac-x64.zip'); - $chromeDriverMac = new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::MACOS()); - $filePath = $this->downloader->download($chromeDriverMac, '.'); - self::assertEquals('./chromedriver', $filePath); - } - - public function testDownloadMacJsonNewEndpoint(): void - { - $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::MACOS(), true); + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverMac, 'mac-x64') + ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient ->expects(self::atLeastOnce()) ->method('request') - ->with('GET', 'https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.0/mac-x64/chromedriver-mac-x64.zip'); + ->with('GET', 'https://dynamic-download-url/driver.zip'); - $chromeDriverMac = new Driver(DriverName::CHROME(), Version::fromString('121.0.6167.0'), OperatingSystem::MACOS()); - $filePath = $this->downloader->download($chromeDriverMac, '.'); + $filePath = $this->downloader->download($chromeDriverMac, '.'); self::assertEquals('./chromedriver', $filePath); } @@ -94,13 +97,20 @@ public function testDownloadLinux(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::LINUX()); + $chromeDriverLinux = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::LINUX()); + + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverLinux, 'chromedriver_linux64') + ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip'); + $this->httpClient ->expects(self::atLeastOnce()) ->method('request') ->with('GET', 'https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip'); - $chromeDriverLinux = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::LINUX()); - $filePath = $this->downloader->download($chromeDriverLinux, '.'); + $filePath = $this->downloader->download($chromeDriverLinux, '.'); self::assertEquals('./chromedriver', $filePath); } @@ -109,28 +119,20 @@ public function testDownloadLinuxJson(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::LINUX(), true); - $this->httpClient - ->expects(self::atLeastOnce()) - ->method('request') - ->with('GET', 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5790.170/linux64/chromedriver-linux64.zip'); - $chromeDriverLinux = new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::LINUX()); - $filePath = $this->downloader->download($chromeDriverLinux, '.'); - - self::assertEquals('./chromedriver', $filePath); - } - public function testDownloadLinuxJsonNewEndpoint(): void - { - $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::LINUX(), true); + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverLinux, 'linux64') + ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient ->expects(self::atLeastOnce()) ->method('request') - ->with('GET', 'https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.0/linux64/chromedriver-linux64.zip'); + ->with('GET', 'https://dynamic-download-url/driver.zip'); - $chromeDriverLinux = new Driver(DriverName::CHROME(), Version::fromString('121.0.6167.0'), OperatingSystem::LINUX()); - $filePath = $this->downloader->download($chromeDriverLinux, '.'); + $filePath = $this->downloader->download($chromeDriverLinux, '.'); self::assertEquals('./chromedriver', $filePath); } @@ -139,13 +141,20 @@ public function testDownloadWindows(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::WINDOWS()); + $chromeDriverWindows = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::WINDOWS()); + + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverWindows, 'chromedriver_win32') + ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_win32.zip'); + $this->httpClient ->expects(self::atLeastOnce()) ->method('request') ->with('GET', 'https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_win32.zip'); - $chromeDriverLinux = new Driver(DriverName::CHROME(), Version::fromString('86.0.4240.22'), OperatingSystem::WINDOWS()); - $filePath = $this->downloader->download($chromeDriverLinux, '.'); + $filePath = $this->downloader->download($chromeDriverWindows, '.'); self::assertEquals('./chromedriver.exe', $filePath); } @@ -154,38 +163,31 @@ public function testDownloadWindowsJson(): void { $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::WINDOWS(), true); - $this->httpClient - ->expects(self::atLeastOnce()) - ->method('request') - ->with('GET', 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/115.0.5790.170/win32/chromedriver-win32.zip'); - $chromeDriverWindows = new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::WINDOWS()); - $filePath = $this->downloader->download($chromeDriverWindows, '.'); - self::assertEquals('./chromedriver.exe', $filePath); - } - - public function testDownloadWindowsJsonNewEndpoint(): void - { - $this->mockFsAndArchiveExtractorForSuccessfulDownload(OperatingSystem::WINDOWS(), true); + $this->downloadUrlResolver + ->expects(self::once()) + ->method('byDriver') + ->with($chromeDriverWindows, 'win32') + ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient ->expects(self::atLeastOnce()) ->method('request') - ->with('GET', 'https://storage.googleapis.com/chrome-for-testing-public/121.0.6167.0/win32/chromedriver-win32.zip'); + ->with('GET', 'https://dynamic-download-url/driver.zip'); - $chromeDriverWindows = new Driver(DriverName::CHROME(), Version::fromString('121.0.6167.0'), OperatingSystem::WINDOWS()); - $filePath = $this->downloader->download($chromeDriverWindows, '.'); + $filePath = $this->downloader->download($chromeDriverWindows, '.'); self::assertEquals('./chromedriver.exe', $filePath); } protected function setUp(): void { - $this->filesystem = $this->createStub(Filesystem::class); - $this->httpClient = $this->createMock(HttpClientInterface::class); - $this->archiveExtractor = $this->createStub(Extractor::class); - $this->downloader = new Downloader($this->filesystem, $this->httpClient, $this->archiveExtractor); + $this->filesystem = $this->createStub(Filesystem::class); + $this->httpClient = $this->createMock(HttpClientInterface::class); + $this->archiveExtractor = $this->createStub(Extractor::class); + $this->downloadUrlResolver = $this->createMock(DownloadUrlResolver::class); + $this->downloader = new Downloader($this->filesystem, $this->httpClient, $this->archiveExtractor, $this->downloadUrlResolver); } private function mockFsAndArchiveExtractorForSuccessfulDownload( From fea36206824941bd902e3c44a48c26b33a37435c Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Fri, 16 Feb 2024 23:58:02 +0100 Subject: [PATCH 5/9] Feature: Small changes to the url resolver, and added a unit test --- .../ChromeDriver/DownloadUrlResolver.php | 12 ++- .../ChromeDriver/DownloadUrlResolverTest.php | 80 +++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/Driver/ChromeDriver/DownloadUrlResolverTest.php diff --git a/src/Driver/ChromeDriver/DownloadUrlResolver.php b/src/Driver/ChromeDriver/DownloadUrlResolver.php index 61803c7..552bcf9 100644 --- a/src/Driver/ChromeDriver/DownloadUrlResolver.php +++ b/src/Driver/ChromeDriver/DownloadUrlResolver.php @@ -6,10 +6,10 @@ use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver as DownloadUrlResolverInterface; use DBrekelmans\BrowserDriverInstaller\Driver\Driver; -use DBrekelmans\BrowserDriverInstaller\Exception\NotImplemented; use Symfony\Contracts\HttpClient\HttpClientInterface; use UnexpectedValueException; +use function is_string; use function Safe\sprintf; final class DownloadUrlResolver implements DownloadUrlResolverInterface @@ -47,13 +47,17 @@ public function byDriver(Driver $driver, string $binaryName): string $downloads = $versions['builds'][$driver->version()->toString()]['downloads']['chromedriver']; foreach ($downloads as $download) { - if ($download['platform'] === $binaryName) { - return (string) $download['url']; + if ($download['platform'] === $binaryName && isset($download['url']) && is_string($download['url'])) { + return $download['url']; } } $operatingSystem = $driver->operatingSystem(); - throw NotImplemented::feature(sprintf('Downloading %s for %s', $driver->name()->getValue(), $operatingSystem->getValue())); + throw new UnexpectedValueException(sprintf( + 'Could not resolve chromedriver download url for version %s with binary %s', + $driver->version()->toString(), + $operatingSystem->getValue() + )); } } diff --git a/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php b/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php new file mode 100644 index 0000000..b3e2ceb --- /dev/null +++ b/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php @@ -0,0 +1,80 @@ + + */ + public static function byDriverDataProvider(): iterable + { + yield 'legacy version' => [ + new Driver(DriverName::CHROME(), Version::fromString('88.0.4299.0'), OperatingSystem::LINUX()), + 'some_binary', + 'https://chromedriver.storage.googleapis.com/88.0.4299.0/some_binary.zip', + ]; + + yield 'new version' => [ + new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::LINUX()), + 'linux64', + 'https://dynamic-url-2/', + ]; + } + + /** + * @dataProvider byDriverDataProvider + */ + public function testByDriver(Driver $driver, string $binaryName, string $expectedUrl): void + { + self::assertSame($expectedUrl, $this->urlResolver->byDriver($driver, $binaryName)); + } + + protected function setUp(): void + { + $httpClientMock = new MockHttpClient( + static function (string $method, string $url): MockResponse { + if ($method === 'GET') { + if ($url === 'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json') { + return new MockResponse( + json_encode([ + 'builds' => [ + '115.0.5790' => [ + 'downloads' => [ + 'chromedriver' => [ + ['platform' => 'win32', 'url' => 'https://dynamic-url-1/'], + ['platform' => 'linux64', 'url' => 'https://dynamic-url-2/'], + ], + ], + ], + ], + ]) + ); + } + } + + return new MockResponse( + 'NoSuchKeyThe specified key does not exist.
No such object: chromedriver/LATEST_RELEASE_xxx
', + ['http_code' => 404] + ); + } + ); + + $this->urlResolver = new DownloadUrlResolver($httpClientMock); + } +} From 293b47169c4078e6297cebfb48f690df62a1b340 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Sat, 17 Feb 2024 00:02:51 +0100 Subject: [PATCH 6/9] Feature: Change multiline call into one liner --- src/Driver/ChromeDriver/DownloadUrlResolver.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Driver/ChromeDriver/DownloadUrlResolver.php b/src/Driver/ChromeDriver/DownloadUrlResolver.php index 552bcf9..55b7af4 100644 --- a/src/Driver/ChromeDriver/DownloadUrlResolver.php +++ b/src/Driver/ChromeDriver/DownloadUrlResolver.php @@ -35,10 +35,7 @@ public function byDriver(Driver $driver, string $binaryName): string ); } - $response = $this->httpClient->request( - 'GET', - self::LATEST_PATCH_WITH_DOWNLOAD_ENDPOINT, - ); + $response = $this->httpClient->request('GET', self::LATEST_PATCH_WITH_DOWNLOAD_ENDPOINT); $versions = $response->toArray(); if (! isset($versions['builds'][$driver->version()->toString()]['downloads']['chromedriver'])) { From c03d2d1ff3c7ae830db7459df20064449922f0ff Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Tue, 20 Feb 2024 18:21:04 +0100 Subject: [PATCH 7/9] Feature: Moved the binary and platform resolving logic to the DownloadUrlResolver --- .../ChromeDriver/DownloadUrlResolver.php | 54 +++++++++++++++++-- src/Driver/ChromeDriver/Downloader.php | 42 +-------------- src/Driver/DownloadUrlResolver.php | 2 +- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/Driver/ChromeDriver/DownloadUrlResolver.php b/src/Driver/ChromeDriver/DownloadUrlResolver.php index 55b7af4..48b49db 100644 --- a/src/Driver/ChromeDriver/DownloadUrlResolver.php +++ b/src/Driver/ChromeDriver/DownloadUrlResolver.php @@ -6,6 +6,8 @@ use DBrekelmans\BrowserDriverInstaller\Driver\DownloadUrlResolver as DownloadUrlResolverInterface; use DBrekelmans\BrowserDriverInstaller\Driver\Driver; +use DBrekelmans\BrowserDriverInstaller\Exception\NotImplemented; +use DBrekelmans\BrowserDriverInstaller\OperatingSystem\OperatingSystem; use Symfony\Contracts\HttpClient\HttpClientInterface; use UnexpectedValueException; @@ -14,6 +16,9 @@ final class DownloadUrlResolver implements DownloadUrlResolverInterface { + private const BINARY_LINUX = 'chromedriver_linux64'; + private const BINARY_MAC = 'chromedriver_mac64'; + private const BINARY_WINDOWS = 'chromedriver_win32'; private const LEGACY_DOWNLOAD_ENDPOINT = 'https://chromedriver.storage.googleapis.com'; private const LATEST_PATCH_WITH_DOWNLOAD_ENDPOINT = 'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json'; @@ -24,14 +29,14 @@ public function __construct(HttpClientInterface $httpClient) $this->httpClient = $httpClient; } - public function byDriver(Driver $driver, string $binaryName): string + public function byDriver(Driver $driver): string { if (! VersionResolver::isJsonVersion($driver->version())) { return sprintf( '%s/%s/%s.zip', self::LEGACY_DOWNLOAD_ENDPOINT, $driver->version()->toBuildString(), - $binaryName + $this->getBinaryName($driver), ); } @@ -42,9 +47,10 @@ public function byDriver(Driver $driver, string $binaryName): string throw new UnexpectedValueException(sprintf('Could not find the chromedriver downloads for version %s', $driver->version()->toString())); } - $downloads = $versions['builds'][$driver->version()->toString()]['downloads']['chromedriver']; + $platformName = $this->getPlatformName($driver); + $downloads = $versions['builds'][$driver->version()->toString()]['downloads']['chromedriver']; foreach ($downloads as $download) { - if ($download['platform'] === $binaryName && isset($download['url']) && is_string($download['url'])) { + if ($download['platform'] === $platformName && isset($download['url']) && is_string($download['url'])) { return $download['url']; } } @@ -57,4 +63,44 @@ public function byDriver(Driver $driver, string $binaryName): string $operatingSystem->getValue() )); } + + private function getBinaryName(Driver $driver): string + { + $operatingSystem = $driver->operatingSystem(); + if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { + return self::BINARY_WINDOWS; + } + + if ($operatingSystem->equals(OperatingSystem::MACOS())) { + return self::BINARY_MAC; + } + + if ($operatingSystem->equals(OperatingSystem::LINUX())) { + return self::BINARY_LINUX; + } + + throw NotImplemented::feature( + sprintf('Downloading %s for %s', $driver->name()->getValue(), $operatingSystem->getValue()) + ); + } + + private function getPlatformName(Driver $driver): string + { + $operatingSystem = $driver->operatingSystem(); + if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { + return 'win32'; + } + + if ($operatingSystem->equals(OperatingSystem::MACOS())) { + return 'mac-x64'; + } + + if ($operatingSystem->equals(OperatingSystem::LINUX())) { + return 'linux64'; + } + + throw NotImplemented::feature( + sprintf('Downloading %s for %s', $driver->name()->getValue(), $operatingSystem->getValue()) + ); + } } diff --git a/src/Driver/ChromeDriver/Downloader.php b/src/Driver/ChromeDriver/Downloader.php index a2e083f..f420e47 100644 --- a/src/Driver/ChromeDriver/Downloader.php +++ b/src/Driver/ChromeDriver/Downloader.php @@ -32,9 +32,6 @@ final class Downloader implements DownloaderInterface { - private const BINARY_LINUX = 'chromedriver_linux64'; - private const BINARY_MAC = 'chromedriver_mac64'; - private const BINARY_WINDOWS = 'chromedriver_win32'; private const BINARY_LINUX_JSON = 'chromedriver-linux64'; private const BINARY_MAC_JSON = 'chromedriver-mac-x64'; private const BINARY_WINDOWS_JSON = 'chromedriver-win32'; @@ -127,7 +124,7 @@ private function downloadArchive(Driver $driver): string $response = $this->httpClient->request( 'GET', - $this->downloadUrlResolver->byDriver($driver, $this->getBinaryName($driver)), + $this->downloadUrlResolver->byDriver($driver), ); $fileHandler = fopen($temporaryFile, 'wb'); @@ -145,43 +142,6 @@ private function downloadArchive(Driver $driver): string return $temporaryFile; } - /** - * @throws NotImplemented - */ - private function getBinaryName(Driver $driver): string - { - $operatingSystem = $driver->operatingSystem(); - if (VersionResolver::isJsonVersion($driver->version())) { - if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { - return 'win32'; - } - - if ($operatingSystem->equals(OperatingSystem::MACOS())) { - return 'mac-x64'; - } - - if ($operatingSystem->equals(OperatingSystem::LINUX())) { - return 'linux64'; - } - } else { - if ($operatingSystem->equals(OperatingSystem::WINDOWS())) { - return self::BINARY_WINDOWS; - } - - if ($operatingSystem->equals(OperatingSystem::MACOS())) { - return self::BINARY_MAC; - } - - if ($operatingSystem->equals(OperatingSystem::LINUX())) { - return self::BINARY_LINUX; - } - } - - throw NotImplemented::feature( - sprintf('Downloading %s for %s', $driver->name()->getValue(), $operatingSystem->getValue()) - ); - } - /** * @throws RuntimeException * @throws IOException diff --git a/src/Driver/DownloadUrlResolver.php b/src/Driver/DownloadUrlResolver.php index a767c23..7d08ada 100644 --- a/src/Driver/DownloadUrlResolver.php +++ b/src/Driver/DownloadUrlResolver.php @@ -6,5 +6,5 @@ interface DownloadUrlResolver { - public function byDriver(Driver $driver, string $binaryName): string; + public function byDriver(Driver $driver): string; } From ca2affadc4fedd87b0f322d6c9bc482d4d4c43f1 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Tue, 20 Feb 2024 18:31:58 +0100 Subject: [PATCH 8/9] Feature: Remove the expected "with" argument --- tests/Driver/ChromeDriver/DownloaderTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Driver/ChromeDriver/DownloaderTest.php b/tests/Driver/ChromeDriver/DownloaderTest.php index 1aa1426..c236f01 100644 --- a/tests/Driver/ChromeDriver/DownloaderTest.php +++ b/tests/Driver/ChromeDriver/DownloaderTest.php @@ -58,7 +58,7 @@ public function testDownloadMac(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverMac, 'chromedriver_mac64') + ->with($chromeDriverMac) ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_mac64.zip'); $this->httpClient @@ -80,7 +80,7 @@ public function testDownloadMacJson(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverMac, 'mac-x64') + ->with($chromeDriverMac) ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient @@ -102,7 +102,7 @@ public function testDownloadLinux(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverLinux, 'chromedriver_linux64') + ->with($chromeDriverLinux) ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip'); $this->httpClient @@ -124,7 +124,7 @@ public function testDownloadLinuxJson(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverLinux, 'linux64') + ->with($chromeDriverLinux) ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient @@ -146,7 +146,7 @@ public function testDownloadWindows(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverWindows, 'chromedriver_win32') + ->with($chromeDriverWindows) ->willReturn('https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_win32.zip'); $this->httpClient @@ -168,7 +168,7 @@ public function testDownloadWindowsJson(): void $this->downloadUrlResolver ->expects(self::once()) ->method('byDriver') - ->with($chromeDriverWindows, 'win32') + ->with($chromeDriverWindows) ->willReturn('https://dynamic-download-url/driver.zip'); $this->httpClient From 66bb74c70cea962e1c7e115aa6360d20b5442b21 Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Tue, 20 Feb 2024 18:33:13 +0100 Subject: [PATCH 9/9] Feature: Updated test, to remove the binary param, and test all OS version on the old and new download endpoint --- .../ChromeDriver/DownloadUrlResolverTest.php | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php b/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php index b3e2ceb..6186091 100644 --- a/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php +++ b/tests/Driver/ChromeDriver/DownloadUrlResolverTest.php @@ -20,29 +20,47 @@ final class DownloadUrlResolverTest extends TestCase private DownloadUrlResolver $urlResolver; /** - * @return iterable + * @return iterable */ public static function byDriverDataProvider(): iterable { - yield 'legacy version' => [ + yield 'legacy version linux' => [ new Driver(DriverName::CHROME(), Version::fromString('88.0.4299.0'), OperatingSystem::LINUX()), - 'some_binary', - 'https://chromedriver.storage.googleapis.com/88.0.4299.0/some_binary.zip', + 'https://chromedriver.storage.googleapis.com/88.0.4299.0/chromedriver_linux64.zip', ]; - yield 'new version' => [ + yield 'legacy version macos' => [ + new Driver(DriverName::CHROME(), Version::fromString('88.0.4299.0'), OperatingSystem::MACOS()), + 'https://chromedriver.storage.googleapis.com/88.0.4299.0/chromedriver_mac64.zip', + ]; + + yield 'legacy version windows' => [ + new Driver(DriverName::CHROME(), Version::fromString('88.0.4299.0'), OperatingSystem::WINDOWS()), + 'https://chromedriver.storage.googleapis.com/88.0.4299.0/chromedriver_win32.zip', + ]; + + yield 'new version linux' => [ new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::LINUX()), - 'linux64', 'https://dynamic-url-2/', ]; + + yield 'new version macos' => [ + new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::MACOS()), + 'https://dynamic-url-3/', + ]; + + yield 'new version windows' => [ + new Driver(DriverName::CHROME(), Version::fromString('115.0.5790.170'), OperatingSystem::WINDOWS()), + 'https://dynamic-url-1/', + ]; } /** * @dataProvider byDriverDataProvider */ - public function testByDriver(Driver $driver, string $binaryName, string $expectedUrl): void + public function testByDriver(Driver $driver, string $expectedUrl): void { - self::assertSame($expectedUrl, $this->urlResolver->byDriver($driver, $binaryName)); + self::assertSame($expectedUrl, $this->urlResolver->byDriver($driver)); } protected function setUp(): void @@ -59,6 +77,7 @@ static function (string $method, string $url): MockResponse { 'chromedriver' => [ ['platform' => 'win32', 'url' => 'https://dynamic-url-1/'], ['platform' => 'linux64', 'url' => 'https://dynamic-url-2/'], + ['platform' => 'mac-x64', 'url' => 'https://dynamic-url-3/'], ], ], ],