@wdio/image-comparison-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/assets/ios/ipadair4th.ipadair5th-bottom.png +0 -0
- package/assets/ios/ipadair4th.ipadair5th-top.png +0 -0
- package/assets/ios/ipadmini6th-bottom.png +0 -0
- package/assets/ios/ipadmini6th-top.png +0 -0
- package/assets/ios/ipadpro11-bottom.png +0 -0
- package/assets/ios/ipadpro11-top.png +0 -0
- package/assets/ios/ipadpro129-bottom.png +0 -0
- package/assets/ios/ipadpro129-top.png +0 -0
- package/assets/ios/iphone11promax-bottom.png +0 -0
- package/assets/ios/iphone11promax-top.png +0 -0
- package/assets/ios/iphone12.iphone12pro-top.png +0 -0
- package/assets/ios/iphone12.iphone12pro.iphone13.iphone13pro.iphone14-bottom.png +0 -0
- package/assets/ios/iphone12mini-top.png +0 -0
- package/assets/ios/iphone12mini.iphone13mini-bottom.png +0 -0
- package/assets/ios/iphone12promax-top.png +0 -0
- package/assets/ios/iphone12promax.iphone13promax.iphone14plus-bottom.png +0 -0
- package/assets/ios/iphone13.iphone13pro.iphone14-top.png +0 -0
- package/assets/ios/iphone13mini-top.png +0 -0
- package/assets/ios/iphone13promax.iphone14plus-top.png +0 -0
- package/assets/ios/iphone14pro-bottom.png +0 -0
- package/assets/ios/iphone14pro-top.png +0 -0
- package/assets/ios/iphone14promax-bottom.png +0 -0
- package/assets/ios/iphone14promax-top.png +0 -0
- package/assets/ios/iphone15-bottom.png +0 -0
- package/assets/ios/iphone15-top.png +0 -0
- package/assets/ios/iphonex.iphonexs.iphone11pro-bottom.png +0 -0
- package/assets/ios/iphonex.iphonexs.iphone11pro-top.png +0 -0
- package/assets/ios/iphonexr.iphone11-bottom.png +0 -0
- package/assets/ios/iphonexr.iphone11-top.png +0 -0
- package/assets/ios/iphonexsmax-bottom.png +0 -0
- package/assets/ios/iphonexsmax-top.png +0 -0
- package/dist/base.d.ts +18 -0
- package/dist/base.d.ts.map +1 -0
- package/dist/base.interfaces.d.ts +200 -0
- package/dist/base.interfaces.d.ts.map +1 -0
- package/dist/base.interfaces.js +1 -0
- package/dist/base.js +58 -0
- package/dist/base.test.d.ts +2 -0
- package/dist/base.test.d.ts.map +1 -0
- package/dist/base.test.js +45 -0
- package/dist/clientSideScripts/checkMetaTag.d.ts +2 -0
- package/dist/clientSideScripts/checkMetaTag.d.ts.map +1 -0
- package/dist/clientSideScripts/checkMetaTag.js +9 -0
- package/dist/clientSideScripts/checkMetaTag.test.d.ts +2 -0
- package/dist/clientSideScripts/checkMetaTag.test.d.ts.map +1 -0
- package/dist/clientSideScripts/checkMetaTag.test.js +25 -0
- package/dist/clientSideScripts/customCss.interfaces.d.ts +8 -0
- package/dist/clientSideScripts/customCss.interfaces.d.ts.map +1 -0
- package/dist/clientSideScripts/customCss.interfaces.js +1 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.d.ts +8 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.d.ts.map +1 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.interfaces.d.ts +4 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.interfaces.d.ts.map +1 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.interfaces.js +1 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.js +275 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.test.d.ts +2 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.test.d.ts.map +1 -0
- package/dist/clientSideScripts/drawTabbableOnCanvas.test.js +251 -0
- package/dist/clientSideScripts/elementPosition.interfaces.d.ts +3 -0
- package/dist/clientSideScripts/elementPosition.interfaces.d.ts.map +1 -0
- package/dist/clientSideScripts/elementPosition.interfaces.js +1 -0
- package/dist/clientSideScripts/getBoundingClientRect.d.ts +6 -0
- package/dist/clientSideScripts/getBoundingClientRect.d.ts.map +1 -0
- package/dist/clientSideScripts/getBoundingClientRect.js +12 -0
- package/dist/clientSideScripts/getBoundingClientRect.test.d.ts +2 -0
- package/dist/clientSideScripts/getBoundingClientRect.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getBoundingClientRect.test.js +28 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.d.ts +5 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.d.ts.map +1 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.js +48 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.test.d.ts +2 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getDocumentScrollHeight.test.js +30 -0
- package/dist/clientSideScripts/getElementPositionTopDom.d.ts +6 -0
- package/dist/clientSideScripts/getElementPositionTopDom.d.ts.map +1 -0
- package/dist/clientSideScripts/getElementPositionTopDom.js +11 -0
- package/dist/clientSideScripts/getElementPositionTopDom.test.d.ts +2 -0
- package/dist/clientSideScripts/getElementPositionTopDom.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getElementPositionTopDom.test.js +11 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.d.ts +14 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.d.ts.map +1 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.js +19 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.test.d.ts +2 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getElementPositionTopScreenNativeMobile.test.js +52 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.d.ts +6 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.d.ts.map +1 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.js +17 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.test.d.ts +2 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getMobileWebviewClickAndDimensions.test.js +47 -0
- package/dist/clientSideScripts/getScreenDimensions.d.ts +6 -0
- package/dist/clientSideScripts/getScreenDimensions.d.ts.map +1 -0
- package/dist/clientSideScripts/getScreenDimensions.js +104 -0
- package/dist/clientSideScripts/getScreenDimensions.test.d.ts +2 -0
- package/dist/clientSideScripts/getScreenDimensions.test.d.ts.map +1 -0
- package/dist/clientSideScripts/getScreenDimensions.test.js +161 -0
- package/dist/clientSideScripts/hideRemoveElements.d.ts +8 -0
- package/dist/clientSideScripts/hideRemoveElements.d.ts.map +1 -0
- package/dist/clientSideScripts/hideRemoveElements.js +60 -0
- package/dist/clientSideScripts/hideRemoveElements.test.d.ts +2 -0
- package/dist/clientSideScripts/hideRemoveElements.test.d.ts.map +1 -0
- package/dist/clientSideScripts/hideRemoveElements.test.js +199 -0
- package/dist/clientSideScripts/hideScrollbars.d.ts +9 -0
- package/dist/clientSideScripts/hideScrollbars.d.ts.map +1 -0
- package/dist/clientSideScripts/hideScrollbars.js +15 -0
- package/dist/clientSideScripts/hideScrollbars.test.d.ts +2 -0
- package/dist/clientSideScripts/hideScrollbars.test.d.ts.map +1 -0
- package/dist/clientSideScripts/hideScrollbars.test.js +12 -0
- package/dist/clientSideScripts/injectWebviewOverlay.d.ts +5 -0
- package/dist/clientSideScripts/injectWebviewOverlay.d.ts.map +1 -0
- package/dist/clientSideScripts/injectWebviewOverlay.js +35 -0
- package/dist/clientSideScripts/injectWebviewOverlay.test.d.ts +2 -0
- package/dist/clientSideScripts/injectWebviewOverlay.test.d.ts.map +1 -0
- package/dist/clientSideScripts/injectWebviewOverlay.test.js +74 -0
- package/dist/clientSideScripts/removeElementFromDom.d.ts +5 -0
- package/dist/clientSideScripts/removeElementFromDom.d.ts.map +1 -0
- package/dist/clientSideScripts/removeElementFromDom.js +9 -0
- package/dist/clientSideScripts/removeElementFromDom.test.d.ts +2 -0
- package/dist/clientSideScripts/removeElementFromDom.test.d.ts.map +1 -0
- package/dist/clientSideScripts/removeElementFromDom.test.js +43 -0
- package/dist/clientSideScripts/screenDimensions.interfaces.d.ts +96 -0
- package/dist/clientSideScripts/screenDimensions.interfaces.d.ts.map +1 -0
- package/dist/clientSideScripts/screenDimensions.interfaces.js +1 -0
- package/dist/clientSideScripts/scrollElementIntoView.d.ts +5 -0
- package/dist/clientSideScripts/scrollElementIntoView.d.ts.map +1 -0
- package/dist/clientSideScripts/scrollElementIntoView.js +31 -0
- package/dist/clientSideScripts/scrollElementIntoView.test.d.ts +2 -0
- package/dist/clientSideScripts/scrollElementIntoView.test.d.ts.map +1 -0
- package/dist/clientSideScripts/scrollElementIntoView.test.js +79 -0
- package/dist/clientSideScripts/scrollToPosition.d.ts +5 -0
- package/dist/clientSideScripts/scrollToPosition.d.ts.map +1 -0
- package/dist/clientSideScripts/scrollToPosition.js +25 -0
- package/dist/clientSideScripts/scrollToPosition.test.d.ts +2 -0
- package/dist/clientSideScripts/scrollToPosition.test.d.ts.map +1 -0
- package/dist/clientSideScripts/scrollToPosition.test.js +72 -0
- package/dist/clientSideScripts/setCustomCss.d.ts +6 -0
- package/dist/clientSideScripts/setCustomCss.d.ts.map +1 -0
- package/dist/clientSideScripts/setCustomCss.js +36 -0
- package/dist/clientSideScripts/setCustomCss.test.d.ts +2 -0
- package/dist/clientSideScripts/setCustomCss.test.d.ts.map +1 -0
- package/dist/clientSideScripts/setCustomCss.test.js +68 -0
- package/dist/clientSideScripts/statusAddressToolBarOffsets.interfaces.d.ts +11 -0
- package/dist/clientSideScripts/statusAddressToolBarOffsets.interfaces.d.ts.map +1 -0
- package/dist/clientSideScripts/statusAddressToolBarOffsets.interfaces.js +1 -0
- package/dist/clientSideScripts/toggleTextTransparency.d.ts +5 -0
- package/dist/clientSideScripts/toggleTextTransparency.d.ts.map +1 -0
- package/dist/clientSideScripts/toggleTextTransparency.js +16 -0
- package/dist/clientSideScripts/toggleTextTransparency.test.d.ts +2 -0
- package/dist/clientSideScripts/toggleTextTransparency.test.d.ts.map +1 -0
- package/dist/clientSideScripts/toggleTextTransparency.test.js +35 -0
- package/dist/clientSideScripts/waitForFonts.d.ts +8 -0
- package/dist/clientSideScripts/waitForFonts.d.ts.map +1 -0
- package/dist/clientSideScripts/waitForFonts.js +20 -0
- package/dist/clientSideScripts/waitForFonts.test.d.ts +2 -0
- package/dist/clientSideScripts/waitForFonts.test.d.ts.map +1 -0
- package/dist/clientSideScripts/waitForFonts.test.js +37 -0
- package/dist/commands/check.interfaces.d.ts +35 -0
- package/dist/commands/check.interfaces.d.ts.map +1 -0
- package/dist/commands/check.interfaces.js +1 -0
- package/dist/commands/checkAppElement.d.ts +7 -0
- package/dist/commands/checkAppElement.d.ts.map +1 -0
- package/dist/commands/checkAppElement.js +44 -0
- package/dist/commands/checkAppElement.test.d.ts +2 -0
- package/dist/commands/checkAppElement.test.d.ts.map +1 -0
- package/dist/commands/checkAppElement.test.js +241 -0
- package/dist/commands/checkAppScreen.d.ts +7 -0
- package/dist/commands/checkAppScreen.d.ts.map +1 -0
- package/dist/commands/checkAppScreen.js +73 -0
- package/dist/commands/checkAppScreen.test.d.ts +2 -0
- package/dist/commands/checkAppScreen.test.d.ts.map +1 -0
- package/dist/commands/checkAppScreen.test.js +295 -0
- package/dist/commands/checkElement.d.ts +7 -0
- package/dist/commands/checkElement.d.ts.map +1 -0
- package/dist/commands/checkElement.js +10 -0
- package/dist/commands/checkElement.test.d.ts +2 -0
- package/dist/commands/checkElement.test.d.ts.map +1 -0
- package/dist/commands/checkElement.test.js +66 -0
- package/dist/commands/checkFullPageScreen.d.ts +7 -0
- package/dist/commands/checkFullPageScreen.d.ts.map +1 -0
- package/dist/commands/checkFullPageScreen.js +56 -0
- package/dist/commands/checkFullPageScreen.test.d.ts +2 -0
- package/dist/commands/checkFullPageScreen.test.d.ts.map +1 -0
- package/dist/commands/checkFullPageScreen.test.js +259 -0
- package/dist/commands/checkScreen.d.ts +7 -0
- package/dist/commands/checkScreen.d.ts.map +1 -0
- package/dist/commands/checkScreen.js +10 -0
- package/dist/commands/checkScreen.test.d.ts +2 -0
- package/dist/commands/checkScreen.test.d.ts.map +1 -0
- package/dist/commands/checkScreen.test.js +62 -0
- package/dist/commands/checkTabbablePage.d.ts +7 -0
- package/dist/commands/checkTabbablePage.d.ts.map +1 -0
- package/dist/commands/checkTabbablePage.js +28 -0
- package/dist/commands/checkTabbablePage.test.d.ts +2 -0
- package/dist/commands/checkTabbablePage.test.d.ts.map +1 -0
- package/dist/commands/checkTabbablePage.test.js +200 -0
- package/dist/commands/checkWebElement.d.ts +7 -0
- package/dist/commands/checkWebElement.d.ts.map +1 -0
- package/dist/commands/checkWebElement.js +52 -0
- package/dist/commands/checkWebElement.test.d.ts +2 -0
- package/dist/commands/checkWebElement.test.d.ts.map +1 -0
- package/dist/commands/checkWebElement.test.js +278 -0
- package/dist/commands/checkWebScreen.d.ts +7 -0
- package/dist/commands/checkWebScreen.d.ts.map +1 -0
- package/dist/commands/checkWebScreen.js +50 -0
- package/dist/commands/checkWebScreen.test.d.ts +2 -0
- package/dist/commands/checkWebScreen.test.d.ts.map +1 -0
- package/dist/commands/checkWebScreen.test.js +227 -0
- package/dist/commands/element.interfaces.d.ts +27 -0
- package/dist/commands/element.interfaces.d.ts.map +1 -0
- package/dist/commands/element.interfaces.js +1 -0
- package/dist/commands/fullPage.interfaces.d.ts +38 -0
- package/dist/commands/fullPage.interfaces.d.ts.map +1 -0
- package/dist/commands/fullPage.interfaces.js +1 -0
- package/dist/commands/save.interfaces.d.ts +27 -0
- package/dist/commands/save.interfaces.d.ts.map +1 -0
- package/dist/commands/save.interfaces.js +1 -0
- package/dist/commands/saveAppElement.d.ts +7 -0
- package/dist/commands/saveAppElement.d.ts.map +1 -0
- package/dist/commands/saveAppElement.js +30 -0
- package/dist/commands/saveAppElement.test.d.ts +2 -0
- package/dist/commands/saveAppElement.test.d.ts.map +1 -0
- package/dist/commands/saveAppElement.test.js +199 -0
- package/dist/commands/saveAppScreen.d.ts +7 -0
- package/dist/commands/saveAppScreen.d.ts.map +1 -0
- package/dist/commands/saveAppScreen.js +43 -0
- package/dist/commands/saveAppScreen.test.d.ts +2 -0
- package/dist/commands/saveAppScreen.test.d.ts.map +1 -0
- package/dist/commands/saveAppScreen.test.js +221 -0
- package/dist/commands/saveElement.d.ts +7 -0
- package/dist/commands/saveElement.d.ts.map +1 -0
- package/dist/commands/saveElement.js +10 -0
- package/dist/commands/saveElement.test.d.ts +2 -0
- package/dist/commands/saveElement.test.d.ts.map +1 -0
- package/dist/commands/saveElement.test.js +62 -0
- package/dist/commands/saveFullPageScreen.d.ts +7 -0
- package/dist/commands/saveFullPageScreen.d.ts.map +1 -0
- package/dist/commands/saveFullPageScreen.js +60 -0
- package/dist/commands/saveFullPageScreen.test.d.ts +2 -0
- package/dist/commands/saveFullPageScreen.test.d.ts.map +1 -0
- package/dist/commands/saveFullPageScreen.test.js +293 -0
- package/dist/commands/saveScreen.d.ts +7 -0
- package/dist/commands/saveScreen.d.ts.map +1 -0
- package/dist/commands/saveScreen.js +10 -0
- package/dist/commands/saveScreen.test.d.ts +2 -0
- package/dist/commands/saveScreen.test.d.ts.map +1 -0
- package/dist/commands/saveScreen.test.js +47 -0
- package/dist/commands/saveTabbablePage.d.ts +7 -0
- package/dist/commands/saveTabbablePage.d.ts.map +1 -0
- package/dist/commands/saveTabbablePage.js +20 -0
- package/dist/commands/saveTabbablePage.test.d.ts +2 -0
- package/dist/commands/saveTabbablePage.test.d.ts.map +1 -0
- package/dist/commands/saveTabbablePage.test.js +74 -0
- package/dist/commands/saveWebElement.d.ts +7 -0
- package/dist/commands/saveWebElement.d.ts.map +1 -0
- package/dist/commands/saveWebElement.js +53 -0
- package/dist/commands/saveWebElement.test.d.ts +2 -0
- package/dist/commands/saveWebElement.test.d.ts.map +1 -0
- package/dist/commands/saveWebElement.test.js +253 -0
- package/dist/commands/saveWebScreen.d.ts +7 -0
- package/dist/commands/saveWebScreen.d.ts.map +1 -0
- package/dist/commands/saveWebScreen.js +48 -0
- package/dist/commands/saveWebScreen.test.d.ts +2 -0
- package/dist/commands/saveWebScreen.test.d.ts.map +1 -0
- package/dist/commands/saveWebScreen.test.js +222 -0
- package/dist/commands/screen.interfaces.d.ts +16 -0
- package/dist/commands/screen.interfaces.d.ts.map +1 -0
- package/dist/commands/screen.interfaces.js +1 -0
- package/dist/commands/tabbable.interfaces.d.ts +29 -0
- package/dist/commands/tabbable.interfaces.d.ts.map +1 -0
- package/dist/commands/tabbable.interfaces.js +1 -0
- package/dist/helpers/afterScreenshot.d.ts +7 -0
- package/dist/helpers/afterScreenshot.d.ts.map +1 -0
- package/dist/helpers/afterScreenshot.interfaces.d.ts +46 -0
- package/dist/helpers/afterScreenshot.interfaces.d.ts.map +1 -0
- package/dist/helpers/afterScreenshot.interfaces.js +1 -0
- package/dist/helpers/afterScreenshot.js +55 -0
- package/dist/helpers/afterScreenshot.test.d.ts +2 -0
- package/dist/helpers/afterScreenshot.test.d.ts.map +1 -0
- package/dist/helpers/afterScreenshot.test.js +241 -0
- package/dist/helpers/beforeScreenshot.d.ts +6 -0
- package/dist/helpers/beforeScreenshot.d.ts.map +1 -0
- package/dist/helpers/beforeScreenshot.interfaces.d.ts +15 -0
- package/dist/helpers/beforeScreenshot.interfaces.d.ts.map +1 -0
- package/dist/helpers/beforeScreenshot.interfaces.js +1 -0
- package/dist/helpers/beforeScreenshot.js +79 -0
- package/dist/helpers/beforeScreenshot.test.d.ts +2 -0
- package/dist/helpers/beforeScreenshot.test.d.ts.map +1 -0
- package/dist/helpers/beforeScreenshot.test.js +261 -0
- package/dist/helpers/compare.interfaces.d.ts +16 -0
- package/dist/helpers/compare.interfaces.d.ts.map +1 -0
- package/dist/helpers/compare.interfaces.js +1 -0
- package/dist/helpers/constants.d.ts +108 -0
- package/dist/helpers/constants.d.ts.map +1 -0
- package/dist/helpers/constants.interfaces.d.ts +30 -0
- package/dist/helpers/constants.interfaces.d.ts.map +1 -0
- package/dist/helpers/constants.interfaces.js +11 -0
- package/dist/helpers/constants.js +436 -0
- package/dist/helpers/options.d.ts +64 -0
- package/dist/helpers/options.d.ts.map +1 -0
- package/dist/helpers/options.interfaces.d.ts +363 -0
- package/dist/helpers/options.interfaces.d.ts.map +1 -0
- package/dist/helpers/options.interfaces.js +1 -0
- package/dist/helpers/options.js +184 -0
- package/dist/helpers/options.test.d.ts +2 -0
- package/dist/helpers/options.test.d.ts.map +1 -0
- package/dist/helpers/options.test.js +428 -0
- package/dist/helpers/utils.d.ts +134 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/helpers/utils.interfaces.d.ts +254 -0
- package/dist/helpers/utils.interfaces.d.ts.map +1 -0
- package/dist/helpers/utils.interfaces.js +1 -0
- package/dist/helpers/utils.js +566 -0
- package/dist/helpers/utils.test.d.ts +2 -0
- package/dist/helpers/utils.test.d.ts.map +1 -0
- package/dist/helpers/utils.test.js +900 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/methods/compareReport.interfaces.d.ts +109 -0
- package/dist/methods/compareReport.interfaces.d.ts.map +1 -0
- package/dist/methods/compareReport.interfaces.js +1 -0
- package/dist/methods/createCompareReport.d.ts +7 -0
- package/dist/methods/createCompareReport.d.ts.map +1 -0
- package/dist/methods/createCompareReport.js +61 -0
- package/dist/methods/createCompareReport.test.d.ts +2 -0
- package/dist/methods/createCompareReport.test.d.ts.map +1 -0
- package/dist/methods/createCompareReport.test.js +236 -0
- package/dist/methods/elementPosition.d.ts +18 -0
- package/dist/methods/elementPosition.d.ts.map +1 -0
- package/dist/methods/elementPosition.interfaces.d.ts +14 -0
- package/dist/methods/elementPosition.interfaces.d.ts.map +1 -0
- package/dist/methods/elementPosition.interfaces.js +1 -0
- package/dist/methods/elementPosition.js +34 -0
- package/dist/methods/images.d.ts +69 -0
- package/dist/methods/images.d.ts.map +1 -0
- package/dist/methods/images.executeImageCompare.test.d.ts +2 -0
- package/dist/methods/images.executeImageCompare.test.d.ts.map +1 -0
- package/dist/methods/images.executeImageCompare.test.js +770 -0
- package/dist/methods/images.interfaces.d.ts +214 -0
- package/dist/methods/images.interfaces.d.ts.map +1 -0
- package/dist/methods/images.interfaces.js +1 -0
- package/dist/methods/images.js +428 -0
- package/dist/methods/images.test.d.ts +2 -0
- package/dist/methods/images.test.d.ts.map +1 -0
- package/dist/methods/images.test.js +1465 -0
- package/dist/methods/instanceData.d.ts +6 -0
- package/dist/methods/instanceData.d.ts.map +1 -0
- package/dist/methods/instanceData.interfaces.d.ts +57 -0
- package/dist/methods/instanceData.interfaces.d.ts.map +1 -0
- package/dist/methods/instanceData.interfaces.js +1 -0
- package/dist/methods/instanceData.js +42 -0
- package/dist/methods/instanceData.test.d.ts +2 -0
- package/dist/methods/instanceData.test.d.ts.map +1 -0
- package/dist/methods/instanceData.test.js +224 -0
- package/dist/methods/processDiffPixels.d.ts +59 -0
- package/dist/methods/processDiffPixels.d.ts.map +1 -0
- package/dist/methods/processDiffPixels.js +242 -0
- package/dist/methods/processDiffPixels.test.d.ts +2 -0
- package/dist/methods/processDiffPixels.test.d.ts.map +1 -0
- package/dist/methods/processDiffPixels.test.js +122 -0
- package/dist/methods/rectangles.d.ts +51 -0
- package/dist/methods/rectangles.d.ts.map +1 -0
- package/dist/methods/rectangles.interfaces.d.ts +146 -0
- package/dist/methods/rectangles.interfaces.d.ts.map +1 -0
- package/dist/methods/rectangles.interfaces.js +1 -0
- package/dist/methods/rectangles.js +266 -0
- package/dist/methods/rectangles.test.d.ts +2 -0
- package/dist/methods/rectangles.test.d.ts.map +1 -0
- package/dist/methods/rectangles.test.js +853 -0
- package/dist/methods/screenshots.d.ts +37 -0
- package/dist/methods/screenshots.d.ts.map +1 -0
- package/dist/methods/screenshots.interfaces.d.ts +203 -0
- package/dist/methods/screenshots.interfaces.d.ts.map +1 -0
- package/dist/methods/screenshots.interfaces.js +1 -0
- package/dist/methods/screenshots.js +394 -0
- package/dist/methods/screenshots.test.d.ts +2 -0
- package/dist/methods/screenshots.test.d.ts.map +1 -0
- package/dist/methods/screenshots.test.js +656 -0
- package/dist/methods/takeElementScreenshots.d.ts +3 -0
- package/dist/methods/takeElementScreenshots.d.ts.map +1 -0
- package/dist/methods/takeElementScreenshots.js +104 -0
- package/dist/methods/takeElementScreenshots.test.d.ts +2 -0
- package/dist/methods/takeElementScreenshots.test.d.ts.map +1 -0
- package/dist/methods/takeElementScreenshots.test.js +314 -0
- package/dist/methods/takeFullPageScreenshots.d.ts +3 -0
- package/dist/methods/takeFullPageScreenshots.d.ts.map +1 -0
- package/dist/methods/takeFullPageScreenshots.js +56 -0
- package/dist/methods/takeFullPageScreenshots.test.d.ts +2 -0
- package/dist/methods/takeFullPageScreenshots.test.d.ts.map +1 -0
- package/dist/methods/takeFullPageScreenshots.test.js +72 -0
- package/dist/methods/takeWebScreenshots.d.ts +3 -0
- package/dist/methods/takeWebScreenshots.d.ts.map +1 -0
- package/dist/methods/takeWebScreenshots.js +41 -0
- package/dist/methods/takeWebScreenshots.test.d.ts +2 -0
- package/dist/methods/takeWebScreenshots.test.d.ts.map +1 -0
- package/dist/methods/takeWebScreenshots.test.js +149 -0
- package/dist/mocks/image.d.ts +4 -0
- package/dist/mocks/image.d.ts.map +1 -0
- package/dist/mocks/image.js +3 -0
- package/dist/mocks/mocks.d.ts +745 -0
- package/dist/mocks/mocks.d.ts.map +1 -0
- package/dist/mocks/mocks.js +392 -0
- package/dist/resemble/compare.interfaces.d.ts +136 -0
- package/dist/resemble/compare.interfaces.d.ts.map +1 -0
- package/dist/resemble/compare.interfaces.js +1 -0
- package/dist/resemble/compareImages.d.ts +3 -0
- package/dist/resemble/compareImages.d.ts.map +1 -0
- package/dist/resemble/compareImages.js +21 -0
- package/dist/resemble/resemble.jimp.cjs +981 -0
- package/dist/resemble/resemble.jimp.d.cts +46 -0
- package/dist/resemble/resemble.jimp.d.cts.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { promises as fsPromises, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import logger from '@wdio/logger';
|
|
5
|
+
import { DEFAULT_RESIZE_DIMENSIONS } from '../helpers/constants.js';
|
|
6
|
+
import { checkIfImageExists, removeDiffImageIfExists, checkBaselineImageExists, getRotatedImageIfNeeded, logDimensionWarning, getAdjustedAxis, handleIOSBezelCorners, cropAndConvertToDataURL, makeCroppedBase64Image, makeFullPageBase64Image, rotateBase64Image, takeResizedBase64Screenshot, } from './images.js';
|
|
7
|
+
const log = logger('test');
|
|
8
|
+
vi.mock('jimp', () => ({
|
|
9
|
+
Jimp: Object.assign(vi.fn().mockImplementation(() => ({
|
|
10
|
+
composite: vi.fn().mockReturnThis(),
|
|
11
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mockImageData'),
|
|
12
|
+
opacity: vi.fn().mockReturnThis(),
|
|
13
|
+
rotate: vi.fn().mockReturnThis(),
|
|
14
|
+
crop: vi.fn().mockReturnThis(),
|
|
15
|
+
})), {
|
|
16
|
+
read: vi.fn(),
|
|
17
|
+
MIME_PNG: 'image/png',
|
|
18
|
+
}),
|
|
19
|
+
JimpMime: {
|
|
20
|
+
png: 'image/png',
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
vi.mock('@wdio/logger', () => import(join(process.cwd(), '__mocks__', '@wdio/logger')));
|
|
24
|
+
vi.mock('node:fs', async () => {
|
|
25
|
+
const actual = await vi.importActual('node:fs');
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
promises: {
|
|
29
|
+
access: vi.fn(),
|
|
30
|
+
unlink: vi.fn(),
|
|
31
|
+
},
|
|
32
|
+
readFileSync: vi.fn(),
|
|
33
|
+
writeFileSync: vi.fn(),
|
|
34
|
+
constants: {
|
|
35
|
+
R_OK: 4,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
vi.mock('../helpers/utils.js', () => ({
|
|
40
|
+
getBase64ScreenshotSize: vi.fn(),
|
|
41
|
+
getAndCreatePath: vi.fn(),
|
|
42
|
+
getIosBezelImageNames: vi.fn(),
|
|
43
|
+
updateVisualBaseline: vi.fn(),
|
|
44
|
+
calculateDprData: vi.fn(),
|
|
45
|
+
}));
|
|
46
|
+
vi.mock('../helpers/constants.js', () => ({
|
|
47
|
+
DEFAULT_RESIZE_DIMENSIONS: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
48
|
+
supportedIosBezelDevices: [
|
|
49
|
+
'iphonex', 'iphonexs', 'iphonexsmax', 'iphonexr', 'iphone11', 'iphone11pro', 'iphone11promax',
|
|
50
|
+
'iphone12', 'iphone12mini', 'iphone12pro', 'iphone12promax', 'iphone13', 'iphone13mini',
|
|
51
|
+
'iphone13pro', 'iphone13promax', 'iphone14', 'iphone14plus', 'iphone14pro', 'iphone14promax',
|
|
52
|
+
'iphone15', 'ipadmini', 'ipadair', 'ipadpro11', 'ipadpro129'
|
|
53
|
+
],
|
|
54
|
+
}));
|
|
55
|
+
vi.mock('./rectangles.js', () => ({
|
|
56
|
+
isWdioElement: vi.fn(),
|
|
57
|
+
determineStatusAddressToolBarRectangles: vi.fn(),
|
|
58
|
+
}));
|
|
59
|
+
vi.mock('./screenshots.js', () => ({
|
|
60
|
+
takeBase64Screenshot: vi.fn(),
|
|
61
|
+
}));
|
|
62
|
+
describe('checkIfImageExists', () => {
|
|
63
|
+
let accessSpy;
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
accessSpy = vi.spyOn(fsPromises, 'access');
|
|
66
|
+
});
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
vi.clearAllMocks();
|
|
69
|
+
accessSpy.mockRestore();
|
|
70
|
+
});
|
|
71
|
+
it('should return true when file exists', async () => {
|
|
72
|
+
accessSpy.mockResolvedValue(undefined);
|
|
73
|
+
const result = await checkIfImageExists('/path/to/image.png');
|
|
74
|
+
expect(result).toMatchSnapshot();
|
|
75
|
+
});
|
|
76
|
+
it('should return false when file does not exist', async () => {
|
|
77
|
+
accessSpy.mockRejectedValue(new Error('File not found'));
|
|
78
|
+
const result = await checkIfImageExists('/path/to/image.png');
|
|
79
|
+
expect(result).toMatchSnapshot();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('removeDiffImageIfExists', () => {
|
|
83
|
+
let accessSpy;
|
|
84
|
+
let unlinkSpy;
|
|
85
|
+
let logInfoSpy;
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
accessSpy = vi.spyOn(fsPromises, 'access');
|
|
88
|
+
unlinkSpy = vi.spyOn(fsPromises, 'unlink');
|
|
89
|
+
logInfoSpy = vi.spyOn(log, 'info').mockImplementation(() => { });
|
|
90
|
+
});
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
vi.clearAllMocks();
|
|
93
|
+
accessSpy.mockRestore();
|
|
94
|
+
unlinkSpy.mockRestore();
|
|
95
|
+
logInfoSpy.mockRestore();
|
|
96
|
+
});
|
|
97
|
+
// Note: We mock fsPromises.access here because removeDiffImageIfExists calls checkIfImageExists internally,
|
|
98
|
+
// which in turn calls fsPromises.access. This allows us to test the real integration between the functions
|
|
99
|
+
// without artificial mocking of internal dependencies.
|
|
100
|
+
it('should remove file when it exists', async () => {
|
|
101
|
+
accessSpy.mockResolvedValue(undefined);
|
|
102
|
+
unlinkSpy.mockResolvedValue(undefined);
|
|
103
|
+
await removeDiffImageIfExists('/path/to/diff.png');
|
|
104
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/diff.png', 4);
|
|
105
|
+
expect(unlinkSpy).toHaveBeenCalledWith('/path/to/diff.png');
|
|
106
|
+
expect(logInfoSpy).toHaveBeenCalledWith('Successfully removed the diff image before comparing at /path/to/diff.png');
|
|
107
|
+
});
|
|
108
|
+
it('should do nothing when file does not exist', async () => {
|
|
109
|
+
accessSpy.mockRejectedValue(new Error('File not found'));
|
|
110
|
+
await removeDiffImageIfExists('/path/to/diff.png');
|
|
111
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/diff.png', 4);
|
|
112
|
+
expect(unlinkSpy).not.toHaveBeenCalled();
|
|
113
|
+
expect(logInfoSpy).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
it('should throw error when file exists but cannot be removed', async () => {
|
|
116
|
+
accessSpy.mockResolvedValue(undefined);
|
|
117
|
+
const unlinkError = new Error('Permission denied');
|
|
118
|
+
unlinkSpy.mockRejectedValue(unlinkError);
|
|
119
|
+
await expect(removeDiffImageIfExists('/path/to/diff.png')).rejects.toThrow('Could not remove the diff image. The following error was thrown: Error: Permission denied');
|
|
120
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/diff.png', 4);
|
|
121
|
+
expect(unlinkSpy).toHaveBeenCalledWith('/path/to/diff.png');
|
|
122
|
+
expect(logInfoSpy).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('checkBaselineImageExists', () => {
|
|
126
|
+
let accessSpy;
|
|
127
|
+
let logInfoSpy;
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
accessSpy = vi.spyOn(fsPromises, 'access');
|
|
130
|
+
logInfoSpy = vi.spyOn(log, 'info').mockImplementation(() => { });
|
|
131
|
+
});
|
|
132
|
+
afterEach(() => {
|
|
133
|
+
vi.clearAllMocks();
|
|
134
|
+
accessSpy.mockRestore();
|
|
135
|
+
logInfoSpy.mockRestore();
|
|
136
|
+
});
|
|
137
|
+
it('should do nothing when baseline exists and no flags are set', async () => {
|
|
138
|
+
accessSpy.mockResolvedValue(undefined);
|
|
139
|
+
await checkBaselineImageExists({
|
|
140
|
+
actualFilePath: '/path/to/actual.png',
|
|
141
|
+
baselineFilePath: '/path/to/baseline.png'
|
|
142
|
+
});
|
|
143
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/baseline.png', 4);
|
|
144
|
+
expect(logInfoSpy).not.toHaveBeenCalled();
|
|
145
|
+
});
|
|
146
|
+
it('should auto-save baseline when file does not exist and autoSaveBaseline is true', async () => {
|
|
147
|
+
accessSpy.mockRejectedValue(new Error('File not found'));
|
|
148
|
+
vi.mocked(readFileSync).mockReturnValue(Buffer.from('image data'));
|
|
149
|
+
vi.mocked(writeFileSync).mockImplementation(() => { });
|
|
150
|
+
await checkBaselineImageExists({
|
|
151
|
+
actualFilePath: '/path/to/actual.png',
|
|
152
|
+
baselineFilePath: '/path/to/baseline.png',
|
|
153
|
+
autoSaveBaseline: true
|
|
154
|
+
});
|
|
155
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/baseline.png', 4);
|
|
156
|
+
expect(vi.mocked(readFileSync)).toHaveBeenCalledWith('/path/to/actual.png');
|
|
157
|
+
expect(vi.mocked(writeFileSync)).toHaveBeenCalledWith('/path/to/baseline.png', Buffer.from('image data'));
|
|
158
|
+
expect(logInfoSpy.mock.calls).toMatchSnapshot();
|
|
159
|
+
});
|
|
160
|
+
it('should update baseline when updateBaseline is true', async () => {
|
|
161
|
+
vi.mocked(readFileSync).mockReturnValue(Buffer.from('image data'));
|
|
162
|
+
vi.mocked(writeFileSync).mockImplementation(() => { });
|
|
163
|
+
await checkBaselineImageExists({
|
|
164
|
+
actualFilePath: '/path/to/actual.png',
|
|
165
|
+
baselineFilePath: '/path/to/baseline.png',
|
|
166
|
+
updateBaseline: true
|
|
167
|
+
});
|
|
168
|
+
expect(vi.mocked(readFileSync)).toHaveBeenCalledWith('/path/to/actual.png');
|
|
169
|
+
expect(vi.mocked(writeFileSync)).toHaveBeenCalledWith('/path/to/baseline.png', Buffer.from('image data'));
|
|
170
|
+
expect(logInfoSpy.mock.calls).toMatchSnapshot();
|
|
171
|
+
});
|
|
172
|
+
it('should throw error when file does not exist and autoSaveBaseline is false', async () => {
|
|
173
|
+
accessSpy.mockRejectedValue(new Error('File not found'));
|
|
174
|
+
await expect(checkBaselineImageExists({
|
|
175
|
+
actualFilePath: '/path/to/actual.png',
|
|
176
|
+
baselineFilePath: '/path/to/baseline.png',
|
|
177
|
+
autoSaveBaseline: false
|
|
178
|
+
})).rejects.toThrow();
|
|
179
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/baseline.png', 4);
|
|
180
|
+
expect(vi.mocked(readFileSync)).not.toHaveBeenCalled();
|
|
181
|
+
expect(vi.mocked(writeFileSync)).not.toHaveBeenCalled();
|
|
182
|
+
expect(logInfoSpy).not.toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
it('should throw error when copying fails', async () => {
|
|
185
|
+
accessSpy.mockRejectedValue(new Error('File not found'));
|
|
186
|
+
const copyError = new Error('Permission denied');
|
|
187
|
+
vi.mocked(readFileSync).mockImplementation(() => { throw copyError; });
|
|
188
|
+
await expect(checkBaselineImageExists({
|
|
189
|
+
actualFilePath: '/path/to/actual.png',
|
|
190
|
+
baselineFilePath: '/path/to/baseline.png',
|
|
191
|
+
autoSaveBaseline: true
|
|
192
|
+
})).rejects.toThrow();
|
|
193
|
+
expect(accessSpy).toHaveBeenCalledWith('/path/to/baseline.png', 4);
|
|
194
|
+
expect(vi.mocked(readFileSync)).toHaveBeenCalledWith('/path/to/actual.png');
|
|
195
|
+
expect(vi.mocked(writeFileSync)).not.toHaveBeenCalled();
|
|
196
|
+
expect(logInfoSpy).not.toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
describe('rotateBase64Image', () => {
|
|
200
|
+
let jimpReadMock;
|
|
201
|
+
beforeEach(async () => {
|
|
202
|
+
const jimp = await import('jimp');
|
|
203
|
+
jimpReadMock = vi.mocked(jimp.Jimp.read);
|
|
204
|
+
});
|
|
205
|
+
afterEach(() => {
|
|
206
|
+
vi.clearAllMocks();
|
|
207
|
+
});
|
|
208
|
+
it('should rotate image by specified degrees', async () => {
|
|
209
|
+
const mockImage = {
|
|
210
|
+
rotate: vi.fn().mockReturnThis(),
|
|
211
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,rotatedImageData')
|
|
212
|
+
};
|
|
213
|
+
jimpReadMock.mockResolvedValue(mockImage);
|
|
214
|
+
const result = await rotateBase64Image({
|
|
215
|
+
base64Image: 'originalImageData',
|
|
216
|
+
degrees: 90
|
|
217
|
+
});
|
|
218
|
+
expect(result).toMatchSnapshot();
|
|
219
|
+
expect(jimpReadMock.mock.calls).toMatchSnapshot();
|
|
220
|
+
expect(mockImage.rotate.mock.calls).toMatchSnapshot();
|
|
221
|
+
expect(mockImage.getBase64.mock.calls).toMatchSnapshot();
|
|
222
|
+
});
|
|
223
|
+
it('should rotate image by 180 degrees', async () => {
|
|
224
|
+
const mockImage = {
|
|
225
|
+
rotate: vi.fn().mockReturnThis(),
|
|
226
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,rotatedImageData')
|
|
227
|
+
};
|
|
228
|
+
jimpReadMock.mockResolvedValue(mockImage);
|
|
229
|
+
const result = await rotateBase64Image({
|
|
230
|
+
base64Image: 'originalImageData',
|
|
231
|
+
degrees: 180
|
|
232
|
+
});
|
|
233
|
+
expect(result).toMatchSnapshot();
|
|
234
|
+
expect(mockImage.rotate.mock.calls).toMatchSnapshot();
|
|
235
|
+
});
|
|
236
|
+
it('should handle different base64 input', async () => {
|
|
237
|
+
const mockImage = {
|
|
238
|
+
rotate: vi.fn().mockReturnThis(),
|
|
239
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,differentRotatedData')
|
|
240
|
+
};
|
|
241
|
+
jimpReadMock.mockResolvedValue(mockImage);
|
|
242
|
+
const result = await rotateBase64Image({
|
|
243
|
+
base64Image: 'differentImageData',
|
|
244
|
+
degrees: 270
|
|
245
|
+
});
|
|
246
|
+
expect(result).toMatchSnapshot();
|
|
247
|
+
expect(jimpReadMock.mock.calls).toMatchSnapshot();
|
|
248
|
+
expect(mockImage.rotate.mock.calls).toMatchSnapshot();
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
describe('getRotatedImageIfNeeded', () => {
|
|
252
|
+
let getBase64ScreenshotSizeMock;
|
|
253
|
+
beforeEach(async () => {
|
|
254
|
+
const utils = await import('../helpers/utils.js');
|
|
255
|
+
getBase64ScreenshotSizeMock = vi.mocked(utils.getBase64ScreenshotSize);
|
|
256
|
+
vi.clearAllMocks();
|
|
257
|
+
});
|
|
258
|
+
afterEach(() => {
|
|
259
|
+
vi.clearAllMocks();
|
|
260
|
+
});
|
|
261
|
+
it('should return original image when no rotation is needed', async () => {
|
|
262
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1920, height: 1080 });
|
|
263
|
+
const result = await getRotatedImageIfNeeded({
|
|
264
|
+
isWebDriverElementScreenshot: false,
|
|
265
|
+
isLandscape: false,
|
|
266
|
+
base64Image: 'originalImageData'
|
|
267
|
+
});
|
|
268
|
+
expect(result).toMatchSnapshot();
|
|
269
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
270
|
+
});
|
|
271
|
+
it('should call rotateBase64Image when landscape and height > width', async () => {
|
|
272
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1080, height: 1920 });
|
|
273
|
+
// We'll test that the function calls rotateBase64Image by checking the result
|
|
274
|
+
// Since we can't easily mock the internal function, we'll verify the logic works
|
|
275
|
+
const result = await getRotatedImageIfNeeded({
|
|
276
|
+
isWebDriverElementScreenshot: false,
|
|
277
|
+
isLandscape: true,
|
|
278
|
+
base64Image: 'originalImageData'
|
|
279
|
+
});
|
|
280
|
+
expect(result).toMatchSnapshot();
|
|
281
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
282
|
+
});
|
|
283
|
+
it('should not rotate when isWebDriverElementScreenshot is true', async () => {
|
|
284
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1080, height: 1920 });
|
|
285
|
+
const result = await getRotatedImageIfNeeded({
|
|
286
|
+
isWebDriverElementScreenshot: true,
|
|
287
|
+
isLandscape: true,
|
|
288
|
+
base64Image: 'originalImageData'
|
|
289
|
+
});
|
|
290
|
+
expect(result).toMatchSnapshot();
|
|
291
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
292
|
+
});
|
|
293
|
+
it('should not rotate when width >= height', async () => {
|
|
294
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1920, height: 1080 });
|
|
295
|
+
const result = await getRotatedImageIfNeeded({
|
|
296
|
+
isWebDriverElementScreenshot: false,
|
|
297
|
+
isLandscape: true,
|
|
298
|
+
base64Image: 'originalImageData'
|
|
299
|
+
});
|
|
300
|
+
expect(result).toMatchSnapshot();
|
|
301
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
302
|
+
});
|
|
303
|
+
it('should not rotate when not landscape', async () => {
|
|
304
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1080, height: 1920 });
|
|
305
|
+
const result = await getRotatedImageIfNeeded({
|
|
306
|
+
isWebDriverElementScreenshot: false,
|
|
307
|
+
isLandscape: false,
|
|
308
|
+
base64Image: 'originalImageData'
|
|
309
|
+
});
|
|
310
|
+
expect(result).toMatchSnapshot();
|
|
311
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
describe('logDimensionWarning', () => {
|
|
315
|
+
let logWarnSpy;
|
|
316
|
+
beforeEach(() => {
|
|
317
|
+
logWarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => { });
|
|
318
|
+
});
|
|
319
|
+
afterEach(() => {
|
|
320
|
+
vi.clearAllMocks();
|
|
321
|
+
logWarnSpy.mockRestore();
|
|
322
|
+
});
|
|
323
|
+
it('should log warning for LEFT type', () => {
|
|
324
|
+
logDimensionWarning({
|
|
325
|
+
dimension: 60,
|
|
326
|
+
maxDimension: 1000,
|
|
327
|
+
position: -10,
|
|
328
|
+
type: 'LEFT'
|
|
329
|
+
});
|
|
330
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
331
|
+
});
|
|
332
|
+
it('should log warning for RIGHT type', () => {
|
|
333
|
+
logDimensionWarning({
|
|
334
|
+
dimension: 50,
|
|
335
|
+
maxDimension: 1000,
|
|
336
|
+
position: 1100,
|
|
337
|
+
type: 'RIGHT'
|
|
338
|
+
});
|
|
339
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
340
|
+
});
|
|
341
|
+
it('should log warning for TOP type', () => {
|
|
342
|
+
logDimensionWarning({
|
|
343
|
+
dimension: 30,
|
|
344
|
+
maxDimension: 800,
|
|
345
|
+
position: -5,
|
|
346
|
+
type: 'TOP'
|
|
347
|
+
});
|
|
348
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
349
|
+
});
|
|
350
|
+
it('should log warning for BOTTOM type', () => {
|
|
351
|
+
logDimensionWarning({
|
|
352
|
+
dimension: 40,
|
|
353
|
+
maxDimension: 800,
|
|
354
|
+
position: 850,
|
|
355
|
+
type: 'BOTTOM'
|
|
356
|
+
});
|
|
357
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
describe('getAdjustedAxis', () => {
|
|
361
|
+
it('should return adjusted coordinates within bounds', () => {
|
|
362
|
+
const result = getAdjustedAxis({
|
|
363
|
+
length: 100,
|
|
364
|
+
maxDimension: 1000,
|
|
365
|
+
paddingEnd: 10,
|
|
366
|
+
paddingStart: 5,
|
|
367
|
+
start: 50,
|
|
368
|
+
warningType: 'WIDTH'
|
|
369
|
+
});
|
|
370
|
+
expect(result).toMatchSnapshot();
|
|
371
|
+
});
|
|
372
|
+
it('should handle zero padding', () => {
|
|
373
|
+
const result = getAdjustedAxis({
|
|
374
|
+
length: 100,
|
|
375
|
+
maxDimension: 1000,
|
|
376
|
+
paddingEnd: 0,
|
|
377
|
+
paddingStart: 0,
|
|
378
|
+
start: 50,
|
|
379
|
+
warningType: 'WIDTH'
|
|
380
|
+
});
|
|
381
|
+
expect(result).toMatchSnapshot();
|
|
382
|
+
});
|
|
383
|
+
it('should clamp start position to 0 when it goes below 0', () => {
|
|
384
|
+
const result = getAdjustedAxis({
|
|
385
|
+
length: 100,
|
|
386
|
+
maxDimension: 1000,
|
|
387
|
+
paddingEnd: 10,
|
|
388
|
+
paddingStart: 60, // This will make adjustedStart = 50 - 60 = -10
|
|
389
|
+
start: 50,
|
|
390
|
+
warningType: 'WIDTH'
|
|
391
|
+
});
|
|
392
|
+
expect(result).toMatchSnapshot();
|
|
393
|
+
});
|
|
394
|
+
it('should clamp end position to maxDimension when it exceeds maxDimension', () => {
|
|
395
|
+
const result = getAdjustedAxis({
|
|
396
|
+
length: 100,
|
|
397
|
+
maxDimension: 1000,
|
|
398
|
+
paddingEnd: 50, // This will make adjustedEnd = 950 + 100 + 50 = 1100
|
|
399
|
+
paddingStart: 10,
|
|
400
|
+
start: 950,
|
|
401
|
+
warningType: 'WIDTH'
|
|
402
|
+
});
|
|
403
|
+
expect(result).toMatchSnapshot();
|
|
404
|
+
});
|
|
405
|
+
it('should handle both start and end clamping', () => {
|
|
406
|
+
const result = getAdjustedAxis({
|
|
407
|
+
length: 100,
|
|
408
|
+
maxDimension: 1000,
|
|
409
|
+
paddingEnd: 50,
|
|
410
|
+
paddingStart: 60,
|
|
411
|
+
start: 50,
|
|
412
|
+
warningType: 'WIDTH'
|
|
413
|
+
});
|
|
414
|
+
expect(result).toMatchSnapshot();
|
|
415
|
+
});
|
|
416
|
+
it('should handle HEIGHT warning type correctly', () => {
|
|
417
|
+
const result = getAdjustedAxis({
|
|
418
|
+
length: 100,
|
|
419
|
+
maxDimension: 800,
|
|
420
|
+
paddingEnd: 50,
|
|
421
|
+
paddingStart: 60,
|
|
422
|
+
start: 50,
|
|
423
|
+
warningType: 'HEIGHT'
|
|
424
|
+
});
|
|
425
|
+
expect(result).toMatchSnapshot();
|
|
426
|
+
});
|
|
427
|
+
it('should handle edge case where start is exactly at maxDimension', () => {
|
|
428
|
+
const result = getAdjustedAxis({
|
|
429
|
+
length: 100,
|
|
430
|
+
maxDimension: 1000,
|
|
431
|
+
paddingEnd: 10,
|
|
432
|
+
paddingStart: 0,
|
|
433
|
+
start: 1000,
|
|
434
|
+
warningType: 'WIDTH'
|
|
435
|
+
});
|
|
436
|
+
expect(result).toMatchSnapshot();
|
|
437
|
+
});
|
|
438
|
+
it('should handle edge case where start is 0', () => {
|
|
439
|
+
const result = getAdjustedAxis({
|
|
440
|
+
length: 100,
|
|
441
|
+
maxDimension: 1000,
|
|
442
|
+
paddingEnd: 10,
|
|
443
|
+
paddingStart: 0,
|
|
444
|
+
start: 0,
|
|
445
|
+
warningType: 'WIDTH'
|
|
446
|
+
});
|
|
447
|
+
expect(result).toMatchSnapshot();
|
|
448
|
+
});
|
|
449
|
+
it('should handle large padding values', () => {
|
|
450
|
+
const result = getAdjustedAxis({
|
|
451
|
+
length: 50,
|
|
452
|
+
maxDimension: 100,
|
|
453
|
+
paddingEnd: 100,
|
|
454
|
+
paddingStart: 100,
|
|
455
|
+
start: 50,
|
|
456
|
+
warningType: 'WIDTH'
|
|
457
|
+
});
|
|
458
|
+
expect(result).toMatchSnapshot();
|
|
459
|
+
});
|
|
460
|
+
it('should handle zero length', () => {
|
|
461
|
+
const result = getAdjustedAxis({
|
|
462
|
+
length: 0,
|
|
463
|
+
maxDimension: 1000,
|
|
464
|
+
paddingEnd: 10,
|
|
465
|
+
paddingStart: 5,
|
|
466
|
+
start: 50,
|
|
467
|
+
warningType: 'WIDTH'
|
|
468
|
+
});
|
|
469
|
+
expect(result).toMatchSnapshot();
|
|
470
|
+
});
|
|
471
|
+
it('should handle negative start position', () => {
|
|
472
|
+
const result = getAdjustedAxis({
|
|
473
|
+
length: 100,
|
|
474
|
+
maxDimension: 1000,
|
|
475
|
+
paddingEnd: 10,
|
|
476
|
+
paddingStart: 0,
|
|
477
|
+
start: -10,
|
|
478
|
+
warningType: 'WIDTH'
|
|
479
|
+
});
|
|
480
|
+
expect(result).toMatchSnapshot();
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
describe('handleIOSBezelCorners', () => {
|
|
484
|
+
let logWarnSpy;
|
|
485
|
+
let getIosBezelImageNamesMock;
|
|
486
|
+
let readFileSyncMock;
|
|
487
|
+
let getBase64ScreenshotSizeMock;
|
|
488
|
+
let mockImage;
|
|
489
|
+
beforeEach(async () => {
|
|
490
|
+
logWarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => { });
|
|
491
|
+
const utilsModule = vi.mocked(await import('../helpers/utils.js'));
|
|
492
|
+
getIosBezelImageNamesMock = vi.spyOn(utilsModule, 'getIosBezelImageNames');
|
|
493
|
+
getBase64ScreenshotSizeMock = vi.spyOn(utilsModule, 'getBase64ScreenshotSize');
|
|
494
|
+
const fsModule = vi.mocked(await import('node:fs'));
|
|
495
|
+
readFileSyncMock = vi.spyOn(fsModule, 'readFileSync');
|
|
496
|
+
mockImage = {
|
|
497
|
+
composite: vi.fn().mockReturnThis(),
|
|
498
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mockImageData'),
|
|
499
|
+
opacity: vi.fn().mockReturnThis(),
|
|
500
|
+
rotate: vi.fn().mockReturnThis(),
|
|
501
|
+
};
|
|
502
|
+
});
|
|
503
|
+
afterEach(() => {
|
|
504
|
+
vi.clearAllMocks();
|
|
505
|
+
logWarnSpy.mockRestore();
|
|
506
|
+
});
|
|
507
|
+
it('should do nothing when addIOSBezelCorners is false', async () => {
|
|
508
|
+
await handleIOSBezelCorners({
|
|
509
|
+
addIOSBezelCorners: false,
|
|
510
|
+
image: mockImage,
|
|
511
|
+
deviceName: 'iPhone 14 Pro',
|
|
512
|
+
devicePixelRatio: 3,
|
|
513
|
+
height: 844,
|
|
514
|
+
isLandscape: false,
|
|
515
|
+
width: 390,
|
|
516
|
+
});
|
|
517
|
+
expect(getIosBezelImageNamesMock).not.toHaveBeenCalled();
|
|
518
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
519
|
+
expect(logWarnSpy).not.toHaveBeenCalled();
|
|
520
|
+
});
|
|
521
|
+
it('should handle supported iPhone device', async () => {
|
|
522
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
523
|
+
topImageName: 'iphone14pro-top',
|
|
524
|
+
bottomImageName: 'iphone14pro-bottom'
|
|
525
|
+
});
|
|
526
|
+
readFileSyncMock.mockReturnValue('mockImageData');
|
|
527
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 100, height: 50 });
|
|
528
|
+
await handleIOSBezelCorners({
|
|
529
|
+
addIOSBezelCorners: true,
|
|
530
|
+
image: mockImage,
|
|
531
|
+
deviceName: 'iPhone 14 Pro',
|
|
532
|
+
devicePixelRatio: 3,
|
|
533
|
+
height: 844,
|
|
534
|
+
isLandscape: false,
|
|
535
|
+
width: 390,
|
|
536
|
+
});
|
|
537
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
538
|
+
expect(readFileSyncMock).toHaveBeenCalledTimes(2);
|
|
539
|
+
expect(mockImage.composite).toHaveBeenCalledTimes(2);
|
|
540
|
+
expect(logWarnSpy).not.toHaveBeenCalled();
|
|
541
|
+
});
|
|
542
|
+
it('should handle supported iPhone device in landscape mode', async () => {
|
|
543
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
544
|
+
topImageName: 'iphone14pro-top',
|
|
545
|
+
bottomImageName: 'iphone14pro-bottom'
|
|
546
|
+
});
|
|
547
|
+
readFileSyncMock.mockReturnValue('mockImageData');
|
|
548
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 100, height: 50 });
|
|
549
|
+
await handleIOSBezelCorners({
|
|
550
|
+
addIOSBezelCorners: true,
|
|
551
|
+
image: mockImage,
|
|
552
|
+
deviceName: 'iPhone 14 Pro',
|
|
553
|
+
devicePixelRatio: 3,
|
|
554
|
+
height: 390,
|
|
555
|
+
isLandscape: true,
|
|
556
|
+
width: 844,
|
|
557
|
+
});
|
|
558
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
559
|
+
expect(readFileSyncMock).toHaveBeenCalledTimes(2);
|
|
560
|
+
expect(mockImage.composite).toHaveBeenCalledTimes(2);
|
|
561
|
+
expect(logWarnSpy).not.toHaveBeenCalled();
|
|
562
|
+
});
|
|
563
|
+
it('should handle supported iPad device with sufficient dimensions', async () => {
|
|
564
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
565
|
+
topImageName: 'ipadair-top',
|
|
566
|
+
bottomImageName: 'ipadair-bottom'
|
|
567
|
+
});
|
|
568
|
+
readFileSyncMock.mockReturnValue('mockImageData');
|
|
569
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 100, height: 50 });
|
|
570
|
+
await handleIOSBezelCorners({
|
|
571
|
+
addIOSBezelCorners: true,
|
|
572
|
+
image: mockImage,
|
|
573
|
+
deviceName: 'iPad Air',
|
|
574
|
+
devicePixelRatio: 2,
|
|
575
|
+
height: 2400, // 2400 / 2 = 1200 >= 1133
|
|
576
|
+
isLandscape: false,
|
|
577
|
+
width: 1600, // 1600 / 2 = 800 < 1133, but height meets requirement
|
|
578
|
+
});
|
|
579
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
580
|
+
expect(readFileSyncMock).toHaveBeenCalledTimes(2);
|
|
581
|
+
expect(mockImage.composite).toHaveBeenCalledTimes(2);
|
|
582
|
+
expect(logWarnSpy).not.toHaveBeenCalled();
|
|
583
|
+
});
|
|
584
|
+
it('should not handle iPad device with insufficient dimensions', async () => {
|
|
585
|
+
await handleIOSBezelCorners({
|
|
586
|
+
addIOSBezelCorners: true,
|
|
587
|
+
image: mockImage,
|
|
588
|
+
deviceName: 'iPad Air',
|
|
589
|
+
devicePixelRatio: 2,
|
|
590
|
+
height: 800, // Below 1133 threshold
|
|
591
|
+
isLandscape: false,
|
|
592
|
+
width: 600,
|
|
593
|
+
});
|
|
594
|
+
expect(getIosBezelImageNamesMock).not.toHaveBeenCalled();
|
|
595
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
596
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
597
|
+
});
|
|
598
|
+
it('should handle device name normalization', async () => {
|
|
599
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
600
|
+
topImageName: 'iphone14pro-top',
|
|
601
|
+
bottomImageName: 'iphone14pro-bottom'
|
|
602
|
+
});
|
|
603
|
+
readFileSyncMock.mockReturnValue('mockImageData');
|
|
604
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 100, height: 50 });
|
|
605
|
+
await handleIOSBezelCorners({
|
|
606
|
+
addIOSBezelCorners: true,
|
|
607
|
+
image: mockImage,
|
|
608
|
+
deviceName: 'iPhone 14 Pro Simulator (5th generation)',
|
|
609
|
+
devicePixelRatio: 3,
|
|
610
|
+
height: 844,
|
|
611
|
+
isLandscape: false,
|
|
612
|
+
width: 390,
|
|
613
|
+
});
|
|
614
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
615
|
+
expect(readFileSyncMock).toHaveBeenCalledTimes(2);
|
|
616
|
+
expect(mockImage.composite).toHaveBeenCalledTimes(2);
|
|
617
|
+
expect(logWarnSpy).not.toHaveBeenCalled();
|
|
618
|
+
});
|
|
619
|
+
it('should handle unsupported device', async () => {
|
|
620
|
+
await handleIOSBezelCorners({
|
|
621
|
+
addIOSBezelCorners: true,
|
|
622
|
+
image: mockImage,
|
|
623
|
+
deviceName: 'iPhone 6',
|
|
624
|
+
devicePixelRatio: 2,
|
|
625
|
+
height: 667,
|
|
626
|
+
isLandscape: false,
|
|
627
|
+
width: 375,
|
|
628
|
+
});
|
|
629
|
+
expect(getIosBezelImageNamesMock).not.toHaveBeenCalled();
|
|
630
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
631
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
632
|
+
});
|
|
633
|
+
it('should handle missing bezel image names', async () => {
|
|
634
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
635
|
+
topImageName: null,
|
|
636
|
+
bottomImageName: null
|
|
637
|
+
});
|
|
638
|
+
await handleIOSBezelCorners({
|
|
639
|
+
addIOSBezelCorners: true,
|
|
640
|
+
image: mockImage,
|
|
641
|
+
deviceName: 'iPhone 14 Pro',
|
|
642
|
+
devicePixelRatio: 3,
|
|
643
|
+
height: 844,
|
|
644
|
+
isLandscape: false,
|
|
645
|
+
width: 390,
|
|
646
|
+
});
|
|
647
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
648
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
649
|
+
expect(mockImage.composite).not.toHaveBeenCalled();
|
|
650
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
651
|
+
});
|
|
652
|
+
it('should handle partial bezel image names', async () => {
|
|
653
|
+
getIosBezelImageNamesMock.mockReturnValue({
|
|
654
|
+
topImageName: 'iphone14pro-top',
|
|
655
|
+
bottomImageName: null
|
|
656
|
+
});
|
|
657
|
+
await handleIOSBezelCorners({
|
|
658
|
+
addIOSBezelCorners: true,
|
|
659
|
+
image: mockImage,
|
|
660
|
+
deviceName: 'iPhone 14 Pro',
|
|
661
|
+
devicePixelRatio: 3,
|
|
662
|
+
height: 844,
|
|
663
|
+
isLandscape: false,
|
|
664
|
+
width: 390,
|
|
665
|
+
});
|
|
666
|
+
expect(getIosBezelImageNamesMock.mock.calls).toMatchSnapshot();
|
|
667
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
668
|
+
expect(mockImage.composite).not.toHaveBeenCalled();
|
|
669
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
670
|
+
});
|
|
671
|
+
it('should handle Android device (not iOS)', async () => {
|
|
672
|
+
await handleIOSBezelCorners({
|
|
673
|
+
addIOSBezelCorners: true,
|
|
674
|
+
image: mockImage,
|
|
675
|
+
deviceName: 'Samsung Galaxy S21',
|
|
676
|
+
devicePixelRatio: 3,
|
|
677
|
+
height: 2400,
|
|
678
|
+
isLandscape: false,
|
|
679
|
+
width: 1080,
|
|
680
|
+
});
|
|
681
|
+
expect(getIosBezelImageNamesMock).not.toHaveBeenCalled();
|
|
682
|
+
expect(readFileSyncMock).not.toHaveBeenCalled();
|
|
683
|
+
expect(logWarnSpy.mock.calls).toMatchSnapshot();
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
describe('cropAndConvertToDataURL', () => {
|
|
687
|
+
let mockImage;
|
|
688
|
+
let mockCroppedImage;
|
|
689
|
+
const defaultCropOptions = {
|
|
690
|
+
addIOSBezelCorners: false,
|
|
691
|
+
base64Image: 'originalImageData',
|
|
692
|
+
deviceName: 'iPhone 14 Pro',
|
|
693
|
+
devicePixelRatio: 3,
|
|
694
|
+
height: 100,
|
|
695
|
+
isIOS: false,
|
|
696
|
+
isLandscape: false,
|
|
697
|
+
sourceX: 50,
|
|
698
|
+
sourceY: 25,
|
|
699
|
+
width: 200,
|
|
700
|
+
};
|
|
701
|
+
beforeEach(async () => {
|
|
702
|
+
mockCroppedImage = {
|
|
703
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,croppedImageData'),
|
|
704
|
+
};
|
|
705
|
+
mockImage = {
|
|
706
|
+
crop: vi.fn().mockReturnValue(mockCroppedImage),
|
|
707
|
+
};
|
|
708
|
+
const jimpModule = vi.mocked(await import('jimp'));
|
|
709
|
+
vi.spyOn(jimpModule.Jimp, 'read').mockResolvedValue(mockImage);
|
|
710
|
+
});
|
|
711
|
+
afterEach(() => {
|
|
712
|
+
vi.clearAllMocks();
|
|
713
|
+
});
|
|
714
|
+
it('should crop image and return base64 data without iOS bezel corners', async () => {
|
|
715
|
+
const result = await cropAndConvertToDataURL(defaultCropOptions);
|
|
716
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
717
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
718
|
+
expect(result).toMatchSnapshot();
|
|
719
|
+
});
|
|
720
|
+
it('should crop image and add iOS bezel corners when isIOS is true', async () => {
|
|
721
|
+
const result = await cropAndConvertToDataURL({
|
|
722
|
+
...defaultCropOptions,
|
|
723
|
+
addIOSBezelCorners: true,
|
|
724
|
+
isIOS: true,
|
|
725
|
+
});
|
|
726
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
727
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
728
|
+
expect(result).toMatchSnapshot();
|
|
729
|
+
});
|
|
730
|
+
it('should handle landscape orientation with iOS bezel corners', async () => {
|
|
731
|
+
const result = await cropAndConvertToDataURL({
|
|
732
|
+
...defaultCropOptions,
|
|
733
|
+
addIOSBezelCorners: true,
|
|
734
|
+
isIOS: true,
|
|
735
|
+
isLandscape: true,
|
|
736
|
+
});
|
|
737
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
738
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
739
|
+
expect(result).toMatchSnapshot();
|
|
740
|
+
});
|
|
741
|
+
it('should handle Android device (isIOS false) without bezel corners', async () => {
|
|
742
|
+
const result = await cropAndConvertToDataURL({
|
|
743
|
+
...defaultCropOptions,
|
|
744
|
+
addIOSBezelCorners: true,
|
|
745
|
+
deviceName: 'Samsung Galaxy S21',
|
|
746
|
+
isIOS: false,
|
|
747
|
+
});
|
|
748
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
749
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
750
|
+
expect(result).toMatchSnapshot();
|
|
751
|
+
});
|
|
752
|
+
it('should handle zero dimensions', async () => {
|
|
753
|
+
const result = await cropAndConvertToDataURL({
|
|
754
|
+
...defaultCropOptions,
|
|
755
|
+
height: 0,
|
|
756
|
+
sourceX: 0,
|
|
757
|
+
sourceY: 0,
|
|
758
|
+
width: 0,
|
|
759
|
+
});
|
|
760
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
761
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
762
|
+
expect(result).toMatchSnapshot();
|
|
763
|
+
});
|
|
764
|
+
it('should handle large crop dimensions', async () => {
|
|
765
|
+
const result = await cropAndConvertToDataURL({
|
|
766
|
+
...defaultCropOptions,
|
|
767
|
+
height: 2000,
|
|
768
|
+
sourceX: 1000,
|
|
769
|
+
sourceY: 500,
|
|
770
|
+
width: 3000,
|
|
771
|
+
});
|
|
772
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
773
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
774
|
+
expect(result).toMatchSnapshot();
|
|
775
|
+
});
|
|
776
|
+
it('should handle different base64 input data', async () => {
|
|
777
|
+
const result = await cropAndConvertToDataURL({
|
|
778
|
+
...defaultCropOptions,
|
|
779
|
+
base64Image: 'differentImageData123',
|
|
780
|
+
});
|
|
781
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
782
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
783
|
+
expect(result).toMatchSnapshot();
|
|
784
|
+
});
|
|
785
|
+
it('should handle different device pixel ratios', async () => {
|
|
786
|
+
const result = await cropAndConvertToDataURL({
|
|
787
|
+
...defaultCropOptions,
|
|
788
|
+
addIOSBezelCorners: true,
|
|
789
|
+
devicePixelRatio: 2,
|
|
790
|
+
isIOS: true,
|
|
791
|
+
});
|
|
792
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
793
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
794
|
+
expect(result).toMatchSnapshot();
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
describe('makeCroppedBase64Image', () => {
|
|
798
|
+
let getBase64ScreenshotSizeMock;
|
|
799
|
+
let mockImage;
|
|
800
|
+
let mockCroppedImage;
|
|
801
|
+
const defaultCropOptions = {
|
|
802
|
+
addIOSBezelCorners: false,
|
|
803
|
+
base64Image: 'originalImageData',
|
|
804
|
+
deviceName: 'iPhone 14 Pro',
|
|
805
|
+
devicePixelRatio: 3,
|
|
806
|
+
isWebDriverElementScreenshot: false,
|
|
807
|
+
isIOS: false,
|
|
808
|
+
isLandscape: false,
|
|
809
|
+
rectangles: {
|
|
810
|
+
height: 100,
|
|
811
|
+
width: 200,
|
|
812
|
+
x: 50,
|
|
813
|
+
y: 25,
|
|
814
|
+
},
|
|
815
|
+
resizeDimensions: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
816
|
+
};
|
|
817
|
+
beforeEach(async () => {
|
|
818
|
+
const utilsModule = vi.mocked(await import('../helpers/utils.js'));
|
|
819
|
+
getBase64ScreenshotSizeMock = vi.spyOn(utilsModule, 'getBase64ScreenshotSize');
|
|
820
|
+
mockCroppedImage = {
|
|
821
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,finalCroppedImageData'),
|
|
822
|
+
composite: vi.fn().mockReturnThis(),
|
|
823
|
+
opacity: vi.fn().mockReturnThis(),
|
|
824
|
+
};
|
|
825
|
+
mockImage = {
|
|
826
|
+
crop: vi.fn().mockReturnValue(mockCroppedImage),
|
|
827
|
+
composite: vi.fn().mockReturnThis(),
|
|
828
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mockImageData'),
|
|
829
|
+
opacity: vi.fn().mockReturnThis(),
|
|
830
|
+
rotate: vi.fn().mockReturnThis(),
|
|
831
|
+
};
|
|
832
|
+
const jimpModule = vi.mocked(await import('jimp'));
|
|
833
|
+
vi.spyOn(jimpModule.Jimp, 'read').mockResolvedValue(mockImage);
|
|
834
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1000, height: 2000 });
|
|
835
|
+
});
|
|
836
|
+
afterEach(() => {
|
|
837
|
+
vi.clearAllMocks();
|
|
838
|
+
});
|
|
839
|
+
it('should create cropped base64 image with default settings', async () => {
|
|
840
|
+
const result = await makeCroppedBase64Image(defaultCropOptions);
|
|
841
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
842
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
843
|
+
expect(mockCroppedImage.getBase64.mock.calls).toMatchSnapshot();
|
|
844
|
+
expect(result).toMatchSnapshot();
|
|
845
|
+
});
|
|
846
|
+
it('should handle landscape orientation with rotation', async () => {
|
|
847
|
+
const result = await makeCroppedBase64Image({
|
|
848
|
+
...defaultCropOptions,
|
|
849
|
+
isLandscape: true,
|
|
850
|
+
});
|
|
851
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
852
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
853
|
+
expect(result).toMatchSnapshot();
|
|
854
|
+
});
|
|
855
|
+
it('should handle web driver element screenshots', async () => {
|
|
856
|
+
const result = await makeCroppedBase64Image({
|
|
857
|
+
...defaultCropOptions,
|
|
858
|
+
isWebDriverElementScreenshot: true,
|
|
859
|
+
});
|
|
860
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
861
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
862
|
+
expect(result).toMatchSnapshot();
|
|
863
|
+
});
|
|
864
|
+
it('should handle iOS devices with bezel corners', async () => {
|
|
865
|
+
const result = await makeCroppedBase64Image({
|
|
866
|
+
...defaultCropOptions,
|
|
867
|
+
addIOSBezelCorners: true,
|
|
868
|
+
isIOS: true,
|
|
869
|
+
});
|
|
870
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
871
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
872
|
+
expect(result).toMatchSnapshot();
|
|
873
|
+
});
|
|
874
|
+
it('should handle custom resize dimensions', async () => {
|
|
875
|
+
const result = await makeCroppedBase64Image({
|
|
876
|
+
...defaultCropOptions,
|
|
877
|
+
resizeDimensions: { top: 10, right: 20, bottom: 15, left: 5 },
|
|
878
|
+
});
|
|
879
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
880
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
881
|
+
expect(result).toMatchSnapshot();
|
|
882
|
+
});
|
|
883
|
+
it('should handle different rectangle dimensions', async () => {
|
|
884
|
+
const result = await makeCroppedBase64Image({
|
|
885
|
+
...defaultCropOptions,
|
|
886
|
+
rectangles: {
|
|
887
|
+
height: 300,
|
|
888
|
+
width: 400,
|
|
889
|
+
x: 100,
|
|
890
|
+
y: 75,
|
|
891
|
+
},
|
|
892
|
+
});
|
|
893
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
894
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
895
|
+
expect(result).toMatchSnapshot();
|
|
896
|
+
});
|
|
897
|
+
it('should handle different screenshot sizes', async () => {
|
|
898
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 800, height: 600 });
|
|
899
|
+
const result = await makeCroppedBase64Image(defaultCropOptions);
|
|
900
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
901
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
902
|
+
expect(result).toMatchSnapshot();
|
|
903
|
+
});
|
|
904
|
+
it('should handle different device pixel ratios', async () => {
|
|
905
|
+
const result = await makeCroppedBase64Image({
|
|
906
|
+
...defaultCropOptions,
|
|
907
|
+
devicePixelRatio: 2,
|
|
908
|
+
});
|
|
909
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
910
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
911
|
+
expect(result).toMatchSnapshot();
|
|
912
|
+
});
|
|
913
|
+
it('should handle zero rectangle dimensions', async () => {
|
|
914
|
+
const result = await makeCroppedBase64Image({
|
|
915
|
+
...defaultCropOptions,
|
|
916
|
+
rectangles: {
|
|
917
|
+
height: 0,
|
|
918
|
+
width: 0,
|
|
919
|
+
x: 0,
|
|
920
|
+
y: 0,
|
|
921
|
+
},
|
|
922
|
+
});
|
|
923
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
924
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
925
|
+
expect(result).toMatchSnapshot();
|
|
926
|
+
});
|
|
927
|
+
it('should handle edge case with padding that exceeds image bounds', async () => {
|
|
928
|
+
const result = await makeCroppedBase64Image({
|
|
929
|
+
...defaultCropOptions,
|
|
930
|
+
rectangles: {
|
|
931
|
+
height: 100,
|
|
932
|
+
width: 200,
|
|
933
|
+
x: 950, // Very close to image width (1000)
|
|
934
|
+
y: 1900, // Very close to image height (2000)
|
|
935
|
+
},
|
|
936
|
+
resizeDimensions: { top: 50, right: 100, bottom: 50, left: 50 },
|
|
937
|
+
});
|
|
938
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
939
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
940
|
+
expect(result).toMatchSnapshot();
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
describe('makeFullPageBase64Image', () => {
|
|
944
|
+
let getBase64ScreenshotSizeMock;
|
|
945
|
+
let mockCanvas;
|
|
946
|
+
let mockImage;
|
|
947
|
+
const defaultScreenshotsData = {
|
|
948
|
+
fullPageHeight: 2000,
|
|
949
|
+
fullPageWidth: 1000,
|
|
950
|
+
data: [
|
|
951
|
+
{
|
|
952
|
+
canvasWidth: 1000,
|
|
953
|
+
canvasYPosition: 0,
|
|
954
|
+
imageHeight: 800,
|
|
955
|
+
imageWidth: 1000,
|
|
956
|
+
imageXPosition: 0,
|
|
957
|
+
imageYPosition: 0,
|
|
958
|
+
screenshot: 'screenshot1-data'
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
canvasWidth: 1000,
|
|
962
|
+
canvasYPosition: 800,
|
|
963
|
+
imageHeight: 800,
|
|
964
|
+
imageWidth: 1000,
|
|
965
|
+
imageXPosition: 0,
|
|
966
|
+
imageYPosition: 0,
|
|
967
|
+
screenshot: 'screenshot2-data'
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
canvasWidth: 1000,
|
|
971
|
+
canvasYPosition: 1600,
|
|
972
|
+
imageHeight: 400,
|
|
973
|
+
imageWidth: 1000,
|
|
974
|
+
imageXPosition: 0,
|
|
975
|
+
imageYPosition: 0,
|
|
976
|
+
screenshot: 'screenshot3-data'
|
|
977
|
+
}
|
|
978
|
+
]
|
|
979
|
+
};
|
|
980
|
+
const defaultOptions = {
|
|
981
|
+
devicePixelRatio: 2,
|
|
982
|
+
isLandscape: false
|
|
983
|
+
};
|
|
984
|
+
beforeEach(async () => {
|
|
985
|
+
const utilsModule = vi.mocked(await import('../helpers/utils.js'));
|
|
986
|
+
getBase64ScreenshotSizeMock = vi.spyOn(utilsModule, 'getBase64ScreenshotSize');
|
|
987
|
+
mockCanvas = {
|
|
988
|
+
composite: vi.fn().mockReturnThis(),
|
|
989
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,fullPageImageData'),
|
|
990
|
+
};
|
|
991
|
+
mockImage = {
|
|
992
|
+
crop: vi.fn().mockReturnThis(),
|
|
993
|
+
composite: vi.fn().mockReturnThis(),
|
|
994
|
+
getBase64: vi.fn().mockResolvedValue('data:image/png;base64,mockImageData'),
|
|
995
|
+
opacity: vi.fn().mockReturnThis(),
|
|
996
|
+
rotate: vi.fn().mockReturnThis(),
|
|
997
|
+
};
|
|
998
|
+
const jimpModule = vi.mocked(await import('jimp'));
|
|
999
|
+
vi.spyOn(jimpModule.Jimp, 'read').mockResolvedValue(mockImage);
|
|
1000
|
+
vi.mocked(jimpModule.Jimp).mockImplementation((options) => {
|
|
1001
|
+
if (options && (options.width || options.height)) {
|
|
1002
|
+
return mockCanvas;
|
|
1003
|
+
}
|
|
1004
|
+
return mockImage;
|
|
1005
|
+
});
|
|
1006
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1000, height: 800 });
|
|
1007
|
+
});
|
|
1008
|
+
afterEach(() => {
|
|
1009
|
+
vi.clearAllMocks();
|
|
1010
|
+
});
|
|
1011
|
+
it('should create full page base64 image with multiple screenshots', async () => {
|
|
1012
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, defaultOptions);
|
|
1013
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1014
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1015
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1016
|
+
expect(mockCanvas.getBase64.mock.calls).toMatchSnapshot();
|
|
1017
|
+
expect(result).toMatchSnapshot();
|
|
1018
|
+
});
|
|
1019
|
+
it('should handle landscape mode with rotation', async () => {
|
|
1020
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 800, height: 1000 });
|
|
1021
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, {
|
|
1022
|
+
...defaultOptions,
|
|
1023
|
+
isLandscape: true
|
|
1024
|
+
});
|
|
1025
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1026
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1027
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1028
|
+
expect(result).toMatchSnapshot();
|
|
1029
|
+
});
|
|
1030
|
+
it('should handle single screenshot', async () => {
|
|
1031
|
+
const singleScreenshotData = {
|
|
1032
|
+
fullPageHeight: 800,
|
|
1033
|
+
fullPageWidth: 1000,
|
|
1034
|
+
data: [
|
|
1035
|
+
{
|
|
1036
|
+
canvasWidth: 1000,
|
|
1037
|
+
canvasYPosition: 0,
|
|
1038
|
+
imageHeight: 800,
|
|
1039
|
+
imageWidth: 1000,
|
|
1040
|
+
imageXPosition: 0,
|
|
1041
|
+
imageYPosition: 0,
|
|
1042
|
+
screenshot: 'single-screenshot-data'
|
|
1043
|
+
}
|
|
1044
|
+
]
|
|
1045
|
+
};
|
|
1046
|
+
const result = await makeFullPageBase64Image(singleScreenshotData, defaultOptions);
|
|
1047
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1048
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1049
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1050
|
+
expect(result).toMatchSnapshot();
|
|
1051
|
+
});
|
|
1052
|
+
it('should handle different device pixel ratios', async () => {
|
|
1053
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, {
|
|
1054
|
+
...defaultOptions,
|
|
1055
|
+
devicePixelRatio: 3
|
|
1056
|
+
});
|
|
1057
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1058
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1059
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1060
|
+
expect(result).toMatchSnapshot();
|
|
1061
|
+
});
|
|
1062
|
+
it('should handle screenshots with different dimensions', async () => {
|
|
1063
|
+
const mixedScreenshotsData = {
|
|
1064
|
+
fullPageHeight: 1500,
|
|
1065
|
+
fullPageWidth: 1200,
|
|
1066
|
+
data: [
|
|
1067
|
+
{
|
|
1068
|
+
canvasWidth: 1200,
|
|
1069
|
+
canvasYPosition: 0,
|
|
1070
|
+
imageHeight: 600,
|
|
1071
|
+
imageWidth: 1200,
|
|
1072
|
+
imageXPosition: 0,
|
|
1073
|
+
imageYPosition: 0,
|
|
1074
|
+
screenshot: 'wide-screenshot-data'
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
canvasWidth: 1200,
|
|
1078
|
+
canvasYPosition: 600,
|
|
1079
|
+
imageHeight: 900,
|
|
1080
|
+
imageWidth: 1200,
|
|
1081
|
+
imageXPosition: 0,
|
|
1082
|
+
imageYPosition: 0,
|
|
1083
|
+
screenshot: 'tall-screenshot-data'
|
|
1084
|
+
}
|
|
1085
|
+
]
|
|
1086
|
+
};
|
|
1087
|
+
getBase64ScreenshotSizeMock
|
|
1088
|
+
.mockReturnValueOnce({ width: 1200, height: 600 })
|
|
1089
|
+
.mockReturnValueOnce({ width: 1200, height: 900 });
|
|
1090
|
+
const result = await makeFullPageBase64Image(mixedScreenshotsData, defaultOptions);
|
|
1091
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1092
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1093
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1094
|
+
expect(result).toMatchSnapshot();
|
|
1095
|
+
});
|
|
1096
|
+
it('should handle screenshots with cropping positions', async () => {
|
|
1097
|
+
const croppedScreenshotsData = {
|
|
1098
|
+
fullPageHeight: 1000,
|
|
1099
|
+
fullPageWidth: 1000,
|
|
1100
|
+
data: [
|
|
1101
|
+
{
|
|
1102
|
+
canvasWidth: 1000,
|
|
1103
|
+
canvasYPosition: 0,
|
|
1104
|
+
imageHeight: 500,
|
|
1105
|
+
imageWidth: 500,
|
|
1106
|
+
imageXPosition: 100,
|
|
1107
|
+
imageYPosition: 50,
|
|
1108
|
+
screenshot: 'cropped-screenshot-data'
|
|
1109
|
+
}
|
|
1110
|
+
]
|
|
1111
|
+
};
|
|
1112
|
+
const result = await makeFullPageBase64Image(croppedScreenshotsData, defaultOptions);
|
|
1113
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1114
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1115
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1116
|
+
expect(result).toMatchSnapshot();
|
|
1117
|
+
});
|
|
1118
|
+
it('should handle landscape mode without rotation when width >= height', async () => {
|
|
1119
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 1000, height: 800 });
|
|
1120
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, {
|
|
1121
|
+
...defaultOptions,
|
|
1122
|
+
isLandscape: true
|
|
1123
|
+
});
|
|
1124
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1125
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1126
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1127
|
+
expect(result).toMatchSnapshot();
|
|
1128
|
+
});
|
|
1129
|
+
it('should handle empty screenshots array', async () => {
|
|
1130
|
+
const emptyScreenshotsData = {
|
|
1131
|
+
fullPageHeight: 0,
|
|
1132
|
+
fullPageWidth: 1000,
|
|
1133
|
+
data: []
|
|
1134
|
+
};
|
|
1135
|
+
const result = await makeFullPageBase64Image(emptyScreenshotsData, defaultOptions);
|
|
1136
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1137
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1138
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1139
|
+
expect(mockCanvas.getBase64.mock.calls).toMatchSnapshot();
|
|
1140
|
+
expect(result).toMatchSnapshot();
|
|
1141
|
+
});
|
|
1142
|
+
it('should handle large canvas dimensions', async () => {
|
|
1143
|
+
const largeScreenshotsData = {
|
|
1144
|
+
fullPageHeight: 5000,
|
|
1145
|
+
fullPageWidth: 3000,
|
|
1146
|
+
data: [
|
|
1147
|
+
{
|
|
1148
|
+
canvasWidth: 3000,
|
|
1149
|
+
canvasYPosition: 0,
|
|
1150
|
+
imageHeight: 2000,
|
|
1151
|
+
imageWidth: 3000,
|
|
1152
|
+
imageXPosition: 0,
|
|
1153
|
+
imageYPosition: 0,
|
|
1154
|
+
screenshot: 'large-screenshot-data'
|
|
1155
|
+
}
|
|
1156
|
+
]
|
|
1157
|
+
};
|
|
1158
|
+
getBase64ScreenshotSizeMock.mockReturnValue({ width: 3000, height: 2000 });
|
|
1159
|
+
const result = await makeFullPageBase64Image(largeScreenshotsData, defaultOptions);
|
|
1160
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1161
|
+
expect(mockImage.crop.mock.calls).toMatchSnapshot();
|
|
1162
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1163
|
+
expect(result).toMatchSnapshot();
|
|
1164
|
+
});
|
|
1165
|
+
it('should handle different screenshot data for each iteration', async () => {
|
|
1166
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, defaultOptions);
|
|
1167
|
+
expect(getBase64ScreenshotSizeMock.mock.calls).toMatchSnapshot();
|
|
1168
|
+
expect(result).toMatchSnapshot();
|
|
1169
|
+
});
|
|
1170
|
+
it('should handle canvas Y positions correctly', async () => {
|
|
1171
|
+
const result = await makeFullPageBase64Image(defaultScreenshotsData, defaultOptions);
|
|
1172
|
+
expect(mockCanvas.composite.mock.calls).toMatchSnapshot();
|
|
1173
|
+
expect(result).toMatchSnapshot();
|
|
1174
|
+
});
|
|
1175
|
+
});
|
|
1176
|
+
describe('takeResizedBase64Screenshot', () => {
|
|
1177
|
+
let mockBrowserInstance;
|
|
1178
|
+
let mockElement;
|
|
1179
|
+
let mockElementRegion;
|
|
1180
|
+
let takeBase64ScreenshotMock;
|
|
1181
|
+
let calculateDprDataMock;
|
|
1182
|
+
let isWdioElementMock;
|
|
1183
|
+
const defaultOptions = {
|
|
1184
|
+
browserInstance: {},
|
|
1185
|
+
element: {},
|
|
1186
|
+
devicePixelRatio: 2,
|
|
1187
|
+
isIOS: false,
|
|
1188
|
+
resizeDimensions: { top: 0, right: 0, bottom: 0, left: 0 }
|
|
1189
|
+
};
|
|
1190
|
+
beforeEach(async () => {
|
|
1191
|
+
mockElementRegion = {
|
|
1192
|
+
height: 100,
|
|
1193
|
+
width: 200,
|
|
1194
|
+
x: 50,
|
|
1195
|
+
y: 25
|
|
1196
|
+
};
|
|
1197
|
+
mockElement = {
|
|
1198
|
+
elementId: 'test-element-id',
|
|
1199
|
+
takeElementScreenshot: vi.fn().mockResolvedValue('nativeScreenshotData')
|
|
1200
|
+
};
|
|
1201
|
+
mockBrowserInstance = {
|
|
1202
|
+
getElementRect: vi.fn().mockResolvedValue(mockElementRegion)
|
|
1203
|
+
};
|
|
1204
|
+
const utilsModule = vi.mocked(await import('../helpers/utils.js'));
|
|
1205
|
+
calculateDprDataMock = vi.spyOn(utilsModule, 'calculateDprData');
|
|
1206
|
+
const screenshotsModule = vi.mocked(await import('./screenshots.js'));
|
|
1207
|
+
takeBase64ScreenshotMock = vi.spyOn(screenshotsModule, 'takeBase64Screenshot');
|
|
1208
|
+
const rectanglesModule = vi.mocked(await import('./rectangles.js'));
|
|
1209
|
+
isWdioElementMock = vi.spyOn(rectanglesModule, 'isWdioElement');
|
|
1210
|
+
takeBase64ScreenshotMock.mockResolvedValue('base64ScreenshotData');
|
|
1211
|
+
calculateDprDataMock.mockReturnValue({
|
|
1212
|
+
height: 100,
|
|
1213
|
+
width: 200,
|
|
1214
|
+
x: 50,
|
|
1215
|
+
y: 25
|
|
1216
|
+
});
|
|
1217
|
+
isWdioElementMock.mockReturnValue(true);
|
|
1218
|
+
});
|
|
1219
|
+
afterEach(() => {
|
|
1220
|
+
vi.clearAllMocks();
|
|
1221
|
+
});
|
|
1222
|
+
it('should take resized base64 screenshot with default settings', async () => {
|
|
1223
|
+
const result = await takeResizedBase64Screenshot({
|
|
1224
|
+
...defaultOptions,
|
|
1225
|
+
browserInstance: mockBrowserInstance,
|
|
1226
|
+
element: mockElement
|
|
1227
|
+
});
|
|
1228
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1229
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1230
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1231
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1232
|
+
expect(result).toBeDefined();
|
|
1233
|
+
});
|
|
1234
|
+
it('should handle iOS device with device pixel ratio', async () => {
|
|
1235
|
+
const result = await takeResizedBase64Screenshot({
|
|
1236
|
+
...defaultOptions,
|
|
1237
|
+
browserInstance: mockBrowserInstance,
|
|
1238
|
+
element: mockElement,
|
|
1239
|
+
devicePixelRatio: 3,
|
|
1240
|
+
isIOS: true
|
|
1241
|
+
});
|
|
1242
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1243
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1244
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1245
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1246
|
+
expect(result).toBeDefined();
|
|
1247
|
+
});
|
|
1248
|
+
it('should handle custom resize dimensions', async () => {
|
|
1249
|
+
const customResizeDimensions = {
|
|
1250
|
+
top: 10,
|
|
1251
|
+
right: 20,
|
|
1252
|
+
bottom: 15,
|
|
1253
|
+
left: 5
|
|
1254
|
+
};
|
|
1255
|
+
const result = await takeResizedBase64Screenshot({
|
|
1256
|
+
...defaultOptions,
|
|
1257
|
+
browserInstance: mockBrowserInstance,
|
|
1258
|
+
element: mockElement,
|
|
1259
|
+
resizeDimensions: customResizeDimensions
|
|
1260
|
+
});
|
|
1261
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1262
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1263
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1264
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1265
|
+
expect(result).toBeDefined();
|
|
1266
|
+
});
|
|
1267
|
+
it('should handle different element regions', async () => {
|
|
1268
|
+
const differentElementRegion = {
|
|
1269
|
+
height: 300,
|
|
1270
|
+
width: 400,
|
|
1271
|
+
x: 100,
|
|
1272
|
+
y: 75
|
|
1273
|
+
};
|
|
1274
|
+
mockBrowserInstance.getElementRect.mockResolvedValue(differentElementRegion);
|
|
1275
|
+
const result = await takeResizedBase64Screenshot({
|
|
1276
|
+
...defaultOptions,
|
|
1277
|
+
browserInstance: mockBrowserInstance,
|
|
1278
|
+
element: mockElement
|
|
1279
|
+
});
|
|
1280
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1281
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1282
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1283
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1284
|
+
expect(result).toBeDefined();
|
|
1285
|
+
});
|
|
1286
|
+
it('should handle non-WDIO element with logging', async () => {
|
|
1287
|
+
const nonWdioElement = { someProperty: 'not-a-wdio-element' };
|
|
1288
|
+
isWdioElementMock.mockReturnValue(false);
|
|
1289
|
+
const result = await takeResizedBase64Screenshot({
|
|
1290
|
+
...defaultOptions,
|
|
1291
|
+
browserInstance: mockBrowserInstance,
|
|
1292
|
+
element: nonWdioElement
|
|
1293
|
+
});
|
|
1294
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1295
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1296
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1297
|
+
expect(result).toBeDefined();
|
|
1298
|
+
});
|
|
1299
|
+
it('should handle different device pixel ratios', async () => {
|
|
1300
|
+
const result = await takeResizedBase64Screenshot({
|
|
1301
|
+
...defaultOptions,
|
|
1302
|
+
browserInstance: mockBrowserInstance,
|
|
1303
|
+
element: mockElement,
|
|
1304
|
+
devicePixelRatio: 1.5
|
|
1305
|
+
});
|
|
1306
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1307
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1308
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1309
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1310
|
+
expect(result).toBeDefined();
|
|
1311
|
+
});
|
|
1312
|
+
it('should handle zero element dimensions', async () => {
|
|
1313
|
+
const zeroElementRegion = {
|
|
1314
|
+
height: 0,
|
|
1315
|
+
width: 0,
|
|
1316
|
+
x: 0,
|
|
1317
|
+
y: 0
|
|
1318
|
+
};
|
|
1319
|
+
mockBrowserInstance.getElementRect.mockResolvedValue(zeroElementRegion);
|
|
1320
|
+
const result = await takeResizedBase64Screenshot({
|
|
1321
|
+
...defaultOptions,
|
|
1322
|
+
browserInstance: mockBrowserInstance,
|
|
1323
|
+
element: mockElement
|
|
1324
|
+
});
|
|
1325
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1326
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1327
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1328
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1329
|
+
expect(result).toBeDefined();
|
|
1330
|
+
});
|
|
1331
|
+
it('should handle large element dimensions', async () => {
|
|
1332
|
+
const largeElementRegion = {
|
|
1333
|
+
height: 2000,
|
|
1334
|
+
width: 3000,
|
|
1335
|
+
x: 1000,
|
|
1336
|
+
y: 500
|
|
1337
|
+
};
|
|
1338
|
+
mockBrowserInstance.getElementRect.mockResolvedValue(largeElementRegion);
|
|
1339
|
+
const result = await takeResizedBase64Screenshot({
|
|
1340
|
+
...defaultOptions,
|
|
1341
|
+
browserInstance: mockBrowserInstance,
|
|
1342
|
+
element: mockElement
|
|
1343
|
+
});
|
|
1344
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1345
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1346
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1347
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1348
|
+
expect(result).toBeDefined();
|
|
1349
|
+
});
|
|
1350
|
+
it('should handle different screenshot data', async () => {
|
|
1351
|
+
takeBase64ScreenshotMock.mockResolvedValue('differentScreenshotData');
|
|
1352
|
+
const result = await takeResizedBase64Screenshot({
|
|
1353
|
+
...defaultOptions,
|
|
1354
|
+
browserInstance: mockBrowserInstance,
|
|
1355
|
+
element: mockElement
|
|
1356
|
+
});
|
|
1357
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1358
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1359
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1360
|
+
expect(result).toBeDefined();
|
|
1361
|
+
});
|
|
1362
|
+
it('should handle element with different elementId', async () => {
|
|
1363
|
+
const elementWithDifferentId = {
|
|
1364
|
+
elementId: 'different-element-id'
|
|
1365
|
+
};
|
|
1366
|
+
const result = await takeResizedBase64Screenshot({
|
|
1367
|
+
...defaultOptions,
|
|
1368
|
+
browserInstance: mockBrowserInstance,
|
|
1369
|
+
element: elementWithDifferentId
|
|
1370
|
+
});
|
|
1371
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1372
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1373
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1374
|
+
expect(result).toBeDefined();
|
|
1375
|
+
});
|
|
1376
|
+
it('should handle Android device (non-iOS)', async () => {
|
|
1377
|
+
const result = await takeResizedBase64Screenshot({
|
|
1378
|
+
...defaultOptions,
|
|
1379
|
+
browserInstance: mockBrowserInstance,
|
|
1380
|
+
element: mockElement,
|
|
1381
|
+
devicePixelRatio: 2.5,
|
|
1382
|
+
isIOS: false
|
|
1383
|
+
});
|
|
1384
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1385
|
+
expect(mockBrowserInstance.getElementRect.mock.calls).toMatchSnapshot();
|
|
1386
|
+
expect(takeBase64ScreenshotMock.mock.calls).toMatchSnapshot();
|
|
1387
|
+
expect(calculateDprDataMock.mock.calls).toMatchSnapshot();
|
|
1388
|
+
expect(result).toBeDefined();
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
describe('takeBase64ElementScreenshot', () => {
|
|
1392
|
+
let mockElement;
|
|
1393
|
+
let mockBrowserInstance;
|
|
1394
|
+
let isWdioElementMock;
|
|
1395
|
+
beforeEach(async () => {
|
|
1396
|
+
mockElement = {
|
|
1397
|
+
elementId: 'test-element-id',
|
|
1398
|
+
takeElementScreenshot: vi.fn().mockResolvedValue('nativeScreenshotData')
|
|
1399
|
+
};
|
|
1400
|
+
mockBrowserInstance = {
|
|
1401
|
+
getElementRect: vi.fn().mockResolvedValue({
|
|
1402
|
+
height: 100,
|
|
1403
|
+
width: 200,
|
|
1404
|
+
x: 50,
|
|
1405
|
+
y: 25
|
|
1406
|
+
})
|
|
1407
|
+
};
|
|
1408
|
+
const rectanglesModule = await import('./rectangles.js');
|
|
1409
|
+
isWdioElementMock = vi.spyOn(rectanglesModule, 'isWdioElement');
|
|
1410
|
+
isWdioElementMock.mockReturnValue(true);
|
|
1411
|
+
});
|
|
1412
|
+
afterEach(() => {
|
|
1413
|
+
vi.clearAllMocks();
|
|
1414
|
+
});
|
|
1415
|
+
it('should use native element screenshot when resizeDimensions equals DEFAULT_RESIZE_DIMENSIONS', async () => {
|
|
1416
|
+
const { takeBase64ElementScreenshot } = await import('./images.js');
|
|
1417
|
+
const result = await takeBase64ElementScreenshot({
|
|
1418
|
+
browserInstance: mockBrowserInstance,
|
|
1419
|
+
element: Promise.resolve(mockElement),
|
|
1420
|
+
devicePixelRatio: 2,
|
|
1421
|
+
isIOS: false,
|
|
1422
|
+
resizeDimensions: DEFAULT_RESIZE_DIMENSIONS
|
|
1423
|
+
});
|
|
1424
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1425
|
+
expect(mockElement.takeElementScreenshot.mock.calls).toMatchSnapshot();
|
|
1426
|
+
expect(result).toBe('nativeScreenshotData');
|
|
1427
|
+
});
|
|
1428
|
+
it('should log error and still call takeElementScreenshot if element is not WDIO element', async () => {
|
|
1429
|
+
isWdioElementMock.mockReturnValue(false);
|
|
1430
|
+
const { takeBase64ElementScreenshot } = await import('./images.js');
|
|
1431
|
+
const errorSpy = vi.spyOn(log, 'error').mockImplementation(() => { });
|
|
1432
|
+
const result = await takeBase64ElementScreenshot({
|
|
1433
|
+
browserInstance: mockBrowserInstance,
|
|
1434
|
+
element: Promise.resolve(mockElement),
|
|
1435
|
+
devicePixelRatio: 2,
|
|
1436
|
+
isIOS: false,
|
|
1437
|
+
resizeDimensions: DEFAULT_RESIZE_DIMENSIONS
|
|
1438
|
+
});
|
|
1439
|
+
expect(isWdioElementMock.mock.calls).toMatchSnapshot();
|
|
1440
|
+
expect(errorSpy.mock.calls).toMatchSnapshot();
|
|
1441
|
+
expect(mockElement.takeElementScreenshot.mock.calls).toMatchSnapshot();
|
|
1442
|
+
expect(result).toBe('nativeScreenshotData');
|
|
1443
|
+
errorSpy.mockRestore();
|
|
1444
|
+
});
|
|
1445
|
+
it('should fallback to takeResizedBase64Screenshot when takeElementScreenshot throws an error', async () => {
|
|
1446
|
+
const { takeBase64ElementScreenshot } = await import('./images.js');
|
|
1447
|
+
const errorSpy = vi.spyOn(log, 'error').mockImplementation(() => { });
|
|
1448
|
+
const mockElementWithError = {
|
|
1449
|
+
elementId: 'test-element-id',
|
|
1450
|
+
takeElementScreenshot: vi.fn().mockRejectedValue(new Error('Screenshot failed'))
|
|
1451
|
+
};
|
|
1452
|
+
const result = await takeBase64ElementScreenshot({
|
|
1453
|
+
browserInstance: mockBrowserInstance,
|
|
1454
|
+
element: Promise.resolve(mockElementWithError),
|
|
1455
|
+
devicePixelRatio: 2,
|
|
1456
|
+
isIOS: false,
|
|
1457
|
+
resizeDimensions: DEFAULT_RESIZE_DIMENSIONS
|
|
1458
|
+
});
|
|
1459
|
+
expect(mockElementWithError.takeElementScreenshot.mock.calls).toMatchSnapshot();
|
|
1460
|
+
expect(errorSpy.mock.calls).toMatchSnapshot();
|
|
1461
|
+
expect(result).toBeDefined();
|
|
1462
|
+
expect(typeof result).toBe('string');
|
|
1463
|
+
errorSpy.mockRestore();
|
|
1464
|
+
});
|
|
1465
|
+
});
|