How to use onMoveToIframeRequest method in Testcafe

Best JavaScript code snippet using testcafe

Run Testcafe automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

move.js

Source: move.js Github

copy
1import hammerhead from '../../deps/hammerhead';
2import testCafeCore from '../../deps/testcafe-core';
3import { MoveOptions } from '../../../../test-run/commands/options';
4import cursor from '../../cursor';
5
6
7import getLineRectIntersection from '../../../../shared/utils/get-line-rect-intersection';
8
9import lastHoveredElementHolder from '../../../../shared/actions/automations/last-hovered-element-holder';
10import AxisValues from '../../../../shared/utils/values/axis-values';
11
12import MoveAutomation from '../../../../shared/actions/automations/move';
13
14const eventSimulator   = hammerhead.eventSandbox.eventSimulator;
15const messageSandbox   = hammerhead.eventSandbox.message;
16
17const positionUtils      = testCafeCore.positionUtils;
18const domUtils           = testCafeCore.domUtils;
19const styleUtils         = testCafeCore.styleUtils;
20
21const MOVE_REQUEST_CMD  = 'automation|move|request';
22const MOVE_RESPONSE_CMD = 'automation|move|response';
23
24function onMoveToIframeRequest (e) {
25    const iframePoint                 = new AxisValues(e.message.endX, e.message.endY);
26    const iframeWin                   = e.source;
27    const iframe                      = domUtils.findIframeByWindow(iframeWin);
28    const iframeBorders               = styleUtils.getBordersWidth(iframe);
29    const iframePadding               = styleUtils.getElementPadding(iframe);
30    const iframeRectangle             = positionUtils.getIframeClientCoordinates(iframe);
31    const iframePointRelativeToParent = positionUtils.getIframePointRelativeToParentFrame(iframePoint, iframeWin);
32    const cursorPosition              = cursor.getPosition();
33
34    const intersectionPoint = positionUtils.isInRectangle(cursorPosition, iframeRectangle) ? cursorPosition :
35        getLineRectIntersection(cursorPosition, iframePointRelativeToParent, iframeRectangle);
36
37    const intersectionRelatedToIframe = {
38        x: intersectionPoint.x - iframeRectangle.left,
39        y: intersectionPoint.y - iframeRectangle.top,
40    };
41
42    const moveOptions = new MoveOptions({
43        modifiers: e.message.modifiers,
44        offsetX:   intersectionRelatedToIframe.x + iframeBorders.left + iframePadding.left,
45        offsetY:   intersectionRelatedToIframe.y + iframeBorders.top + iframePadding.top,
46        speed:     e.message.speed,
47
48        // NOTE: we should not perform scrolling because the active window was
49        // already scrolled to the target element before the request (GH-847)
50        skipScrolling: true,
51    }, false);
52
53    const responseMsg = {
54        cmd: MOVE_RESPONSE_CMD,
55        x:   intersectionRelatedToIframe.x,
56        y:   intersectionRelatedToIframe.y,
57    };
58
59    if (cursor.getActiveWindow(window) !== iframeWin) {
60        // const moveAutomation = new MoveAutomation(iframe, moveOptions);
61        MoveAutomation.create(iframe, moveOptions, window, cursor)
62            .then(moveAutomation => {
63                return moveAutomation.run();
64            })
65            .then(() => {
66                cursor.setActiveWindow(iframeWin);
67
68                messageSandbox.sendServiceMsg(responseMsg, iframeWin);
69            });
70    }
71    else
72        messageSandbox.sendServiceMsg(responseMsg, iframeWin);
73}
74
75function onMoveOutRequest (e) {
76    const parentWin = e.source;
77
78    const iframeRectangle = {
79        left:   e.message.left,
80        right:  e.message.right,
81        top:    e.message.top,
82        bottom: e.message.bottom,
83    };
84
85    if (!e.message.iframeUnderCursor) {
86        const { startX, startY } = e.message;
87
88        const clientX = startX - iframeRectangle.left;
89        const clientY = startY - iframeRectangle.top;
90
91        // NOTE: We should not emulate mouseout and mouseleave if iframe was reloaded.
92        const element = lastHoveredElementHolder.get();
93
94        if (element) {
95            eventSimulator.mouseout(element, { clientX, clientY, relatedTarget: null });
96            eventSimulator.mouseleave(element, { clientX, clientY, relatedTarget: null });
97        }
98
99        messageSandbox.sendServiceMsg({ cmd: MOVE_RESPONSE_CMD }, parentWin);
100
101        return;
102    }
103
104    const cursorPosition    = cursor.getPosition();
105    const startPoint        = AxisValues.create(iframeRectangle).add(cursorPosition);
106    const endPoint          = new AxisValues(e.message.endX, e.message.endY);
107    const intersectionPoint = getLineRectIntersection(startPoint, endPoint, iframeRectangle);
108
109    // NOTE: We should not move the cursor out of the iframe if
110    // the cursor path does not intersect with the iframe borders.
111    if (!intersectionPoint) {
112        messageSandbox.sendServiceMsg({
113            cmd: MOVE_RESPONSE_CMD,
114            x:   iframeRectangle.left,
115            y:   iframeRectangle.top,
116        }, parentWin);
117
118        return;
119    }
120
121    const moveOptions = new MoveOptions({
122        modifiers: e.message.modifiers,
123        offsetX:   intersectionPoint.x - iframeRectangle.left,
124        offsetY:   intersectionPoint.y - iframeRectangle.top,
125        speed:     e.message.speed,
126
127        // NOTE: we should not perform scrolling because the active window was
128        // already scrolled to the target element before the request (GH-847)
129        skipScrolling: true,
130    }, false);
131
132    MoveAutomation.create(document.documentElement, moveOptions, window, cursor)
133        .then(moveAutomation => {
134            return moveAutomation.run();
135        })
136        .then(() => {
137            const responseMsg = {
138                cmd: MOVE_RESPONSE_CMD,
139                x:   intersectionPoint.x,
140                y:   intersectionPoint.y,
141            };
142
143            cursor.setActiveWindow(parentWin);
144            messageSandbox.sendServiceMsg(responseMsg, parentWin);
145        });
146}
147
148// Setup cross-iframe interaction
149messageSandbox.on(messageSandbox.SERVICE_MSG_RECEIVED_EVENT, e => {
150    if (e.message.cmd === MOVE_REQUEST_CMD) {
151        if (e.source.parent === window)
152            onMoveToIframeRequest(e);
153        else {
154            hammerhead.on(hammerhead.EVENTS.beforeUnload, () => messageSandbox.sendServiceMsg({ cmd: MOVE_RESPONSE_CMD }, e.source));
155
156            onMoveOutRequest(e);
157        }
158    }
159});
160
161export default MoveAutomation;
162
Full Screen

