Skip to content

Commit 356ee98

Browse files
committed
minor #3240 Add E2E tests for CropperJS (Kocal)
This PR was merged into the 2.x branch. Discussion ---------- Add E2E tests for CropperJS | Q | A | -------------- | --- | Bug fix? | no | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md --> | Documentation? | no <!-- required for new features, or documentation updates --> | Issues | Fix #3019 <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Update/add documentation as required (we can help!) - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Commits ------- 1ac8469 Add E2E tests for CropperJS
2 parents b51d285 + 1ac8469 commit 356ee98

File tree

7 files changed

+162
-14
lines changed

7 files changed

+162
-14
lines changed

apps/e2e/public/images/example.jpg

19.6 KB
Loading

apps/e2e/src/Controller/CropperjsController.php

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,77 @@
33
namespace App\Controller;
44

55
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
6+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
7+
use Symfony\Component\HttpFoundation\Request;
68
use Symfony\Component\HttpFoundation\Response;
79
use Symfony\Component\Routing\Attribute\Route;
10+
use Symfony\UX\Cropperjs\Factory\CropperInterface;
11+
use Symfony\UX\Cropperjs\Form\CropperType;
812

913
#[Route('/ux-cropperjs', name: 'app_ux_cropperjs_')]
1014
final class CropperjsController extends AbstractController
1115
{
12-
#[Route('/', name: 'index')]
13-
public function index(): Response
16+
public function __construct(
17+
#[Autowire('%kernel.project_dir%/public')]
18+
private string $publicDir
19+
) {}
20+
21+
#[Route('/crop', name: 'crop')]
22+
public function crop(CropperInterface $cropper, Request $request): Response
1423
{
15-
return $this->render('ux_cropperjs/index.html.twig', [
16-
'controller_name' => 'CropperjsController',
24+
$crop = $cropper->createCrop($this->publicDir . '/images/example.jpg');
25+
$crop->setCroppedMaxSize(800, 600);
26+
27+
$form = $this->createFormBuilder(['crop' => $crop])
28+
->add('crop', CropperType::class, [
29+
'public_url' => '/images/example.jpg',
30+
'cropper_options' => [
31+
'viewMode' => 1,
32+
],
33+
])
34+
->getForm();
35+
36+
$form->handleRequest($request);
37+
38+
$croppedImageData = null;
39+
if ($form->isSubmitted() && $form->isValid()) {
40+
// Get the cropped image as base64
41+
$croppedImageData = base64_encode($crop->getCroppedImage());
42+
}
43+
44+
return $this->render('ux_cropperjs/crop.html.twig', [
45+
'form' => $form,
46+
'croppedImageData' => $croppedImageData,
47+
]);
48+
}
49+
50+
#[Route('/crop-with-aspect-ratio', name: 'crop_with_aspect_ratio')]
51+
public function cropWithAspectRatio(CropperInterface $cropper, Request $request): Response
52+
{
53+
$crop = $cropper->createCrop($this->publicDir . '/images/example.jpg');
54+
$crop->setCroppedMaxSize(1920, 1080);
55+
56+
$form = $this->createFormBuilder(['crop' => $crop])
57+
->add('crop', CropperType::class, [
58+
'public_url' => '/images/example.jpg',
59+
'cropper_options' => [
60+
'aspectRatio' => 16 / 9,
61+
'viewMode' => 1,
62+
],
63+
])
64+
->getForm();
65+
66+
$form->handleRequest($request);
67+
68+
$croppedImageData = null;
69+
if ($form->isSubmitted() && $form->isValid()) {
70+
// Get the cropped image as base64
71+
$croppedImageData = base64_encode($crop->getCroppedImage());
72+
}
73+
74+
return $this->render('ux_cropperjs/crop.html.twig', [
75+
'form' => $form,
76+
'croppedImageData' => $croppedImageData,
1777
]);
1878
}
1979
}

apps/e2e/src/Repository/ExampleRepository.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public function __construct()
3030
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'),
3131
new Example(UxPackage::ChartJs, 'Pie chart', 'A pie chart displaying data distribution across different categories.', 'app_ux_chartjs_pie'),
3232
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'),
33+
new Example(UxPackage::Cropperjs, 'Image cropper', 'Crop an image with Cropper.js using default options.', 'app_ux_cropperjs_crop'),
34+
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'),
3335
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'),
3436
new Example(UxPackage::LiveComponent, 'Counter', 'A basic counter that you can increment or decrement.', 'app_ux_live_component_counter'),
3537
new Example(UxPackage::Turbo, 'Turbo Drive navigation', 'Navigate between pages without full page reload using Turbo Drive.', 'app_ux_turbo_drive'),
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{% extends 'example.html.twig' %}
2+
3+
{% block example %}
4+
<div class="row">
5+
<div class="col-md-8 mx-auto">
6+
{% if croppedImageData %}
7+
<div class="alert alert-success mb-4" id="crop-success">
8+
<h5>Image cropped successfully!</h5>
9+
<p>Here is your cropped image:</p>
10+
<img src="data:image/jpeg;base64,{{ croppedImageData }}"
11+
alt="Cropped image"
12+
id="cropped-result"
13+
class="img-fluid border"
14+
style="max-width: 100%;">
15+
</div>
16+
{% endif %}
17+
18+
{{ form_start(form, { attr: { 'data-turbo': 'false' } }) }}
19+
{{ form_row(form.crop) }}
20+
<div class="mt-3">
21+
<button type="submit" class="btn btn-primary" id="crop-submit">Crop Image</button>
22+
</div>
23+
{{ form_end(form) }}
24+
</div>
25+
</div>
26+
{% endblock %}

apps/e2e/templates/ux_cropperjs/index.html.twig

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test('Can display and interact with Cropper.js', async ({ page }) => {
4+
await page.goto('/ux-cropperjs/crop');
5+
6+
// Wait for the cropper to be initialized
7+
const cropperContainer = page.locator('.cropperjs');
8+
await expect(cropperContainer).toBeVisible();
9+
10+
// Check that the canvas is created by Cropper.js
11+
const cropperCanvas = page.locator('.cropper-canvas');
12+
await expect(cropperCanvas).toBeVisible();
13+
14+
// Check that the crop box is visible
15+
const cropBox = page.locator('.cropper-crop-box');
16+
await expect(cropBox).toBeVisible();
17+
18+
const actionE = page.locator('[data-cropper-action="e"].cropper-line');
19+
const actionEBox = await cropBox.boundingBox();
20+
if (actionEBox) {
21+
// Drag the east handle to resize the crop box
22+
await actionE.hover({ force: true });
23+
await page.mouse.down();
24+
await page.mouse.move(actionEBox.x + actionEBox.width - 200, actionEBox.y + actionEBox.height / 2);
25+
await page.mouse.up();
26+
}
27+
28+
// Submit the form to crop the image
29+
await page.click('#crop-submit');
30+
31+
// Wait for the cropped image to be displayed
32+
await expect(page.locator('#crop-success')).toBeVisible();
33+
await expect(page.locator('#cropped-result')).toBeVisible();
34+
35+
// Verify the cropped image is a valid base64 image
36+
const croppedImage = page.locator('#cropped-result');
37+
expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,');
38+
const croppedImageWidth = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth);
39+
const croppedImageHeight = await croppedImage.evaluate((img: HTMLImageElement) => img.naturalHeight);
40+
expect(croppedImageWidth).toBeGreaterThanOrEqual(540); // Chrome
41+
expect(croppedImageWidth).toBeLessThanOrEqual(541); // Firefox
42+
expect(croppedImageHeight).toEqual(461);
43+
});
44+
45+
test('Can display Cropper.js with aspect ratio constraint', async ({ page }) => {
46+
await page.goto('/ux-cropperjs/crop-with-aspect-ratio');
47+
48+
// Wait for the cropper to be initialized
49+
const cropperContainer = page.locator('.cropperjs');
50+
await expect(cropperContainer).toBeVisible();
51+
52+
// Check that the canvas and crop box are visible
53+
await expect(page.locator('.cropper-canvas')).toBeVisible();
54+
await expect(page.locator('.cropper-crop-box')).toBeVisible();
55+
56+
// Submit the form to crop the image
57+
await page.click('#crop-submit');
58+
59+
// Wait for the cropped image to be displayed
60+
await expect(page.locator('#crop-success')).toBeVisible();
61+
await expect(page.locator('#cropped-result')).toBeVisible();
62+
63+
// Verify the cropped image is a valid base64 image, and has the correct aspect ratio (16:9)
64+
const croppedImage = page.locator('#cropped-result');
65+
expect(await croppedImage.getAttribute('src')).toContain('data:image/jpeg;base64,');
66+
expect(await croppedImage.evaluate((img: HTMLImageElement) => img.naturalWidth / img.naturalHeight)).toBeCloseTo(
67+
16 / 9,
68+
1
69+
);
70+
});

src/Cropperjs/assets/test/browser/placeholder.test.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)