@@ -706,3 +706,160 @@ test.describe("image-cropper / circle", () => {
706706 expect ( newRect . width ) . toEqual ( newRect . height )
707707 } )
708708} )
709+
710+ test . describe ( "image-cropper / viewport resize" , ( ) => {
711+ test . beforeEach ( async ( { page } ) => {
712+ I = new ImageCropperModel ( page )
713+ await I . goto ( )
714+ await I . waitForImageLoad ( )
715+ } )
716+
717+ test ( "[viewport] should scale crop proportionally when viewport shrinks" , async ( ) => {
718+ const { width : initialWidth , height : initialHeight } = await I . getViewportSize ( )
719+ const initialCrop = await I . getSelectionRect ( )
720+
721+ const initialWidthRatio = initialCrop . width / initialWidth
722+ const initialHeightRatio = initialCrop . height / initialHeight
723+
724+ const newWidth = Math . floor ( initialWidth * 0.8 )
725+ const newHeight = Math . floor ( initialHeight * 0.8 )
726+ await I . resizeViewport ( newWidth , newHeight )
727+
728+ const newViewport = await I . getViewportSize ( )
729+ const newCrop = await I . getSelectionRect ( )
730+
731+ expect ( newViewport . width ) . toBe ( newWidth )
732+ expect ( newViewport . height ) . toBe ( newHeight )
733+
734+ const newWidthRatio = newCrop . width / newViewport . width
735+ const newHeightRatio = newCrop . height / newViewport . height
736+
737+ expect ( newWidthRatio ) . toBe ( initialWidthRatio )
738+ expect ( newHeightRatio ) . toBe ( initialHeightRatio )
739+ } )
740+
741+ test ( "[viewport] should scale crop proportionally when viewport grows" , async ( ) => {
742+ const { width : initialWidth , height : initialHeight } = await I . getViewportSize ( )
743+ const initialCrop = await I . getSelectionRect ( )
744+
745+ const initialWidthRatio = initialCrop . width / initialWidth
746+ const initialHeightRatio = initialCrop . height / initialHeight
747+
748+ const newWidth = Math . floor ( initialWidth * 1.2 )
749+ const newHeight = Math . floor ( initialHeight * 1.2 )
750+ await I . resizeViewport ( newWidth , newHeight )
751+
752+ const newViewport = await I . getViewportSize ( )
753+ const newCrop = await I . getSelectionRect ( )
754+
755+ expect ( newViewport . width ) . toBe ( newWidth )
756+ expect ( newViewport . height ) . toBe ( newHeight )
757+
758+ const newWidthRatio = newCrop . width / newViewport . width
759+ const newHeightRatio = newCrop . height / newViewport . height
760+
761+ expect ( newWidthRatio ) . toBe ( initialWidthRatio )
762+ expect ( newHeightRatio ) . toBe ( initialHeightRatio )
763+ } )
764+
765+ test ( "[viewport] should maintain aspect ratio during resize when aspectRatio is set" , async ( ) => {
766+ await I . controls . num ( "aspectRatio" , "1.5" )
767+ await I . wait ( 100 )
768+
769+ const { width : initialWidth , height : initialHeight } = await I . getViewportSize ( )
770+ const newWidth = Math . floor ( initialWidth * 0.7 )
771+ const newHeight = Math . floor ( initialHeight * 0.7 )
772+ await I . resizeViewport ( newWidth , newHeight )
773+
774+ const newCrop = await I . getSelectionRect ( )
775+ const newAspectRatio = newCrop . width / newCrop . height
776+
777+ expect ( newAspectRatio ) . toBeCloseTo ( 1.5 , 2 )
778+ } )
779+
780+ test ( "[viewport] should keep crop within bounds after resize" , async ( ) => {
781+ const initialViewport = await I . getViewportRect ( )
782+ const initialCrop = await I . getSelectionRect ( )
783+
784+ const relativeMaxX = initialViewport . width - initialCrop . width
785+ const relativeMaxY = initialViewport . height - initialCrop . height
786+ const relativeCropX = initialCrop . x - initialViewport . x
787+ const relativeCropY = initialCrop . y - initialViewport . y
788+
789+ await I . dragSelection ( relativeMaxX - relativeCropX , relativeMaxY - relativeCropY )
790+ await I . wait ( 100 )
791+
792+ const newWidth = Math . floor ( initialViewport . width * 0.6 )
793+ const newHeight = Math . floor ( initialViewport . height * 0.6 )
794+ await I . resizeViewport ( newWidth , newHeight )
795+
796+ const newViewport = await I . getViewportRect ( )
797+ const newCrop = await I . getSelectionRect ( )
798+
799+ const relativeCropLeft = newCrop . x - newViewport . x
800+ const relativeCropTop = newCrop . y - newViewport . y
801+ const relativeCropRight = relativeCropLeft + newCrop . width
802+ const relativeCropBottom = relativeCropTop + newCrop . height
803+
804+ expect ( relativeCropLeft ) . toBeGreaterThanOrEqual ( 0 )
805+ expect ( relativeCropTop ) . toBeGreaterThanOrEqual ( 0 )
806+ expect ( relativeCropRight ) . toBeLessThanOrEqual ( newViewport . width )
807+ expect ( relativeCropBottom ) . toBeLessThanOrEqual ( newViewport . height )
808+ } )
809+
810+ test ( "[viewport] should respect minSize constraints after resize" , async ( ) => {
811+ await I . controls . num ( "minWidth" , "100" )
812+ await I . controls . num ( "minHeight" , "80" )
813+ await I . wait ( 100 )
814+
815+ const newWidth = 150
816+ const newHeight = 120
817+ await I . resizeViewport ( newWidth , newHeight )
818+
819+ const newCrop = await I . getSelectionRect ( )
820+
821+ expect ( newCrop . width ) . toBeGreaterThanOrEqual ( 100 )
822+ expect ( newCrop . height ) . toBeGreaterThanOrEqual ( 80 )
823+ } )
824+
825+ test ( "[viewport] should respect maxSize constraints after resize" , async ( ) => {
826+ await I . controls . num ( "maxWidth" , "200" )
827+ await I . controls . num ( "maxHeight" , "150" )
828+ await I . wait ( 100 )
829+
830+ const { width : initialWidth , height : initialHeight } = await I . getViewportSize ( )
831+
832+ const newWidth = Math . floor ( initialWidth * 1.5 )
833+ const newHeight = Math . floor ( initialHeight * 1.5 )
834+ await I . resizeViewport ( newWidth , newHeight )
835+
836+ const newCrop = await I . getSelectionRect ( )
837+ const newViewport = await I . getViewportRect ( )
838+
839+ const expectedMaxWidth = Math . min ( 200 , newViewport . width )
840+ const expectedMaxHeight = Math . min ( 150 , newViewport . height )
841+
842+ expect ( newCrop . width ) . toBeLessThanOrEqual ( expectedMaxWidth )
843+ expect ( newCrop . height ) . toBeLessThanOrEqual ( expectedMaxHeight )
844+ } )
845+
846+ test ( "[viewport] should handle multiple sequential resizes" , async ( ) => {
847+ const initialCrop = await I . getSelectionRect ( )
848+ const { width : initialWidth , height : initialHeight } = await I . getViewportSize ( )
849+
850+ await I . resizeViewport ( Math . floor ( initialWidth * 0.8 ) , Math . floor ( initialHeight * 0.8 ) )
851+ const afterShrink = await I . getSelectionRect ( )
852+
853+ await I . resizeViewport ( Math . floor ( initialWidth * 1.1 ) , Math . floor ( initialHeight * 1.1 ) )
854+ const afterGrow = await I . getSelectionRect ( )
855+
856+ await I . resizeViewport ( initialWidth , initialHeight )
857+ const finalCrop = await I . getSelectionRect ( )
858+
859+ expect ( afterShrink . width ) . toBeLessThan ( initialCrop . width )
860+ expect ( afterGrow . width ) . toBeGreaterThan ( afterShrink . width )
861+
862+ expect ( finalCrop . width ) . toBeCloseTo ( initialCrop . width , 0 )
863+ expect ( finalCrop . height ) . toBeCloseTo ( initialCrop . height , 0 )
864+ } )
865+ } )
0 commit comments