index.js

Source: index.js Github

copy
1import hammerhead from '../../deps/hammerhead';
2import testCafeCore from '../../deps/testcafe-core';
3import { ScrollOptions, MoveOptions } from '../../../../test-run/commands/options';
4import ScrollAutomation from '../scroll';
5import cursor from '../../cursor';
6
7import { underCursor as getElementUnderCursor } from '../../get-element';
8import getAutomationPoint from '../../utils/get-automation-point';
9import getLineRectIntersection from '../../utils/get-line-rect-intersection';
10import getDevicePoint from '../../utils/get-device-point';
11import nextTick from '../../utils/next-tick';
12import AutomationSettings from '../../settings';
13import DragAndDropState from '../drag/drag-and-drop-state';
14import createEventSequence from './event-sequence/create-event-sequence';
15import lastHoveredElementHolder from '../last-hovered-element-holder';
16
17const Promise          = hammerhead.Promise;
18const nativeMethods    = hammerhead.nativeMethods;
19const featureDetection = hammerhead.utils.featureDetection;
20const htmlUtils        = hammerhead.utils.html;
21const urlUtils         = hammerhead.utils.url;
22const eventSimulator   = hammerhead.eventSandbox.eventSimulator;
23const messageSandbox   = hammerhead.eventSandbox.message;
24const DataTransfer     = hammerhead.eventSandbox.DataTransfer;
25const DragDataStore    = hammerhead.eventSandbox.DragDataStore;
26
27const positionUtils      = testCafeCore.positionUtils;
28const domUtils           = testCafeCore.domUtils;
29const styleUtils         = testCafeCore.styleUtils;
30const eventUtils         = testCafeCore.eventUtils;
31const promiseUtils       = testCafeCore.promiseUtils;
32const sendRequestToFrame = testCafeCore.sendRequestToFrame;
33
34
35const MOVE_REQUEST_CMD  = 'automation|move|request';
36const MOVE_RESPONSE_CMD = 'automation|move|response';
37
38// Setup cross-iframe interaction
39messageSandbox.on(messageSandbox.SERVICE_MSG_RECEIVED_EVENT, e => {
40    if (e.message.cmd === MOVE_REQUEST_CMD) {
41        if (e.source.parent === window)
42            MoveAutomation.onMoveToIframeRequest(e);
43        else {
44            hammerhead.on(hammerhead.EVENTS.beforeUnload, () => messageSandbox.sendServiceMsg({ cmd: MOVE_RESPONSE_CMD }, e.source));
45
46            MoveAutomation.onMoveOutRequest(e);
47        }
48    }
49});
50
51// Utils
52function findDraggableElement (element) {
53    let parentNode = element;
54
55    while (parentNode) {
56        if (parentNode.draggable)
57            return parentNode;
58
59        parentNode = nativeMethods.nodeParentNodeGetter.call(parentNode);
60    }
61
62    return null;
63}
64
65export default class MoveAutomation {
66    constructor (element, moveOptions) {
67        this.touchMode = featureDetection.isTouchDevice;
68        this.moveEvent = this.touchMode ? 'touchmove' : 'mousemove';
69
70        this.holdLeftButton = moveOptions.holdLeftButton;
71        this.dragElement    = null;
72
73        this.dragAndDropState = new DragAndDropState();
74
75        this.automationSettings = new AutomationSettings(moveOptions.speed);
76
77        const target = MoveAutomation.getTarget(element, moveOptions.offsetX, moveOptions.offsetY);
78
79        this.element     = target.element;
80        this.offsetX     = target.offsetX;
81        this.offsetY     = target.offsetY;
82        this.speed       = moveOptions.speed;
83        this.cursorSpeed = this.holdLeftButton ? this.automationSettings.draggingSpeed : this.automationSettings.cursorSpeed;
84
85        this.minMovingTime           = moveOptions.minMovingTime || null;
86        this.modifiers               = moveOptions.modifiers || {};
87        this.skipScrolling           = moveOptions.skipScrolling;
88        this.skipDefaultDragBehavior = moveOptions.skipDefaultDragBehavior;
89
90        this.endPoint = null;
91
92        // moving state
93        this.movingTime = null;
94        this.x          = null;
95        this.y          = null;
96        this.startTime  = null;
97        this.endTime    = null;
98        this.distanceX  = null;
99        this.distanceY  = null;
100
101        this.firstMovingStepOccured = false;
102    }
103
104    static getTarget (el, offsetX, offsetY) {
105        // NOTE: if the target point (considering offsets) is out of
106        // the element change the target element to the document element
107        const relateToDocument = !positionUtils.containsOffset(el, offsetX, offsetY);
108        const relatedPoint     = relateToDocument ? getAutomationPoint(el, offsetX, offsetY) : { x: offsetX, y: offsetY };
109
110        return {
111            element: relateToDocument ? document.documentElement : el,
112            offsetX: relatedPoint.x,
113            offsetY: relatedPoint.y
114        };
115    }
116
117    static onMoveToIframeRequest (e) {
118        const iframePoint = {
119            x: e.message.endX,
120            y: e.message.endY
121        };
122
123        const iframeWin                   = e.source;
124        const iframe                      = domUtils.findIframeByWindow(iframeWin);
125        const iframeBorders               = styleUtils.getBordersWidth(iframe);
126        const iframePadding               = styleUtils.getElementPadding(iframe);
127        const iframeRectangle             = positionUtils.getIframeClientCoordinates(iframe);
128        const iframePointRelativeToParent = positionUtils.getIframePointRelativeToParentFrame(iframePoint, iframeWin);
129        const cursorPosition              = cursor.position;
130
131        const intersectionPoint = positionUtils.isInRectangle(cursorPosition, iframeRectangle) ? cursorPosition :
132            getLineRectIntersection(cursorPosition, iframePointRelativeToParent, iframeRectangle);
133
134        const intersectionRelatedToIframe = {
135            x: intersectionPoint.x - iframeRectangle.left,
136            y: intersectionPoint.y - iframeRectangle.top
137        };
138
139        const moveOptions = new MoveOptions({
140            modifiers: e.message.modifiers,
141            offsetX:   intersectionRelatedToIframe.x + iframeBorders.left + iframePadding.left,
142            offsetY:   intersectionRelatedToIframe.y + iframeBorders.top + iframePadding.top,
143            speed:     e.message.speed,
144
145            // NOTE: we should not perform scrolling because the active window was
146            // already scrolled to the target element before the request (GH-847)
147            skipScrolling: true
148        }, false);
149
150        const moveAutomation = new MoveAutomation(iframe, moveOptions);
151
152        const responseMsg = {
153            cmd: MOVE_RESPONSE_CMD,
154            x:   intersectionRelatedToIframe.x,
155            y:   intersectionRelatedToIframe.y
156        };
157
158        if (cursor.activeWindow !== iframeWin) {
159            moveAutomation
160                .run()
161                .then(() => {
162                    cursor.activeWindow = iframeWin;
163
164                    messageSandbox.sendServiceMsg(responseMsg, iframeWin);
165                });
166        }
167        else
168            messageSandbox.sendServiceMsg(responseMsg, iframeWin);
169    }
170
171    static onMoveOutRequest (e) {
172        const parentWin = e.source;
173
174        const iframeRectangle = {
175            left:   e.message.left,
176            right:  e.message.right,
177            top:    e.message.top,
178            bottom: e.message.bottom
179        };
180
181        if (!e.message.iframeUnderCursor) {
182            const { startX, startY } = e.message;
183
184            const clientX = startX - iframeRectangle.left;
185            const clientY = startY - iframeRectangle.top;
186
187            // NOTE: We should not emulate mouseout and mouseleave if iframe was reloaded.
188            const element = lastHoveredElementHolder.get();
189
190            if (element) {
191                eventSimulator.mouseout(element, { clientX, clientY, relatedTarget: null });
192                eventSimulator.mouseleave(element, { clientX, clientY, relatedTarget: null });
193            }
194
195            messageSandbox.sendServiceMsg({ cmd: MOVE_RESPONSE_CMD }, parentWin);
196
197            return;
198        }
199
200        const cursorPosition = cursor.position;
201
202        const startPoint = {
203            x: iframeRectangle.left + cursorPosition.x,
204            y: iframeRectangle.top + cursorPosition.y
205        };
206
207        const endPoint          = { x: e.message.endX, y: e.message.endY };
208        const intersectionPoint = getLineRectIntersection(startPoint, endPoint, iframeRectangle);
209
210        // NOTE: We should not move the cursor out of the iframe if
211        // the cursor path does not intersect with the iframe borders.
212        if (!intersectionPoint) {
213            messageSandbox.sendServiceMsg({
214                cmd: MOVE_RESPONSE_CMD,
215                x:   iframeRectangle.left,
216                y:   iframeRectangle.top
217            }, parentWin);
218
219            return;
220        }
221
222        const moveOptions = new MoveOptions({
223            modifiers: e.message.modifiers,
224            offsetX:   intersectionPoint.x - iframeRectangle.left,
225            offsetY:   intersectionPoint.y - iframeRectangle.top,
226            speed:     e.message.speed,
227
228            // NOTE: we should not perform scrolling because the active window was
229            // already scrolled to the target element before the request (GH-847)
230            skipScrolling: true
231        }, false);
232
233        const moveAutomation = new MoveAutomation(document.documentElement, moveOptions);
234
235        moveAutomation
236            .run()
237            .then(() => {
238                const responseMsg = {
239                    cmd: MOVE_RESPONSE_CMD,
240                    x:   intersectionPoint.x,
241                    y:   intersectionPoint.y
242                };
243
244                cursor.activeWindow = parentWin;
245                messageSandbox.sendServiceMsg(responseMsg, parentWin);
246            });
247    }
248
249    _getTargetClientPoint () {
250        const scroll = styleUtils.getElementScroll(this.element);
251
252        if (domUtils.isHtmlElement(this.element)) {
253            return {
254                x: Math.floor(this.offsetX - scroll.left),
255                y: Math.floor(this.offsetY - scroll.top)
256            };
257        }
258
259        const clientPosition = positionUtils.getClientPosition(this.element);
260        const isDocumentBody = this.element.tagName && domUtils.isBodyElement(this.element);
261
262        return {
263            x: Math.floor(isDocumentBody ? clientPosition.x + this.offsetX : clientPosition.x + this.offsetX -
264                                                                             scroll.left),
265            y: Math.floor(isDocumentBody ? clientPosition.y + this.offsetY : clientPosition.y + this.offsetY -
266                                                                             scroll.top)
267        };
268    }
269
270    _emulateEvents (currentElement) {
271        const button      = this.holdLeftButton ? eventUtils.BUTTONS_PARAMETER.leftButton : eventUtils.BUTTONS_PARAMETER.noButton;
272        const devicePoint = getDevicePoint({ x: this.x, y: this.y });
273
274        const eventOptions = {
275            clientX:      this.x,
276            clientY:      this.y,
277            screenX:      devicePoint.x,
278            screenY:      devicePoint.y,
279            buttons:      button,
280            ctrl:         this.modifiers.ctrl,
281            alt:          this.modifiers.alt,
282            shift:        this.modifiers.shift,
283            meta:         this.modifiers.meta,
284            dataTransfer: this.dragAndDropState.dataTransfer
285        };
286
287        const eventSequenceOptions = { moveEvent: this.moveEvent, holdLeftButton: this.holdLeftButton };
288        const eventSequence        = createEventSequence(this.dragAndDropState.enabled, this.firstMovingStepOccured, eventSequenceOptions);
289
290        const { dragAndDropMode, dropAllowed } = eventSequence.run(
291            currentElement,
292            lastHoveredElementHolder.get(),
293            eventOptions,
294            this.dragElement,
295            this.dragAndDropState.dataStore
296        );
297
298        this.firstMovingStepOccured       = true;
299        this.dragAndDropState.enabled     = dragAndDropMode;
300        this.dragAndDropState.dropAllowed = dropAllowed;
301
302        lastHoveredElementHolder.set(currentElement);
303    }
304
305    _movingStep () {
306        if (this.touchMode && !this.holdLeftButton) {
307            this.x = this.endPoint.x;
308            this.y = this.endPoint.y;
309        }
310        else if (!this.startTime) {
311            this.startTime = nativeMethods.dateNow();
312            this.endTime   = this.startTime + this.movingTime;
313
314            // NOTE: the mousemove event can't be simulated at the point where the cursor
315            // was located at the start. Therefore, we add a minimal distance 1 px.
316            this.x += this.distanceX > 0 ? 1 : -1;
317            this.y += this.distanceY > 0 ? 1 : -1;
318        }
319        else {
320            const currentTime = Math.min(nativeMethods.dateNow(), this.endTime);
321            const progress    = (currentTime - this.startTime) / (this.endTime - this.startTime);
322
323            this.x = Math.floor(this.startPoint.x + this.distanceX * progress);
324            this.y = Math.floor(this.startPoint.y + this.distanceY * progress);
325        }
326
327        return cursor
328            .move(this.x, this.y)
329            .then(getElementUnderCursor)
330            // NOTE: in touch mode, events are simulated for the element for which mousedown was simulated (GH-372)
331            .then(topElement => {
332                const currentElement = this.holdLeftButton && this.touchMode ? this.dragElement : topElement;
333
334                // NOTE: it can be null in IE
335                if (!currentElement)
336                    return null;
337
338                return this._emulateEvents(currentElement);
339            })
340            .then(nextTick);
341    }
342
343    _isMovingFinished () {
344        return this.x === this.endPoint.x && this.y === this.endPoint.y;
345    }
346
347    _move () {
348        this.startPoint = cursor.position;
349        this.x          = this.startPoint.x;
350        this.y          = this.startPoint.y;
351
352        this.distanceX = this.endPoint.x - this.startPoint.x;
353        this.distanceY = this.endPoint.y - this.startPoint.y;
354
355        this.movingTime = Math.max(Math.abs(this.distanceX), Math.abs(this.distanceY)) / this.cursorSpeed;
356
357        if (this.minMovingTime)
358            this.movingTime = Math.max(this.movingTime, this.minMovingTime);
359
360        return promiseUtils.whilst(() => !this._isMovingFinished(), () => this._movingStep());
361    }
362
363    _scroll () {
364        if (this.skipScrolling)
365            return Promise.resolve();
366
367        const scrollOptions    = new ScrollOptions({ offsetX: this.offsetX, offsetY: this.offsetY }, false);
368        const scrollAutomation = new ScrollAutomation(this.element, scrollOptions);
369
370        return scrollAutomation.run();
371    }
372
373    _moveToCurrentFrame () {
374        if (cursor.active)
375            return Promise.resolve();
376
377        const { x, y }          = cursor.position;
378        const activeWindow      = cursor.activeWindow;
379        let iframe            = null;
380        let iframeUnderCursor = null;
381        let iframeRectangle   = null;
382
383        const msg = {
384            cmd:       MOVE_REQUEST_CMD,
385            startX:    x,
386            startY:    y,
387            endX:      this.endPoint.x,
388            endY:      this.endPoint.y,
389            modifiers: this.modifiers,
390            speed:     this.speed
391        };
392
393        if (activeWindow.parent === window) {
394            iframe          = domUtils.findIframeByWindow(activeWindow);
395            iframeRectangle = positionUtils.getIframeClientCoordinates(iframe);
396
397            msg.left   = iframeRectangle.left;
398            msg.top    = iframeRectangle.top;
399            msg.right  = iframeRectangle.right;
400            msg.bottom = iframeRectangle.bottom;
401        }
402
403        return getElementUnderCursor()
404            .then(topElement => {
405                iframeUnderCursor = topElement === iframe;
406
407                if (activeWindow.parent === window)
408                    msg.iframeUnderCursor = iframeUnderCursor;
409
410                return sendRequestToFrame(msg, MOVE_RESPONSE_CMD, activeWindow);
411            })
412            .then(message => {
413                cursor.activeWindow = window;
414
415                if (iframeUnderCursor || window.top !== window)
416                    return cursor.move(message.x, message.y);
417
418                return null;
419            });
420    }
421
422    run () {
423        return getElementUnderCursor()
424            .then(topElement => {
425                this.dragElement = this.holdLeftButton ? topElement : null;
426
427                const draggable = findDraggableElement(this.dragElement);
428
429                // NOTE: we should skip simulating drag&drop's native behavior if the mousedown event was prevented (GH - 2529)
430                if (draggable && featureDetection.hasDataTransfer && !this.skipDefaultDragBehavior) {
431                    this.dragAndDropState.enabled      = true;
432                    this.dragElement                   = draggable;
433                    this.dragAndDropState.element      = this.dragElement;
434                    this.dragAndDropState.dataStore    = new DragDataStore();
435                    this.dragAndDropState.dataTransfer = new DataTransfer(this.dragAndDropState.dataStore);
436
437                    const isLink = domUtils.isAnchorElement(this.dragElement);
438
439                    if (isLink || domUtils.isImgElement(this.dragElement)) {
440                        const srcAttr   = isLink ? 'href' : 'src';
441                        const parsedUrl = urlUtils.parseProxyUrl(this.dragElement[srcAttr]);
442                        const src       = parsedUrl ? parsedUrl.destUrl : this.dragElement[srcAttr];
443                        const outerHTML = htmlUtils.cleanUpHtml(nativeMethods.elementOuterHTMLGetter.call(this.dragElement));
444
445                        this.dragAndDropState.dataTransfer.setData('text/plain', src);
446                        this.dragAndDropState.dataTransfer.setData('text/uri-list', src);
447                        this.dragAndDropState.dataTransfer.setData('text/html', outerHTML);
448                    }
449                }
450
451                return this._scroll();
452            })
453            .then(() => {
454                const { x, y }     = this._getTargetClientPoint();
455                const windowWidth  = styleUtils.getWidth(window);
456                const windowHeight = styleUtils.getHeight(window);
457
458                if (x >= 0 && x <= windowWidth && y >= 0 && y <= windowHeight) {
459                    this.endPoint = { x, y };
460
461                    return this
462                        ._moveToCurrentFrame()
463                        .then(() => this._move());
464                }
465
466                return null;
467            })
468            .then(() => this.dragAndDropState);
469    }
470}
471
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Testcafe on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)