diff --git a/diffy b/diffy index d72cac6..5ea77b2 100755 --- a/diffy +++ b/diffy @@ -24,6 +24,25 @@ $classLoader = require $autoloaderPath; // Customization variables $argv = $_SERVER['argv']; + +// Handle --base-url option globally before passing argv to Robo. +foreach ($argv as $i => $arg) { + if (strpos($arg, '--base-url=') === 0) { + $baseUrl = rtrim(substr($arg, strlen('--base-url=')), '/') . '/'; + \Diffy\Diffy::$baseUrl = $baseUrl; + \Diffy\Diffy::$client = null; + unset($argv[$i]); + break; + } elseif ($arg === '--base-url' && isset($argv[$i + 1])) { + $baseUrl = rtrim($argv[$i + 1], '/') . '/'; + \Diffy\Diffy::$baseUrl = $baseUrl; + \Diffy\Diffy::$client = null; + unset($argv[$i], $argv[$i + 1]); + break; + } +} +$argv = array_values($argv); + $appName = "DiffyCli"; $appVersion = trim(file_get_contents(__DIR__ . '/VERSION')); $commandClasses = [ diff --git a/src/Commands/ScreenshotCommand.php b/src/Commands/ScreenshotCommand.php index 29a0a5f..197c742 100644 --- a/src/Commands/ScreenshotCommand.php +++ b/src/Commands/ScreenshotCommand.php @@ -13,6 +13,7 @@ class ScreenshotCommand extends Tasks { + const UPLOAD_BATCH_SIZE = 10; /** * Create a screenshot from environment * @@ -229,6 +230,106 @@ public function createScreenshotBaseline($projectId, $environment, array $option return new ResultData(); } + /** + * Create a functional test snapshot from a folder of screenshots + * + * @command screenshot:create-folder + * + * @param int $projectId ID of the project + * @param string $folderPath Path to the folder containing screenshot images (PNG/JPG) + * + * @usage screenshot:create-folder 342 ./screenshots Upload screenshots from folder as a functional test snapshot. + */ + public function createFolderScreenshot($projectId, string $folderPath) + { + $apiKey = Config::getConfig()['key']; + + Diffy::setApiKey($apiKey); + + if (!is_dir($folderPath)) { + $this->io()->write(sprintf('Folder not found: %s', $folderPath)); + throw new InvalidArgumentException(); + } + + $basePath = realpath($folderPath); + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($basePath, \RecursiveDirectoryIterator::SKIP_DOTS) + ); + + // Collect [filepath => url] sorted by relative path for deterministic ordering. + $found = []; + foreach ($iterator as $fileInfo) { + $ext = strtolower($fileInfo->getExtension()); + if (!in_array($ext, ['png', 'webp'])) { + continue; + } + $filepath = $fileInfo->getPathname(); + $relativePath = ltrim(substr($filepath, strlen($basePath)), DIRECTORY_SEPARATOR); + // Build URL: replace directory separators with "-", strip extension, make URL-safe. + $withoutExt = pathinfo(str_replace(DIRECTORY_SEPARATOR, '-', $relativePath), PATHINFO_FILENAME); + $urlSlug = strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $withoutExt)); + $found[$relativePath] = ['filepath' => $filepath, 'url' => '/' . $urlSlug]; + } + ksort($found); + $found = array_values($found); + + if (empty($found)) { + $this->io()->write(sprintf('No PNG/WebP images found in folder: %s', $folderPath)); + throw new InvalidArgumentException(); + } + + $batches = array_chunk($found, self::UPLOAD_BATCH_SIZE); + + $progressBar = $this->io()->createProgressBar(count($found)); + $progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%%'); + $progressBar->start(); + + $screenshotId = null; + foreach ($batches as $batchIndex => $batch) { + $data = [ + ['name' => 'snapshotName', 'contents' => basename($basePath)], + ['name' => 'functionalTest', 'contents' => '1'], + ]; + + foreach ($batch as $key => $item) { + $filepath = $item['filepath']; + $imageSize = getimagesize($filepath); + if ($imageSize === false) { + $progressBar->finish(); + $this->io()->newLine(); + $this->io()->write(sprintf('Could not read image dimensions for file: %s', $filepath)); + throw new InvalidArgumentException(); + } + $width = $imageSize[0]; + + $data[] = ['name' => 'breakpoints[' . $key . ']', 'contents' => (string) $width]; + $data[] = ['name' => 'urls[' . $key . ']', 'contents' => $item['url']]; + $data[] = [ + 'Content-type' => 'multipart/form-data', + 'name' => 'files[' . $key . ']', + 'filename' => basename($filepath), + 'contents' => file_get_contents($filepath), + ]; + } + + if ($batchIndex === 0) { + $screenshotId = Diffy::multipartRequest('POST', 'projects/' . $projectId . '/create-custom-snapshot', $data); + } else { + Diffy::multipartRequest('POST', 'snapshots/' . $screenshotId, $data); + } + + $progressBar->advance(count($batch)); + } + + $progressBar->finish(); + $this->io()->newLine(); + + $this->io()->write($screenshotId); + + // Successful exit. + return new ResultData(); + } + /** * Sets a new baseline from a screenshot ID. *