How to use element.getRect method in Appium

Best JavaScript code snippet using appium

Run Appium automation tests on LambdaTest cloud grid

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

intro.js

Source: intro.js Github

copy
1define(function (require, exports, module) {
2
3    'use strict';
4
5    // Libraries
6    var _         = require('underscore');
7    var Vector2   = require('common/math/vector2');
8    var Rectangle = require('common/math/rectangle');
9
10    // Project dependiencies
11    var FixedIntervalSimulation     = require('common/simulation/fixed-interval-simulation');
12    var PiecewiseCurve              = require('common/math/piecewise-curve');
13    var Air                         = require('models/air');
14    var Beaker                      = require('models/element/beaker');
15    var BeakerContainer             = require('models/element/beaker-container');
16    var Burner                      = require('models/element/burner');
17    var Block                       = require('models/element/block');
18    var Brick                       = require('models/element/brick');
19    var IronBlock                   = require('models/element/iron-block');
20    var ElementFollowingThermometer = require('models/element/element-following-thermometer');
21
22    // Constants
23    var Constants = require('constants');
24
25    /**
26     * 
27     */
28    var IntroSimulation = FixedIntervalSimulation.extend({
29
30        defaults: _.extend(FixedIntervalSimulation.prototype.defaults, {}),
31        
32        
33        initialize: function(attributes, options) {
34
35            options = options || {};
36            options.framesPerSecond = Constants.FRAMES_PER_SECOND;
37
38            FixedIntervalSimulation.prototype.initialize.apply(this, arguments);
39        },
40
41        /**
42         * Sets up all the models necessary for the simulation.
43         */
44        initComponents: function() {
45            // Burners
46            this.rightBurner = new Burner({ position: new Vector2(0.18, 0) });
47            this.leftBurner  = new Burner({ position: new Vector2(0.08, 0) });
48
49            // Moveable thermal model objects
50            this.brick     = new Brick(    { position: new Vector2(-0.1,   0) });
51            this.ironBlock = new IronBlock({ position: new Vector2(-0.175, 0) });
52
53            this.beaker = new BeakerContainer({ 
54                position: new Vector2(-0.015, 0), 
55                potentiallyContainedObjects: [
56                    this.brick,
57                    this.ironBlock
58                ],
59                width:  IntroSimulation.BEAKER_WIDTH,
60                height: IntroSimulation.BEAKER_HEIGHT
61            });
62
63            // Thermometers
64            this.thermometers = [];
65            for (var i = 0; i < IntroSimulation.NUM_THERMOMETERS; i++) {
66                var thermometer = new ElementFollowingThermometer({
67                    position: IntroSimulation.INITIAL_THERMOMETER_LOCATION,
68                    active: false
69                }, {
70                    elementLocator: this
71                });
72
73                this.listenTo(thermometer, 'change:sensedElement', this.thermometerSensedElementChanged);
74
75                this.thermometers.push(thermometer);
76            }
77
78            // Air
79            this.air = new Air();
80
81            // Element groups
82            this.movableElements = [
83                this.ironBlock,
84                this.brick,
85                this.beaker
86            ];
87            this.supportingSurfaces = [
88                this.leftBurner,
89                this.rightBurner,
90                this.brick,
91                this.ironBlock,
92                this.beaker
93            ];
94            this.burners = [
95                this.leftBurner,
96                this.rightBurner
97            ];
98            this.blocks = [
99                this.brick,
100                this.ironBlock
101            ];
102
103            // List of all objects that need updating
104            this.models = _.flatten([
105                this.air,
106                this.supportingSurfaces,
107                this.thermometers
108            ]);
109
110            // Cached objects
111            this._location   = new Vector2();
112            this._pointAbove = new Vector2();
113            this._translation = new Vector2();
114            this._allowedTranslation = new Vector2();
115            this._initialMotionConstraints = new Rectangle();
116            this._burnerBlockingRect = new Rectangle();
117            this._beakerLeftSide = new Rectangle();
118            this._beakerRightSide = new Rectangle();
119            this._beakerBottom = new Rectangle();
120            this._testRect = new Rectangle();
121
122            // Just for debugging
123            this.leftBurner.cid = 'leftBurner';
124            this.rightBurner.cid = 'rightBurner';
125            this.brick.cid = 'brick';
126            this.ironBlock.cid = 'ironBlock';
127            this.beaker.cid = 'beaker';
128            this.air.cid = 'air';
129        },
130
131        /**
132         * This is called on a reset to set the simulation
133         *   components back to defaults.  The inherited 
134         *   behavior is to just call initComponents, but
135         *   since we want to manually reset each component 
136         *   in this simulation instead of clearing them 
137         *   out and starting over, we override this
138         *   function.
139         */
140        resetComponents: function() {
141            this.air.reset();
142            this.leftBurner.reset();
143            this.rightBurner.reset();
144            this.ironBlock.reset();
145            this.brick.reset();
146            this.beaker.reset();
147            _.each(this.thermometers, function(thermometer){
148                thermometer.reset();
149            });
150        },
151
152        /**
153         * Resets the playback speed to normal.
154         */
155        resetTimeScale: function() {
156            this.set('timeScale', 1);
157        },
158
159        /**
160         * 
161         */
162        fastForward: function() {
163            this.set('timeScale', Constants.FAST_FORWARD_TIMESCALE);
164        },
165
166        /**
167         * 
168         */
169        _update: function(time, deltaTime) {
170            // For the time slider and anything else relying on time
171            this.set('time', time);
172
173            // Reposition elements with physics/snapping
174            this._findSupportingSurfaces(time, deltaTime);
175
176            // Update the fluid level in the beaker, which could be displaced by
177            //   one or more of the blocks.
178            this.beaker.updateFluidLevel([
179                this.brick.getRect(), 
180                this.ironBlock.getRect()
181            ]);
182
183            // Exchange energy between objects
184            this._exchangeEnergy(time, deltaTime);
185
186            for (var i = 0; i < this.models.length; i++)
187                this.models[i].update(time, deltaTime);
188        },
189
190        /**
191         * PhET Original explanation: 
192         *   "Cause any user-movable model elements that are not supported by a
193         *      surface to fall (or, in some cases, jump up) towards the nearest
194         *      supporting surface."
195         */
196        _findSupportingSurfaces: function(time, deltaTime) {
197            for (var i = 0; i < this.movableElements.length; i++) {
198                var element = this.movableElements[i];
199
200                // If the user is moving it, do nothing; if it's already at rest, do nothing.
201                if (!element.get('userControlled') && !element.getSupportingSurface() && element.get('position').y !== 0) {
202                    var minYPos = 0;
203                    
204                    // Determine whether there is something below this element that
205                    //   it can land upon.
206                    var potentialSupportingSurface = this.findBestSupportSurface(element);
207                    if (potentialSupportingSurface) {
208                        minYPos = potentialSupportingSurface.yPos;
209
210                        // Center the element above its new parent
211                        var targetX = potentialSupportingSurface.getCenterX();
212                        //console.log('setting x');
213                        element.setX(targetX);
214                        //console.log('done setting x');
215                    }
216                    
217                    // Calculate a proposed Y position based on gravitational falling.
218                    var acceleration = -9.8; // meters/s*s
219                    var velocity = element.get('verticalVelocity') + acceleration * deltaTime;
220                    var proposedYPos = element.get('position').y + velocity * deltaTime;
221                    if (proposedYPos < minYPos) {
222                        // The element has landed on the ground or some other surface.
223                        proposedYPos = minYPos;
224                        element.set('verticalVelocity', 0);
225                        if (potentialSupportingSurface) {
226                            element.setSupportingSurface(potentialSupportingSurface);
227                            potentialSupportingSurface.addElementToSurface(element);
228                        }
229                    }
230                    else {
231                        element.set('verticalVelocity', velocity);
232                    }
233                    element.setY(proposedYPos);
234                }
235            }
236        },
237
238        /**
239         * 
240         */
241        _exchangeEnergy: function(time, deltaTime) {
242            var i;
243            var j;
244            var burner;
245            var element;
246            var otherElement;
247            var chunk;
248
249            /**
250             *  Note: The original intent was to design all the energy containers
251             *   such that the order of the exchange didn't matter, nor who was
252             *   exchanging with whom.  This turned out to be a lot of extra work to
253             *   maintain, and was eventually abandoned.  So, the order and nature of
254             *   the exchanged below should be maintained unless there is a good
255             *   reason not to, and any changes should be well tested.
256             */
257
258            // Loop through all the movable thermal energy containers and have them
259            //   exchange energy with one another.
260            for (i = 0; i < this.movableElements.length - 1; i++) {
261                for (j = i + 1; j < this.movableElements.length; j++) {
262                    this.movableElements[i].exchangeEnergyWith(this.movableElements[j], deltaTime);
263                }
264            }
265
266            // Exchange thermal energy between the burners and the other thermal
267            //   model elements, including air.
268            for (i = 0; i < this.burners.length; i++) {
269                burner = this.burners[i];
270                if (burner.areAnyOnTop(this.movableElements)) {
271                    for (j = 0; j < this.movableElements.length; j++) {
272                        burner.addOrRemoveEnergyToFromObject(this.movableElements[j], deltaTime);
273                    }
274                }
275                else {
276                    burner.addOrRemoveEnergyToFromAir(this.air, deltaTime);
277                }
278            }
279
280            // Exchange energy chunks between burners and non-air energy containers.
281            for (i = 0; i < this.burners.length; i++) {
282                burner = this.burners[i];
283                for (j = 0; j < this.movableElements.length; j++) {
284                    element = this.movableElements[j];
285                    if (burner.inContactWith(element)) {
286                        if (burner.canSupplyEnergyChunk() && (burner.getEnergyChunkBalanceWithObjects() > 0 || element.getEnergyChunkBalance() < 0)) {
287                            // Push an energy chunk into the item on the burner.
288                            element.addEnergyChunk(burner.extractClosestEnergyChunk(element.getCenterPoint()));
289                        }
290                        else if (burner.canAcceptEnergyChunk() && (burner.getEnergyChunkBalanceWithObjects() < 0 || element.getEnergyChunkBalance() > 0)) {
291                            // Extract an energy chunk from the model element.
292                            chunk = element.extractClosestEnergyChunk(burner.getFlameIceRect());
293                            if (chunk)
294                                burner.addEnergyChunk(chunk);
295                        }
296                    }
297                }
298            }
299
300            // Exchange energy chunks between movable thermal energy containers.
301            var elem1;
302            var elem2;
303            for (i = 0; i < this.movableElements.length - 1; i++) {
304                for (j = i + 1; j < this.movableElements.length; j++) {
305                    elem1 = this.movableElements[i];
306                    elem2 = this.movableElements[j];
307                    if (elem1.getThermalContactArea().getThermalContactLength(elem2.getThermalContactArea()) > 0) {
308                        // Exchange chunks if appropriate
309                        if (elem1.getEnergyChunkBalance() > 0 && elem2.getEnergyChunkBalance() < 0)
310                            elem2.addEnergyChunk(elem1.extractClosestEnergyChunk(elem2.getThermalContactArea().getBounds()));
311                        else if (elem1.getEnergyChunkBalance() < 0 && elem2.getEnergyChunkBalance() > 0)
312                            elem1.addEnergyChunk(elem2.extractClosestEnergyChunk(elem1.getThermalContactArea().getBounds()));
313                    }
314                }
315            }
316
317            // Patrick's note: I have no idea why we're exchanging chunks between movable elements twice.
318
319            // Exchange energy and energy chunks between the movable thermal
320            //   energy containers and the air.
321            for (i = 0; i < this.movableElements.length; i++) {
322                element = this.movableElements[i];
323                // Set up some variables that are used to decide whether or not
324                //   energy should be exchanged with air.
325                var contactWithOtherMovableElement = false;
326                var immersedInBeaker = false;
327                var maxTemperatureDifference = 0;
328
329                // Figure out the max temperature difference between touching
330                //   energy containers.
331                for (j = 0; j < this.movableElements.length; j++) {
332                    otherElement = this.movableElements[j];
333
334                    if (element === otherElement)
335                        break;
336
337                    if (element.getThermalContactArea().getThermalContactLength(otherElement.getThermalContactArea()) > 0) {
338                        contactWithOtherMovableElement = true;
339                        maxTemperatureDifference = Math.max(Math.abs(element.getTemperature() - otherElement.getTemperature()), maxTemperatureDifference);
340                    }
341                }
342
343                if (this.beaker.getThermalContactArea().getBounds().contains(element.getRect())) {
344                    // This model element is immersed in the beaker.
345                    immersedInBeaker = true;
346                }
347
348                // Exchange energy and energy chunks with the air if appropriate
349                //   conditions met.
350                if (!contactWithOtherMovableElement || (
351                        !immersedInBeaker && (
352                            maxTemperatureDifference < IntroSimulation.MIN_TEMPERATURE_DIFF_FOR_MULTI_BODY_AIR_ENERGY_EXCHANGE ||
353                            element.getEnergyBeyondMaxTemperature() > 0
354                        )
355                    )
356                ) {
357                    this.air.exchangeEnergyWith(element, deltaTime);
358                    if (element.getEnergyChunkBalance() > 0) {
359                        var pointAbove = this._pointAbove.set(
360                            Math.random() * element.getRect().w + element.getRect().left(),
361                            element.getRect().top()
362                        );
363                        chunk = element.extractClosestEnergyChunk(pointAbove);
364                        if (chunk) {
365                            //console.log('(' + element.cid + ') giving chunk to air');
366                            var initialMotionConstraints = null;
367                            if (element instanceof Beaker) {
368                                // Constrain the energy chunk's motion so that it
369                                // doesn't go through the edges of the beaker.
370                                // There is a bit of a fudge factor in here to
371                                // make sure that the sides of the energy chunk,
372                                // and not just the center, stay in bounds.
373                                var energyChunkWidth = 0.01;
374                                initialMotionConstraints = this._initialMotionConstraints.set( 
375                                    element.getRect().x + energyChunkWidth / 2,
376                                    element.getRect().y,
377                                    element.getRect().w - energyChunkWidth,
378                                    element.getRect().h 
379                                );
380                            }
381                            this.air.addEnergyChunk(chunk, initialMotionConstraints);
382                        }
383                    }
384                    else if (element.getEnergyChunkBalance() < 0 && element.getTemperature() < this.air.getTemperature()) {
385                        element.addEnergyChunk(this.air.requestEnergyChunk(element.getCenterPoint()));
386                    }
387                }
388            }
389
390            // Exchange energy chunks between the air and the burners.
391            for (i = 0; i < this.burners.length; i++) {
392                burner = this.burners[i];
393                var energyChunkCountForAir = burner.getEnergyChunkCountForAir();
394                if (energyChunkCountForAir > 0)
395                    this.air.addEnergyChunk(burner.extractClosestEnergyChunk(burner.getCenterPoint()), null);
396                else if (energyChunkCountForAir < 0)
397                    burner.addEnergyChunk(this.air.requestEnergyChunk(burner.getCenterPoint()));
398            }
399        },
400
401        /**
402         * Validate the position being proposed for the given model element.  This
403         * evaluates whether the proposed position would cause the model element
404         * to move through another solid element, or the side of the beaker, or
405         * something that would look weird to the user and, if so, prevent the odd
406         * behavior from happening by returning a location that works better.
407         *
408         * @param element         Element whose position is being validated.
409         * @param proposedPosition Proposed new position for element
410         * @return The original proposed position if valid, or alternative position
411         *         if not.
412         */
413        validatePosition: function(element, proposedPosition) {
414            // Compensate for the element's center X position
415            var translation = this._translation
416                .set(proposedPosition)
417                .sub(element.get('position'));
418
419            // Figure out how far the block's right edge appears to protrude to
420            //   the side due to perspective.
421            var blockPerspectiveExtension = Block.SURFACE_WIDTH * Constants.BlockView.PERSPECTIVE_EDGE_PROPORTION * Math.cos(Constants.BlockView.PERSPECTIVE_ANGLE) / 2;
422
423            // Validate against burner boundaries.  Treat the burners as one big
424            //   blocking rectangle so that the user can't drag things between
425            //   them.  Also, compensate for perspective so that we can avoid
426            //   difficult z-order issues.
427            var standPerspectiveExtension = this.leftBurner.getOutlineRect().h * Burner.EDGE_TO_HEIGHT_RATIO * Math.cos(Constants.BurnerStandView.PERSPECTIVE_ANGLE) / 2;
428            var burnerRectX = this.leftBurner.getOutlineRect().x - standPerspectiveExtension - (element !== this.beaker ? blockPerspectiveExtension : 0);
429            var burnerBlockingRect = this._burnerBlockingRect.set( 
430                burnerRectX,
431                this.leftBurner.getOutlineRect().y,
432                this.rightBurner.getOutlineRect().right() - burnerRectX,
433                this.leftBurner.getOutlineRect().h
434            );
435            translation = this.determineAllowedTranslation(element.getRect(), burnerBlockingRect, translation, false);
436
437            // Validate against the sides of the beaker.
438            if (element !== this.beaker) {
439                // Create three rectangles to represent the two sides and the top
440                //   of the beaker.
441                var testRectThickness = 1E-3; // 1 mm thick walls.
442                var beakerRect = this.beaker.getRect();
443                var beakerLeftSide = this._beakerLeftSide.set(
444                    beakerRect.left() - blockPerspectiveExtension,
445                    this.beaker.getRect().bottom(),
446                    testRectThickness + blockPerspectiveExtension * 2,
447                    this.beaker.getRect().h + blockPerspectiveExtension
448                );
449                var beakerRightSide = this._beakerRightSide.set(
450                    this.beaker.getRect().right() - testRectThickness - blockPerspectiveExtension,
451                    this.beaker.getRect().bottom(),
452                    testRectThickness + blockPerspectiveExtension * 2,
453                    this.beaker.getRect().h + blockPerspectiveExtension
454                );
455                var beakerBottom = this._beakerBottom.set(
456                    this.beaker.getRect().left(), 
457                    this.beaker.getRect().bottom(), 
458                    this.beaker.getRect().w, 
459                    testRectThickness
460                );
461
462                // Do not restrict the model element's motion in positive Y
463                //   direction if the beaker is sitting on top of the model 
464                //   element - the beaker will simply be lifted up.
465                var restrictPositiveY = !this.beaker.isStackedUpon(element);
466
467                // Clamp the translation based on the beaker position.
468                translation = this.determineAllowedTranslation(element.getRect(), beakerLeftSide,  translation, restrictPositiveY);
469                translation = this.determineAllowedTranslation(element.getRect(), beakerRightSide, translation, restrictPositiveY);
470                translation = this.determineAllowedTranslation(element.getRect(), beakerBottom,    translation, restrictPositiveY);
471            }
472
473            // Now check the model element's motion against each of the blocks.
474            for (var i = 0; i < this.blocks.length; i++) {
475                var block = this.blocks[i];
476
477                if (element === block)
478                    continue;
479
480                // Do not restrict the model element's motion in positive Y
481                //   direction if the tested block is sitting on top of the model
482                //   element - the block will simply be lifted up.
483                var restrictPositiveY = !block.isStackedUpon(element);
484
485                var testRect = this._testRect.set(element.getRect());
486                if (element === this.beaker) {
487                    // Special handling for the beaker - block it at the outer
488                    // edge of the block instead of the center in order to
489                    // simplify z-order handling.
490                    testRect.set( 
491                        testRect.x - blockPerspectiveExtension,
492                        testRect.y,
493                        testRect.w + blockPerspectiveExtension * 2,
494                        testRect.h
495                    );
496                }
497
498                // Clamp the translation based on the test block's position, but
499                //   handle the case where the block is immersed in the beaker.
500                if (element !== this.beaker || !this.beaker.getRect().contains(block.getRect())) {
501                    translation = this.determineAllowedTranslation(testRect, block.getRect(), translation, restrictPositiveY);
502                }
503            }
504
505            // Determine the new position based on the resultant translation and return it.
506            return translation.add(element.get('position'));
507        },
508
509        /*
510         * Determine the portion of a proposed translation that may occur given
511         * a moving rectangle and a stationary rectangle that can block the moving
512         * one.
513         *
514         * @param movingRect
515         * @param stationaryRect
516         * @param proposedTranslation
517         * @param restrictPosY        Boolean that controls whether the positive Y
518         *                            direction is restricted.  This is often set
519         *                            false if there is another model element on
520         *                            top of the one being tested.
521         * @return
522         */
523        determineAllowedTranslation: function(movingRect, stationaryRect, proposedTranslation, restrictPosY) {
524            var translation;
525
526            translation = this.checkOverlapOnProposedTranslation(movingRect, stationaryRect, proposedTranslation, restrictPosY);
527            
528            if (translation)
529                return translation;
530
531            translation = this.checkCollisionsOnProposedTranslation(movingRect, stationaryRect, proposedTranslation, restrictPosY);
532
533            return translation;
534        },
535
536        checkOverlapOnProposedTranslation: function(movingRect, stationaryRect, proposedTranslation, restrictPosY) {
537            var translation = this._allowedTranslation;
538
539            // Test for case where rectangles already overlap.
540            if (movingRect.overlaps(stationaryRect)) {
541                // The rectangles already overlap.  Are they right on top of one another?
542                if (movingRect.center().x === stationaryRect.center().x && movingRect.center().x === stationaryRect.center().x) {
543                    console.error('IntroSimulation - Warning: Rectangle centers in same location--returning zero vector.');
544                    return translation.set(0, 0);
545                }
546
547                // Determine the motion in the X & Y directions that will "cure"
548                //   the overlap.
549                var xOverlapCure = 0;
550                if (movingRect.right() > stationaryRect.left() && movingRect.left() < stationaryRect.left()) {
551                    xOverlapCure = stationaryRect.left() - movingRect.right();
552                }
553                else if (stationaryRect.right() > movingRect.left() && stationaryRect.left() < movingRect.left()) {
554                    xOverlapCure = stationaryRect.right() - movingRect.left();
555                }
556                var yOverlapCure = 0;
557                if (movingRect.top() > stationaryRect.bottom() && movingRect.bottom() < stationaryRect.bottom()) {
558                    yOverlapCure = stationaryRect.bottom() - movingRect.top();
559                }
560                else if ( stationaryRect.top() > movingRect.bottom() && stationaryRect.bottom() < movingRect.bottom()) {
561                    yOverlapCure = stationaryRect.top() - movingRect.bottom();
562                }
563
564                // Something is wrong with algorithm if both values are zero,
565                //   since overlap was detected by the "intersects" method.
566                if (xOverlapCure === 0 && yOverlapCure === 0)
567                    return;
568
569                // Return a vector with the smallest valid "cure" value, leaving
570                //   the other translation value unchanged.
571                if (xOverlapCure !== 0 && Math.abs(xOverlapCure) < Math.abs(yOverlapCure)) {
572                    return translation.set(xOverlapCure, proposedTranslation.y);
573                }
574                else {
575                    return translation.set(proposedTranslation.x, yOverlapCure);
576                }
577            }
578        },
579
580        checkCollisionsOnProposedTranslation: function(movingRect, stationaryRect, proposedTranslation, restrictPosY) {
581            var translation = this._allowedTranslation;
582
583            var xTranslation = proposedTranslation.x;
584            var yTranslation = proposedTranslation.y;
585
586            // X direction.
587            if (proposedTranslation.x > 0) {
588                // Check for collisions moving right.
589                var rightEdgeSmear = this.projectShapeFromLine(movingRect.right(), movingRect.bottom(), movingRect.right(), movingRect.top(), proposedTranslation);
590
591                if (movingRect.right() <= stationaryRect.left() && rightEdgeSmear.intersects(stationaryRect)) {
592                    // Collision detected, limit motion.
593                    xTranslation = stationaryRect.left() - movingRect.right() - IntroSimulation.MIN_INTER_ELEMENT_DISTANCE;
594                }
595            }
596            else if (proposedTranslation.x < 0) {
597                // Check for collisions moving left.
598                var leftEdgeSmear = this.projectShapeFromLine(movingRect.left(), movingRect.bottom(), movingRect.left(), movingRect.top(), proposedTranslation);
599
600                if (movingRect.left() >= stationaryRect.right() && leftEdgeSmear.intersects(stationaryRect)) {
601                    // Collision detected, limit motion.
602                    xTranslation = stationaryRect.right() - movingRect.left() + IntroSimulation.MIN_INTER_ELEMENT_DISTANCE;
603                }
604            }
605
606            // Y direction.
607            if (proposedTranslation.y > 0 && restrictPosY) {
608                // Check for collisions moving up.
609                var topEdgeSmear = this.projectShapeFromLine(movingRect.left(), movingRect.top(), movingRect.right(), movingRect.top(), proposedTranslation);
610
611                if (movingRect.top() <= stationaryRect.bottom() && topEdgeSmear.intersects(stationaryRect)) {
612                    // Collision detected, limit motion.
613                    yTranslation = stationaryRect.bottom() - movingRect.top() - IntroSimulation.MIN_INTER_ELEMENT_DISTANCE;
614                }
615            }
616            if (proposedTranslation.y < 0) {
617                // Check for collisions moving down.
618                var bottomEdgeSmear = this.projectShapeFromLine(movingRect.left(), movingRect.bottom(), movingRect.right(), movingRect.bottom(), proposedTranslation);
619
620                if (movingRect.bottom() >= stationaryRect.top() && bottomEdgeSmear.intersects(stationaryRect)) {
621                    // Collision detected, limit motion.
622                    yTranslation = stationaryRect.top() - movingRect.bottom() + IntroSimulation.MIN_INTER_ELEMENT_DISTANCE;
623                }
624            }
625
626            return translation.set(xTranslation, yTranslation);
627        },
628
629        projectShapeFromLine: function(x1, y1, x2, y2, projection) {
630            var curve = new PiecewiseCurve();
631            curve.moveTo(x1, y1);
632            curve.lineTo(x1 + projection.x, y1 + projection.y);
633            curve.lineTo(x2 + projection.x, y2 + projection.y);
634            curve.lineTo(x2, y2);
635            curve.close();
636            return curve;
637        },
638
639        /**
640         * Finds the most appropriate supporting surface for the element.
641         */
642        findBestSupportSurface: function(element) {
643            var bestOverlappingSurface = null;
644
645            // Check each of the possible supporting elements in the model to see
646            //   if this element can go on top of it.
647            for (var i = 0; i < this.supportingSurfaces.length; i++) {
648                var potentialSupportingElement = this.supportingSurfaces[i];
649
650                if (potentialSupportingElement === element || potentialSupportingElement.isStackedUpon(element)) {
651                    // The potential supporting element is either the same as the
652                    //   test element or is sitting on top of the test element.  In
653                    //   either case, it can't be used to support the test element,
654                    //   so skip it.
655                    continue;
656                }
657
658                if (element.getBottomSurface().overlapsWith( potentialSupportingElement.getTopSurface())) {
659                    // There is at least some overlap.  Determine if this surface
660                    //   is the best one so far.
661                    var surfaceOverlap = this.getHorizontalOverlap(potentialSupportingElement.getTopSurface(), element.getBottomSurface());
662                    
663                    // The following nasty 'if' clause determines if the potential
664                    //   supporting surface is a better one than we currently have
665                    //   based on whether we have one at all, or has more overlap
666                    //   than the previous best choice, or is directly above the
667                    //   current one.
668                    if (bestOverlappingSurface === null || (
669                            surfaceOverlap > this.getHorizontalOverlap(bestOverlappingSurface, element.getBottomSurface()) && 
670                            !this.isDirectlyAbove(bestOverlappingSurface, potentialSupportingElement.getTopSurface())
671                        ) || (
672                            this.isDirectlyAbove(potentialSupportingElement.getTopSurface(), bestOverlappingSurface)
673                        )) {
674                        bestOverlappingSurface = potentialSupportingElement.getTopSurface();
675                    }
676                }
677            }
678
679            // Make sure that the best supporting surface isn't at the bottom of
680            //   a stack, which can happen in cases where the model element being
681            //   tested isn't directly above the best surface's center.
682            if (bestOverlappingSurface) {
683                while (bestOverlappingSurface.elementOnSurface !== null ) {
684                    bestOverlappingSurface = bestOverlappingSurface.elementOnSurface.getTopSurface();
685                }
686            }
687
688            return bestOverlappingSurface;
689        },
690
691        /**
692         * Get the amount of overlap in the x direction between two horizontal surfaces.
693         */
694        getHorizontalOverlap: function(s1, s2) {
695            var lowestMax  = Math.min(s1.xMax, s2.xMax);
696            var highestMin = Math.max(s1.xMin, s2.xMin);
697            return Math.max(lowestMax - highestMin, 0);
698        },
699        
700        /**
701         * Returns true if surface s1's center is above surface s2.
702         */
703        isDirectlyAbove: function(s1, s2) {
704            return s2.containsX(s1.getCenterX()) && s1.yPos > s2.yPos;
705        },
706
707        /**
708         * This replaces EFACIntroModel.getTemperatureAndColorAtLocation because
709         *   I believe it should be the job of the element model to internally
710         *   decide what its temperature should be, and it should be up to the
711         *   element view to determine the color.  Therefore, the simulation
712         *   model will only return the element, and objects that use this will
713         *   be responsible for requesting the temperature and color at location
714         *   from the returned element.
715         */
716        getElementAtLocation: function(x, y) {
717            var location = this._location;
718            if (x instanceof Vector2)
719                location.set(x);
720            else
721                location.set(x, y);
722
723            // Test blocks first.  This is a little complicated since the z-order
724            //   must be taken into account.
725            this.blocks.sort(function(b1, b2) {
726                if (b1.get('position').equals(b2.get('position')))
727                    return 0;
728                if (b2.get('position').x > b1.get('position').x || b2.get('position').y > b1.get('position').y)
729                    return 1;
730                return -1;
731            });
732
733            for (var i = 0; i < this.blocks.length; i++) {
734                if (this.blocks[i].getProjectedShape().contains(location))
735                    return this.blocks[i];
736            }
737            
738            // Test if this point is in the water or steam associated with the beaker.
739            if (this.beaker.getThermalContactArea().getBounds().contains(location) ||
740                (this.beaker.getSteamArea().contains(location) && this.beaker.get('steamingProportion') > 0)) {
741                return this.beaker;
742            }
743            
744            // Test if the point is a burner.
745            for (var j = 0; j < this.burners.length; j++) {
746                if (this.burners[j].getFlameIceRect().contains(location))
747                    return this.burners[j];
748            }
749
750            // Point is in nothing else, so return the air.
751            return this.air;
752        },
753
754        getBlockList: function() {
755            return this.blocks;
756        },
757
758        getBeaker: function() {
759            return this.beaker;
760        },
761
762        thermometerSensedElementChanged: function(thermometer, element) {
763            var blockWidthIncludingPerspective = this.ironBlock.getProjectedShape().getBounds().w;
764            var beakerLeft  = this.beaker.getRect().center().x - blockWidthIncludingPerspective / 2;
765            var beakerRight = this.beaker.getRect().center().x + blockWidthIncludingPerspective / 2;
766            var thermometerX = thermometer.get('position').x;
767            if (thermometer.previous('sensedElement') === this.beaker && 
768                !thermometer.get('userControlled') && 
769                thermometerX >= beakerLeft && 
770                thermometerX <= beakerRight
771            ) {
772                thermometer.set('userControlled', true);
773                thermometer.setPosition(this.beaker.getRect().right() - 0.01, this.beaker.getRect().bottom() + this.beaker.getRect().h * 0.33);
774                thermometer.set('userControlled', false);
775            }
776        }
777
778    }, Constants.IntroSimulation);
779
780    return IntroSimulation;
781});
782
Full Screen

