From 1ac8469f6e91f53c7f9b83bdfa0c07d5ae24b012 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Wed, 17 Dec 2025 08:06:43 +0100 Subject: [PATCH] Add E2E tests for CropperJS --- apps/e2e/public/images/example.jpg | Bin 0 -> 20032 bytes .../src/Controller/CropperjsController.php | 68 ++++++++++++++++- apps/e2e/src/Repository/ExampleRepository.php | 2 + .../e2e/templates/ux_cropperjs/crop.html.twig | 26 +++++++ .../templates/ux_cropperjs/index.html.twig | 3 - .../assets/test/browser/cropperjs.test.ts | 70 ++++++++++++++++++ .../assets/test/browser/placeholder.test.ts | 7 -- 7 files changed, 162 insertions(+), 14 deletions(-) create mode 100644 apps/e2e/public/images/example.jpg create mode 100644 apps/e2e/templates/ux_cropperjs/crop.html.twig delete mode 100644 apps/e2e/templates/ux_cropperjs/index.html.twig create mode 100644 src/Cropperjs/assets/test/browser/cropperjs.test.ts delete mode 100644 src/Cropperjs/assets/test/browser/placeholder.test.ts diff --git a/apps/e2e/public/images/example.jpg b/apps/e2e/public/images/example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49649fb6ca30edc09bd478989f649e132f329244 GIT binary patch literal 20032 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 literal 0 HcmV?d00001 diff --git a/apps/e2e/src/Controller/CropperjsController.php b/apps/e2e/src/Controller/CropperjsController.php index 0d5ea357f2a..7ea31d9dbac 100644 --- a/apps/e2e/src/Controller/CropperjsController.php +++ b/apps/e2e/src/Controller/CropperjsController.php @@ -3,17 +3,77 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\UX\Cropperjs\Factory\CropperInterface; +use Symfony\UX\Cropperjs\Form\CropperType; #[Route('/ux-cropperjs', name: 'app_ux_cropperjs_')] final class CropperjsController extends AbstractController { - #[Route('/', name: 'index')] - public function index(): Response + public function __construct( + #[Autowire('%kernel.project_dir%/public')] + private string $publicDir + ) {} + + #[Route('/crop', name: 'crop')] + public function crop(CropperInterface $cropper, Request $request): Response { - return $this->render('ux_cropperjs/index.html.twig', [ - 'controller_name' => 'CropperjsController', + $crop = $cropper->createCrop($this->publicDir . '/images/example.jpg'); + $crop->setCroppedMaxSize(800, 600); + + $form = $this->createFormBuilder(['crop' => $crop]) + ->add('crop', CropperType::class, [ + 'public_url' => '/images/example.jpg', + 'cropper_options' => [ + 'viewMode' => 1, + ], + ]) + ->getForm(); + + $form->handleRequest($request); + + $croppedImageData = null; + if ($form->isSubmitted() && $form->isValid()) { + // Get the cropped image as base64 + $croppedImageData = base64_encode($crop->getCroppedImage()); + } + + return $this->render('ux_cropperjs/crop.html.twig', [ + 'form' => $form, + 'croppedImageData' => $croppedImageData, + ]); + } + + #[Route('/crop-with-aspect-ratio', name: 'crop_with_aspect_ratio')] + public function cropWithAspectRatio(CropperInterface $cropper, Request $request): Response + { + $crop = $cropper->createCrop($this->publicDir . '/images/example.jpg'); + $crop->setCroppedMaxSize(1920, 1080); + + $form = $this->createFormBuilder(['crop' => $crop]) + ->add('crop', CropperType::class, [ + 'public_url' => '/images/example.jpg', + 'cropper_options' => [ + 'aspectRatio' => 16 / 9, + 'viewMode' => 1, + ], + ]) + ->getForm(); + + $form->handleRequest($request); + + $croppedImageData = null; + if ($form->isSubmitted() && $form->isValid()) { + // Get the cropped image as base64 + $croppedImageData = base64_encode($crop->getCroppedImage()); + } + + return $this->render('ux_cropperjs/crop.html.twig', [ + 'form' => $form, + 'croppedImageData' => $croppedImageData, ]); } } diff --git a/apps/e2e/src/Repository/ExampleRepository.php b/apps/e2e/src/Repository/ExampleRepository.php index 4977d979cde..61379893b09 100644 --- a/apps/e2e/src/Repository/ExampleRepository.php +++ b/apps/e2e/src/Repository/ExampleRepository.php @@ -30,6 +30,8 @@ public function __construct() new Example(UxPackage::ChartJs, 'Line chart with options', 'A line chart with custom options (showLines: false) that displays data points without connecting lines.', 'app_ux_chartjs_with_options'), new Example(UxPackage::ChartJs, 'Pie chart', 'A pie chart displaying data distribution across different categories.', 'app_ux_chartjs_pie'), new Example(UxPackage::ChartJs, 'Pie chart with options', 'A pie chart with custom options to control the appearance and behavior.', 'app_ux_chartjs_pie_with_options'), + new Example(UxPackage::Cropperjs, 'Image cropper', 'Crop an image with Cropper.js using default options.', 'app_ux_cropperjs_crop'), + new Example(UxPackage::Cropperjs, 'Image cropper with aspect ratio', 'Crop an image with a fixed 16:9 aspect ratio constraint.', 'app_ux_cropperjs_crop_with_aspect_ratio'), new Example(UxPackage::LiveComponent, 'Examples filtering', "On this page, you can filter all examples by query terms, and observe how the UI and URLs update during and after processing.", 'app_home'), new Example(UxPackage::LiveComponent, 'Counter', 'A basic counter that you can increment or decrement.', 'app_ux_live_component_counter'), new Example(UxPackage::Turbo, 'Turbo Drive navigation', 'Navigate between pages without full page reload using Turbo Drive.', 'app_ux_turbo_drive'), diff --git a/apps/e2e/templates/ux_cropperjs/crop.html.twig b/apps/e2e/templates/ux_cropperjs/crop.html.twig new file mode 100644 index 00000000000..057c6a176f3 --- /dev/null +++ b/apps/e2e/templates/ux_cropperjs/crop.html.twig @@ -0,0 +1,26 @@ +{% extends 'example.html.twig' %} + +{% block example %} +
+
+ {% if croppedImageData %} +
+
Image cropped successfully!
+

