UNPKG

@labnex/cli

Version:

CLI for Labnex, an AI-Powered Testing Automation Platform

226 lines 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleDragAndDrop = handleDragAndDrop; const elementFinderV2_1 = require("../elementFinderV2"); // Updated import const extractHintedSelector_1 = require("../parserHelpers/extractHintedSelector"); // Added import async function handleDragAndDrop(page, currentFrame, addLog, sourceSelectorOrText, destinationSelectorOrText, originalStep, retryApiCallFn) { if (!currentFrame) throw new Error('Current frame not available for drag and drop'); if (!page) throw new Error('Page context not available for drag and drop'); if (!sourceSelectorOrText) throw new Error('Source selector not provided for drag and drop'); if (!destinationSelectorOrText) throw new Error('Destination selector not provided for drag and drop'); addLog('[handleDragAndDrop] Attempting to reset mouse state before operation.'); try { await page.mouse.reset(); addLog('[handleDragAndDrop] Mouse state reset successful.'); } catch (resetError) { addLog(`[handleDragAndDrop] Warning: Mouse reset failed: ${resetError.message}. Proceeding cautiously.`); } // Check if currently in an iframe context const isInIframe = currentFrame !== page; if (isInIframe) { addLog('[handleDragAndDrop] Currently in an iframe. Starting a 7-second pause for content to load.'); await new Promise(resolve => setTimeout(resolve, 7000)); addLog('[handleDragAndDrop] 7-second iframe pause COMPLETED.'); } else { addLog('[handleDragAndDrop] Not in an iframe. Proceeding directly.'); } addLog(`Attempting to find source element: "${sourceSelectorOrText}"`); const sourceElement = await (0, elementFinderV2_1.findElementWithFallbacks)(page, currentFrame, addLog, sourceSelectorOrText, `source element (${sourceSelectorOrText})`, originalStep, false, retryApiCallFn); addLog(`Attempting to find destination element: "${destinationSelectorOrText}"`); const destinationElement = await (0, elementFinderV2_1.findElementWithFallbacks)(page, currentFrame, addLog, destinationSelectorOrText, `destination element (${destinationSelectorOrText})`, originalStep, false, retryApiCallFn); if (!sourceElement || !destinationElement) { if (sourceElement) await sourceElement.dispose(); if (destinationElement) await destinationElement.dispose(); throw new Error('Source or destination element not found for drag and drop.'); } addLog('[handleDragAndDrop] Ensuring elements are scrolled into view.'); await sourceElement.evaluate(el => el.scrollIntoView()); await destinationElement.evaluate(el => el.scrollIntoView()); await new Promise(resolve => setTimeout(resolve, 100)); // Brief pause after scroll, corrected usage const DRAG_AND_DROP_TIMEOUT = 60000; // 60 seconds timeout for the entire operation let nativeDragDropSuccessful = false; // --- Heuristic for problematic D&D sites --- let alwaysUseFallback = false; try { const currentPageUrlForCheck = page.url(); if (currentPageUrlForCheck && currentPageUrlForCheck.includes('globalsqa.com/demo-site/draganddrop')) { addLog('[DND Heuristic] GlobalsQA D&D page detected. Forcing fallback.'); alwaysUseFallback = true; } } catch (urlError) { const errorMessage = (urlError instanceof Error) ? urlError.message : 'Unknown URL check error'; addLog(`[DND Heuristic] Error checking URL: ${errorMessage}`); } // --- End Heuristic --- addLog(`Attempting drag from source to destination using Puppeteer\'s built-in dragAndDrop.`); if (!alwaysUseFallback) { try { await sourceElement.dragAndDrop(destinationElement); addLog('Drag and drop successful using native method.'); nativeDragDropSuccessful = true; } catch (error) { addLog(`Native dragAndDrop failed: ${error.message}. Attempting fallback mouse events.`); // Reset mouse state only if native drag failed and we are about to try fallback addLog('[handleDragAndDrop] Attempting to reset mouse state before fallback D&D (after native failure).'); try { await page.mouse.reset(); addLog('[handleDragAndDrop] Mouse state reset before fallback D&D (after native failure) successful.'); } catch (resetError) { addLog(`[handleDragAndDrop] Warning: Mouse reset before fallback D&D (after native failure) failed: ${resetError.message}.`); } } } else { addLog('[handleDragAndDrop] Skipping native dragAndDrop due to \'alwaysUseFallback\' heuristic for GlobalsQA.'); // Ensure mouse is reset if we are forcing fallback from the start addLog('[handleDragAndDrop] Attempting to reset mouse state before forced fallback D&D (GlobalsQA heuristic).'); try { await page.mouse.reset(); addLog('[handleDragAndDrop] Mouse state reset before forced fallback D&D (GlobalsQA heuristic) successful.'); } catch (resetError) { addLog(`[handleDragAndDrop] Warning: Mouse reset before forced fallback D&D (GlobalsQA heuristic) failed: ${resetError.message}.`); } // nativeDragDropSuccessful remains false, so the next block will execute the fallback } if (!nativeDragDropSuccessful) { // This condition now correctly covers both native failure and forced fallback for GlobalsQA if (alwaysUseFallback) { addLog('[handleDragAndDrop] Proceeding to fallback mouse event simulation due to GlobalsQA heuristic (native D&D was skipped).'); } else { // This case means nativeDragDropSuccessful is false AND alwaysUseFallback was false (i.e. native D&D was tried and failed for a non-GlobalsQA site) addLog('[handleDragAndDrop] Native dragAndDrop failed. Proceeding to fallback mouse event simulation.'); } try { addLog('[handleDragAndDrop] Disabling drag interception for fallback mouse events.'); await page.setDragInterception(false); // Disable for manual mouse events await Promise.race([ (async () => { const sourceBox = await sourceElement.boundingBox(); const destinationBox = await destinationElement.boundingBox(); if (!sourceBox || !destinationBox) { throw new Error('Could not get bounding box for source or destination element for drag and drop fallback.'); } addLog(`[DND Fallback] Source Box: ${JSON.stringify(sourceBox)}`); addLog(`[DND Fallback] Destination Box: ${JSON.stringify(destinationBox)}`); addLog('Performing drag and drop using simulated mouse events.'); const mouseController = page.mouse; let mouseDownSuccessful = false; try { addLog('[DND Fallback] Hovering over source element center.'); await sourceElement.hover(); // Use Puppeteer's hover await new Promise(resolve => setTimeout(resolve, 100)); // Pause after hover addLog('[DND Fallback] Moving to source element center for mousedown.'); await mouseController.move(sourceBox.x + sourceBox.width / 2, sourceBox.y + sourceBox.height / 2, { steps: 5 }); addLog('[DND Fallback] Performing mouse down on source.'); await mouseController.down(); mouseDownSuccessful = true; addLog('[DND Fallback] Mouse down successful. Waiting 200ms.'); await new Promise(resolve => setTimeout(resolve, 200)); addLog('[DND Fallback] Moving to destination element center.'); await mouseController.move(destinationBox.x + destinationBox.width / 2, destinationBox.y + destinationBox.height / 2, { steps: 10 }); addLog('[DND Fallback] Mouse move to destination successful. Waiting 200ms.'); await new Promise(resolve => setTimeout(resolve, 200)); addLog('[DND Fallback] Performing mouse up at destination.'); await mouseController.up(); addLog('[DND Fallback] Mouse up successful. Waiting 500ms for drop to process.'); await new Promise(resolve => setTimeout(resolve, 500)); addLog('[DND Fallback] Performing a slight mouse wiggle after drop.'); await mouseController.move(destinationBox.x + destinationBox.width / 2 + 1, destinationBox.y + destinationBox.height / 2 + 1, { steps: 2 }); await new Promise(resolve => setTimeout(resolve, 100)); addLog('[DND Fallback] Drag and drop with mouse events completed.'); } catch (mouseError) { addLog(`[DND Fallback] Error during mouse event simulation: ${mouseError.message}`); throw mouseError; // Re-throw the error } finally { if (mouseDownSuccessful) { addLog('[DND Fallback] In finally block. Ensuring mouse is up.'); try { await mouseController.up(); addLog('[DND Fallback] Explicit mouse.up() in finally block executed.'); } catch (releaseError) { addLog(`[DND Fallback] Error during explicit mouse.up() in finally: ${releaseError.message}.`); } } } })(), new Promise((_, reject) => setTimeout(() => reject(new Error(`Drag and drop fallback operation timed out after ${DRAG_AND_DROP_TIMEOUT / 1000} seconds.`)), DRAG_AND_DROP_TIMEOUT)) ]); } finally { addLog('[handleDragAndDrop] Re-enabling drag interception after fallback attempt.'); await page.setDragInterception(true); // Re-enable after fallback } } // Log DOM state of source and destination after drag for debugging addLog('[handleDragAndDrop] Attempting to log post-drag DOM state...'); try { const cleanDestinationSelector = (0, extractHintedSelector_1.extractHintedSelector)(destinationSelectorOrText || '').selectorValue || destinationSelectorOrText; try { addLog('[handleDragAndDrop] Logging destination element DOM state (max 2s wait)...'); const trashHTMLElement = await Promise.race([ currentFrame.evaluate(selector => { const el = document.querySelector(selector); return el ? el.outerHTML : 'Trash element not found post-drag.'; }, cleanDestinationSelector), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout logging destination DOM')), 2000)) ]); addLog(`[handleDragAndDrop] #trash content after drag (selector: ${cleanDestinationSelector}): ${trashHTMLElement.substring(0, 500)}...`); } catch (logDestError) { addLog(`[handleDragAndDrop] Error or timeout logging destination element's post-drag DOM state: ${logDestError.message}`); } const cleanSourceSelectorForGallery = ((0, extractHintedSelector_1.extractHintedSelector)(sourceSelectorOrText || '').selectorValue || sourceSelectorOrText || '').replace(/ li:nth-child\(\d+\) img$/, ''); try { addLog('[handleDragAndDrop] Logging source element DOM state (max 2s wait)...'); const galleryHTMLElement = await Promise.race([ currentFrame.evaluate(selector => { const el = document.querySelector(selector); return el ? el.outerHTML : 'Gallery element not found post-drag.'; }, cleanSourceSelectorForGallery), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout logging source DOM')), 2000)) ]); addLog(`[handleDragAndDrop] #gallery content after drag (selector: ${cleanSourceSelectorForGallery}): ${galleryHTMLElement.substring(0, 500)}...`); } catch (logSourceError) { addLog(`[handleDragAndDrop] Error or timeout logging source element's post-drag DOM state: ${logSourceError.message}`); } } catch (logError) { // This catch block might be redundant now with individual catches, but kept for safety. addLog(`[handleDragAndDrop] General error logging post-drag DOM state: ${logError.message}`); } addLog('[handleDragAndDrop] Finished logging post-drag DOM state.'); // Ensure elements are disposed if (sourceElement && !sourceElement._disposed) { // Check if not already disposed by successful native drag try { await sourceElement.dispose(); } catch (e) { addLog('Error disposing sourceElement: ' + e.message); } } if (destinationElement && !destinationElement._disposed) { // Check if not already disposed try { await destinationElement.dispose(); } catch (e) { addLog('Error disposing destinationElement: ' + e.message); } } } //# sourceMappingURL=handleDragAndDrop.js.map