5_chessboard_interaction.js

Source: 5_chessboard_interaction.js Github

copy
1/******************************************************************************
2 *                                                                            *
3 *    This file is part of Kokopu-React, a JavaScript chess library.          *
4 *    Copyright (C) 2021-2022  Yoann Le Montagner <yo35 -at- melix.net>       *
5 *                                                                            *
6 *    This program is free software: you can redistribute it and/or           *
7 *    modify it under the terms of the GNU Lesser General Public License      *
8 *    as published by the Free Software Foundation, either version 3 of       *
9 *    the License, or (at your option) any later version.                     *
10 *                                                                            *
11 *    This program is distributed in the hope that it will be useful,         *
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of          *
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
14 *    GNU Lesser General Public License for more details.                     *
15 *                                                                            *
16 *    You should have received a copy of the GNU Lesser General               *
17 *    Public License along with this program. If not, see                     *
18 *    <http://www.gnu.org/licenses/>.                                         *
19 *                                                                            *
20 ******************************************************************************/
21
22
23const { openBrowser, closeBrowser, itCustom, setSandbox, compareSandbox, takeScreenshot, compareScreenshot } = require('./common/graphic');
24
25
26describe('Chessboard interaction', function() {
27
28	const browserContext = {};
29
30	before(async function() {
31		await openBrowser(this, browserContext);
32	});
33
34	after(async function() {
35		await closeBrowser(browserContext);
36	});
37
38	function itCheckClickSquare(itemIndex, label, targets) {
39		itCustom(browserContext, '07_chessboard_click_squares', itemIndex, label, async function(element) {
40			let actions = browserContext.driver.actions({ async: true });
41			let area = await element.getRect();
42			for (let i = 0; i < targets.length; ++i) {
43				let target = targets[i];
44				await actions.move({ x: area.x + target.x, y: area.y + target.y }).click().perform();
45				await compareSandbox(browserContext, target.expectedText);
46			}
47		});
48	}
49
50	itCheckClickSquare(0, 'default', [
51		{ x: 225, y: 75, expectedText: 'square clicked: e7' },
52		{ x: 25, y: 375, expectedText: 'square clicked: a1' },
53		{ x: 325, y: 175, expectedText: 'square clicked: g5' },
54	]);
55	itCheckClickSquare(1, 'with flip', [
56		{ x: 325, y: 175, expectedText: 'square clicked: b4' },
57	]);
58	itCheckClickSquare(2, 'over annotations', [
59		{ x: 25, y: 25, expectedText: 'square clicked: a8' },
60		{ x: 75, y: 125, expectedText: 'square clicked: b6' },
61		{ x: 175, y: 125, expectedText: 'square clicked: d6' },
62		{ x: 75, y: 225, expectedText: 'square clicked: b4' },
63	]);
64
65	function itCheckMovePiece(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName, expectedText) {
66		itCustom(browserContext, '08_chessboard_move_pieces', itemIndex, label, async function(element) {
67			let actions = browserContext.driver.actions({ async: true });
68			let area = await element.getRect();
69			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
70			await takeScreenshot(browserContext, imageBaseName, element);
71			await actions.release().perform();
72			await compareScreenshot(browserContext, imageBaseName);
73			await compareSandbox(browserContext, expectedText);
74		});
75	}
76
77	itCheckMovePiece(0, 'over empty', 275, 385, 225, 175, 'over_empty', 'piece moved: f1 -> e5');
78	itCheckMovePiece(0, 'over non-empty 1', 280, 365, 75, 75, 'over_non_empty_1', 'piece moved: f1 -> b7');
79	itCheckMovePiece(0, 'over non-empty 2', 130, 75, 225, 375, 'over_non_empty_2', 'piece moved: c7 -> e1');
80	itCheckMovePiece(1, 'over square marker', 10, 325, 275, 175, 'over_square_marker', 'piece moved: h7 -> c4');
81	itCheckMovePiece(1, 'over text marker', 225, 25, 10, 135, 'over_text_marker', 'piece moved: d1 -> h3');
82	itCheckMovePiece(1, 'over arrow marker', 325, 25, 315, 260, 'over_arrow_marker', 'piece moved: b1 -> b6');
83	itCheckMovePiece(2, 'after move', 225, 225, 75, 260, 'after_move', 'piece moved: e4 -> b3');
84
85	function itCheckNonMovePiece(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName) {
86		itCustom(browserContext, '08_chessboard_move_pieces', itemIndex, label, async function(element) {
87			await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
88			let actions = browserContext.driver.actions({ async: true });
89			let area = await element.getRect();
90			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
91			await takeScreenshot(browserContext, imageBaseName, element);
92			await actions.release().perform();
93			await compareScreenshot(browserContext, imageBaseName);
94			await compareSandbox(browserContext, imageBaseName);
95		});
96	}
97
98	itCheckNonMovePiece(0, 'move empty square', 175, 225, 275, 75, 'empty_square');
99	itCheckNonMovePiece(0, 'from == to', 75, 375, 80, 360, 'null_vector');
100	itCheckNonMovePiece(0, 'out of board', 175, 25, 500, 210, 'out_of_board');
101
102	function itCheckEditArrow(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName, expectedText) {
103		itCustom(browserContext, '09_chessboard_edit_arrows', itemIndex, label, async function(element) {
104			let actions = browserContext.driver.actions({ async: true });
105			let area = await element.getRect();
106			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
107			await takeScreenshot(browserContext, imageBaseName, element);
108			await actions.release().perform();
109			await compareScreenshot(browserContext, imageBaseName);
110			await compareSandbox(browserContext, expectedText);
111		});
112	}
113
114	itCheckEditArrow(0, 'base 1', 325, 275, 110, 140, 'base_1', 'arrow edited: g3 -> c6');
115	itCheckEditArrow(0, 'base 2', 260, 10, 175, 375, 'base_2', 'arrow edited: f8 -> d1');
116	itCheckEditArrow(1, 'over square marker', 275, 125, 275, 230, 'over_square_marker', 'arrow edited: c3 -> c5');
117	itCheckEditArrow(1, 'over arrow marker', 40, 110, 125, 290, 'over_arrow_marker', 'arrow edited: h3 -> f6');
118
119	function itCheckNonEditArrow(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName) {
120		itCustom(browserContext, '09_chessboard_edit_arrows', itemIndex, label, async function(element) {
121			await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
122			let actions = browserContext.driver.actions({ async: true });
123			let area = await element.getRect();
124			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
125			await takeScreenshot(browserContext, imageBaseName, element);
126			await actions.release().perform();
127			await compareScreenshot(browserContext, imageBaseName);
128			await compareSandbox(browserContext, imageBaseName);
129		});
130	}
131
132	itCheckNonEditArrow(2, 'edit color not set', 125, 175, 325, 225, 'edit_color_not_set');
133	itCheckNonEditArrow(0, 'from == to', 275, 225, 290, 210, 'null_vector');
134	itCheckNonEditArrow(0, 'out of board', 175, 225, 500, 280, 'out_of_board');
135});
136
Full Screen