Here is your cropped image:

+ Cropped image +
+ {% endif %} + + {{ form_start(form, { attr: { 'data-turbo': 'false' } }) }} + {{ form_row(form.crop) }} +
+ +
+ {{ form_end(form) }} +
+
+{% endblock %} diff --git a/apps/e2e/templates/ux_cropperjs/index.html.twig b/apps/e2e/templates/ux_cropperjs/index.html.twig deleted file mode 100644 index 78c01e96007..00000000000 --- a/apps/e2e/templates/ux_cropperjs/index.html.twig +++ /dev/null @@ -1,3 +0,0 @@ -{% extends 'example.html.twig' %} - -{% block example %}{% endblock %} diff --git a/src/Cropperjs/assets/test/browser/cropperjs.test.ts b/src/Cropperjs/assets/test/browser/cropperjs.test.ts new file mode 100644 index 00000000000..2806cf64ce2 --- /dev/null +++ b/src/Cropperjs/assets/test/browser/cropperjs.test.ts @@ -0,0 +1,70 @@ +import { expect, test } from '@playwright/test'; + +test('Can display and interact with Cropper.js', async ({ page }) => { + await page.goto('/ux-cropperjs/crop'); + + // Wait for the cropper to be initialized + const cropperContainer = page.locator('.cropperjs'); + await expect(cropperContainer).toBeVisible(); + + // Check that the canvas is created by Cropper.js + const cropperCanvas = page.locator('.cropper-canvas'); + await expect(cropperCanvas).toBeVisible(); + + // Check that the crop box is visible + const cropBox = page.locator('.cropper-crop-box'); + await expect(cropBox).toBeVisible(); + + const actionE = page.locator('[data-cropper-action="e"].cropper-line'); + const actionEBox = await cropBox.boundingBox(); + if (actionEBox) { + // Drag the east handle to resize the crop box + await actionE.hover({ force: true }); + await page.mouse.down(); + await page.mouse.move(actionEBox.x + actionEBox.width - 200, actionEBox.y + actionEBox.height / 2); + await page.mouse.up(); + } + + // Submit the form to crop the image + await page.click('#crop-submit'); + + // Wait for the cropped image to be displayed + await expect(page.locator('#crop-success')).toBeVisible(); + await expect(page.locator('#cropped-result')).toBeVisible(); + + // Verify the cropped image is a valid base64 image + const croppedImage = page.locator('#cropped-result'); + expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,'); + const croppedImageWidth = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth); + const croppedImageHeight = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalHeight); + expect(croppedImageWidth).toBeGreaterThanOrEqual(540); // Chrome + expect(croppedImageWidth).toBeLessThanOrEqual(541); // Firefox + expect(croppedImageHeight).toEqual(461); +}); + +test('Can display Cropper.js with aspect ratio constraint', async ({ page }) => { + await page.goto('/ux-cropperjs/crop-with-aspect-ratio'); + + // Wait for the cropper to be initialized + const cropperContainer = page.locator('.cropperjs'); + await expect(cropperContainer).toBeVisible(); + + // Check that the canvas and crop box are visible + await expect(page.locator('.cropper-canvas')).toBeVisible(); + await expect(page.locator('.cropper-crop-box')).toBeVisible(); + + // Submit the form to crop the image + await page.click('#crop-submit'); + + // Wait for the cropped image to be displayed + await expect(page.locator('#crop-success')).toBeVisible(); + await expect(page.locator('#cropped-result')).toBeVisible(); + + // Verify the cropped image is a valid base64 image, and has the correct aspect ratio (16:9) + const croppedImage = page.locator('#cropped-result'); + expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,'); + expect(await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth / img.naturalHeight)).toBeCloseTo( + 16 / 9, + 1 + ); +}); diff --git a/src/Cropperjs/assets/test/browser/placeholder.test.ts b/src/Cropperjs/assets/test/browser/placeholder.test.ts deleted file mode 100644 index f5f1d10f22e..00000000000 --- a/src/Cropperjs/assets/test/browser/placeholder.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { expect, test } from '@playwright/test'; - -test('Can see homepage', async ({ page }) => { - await page.goto('/'); - - await expect(page.getByText("Symfony UX's E2E App")).toBeVisible(); -});