6_chessboard_play_moves.js

Source: 6_chessboard_play_moves.js Github

copy
1/******************************************************************************
2 *                                                                            *
3 *    This file is part of Kokopu-React, a JavaScript chess library.          *
4 *    Copyright (C) 2021-2022  Yoann Le Montagner <yo35 -at- melix.net>       *
5 *                                                                            *
6 *    This program is free software: you can redistribute it and/or           *
7 *    modify it under the terms of the GNU Lesser General Public License      *
8 *    as published by the Free Software Foundation, either version 3 of       *
9 *    the License, or (at your option) any later version.                     *
10 *                                                                            *
11 *    This program is distributed in the hope that it will be useful,         *
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of          *
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
14 *    GNU Lesser General Public License for more details.                     *
15 *                                                                            *
16 *    You should have received a copy of the GNU Lesser General               *
17 *    Public License along with this program. If not, see                     *
18 *    <http://www.gnu.org/licenses/>.                                         *
19 *                                                                            *
20 ******************************************************************************/
21
22
23const { openBrowser, closeBrowser, itCustom, setSandbox, compareSandbox, takeScreenshot, compareScreenshot } = require('./common/graphic');
24
25
26describe('Chessboard play moves', function() {
27
28	const browserContext = {};
29
30	before(async function() {
31		await openBrowser(this, browserContext);
32	});
33
34	after(async function() {
35		await closeBrowser(browserContext);
36	});
37
38	function itCheckPlayMove(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName, expectedText) {
39		itCustom(browserContext, '10_chessboard_play_moves', itemIndex, label, async function(element) {
40			let actions = browserContext.driver.actions({ async: true });
41			let area = await element.getRect();
42			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
43			await takeScreenshot(browserContext, imageBaseName, element);
44			await actions.release().perform();
45			await compareScreenshot(browserContext, imageBaseName);
46			await compareSandbox(browserContext, expectedText);
47		});
48	}
49
50	itCheckPlayMove(0, 'regular move 1', 225, 335, 220, 225, 'regular_move_1', 'move played: e4');
51	itCheckPlayMove(1, 'regular move 2', 275, 225, 135, 85, 'regular_move_2', 'move played: Bxf2+');
52	itCheckPlayMove(1, 'castling move', 175, 375, 75, 375, 'castling_move', 'move played: O-O');
53	itCheckPlayMove(3, 'chess960 castling move 1', 275, 375, 75, 375, 'chess960_castling_move_1', 'move played: O-O-O');
54	itCheckPlayMove(3, 'chess960 castling move 2', 275, 375, 375, 375, 'chess960_castling_move_2', 'move played: O-O');
55	itCheckPlayMove(3, 'chess960 ambiguous king move', 275, 375, 320, 375, 'chess960_ambiguous_king_move', 'move played: Kg1');
56
57	function itCheckNonPlayMove(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName) {
58		itCustom(browserContext, '10_chessboard_play_moves', itemIndex, label, async function(element) {
59			await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
60			let actions = browserContext.driver.actions({ async: true });
61			let area = await element.getRect();
62			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
63			await takeScreenshot(browserContext, imageBaseName, element);
64			await actions.release().perform();
65			await compareScreenshot(browserContext, imageBaseName);
66			await compareSandbox(browserContext, imageBaseName);
67		});
68	}
69
70	itCheckNonPlayMove(2, 'illegal position', 225, 325, 225, 225, 'illegal_position');
71	itCheckNonPlayMove(0, 'wrong color moved', 75, 25, 125, 125, 'wrong_color');
72	itCheckNonPlayMove(0, 'illegal move', 75, 375, 125, 225, 'illegal_move');
73	itCheckNonPlayMove(0, 'from == to', 75, 375, 85, 365, 'null_vector');
74	itCheckNonPlayMove(0, 'out of board', 325, 375, 450, 285, 'out_of_board');
75	itCheckNonPlayMove(3, 'chess960 non-KxR castling', 275, 375, 130, 375, 'chess960_non_kxr_castling');
76
77	function itCheckPlayPromotion(itemIndex, label, xFrom, yFrom, xTo, yTo, xPromo, yPromo, imageBaseName, expectedText) {
78		itCustom(browserContext, '11_chessboard_play_promotions', itemIndex, label, async function(element) {
79			let actions = browserContext.driver.actions({ async: true });
80			let area = await element.getRect();
81			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).release().perform();
82			await takeScreenshot(browserContext, imageBaseName, element);
83			await actions.move({ x: area.x + xPromo, y: area.y + yPromo }).click().perform();
84			await compareScreenshot(browserContext, imageBaseName);
85			await compareSandbox(browserContext, expectedText);
86		});
87	}
88
89	itCheckPlayPromotion(0, 'regular promotion 1', 75, 75, 75, 25, 60, 10, 'regular_promotion_1', 'promotion move played: b8=Q');
90	itCheckPlayPromotion(1, 'regular promotion 2', 75, 325, 25, 375, 15, 280, 'regular_promotion_2', 'promotion move played: bxa1=B');
91	itCheckPlayPromotion(2, 'antichess promotion', 325, 325, 325, 375, 325, 175, 'antichess_promotion', 'promotion move played: b8=K');
92
93	function itCheckNonPlayPromotion(itemIndex, label, xFrom, yFrom, xTo, yTo, xPromo, yPromo, imageBaseName) {
94		itCustom(browserContext, '11_chessboard_play_promotions', itemIndex, label, async function(element) {
95			await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
96			let actions = browserContext.driver.actions({ async: true });
97			let area = await element.getRect();
98			await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).release().perform();
99			await takeScreenshot(browserContext, imageBaseName, element);
100			await actions.move({ x: area.x + xPromo, y: area.y + yPromo }).click().perform();
101			await compareScreenshot(browserContext, imageBaseName);
102			await compareSandbox(browserContext, imageBaseName);
103		});
104	}
105
106	itCheckNonPlayPromotion(1, 'cancel promotion', 75, 325, 75, 375, 190, 220, 'cancel_promotion');
107});
108
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 Appium 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)