How to use commitUpdate method in Playwright Internal

Best JavaScript code snippet using playwright-internal

helpers.js

Source:helpers.js Github

copy

Full Screen

1function loadSection(xmlData, startPoint, endPoint, editFunction, loopMatches, includeHeaders) {2 var firstMatchPos = null;3 // Infinite loop4 while(true) {5 // Grab the start chunk we were looking for6 var startPos = -1;7 if(typeof(startPoint) == 'string') {8 startPos = xmlData.indexOf(startPoint, firstMatchPos);9 if(startPos == -1) return xmlData;10 if(!includeHeaders) {11 startPos += startPoint.length;12 }13 } else {14 if(typeof(startPoint.exec) == 'function') {15 var match = startPoint.exec(xmlData.substr(firstMatchPos));16 if(match && match.index != -1) {17 startPos = match.index + firstMatchPos;18 if(!includeHeaders) {19 startPos += match[0].length;20 }21 }22 }23 }24 if(startPos == -1) return xmlData;25 // Grab the end chunk we were looking for26 var endPos = -1;27 if(typeof(endPoint) == 'string') {28 endPos = xmlData.indexOf(endPoint, startPos);29 if(includeHeaders) {30 endPos += endPoint.length;31 }32 } else {33 if(typeof(endPoint.exec) == 'function') {34 var match = endPoint.exec(xmlData.substr(startPos));35 if(match && match.index != -1) {36 endPos = match.index + startPos;37 if(includeHeaders) {38 endPos += match[0].length;39 }40 }41 }42 }43 44 if(endPos == -1) return xmlData;45 // Grab the data we wanted to edit46 var toEditData = xmlData.substring(startPos, endPos);47 // Attempt an edit48 var possibleReturn = editFunction(toEditData);49 if(possibleReturn != null) {50 // There was a change, merge the change51 xmlData = xmlData.substring(0, startPos) + possibleReturn + xmlData.substring(endPos);52 }53 // Are we going to continue looping and find more matches?54 if(!loopMatches) {55 // Nope, return the modified data56 return xmlData;57 }58 // Store where this match was59 firstMatchPos = endPos;60 }61}62function loadLayerSimple(layerName, mergeExtra, commitUpdate) {63 // Edit layer64 var res = loadSection(65 window.activeMap.Data,66 '<Simple name="' + layerName + '" value="',67 '" />',68 function(theData2) {69 // Load it70 if(mergeExtra != null) {71 theData2 = mergeExtra + theData2;72 }73 var thisRes = loadLayerDirect(layerName, theData2, true, commitUpdate);74 if(thisRes != null && mergeExtra != null) {75 thisRes = thisRes.substr(mergeExtra.length);76 }77 return thisRes;78 }79 );80 if(commitUpdate && res != null) {81 // Update the res82 window.activeMap.Data = res;83 }84}85function loadLayer(layerName, commitUpdate) {86 // Edit layer87 var res = loadSection(88 window.activeMap.Data,89 '<Complex name="' + layerName + '">',90 '</Complex>',91 function(theData) {92 return loadSection(93 theData,94 '<Simple name="Cells" value="',95 '" />',96 function(theData2) {97 // Load it98 return loadLayerDirect(layerName, theData2, false, commitUpdate);99 }100 )101 }102 );103 if(commitUpdate && res != null) {104 // Update the res105 window.activeMap.Data = res;106 }107}108function loadLayerDirect(layerName, layerData, isSigned, commitUpdate) {109 var dataParts = layerData.split('|');110 if(dataParts.length != 3) {111 alertify.error(112 window.getTranslation(113 'trErrorUnknownLengthForLayer',114 'Unknown length for terrain layer -- {{id}}',115 {116 id: dataParts.length117 }118 ));119 return;120 }121 // Ensure there is a layer store122 window.layerStore[layerName] = window.layerStore[layerName] || {};123 var myLayer = window.layerStore[layerName];124 // Are we doing an update?125 if(commitUpdate) {126 // We are doing an update127 var base64Data = mapArrayToBase64(myLayer.data, isSigned);128 return myLayer.width + '|' + myLayer.height + '|' + base64Data;129 } else {130 // Store the data131 myLayer.width = parseInt(dataParts[0]);132 myLayer.height = parseInt(dataParts[1]);133 myLayer.data = base64MapToArray(dataParts[2], isSigned);134 }135}136// Converts a base64 string into a 1d array that can be used in other functions137function base64MapToArray(data, isSigned) {138 var buf = new buffer.Buffer(data, 'base64');139 // Map size info140 var intSize = 4;141 var totalData = Math.floor(buf.length / intSize);142 var outputArray = [];143 // Read in map144 for (var i = 0; i < totalData; i++) {145 if(isSigned) {146 outputArray[i] = buf.readInt32LE(i * intSize);147 } else {148 outputArray[i] = buf.readUInt32LE(i * intSize);149 }150 }151 return outputArray;152}153// Converts an array of image data to a base64 string154function mapArrayToBase64(someArray, isSigned) {155 var intSize = 4;156 var buff = new buffer.Buffer(someArray.length * intSize);157 for(var i=0; i<someArray.length; ++i) {158 if(isSigned) {159 buff.writeInt32LE(someArray[i], i * intSize);160 } else {161 buff.writeUInt32LE(someArray[i], i * intSize);162 }163 }164 return buff.toString('base64');165}166function getRBG(oldColor) {167 return 'rgba(' +168 oldColor.red + ', ' +169 oldColor.green + ', ' + 170 oldColor.blue + ', ' +171 (oldColor.alpha / 255) +172 ')';173}174// Renders a terrain175function renderLayer(layerName) {176 var mapData = window.layerStore[layerName];177 // Grab the data178 var width = mapData.width;179 var height = mapData.height;180 // Grab the canvas181 var canvas = mapData.canvas;182 var ctx = canvas.getContext('2d');183 // Change the canvas's size184 canvas.width = width * window.pixelSize;185 canvas.height = height * window.pixelSize;186 // Popular the image based on the numbers from the files187 for(var y=0; y<height; ++y) {188 for(var x=0; x<width; ++x) {189 renderPixel(mapData, x, y);190 }191 }192}193// Renders a pixel194function renderPixel(mapData, x, y) {195 var canvas = mapData.canvas;196 var ctx = canvas.getContext('2d');197 var width = mapData.width;198 var mapPos = width * y + x;199 // Data is stored inverted200 if(mapData.name == 'LayerFog') {201 mapPos = width * x + y;202 }203 var theNumber = mapData.data[mapPos];204 var theColor = mapData.colorMap[theNumber] || mapData.defaultColor;205 var theX = (width - x - 1) * window.pixelSize;206 var theY = (y) * window.pixelSize;207 // Do we need to do a clear?208 if(theColor.alpha < 255) {209 ctx.clearRect(theX, theY, window.pixelSize, window.pixelSize);210 }211 ctx.fillStyle = getRBG(theColor);212 ctx.fillRect(theX, theY, window.pixelSize, window.pixelSize);213}214// Updates a pixel215function updatePixel(mapData, xReverse, y, theNumber, noHistory) {216 // Is the number well defined, or should we pick a random one?217 if(typeof(theNumber) != 'string' && typeof(theNumber) != 'number') {218 theNumber = theNumber[Math.floor(Math.random()*theNumber.length)];219 }220 var width = mapData.width;221 // do not allow invalid pixels to be updated222 if(x < 0 || x >= width || y < 0 || y >= mapData.height) return;223 // We need to convert xReverse into x224 var x = (width - xReverse - 1);225 // We need to grab the datastore position226 var mapPos = width * y + x;227 if(mapData.name == 'LayerFog') {228 mapPos = width * x + y;229 }230 if(mapData.data[mapPos] != theNumber) {231 // Add undo history232 if(!noHistory) {233 window.addHistory({234 actionSort: historyItemDrawPixel,235 stackable: true,236 data: [{237 layer: mapData.name,238 xReverse: xReverse,239 y: y,240 redo: theNumber,241 undo: mapData.data[mapPos]242 }]243 });244 }245 // Store it246 mapData.data[mapPos] = theNumber;247 // Re-render the canvas for this pixel248 renderPixel(mapData, x, y);249 // We are no longer up to date250 window.setMapExportUpToDate(false);251 }252}253function renderEntitiesLayer(entities, onlyUpdatePositions) {254 if(entities == null) return;255 for(var entityType in entities) {256 var entList = entities[entityType];257 for(var i=0; i<entList.length; ++i) {258 var theEnt = entList[i];259 if(onlyUpdatePositions) {260 if(theEnt.lastContainer != null && theEnt.__posInfo != null) {261 // Calculate offsets262 var newWidth = theEnt.__posInfo.width * window.zoomFactor + 'px';263 var newHeight = theEnt.__posInfo.height * window.zoomFactor + 'px';264 var newX = theEnt.__posInfo.posX * window.zoomFactor + 'px';265 var newY = theEnt.__posInfo.posY * window.zoomFactor + 'px';266 // Move the ent / resize it267 theEnt.lastContainer.css('width', newWidth);268 theEnt.lastContainer.css('height', newHeight);269 theEnt.lastContainer.css('left', newX);270 theEnt.lastContainer.css('top', newY);271 // Update draggable grid272 theEnt.lastContainer.draggable('option', 'grid', [window.zoomFactor, window.zoomFactor] );273 }274 } else {275 // Last entity is null276 theEnt.lastContainer = null;277 // Should we draw it?278 if(!theEnt.shouldHide) {279 // Add the visual ent280 addVisualEnt(entList[i]);281 }282 }283 }284 }285}286function renderEntities(onlyUpdatePositions) {287 if(!onlyUpdatePositions) {288 // Remove past entities289 $('.mapEntity').remove();290 }291 renderEntitiesLayer(window.layerStore.entities, onlyUpdatePositions);292 renderEntitiesLayer(window.layerStore.extraEntities, onlyUpdatePositions);293}294function getEntityOffsets(ent) {295 // Do we know what kind of entity this is?296 if(ent.__entityType != null) {297 // Yep, lets see if we have scaling information298 var niceEntityType = ent.__entityType.split(',')[0];299 var entitySizeInfo = window.entitySizes[niceEntityType];300 if(ent.Size != null) {301 var possibleParts = ent.Size.split(';');302 if(possibleParts.length == 2) {303 var partX = parseFloat(possibleParts[0]);304 var partY = parseFloat(possibleParts[1]);305 if(!isNaN(partX) && partX > 0 && !isNaN(partY) && partY > 0) {306 entitySizeInfo = {307 width: partX,308 height: partY309 };310 }311 }312 }313 if(entitySizeInfo != null) {314 // We do, store it315 var scaleWidth = entitySizeInfo.width;316 var scaleHeight = entitySizeInfo.height;317 // Adjust offset318 //var posX = - Math.ceil((entitySizeInfo.width - 1 - 0.5) / 2);319 //var posY = - Math.ceil((entitySizeInfo.height - 1) / 2);320 var posX = 0;321 var posY = 0;322 switch(scaleWidth) {323 case 3:324 posX -= 1;325 break;326 case 4:327 posX -= 1;328 break;329 case 5:330 posX = -2;331 break;332 }333 switch(scaleHeight) {334 case 2:335 posY -= 1;336 break;337 case 3:338 posY -= 1;339 break;340 case 4:341 posY -= 2;342 break;343 case 5:344 posY = -2;345 break;346 }347 if(ent.__entityType == 'ZX.Components.CUnitGenerator') {348 349 }350 return {351 width: scaleWidth,352 height: scaleHeight,353 offsetX: posX,354 offsetY: posY355 };356 }357 }358 return {359 width: 1,360 height: 1,361 offsetX: 0,362 offsetY: 0363 };364}365function addVisualEnt(ent) {366 if(ent.Position == null) return;367 // Update the posinfo368 updatePosInfo(ent);369 var __posInfo = ent.__posInfo;370 var posX = __posInfo.posX;371 var posY = __posInfo.posY;372 var offsetWidth = __posInfo.width;373 var offsetHeight = __posInfo.height;374 var offsetX = __posInfo.offsetX;375 var offsetY = __posInfo.offsetY;376 // Update position to reflect the actual drawing377 posX = posX * window.zoomFactor;378 posY = posY * window.zoomFactor;379 // Remove it if it already exist380 if(ent.lastContainer != null) {381 ent.lastContainer.remove();382 }383 var entName = (ent.__entityType || 'unknown').split(',')[0];384 var red = Math.floor(Math.random() * 255);385 var green = Math.floor(Math.random() * 255);386 var blue = Math.floor(Math.random() * 255);387 if(unitColorMap[entName] != null) {388 red = unitColorMap[entName].red;389 green = unitColorMap[entName].green;390 blue = unitColorMap[entName].blue;391 }392 var cssColor = 'rgb(' + red + ',' + green + ',' + blue + ')';393 var cssColor2 = 'rgb(' + (255-red) + ',' + (255-green) + ',' + (255-blue) + ')';394 ent.lastContainer = $('<div>', {395 class: 'mapEntity',396 mousedown: function() {397 // Is this entity active?398 if(!ent.isActive) {399 // Nope, view it, and load the menu:400 window.viewEntityProps(ent);401 window.updateEntityMenu();402 }403 }404 })405 .css('width', (offsetWidth * window.zoomFactor) + 'px')406 .css('height', (offsetHeight * window.zoomFactor) + 'px')407 .css('background-color', cssColor)408 .css('border', '1px solid ' + cssColor2)409 .css('position', 'absolute')410 .css('top', posY + 'px')411 .css('left', posX + 'px')412 .appendTo($('#mapDisplayHolder'))413 .append($('<span>', {414 class: 'mapEntityText',415 text: entName416 }));417 if(entName == 'ZX.Components.CUnitGenerator') {418 ent.lastContainer.addClass('rotateEntity');419 }420 // Should we hide it?421 if(ent.shouldHide) {422 ent.lastContainer.hide();423 }424 // Are we active?425 if(ent.isActive) {426 ent.lastContainer.addClass('entityIsSelected');427 }428 // Make it dragable429 ent.lastContainer.draggable({430 // Not allowed out of terrain area431 containment: $('#helperLayer'),432 stack: '.mapEntity',433 grid: [window.zoomFactor, window.zoomFactor],434 stop: function(event, ui) {435 var xNice = ui.position.left;436 var yNice = ui.position.top;437 xNice -= offsetX;438 yNice -= offsetY;439 xNice = xNice / window.zoomFactor;440 yNice = yNice / window.zoomFactor;441 var x = (window.layerStore.LayerTerrain.width - xNice - 1).toFixed(0);442 var y = yNice.toFixed(0);443 var mapCoords = y + ';' + x;444 ent.Position = mapCoords;445 // When props are changed446 window.onPropsChanged(ent);447 // Update the posinfo448 updatePosInfo(ent);449 }450 });451}452function updatePosInfo(ent) {453 if(ent == null) return;454 var pos = ent.Position;455 if(pos == null) return;456 var width = window.layerStore.LayerTerrain.width;457 var posParts = pos.split(';');458 var posX = (width - parseInt(posParts[1]) - 1);459 var posY = parseInt(posParts[0]);460 // Grab offsets and make adjustments461 var offsets = getEntityOffsets(ent);462 posX += offsets.offsetX;463 posY += offsets.offsetY;464 // Store vars onto it465 ent.__posInfo = {466 posX: posX,467 posY: posY,468 width: offsets.width,469 height: offsets.height,470 offsetX: offsets.offsetX,471 offsetY: offsets.offsetY472 };473}474// Generates a checksum for a string475function generateChecksum(str) {476 var buff = new buffer.Buffer(str);477 var num = 0;478 for(var i = 0; i < buff.length; i++) {479 num += buff.readUInt8(i);480 // Handle overflow481 num = num % 4294967296;482 }483 484 return (num * 157 + num) % 4294967296;485}486function blobToBuffer(blob, callback) {487 if (typeof Blob === 'undefined' || !(blob instanceof Blob)) {488 throw new Error('first argument must be a Blob');489 }490 if (typeof callback !== 'function') {491 throw new Error('second argument must be a function');492 }493 var reader = new FileReader();494 function onLoadEnd (e) {495 reader.removeEventListener('loadend', onLoadEnd, false);496 if(e.error) {497 callback(e.error);498 } else {499 callback(null, buffer.Buffer.from(reader.result));500 }501 }502 reader.addEventListener('loadend', onLoadEnd, false);503 reader.readAsArrayBuffer(blob);504}505function replaceEntityProperty(entityXML, useReplace1, regexMatch1, regexMatch2, replace1, replace2) {506 var theRes = null;507 if(useReplace1) {508 theRes = replace1;509 } else {510 theRes = replace2;511 }512 if(regexMatch1 != null) entityXML = entityXML.replace(regexMatch1, theRes);513 if(regexMatch2 != null) entityXML = entityXML.replace(regexMatch2, theRes);514 return entityXML;515}516function updateLayerSer() {517 // Edit layer518 var res = loadSection(519 window.activeMap.Data,520 '<Simple name="SerTerrainResourceCells" value="',521 '" />',522 function(theData) {523 var layerTerrain = window.layerStore.LayerTerrain;524 var layerObjects = window.layerStore.LayerObjects;525 var mergedBuff = [];526 for(var i=0; i<layerObjects.data.length; ++i) {527 // Get the number from objects528 var theNumber = layerObjects.data[i];529 var mapFile = mapSerTerrain.objects;530 // Was there nothing here?531 if(theNumber == 0) {532 // Get the number from terrain instead533 mapFile = mapSerTerrain.terrain;534 }535 mergedBuff[i] = mapFile[theNumber] || 0;536 }537 return layerTerrain.width + '|' + layerTerrain.height + '|' + mapArrayToBase64(mergedBuff, false);538 }539 );540 if(res != null) {541 window.activeMap.Data = res;542 }543}544function extractOrReplaceMapProp(theData, propName, storage, commitUpdate) {545 var regex = new RegExp('<Simple name="' + propName + '" value="([^"]*)" \\/>', 'g');546 if(commitUpdate) {547 if(storage[propName] != null) {548 theData = theData.replace(regex, '<Simple name="' + propName + '" value="' + storage[propName] + '" />');549 }550 } else {551 // Find and store the match552 var theMatch = (regex.exec(theData) || [])[1] || '';553 storage[propName] = theMatch;554 }555 return theData;556}557// Allows map props to be loaded / edited558function loadMapProps(commitUpdate) {559 // Grab useful stuff560 var theData = window.activeMap.Data;561 var storage = window.layerStore.MapProps || {};562 window.layerStore.MapProps = storage;563 // Extract / commit the data564 var toExtract = [565 'ShowFullMap',566 'FoodReserved',567 'WorkersReserved',568 'EnergyReserved',569 'WoodProductionReserved',570 'StoneProductionReserved',571 'IronProductionReserved',572 'OilProductionReserved',573 'NTurnsWithNegativeGold',574 'NTurnsWithNegativeFood',575 'ArmyGoldCost',576 'ArmyFoodPenaltyCost',577 'StructuresGoldCost',578 'TotalGoldPerColonists',579 'NMarkets',580 'MaxGoldByExportingExcedents',581 'Wood',582 'Stone',583 'Iron',584 'Oil',585 'Gold',586 'WoodProduction',587 'GoldProduction',588 'IronProduction',589 'StoneProduction',590 'OilProduction',591 'NZombiesDead',592 'NSoldiersDead',593 'NColonistsDead',594 'MaxColonists',595 'NColonistsInfected',596 'GameTime',597 'LastGameTimeIAUpdate',598 'Date',599 'Physics_TimeLastBodyPosition',600 'Physics_TimeBodyPosition',601 'Physics_FactorTimeForInterpolation',602 'Physics_LastTimeUpdatePhysics',603 'Seed',604 'ThemeType',605 'FactorGameDuration',606 'FactorZombiePopulation',607 //'DifficultyType',608 //'Difficulty',609 'PlayableArea',610 'FactorPlayableArea'611 ];612 // We need two cell size values:613 /*loadSection(614 theData,615 '<Complex name="CurrentGeneratedLevel">',616 '<Simple name="FactorPlayableArea"',617 function(interestingData) {618 loadSection(619 interestingData,620 '<Simple name="NCells" value="',621 '" />',622 function(res) {623 storage.__ncells1 = parseInt(res);624 }625 );626 }627 );*/628 // Real ncells629 loadSection(630 theData,631 '<Complex name="SurvivalModeParams">',632 '</Complex>',633 function(theData2) {634 loadSection(635 theData2,636 '<Simple name="NCells" value="',637 '" />',638 function(interestingData) {639 storage._ncellsReal = parseInt(interestingData);640 }641 );642 }643 );644 // Map Theme645 if(commitUpdate) {646 storage.ThemeType = $('#dropdownMapTheme').val();647 }648 for(var i=0; i<toExtract.length; ++i) {649 theData = extractOrReplaceMapProp(theData, toExtract[i], storage, commitUpdate);650 }651 // Map Name652 var fieldName = '<Simple name="Name" value="';653 var fieldNamePos = theData.lastIndexOf(fieldName);654 if(fieldNamePos != -1) {655 fieldNamePos += fieldName.length;656 var endFieldPos = theData.indexOf('"', fieldNamePos);657 if(commitUpdate) {658 // Read in the new map name659 storage._mapName = $('#inputSaveFileName').val();660 if(storage._mapName != null && storage._mapName.length > 0) {661 theData = theData.substring(0, fieldNamePos) +662 storage._mapName +663 theData.substring(endFieldPos);664 }665 } else {666 var mapName = theData.substring(fieldNamePos, endFieldPos);667 storage._mapName = mapName; 668 // Put it into the element669 $('#inputSaveFileName').val(mapName);670 }671 }672 673 // Do we commit?674 if(commitUpdate) {675 window.activeMap.Data = theData;676 } else {677 // We need to store stuff back into the UI678 // Map Theme679 $('#dropdownMapTheme').val(storage['ThemeType']);680 }681};682// Loads info about the map683function loadInfo(commitUpdate) {684 if(commitUpdate) {685 var mapInfo = window.mapInfo;686 if(mapInfo == null) return;687 var info = window.activeMap.Info;688 if(info == null) return;689 var mapTitle = mapInfo.title;690 // Update title691 info = info.replace(692 /<Simple name="Name" value="([^"]*)" \/>/,693 '<Simple name="Name" value="' + mapTitle + '" />'694 );695 // Update filename696 info = info.replace(697 /<Simple name="FileName" value="([^"]*)" \/>/,698 '<Simple name="FileName" value="' + mapTitle + '.zxsav" />'699 );700 // This is now our active map name701 window.activeMap.name = mapTitle + '.zxsav'702 // Store the new info703 window.activeMap.Info = info;704 return;705 }706 var mapInfo = {};707 window.mapInfo = mapInfo;708 var info = window.activeMap.Info;709 if(info == null) return;710 var mapTitle = (/<Simple name="Name" value="([^"]*)" \/>/.exec(info) || [])[1] || 'Untitled';711 // Store the title712 mapInfo.title = mapTitle;713 // Update it in the UI714 $('#mapNameHolder').val(mapTitle);715};716// Allows loading of extra entities717function loadLevelExtraEntites(commitUpdate) {718 var matchStart = '<Collection name="ExtraEntities" elementType="DXVision.DXEntity, DXVision">';719 var matchEnd = /<\/Complex>[\n\r ]*<\/Items>[\n\r ]*<\/Collection>/;720 // Attempt to load using standard mode721 var useAltMode = true;722 loadSection(window.activeMap.Data,723 matchStart,724 matchEnd,725 function() {726 // Standard mode works, don't use alt727 useAltMode = false;728 }, false, true729 );730 // If we should use alt, change it731 if(useAltMode) {732 matchEnd = /<\/Collection>/733 }734 // Find the part we need to edit735 var res = loadSection(736 window.activeMap.Data,737 matchStart,738 matchEnd,739 function(theData) {740 var theStorage;741 if(commitUpdate) {742 theStorage = window.layerStore.extraEntities || {};743 } else {744 theStorage = {};745 window.layerStore.extraEntities = theStorage;746 }747 // Used if we use commit mode748 //var newStorage = {};749 // Can we make changes?750 if(commitUpdate) {751 // We will use referenceData to build a new xmlData output752 var theOutput = '';753 theOutput += '<Collection name="ExtraEntities" elementType="DXVision.DXEntity, DXVision">\n';754 var totalNewEnts = 0;755 for(var key in theStorage) {756 totalNewEnts += theStorage[key].length;757 }758 theOutput += '<Properties>\n';759 theOutput += '<Simple name="Capacity" value="' + totalNewEnts + '" />\n';760 theOutput += '</Properties>\n';761 theOutput += '<Items>\n';762 763 for(var entityType in theStorage) {764 var allEntitiesOfThisType = theStorage[entityType];765 for(var i=0; i<allEntitiesOfThisType.length; ++i) {766 var thisEntity = allEntitiesOfThisType[i];767 var thisXML = thisEntity.rawXML;768 var newEntityId = ++window.totalEntities;769 // Normal properties770 for(propertyName in thisEntity) {771 // Ignore these properties772 if(hiddenFields[propertyName]) continue;773 var theValue = thisEntity[propertyName];774 theValue = '<Simple name="' + propertyName + '" value="' + theValue + '" />';775 /*if(theValue == null || theValue == "") {776 theValue = '<Null name="' + propertyName + '" />';777 } else {778 theValue = '<Simple name="' + propertyName + '" value="' + theValue + '" />';779 }*/780 // Replace the property781 thisXML = thisXML.replace(782 new RegExp('<Simple name="' + propertyName + '" value="[^"]*" \/>'),783 theValue784 );785 }786 // EntityId again787 /*thisXML = replaceEntityProperty(788 thisXML,789 true,790 /<Simple name="ID" value="[^"]*" \/>/,791 null,792 '<Simple name="ID" value="' + newEntityId + '" />'793 );*/794 // Add the XML795 theOutput += thisXML;796 }797 }798 theOutput += '</Items>\n';799 theOutput += '</Collection>';800 return theOutput;801 }802 loadSection(803 theData,804 /<Complex( type="[^"]*")?>/,805 /<Simple name="Z_Offset" value="[^"]*" \/>[\n\r ]*<\/Properties>[\n\r ]*<\/Complex>/,806 function(theData2) {807 var thisEntityStore = window.extractEntityInfo(theData2);808 var entityType = thisEntityStore.__entityType;809 theStorage[entityType] = theStorage[entityType] || [];810 theStorage[entityType].push(thisEntityStore);811 // Hidden by default812 thisEntityStore.shouldHide = true;813 // Store a reference to the store814 thisEntityStore.__theStore = theStorage[entityType];815 }, true, true816 );817 }, false, true818 );819 if(commitUpdate && res != null) {820 window.activeMap.Data = res;821 }822}823// Extracts the info from an entity824window.extractEntityInfo = function(thisItemData) {825 var entityType = (/<Complex type="([^"]*)">/.exec(thisItemData) || [])[1] || 'Unknown';826 /*var findEntityId = /<Simple[ ]*value="([^"]*)"[ ]*\/>/;827 var possibleEntityId = findEntityId.exec(thisItemData);828 if(possibleEntityId == null || possibleEntityId.length != 2) return;829 var entityId = possibleEntityId[1];*/830 var thisEntityStore = {};831 var blackListedProps = {};832 var propertyExtractor = /<Simple name="([^"]*)" value="([^"]*)" \/>/g;833 var theMatch;834 while((theMatch = propertyExtractor.exec(thisItemData)) != null) {835 if(theMatch.length < 3) continue;836 // Grab stuff837 var propertyName = theMatch[1];838 var propertyValue = theMatch[2];839 // Is this blacklisted?840 if(blackListedProps[propertyName]) continue;841 // Have we already collected this prop?842 if(thisEntityStore[propertyName] != null) {843 // We are not touching this prop844 delete thisEntityStore[propertyName];845 blackListedProps[propertyName] = true;846 continue;847 }848 // Store it849 thisEntityStore[propertyName] = propertyValue;850 }851 // Add raw xml852 thisEntityStore.rawXML = thisItemData;853 // Store the entity type854 thisEntityStore.__entityType = entityType;855 // Return it856 return thisEntityStore;857}858// Load in the bonus entities859function loadBonusEntities(commitUpdate) {860 var res = loadSection(861 window.activeMap.Data,862 '<Dictionary name="BonusEntityTemplates" keyType="System.UInt64, mscorlib" valueType="System.Int32, mscorlib">',863 '</Dictionary>',864 function(theData) {865 if(commitUpdate) {866 // Commit the update867 var theOutput = '';868 theOutput += '<Dictionary name="BonusEntityTemplates" keyType="System.UInt64, mscorlib" valueType="System.Int32, mscorlib">\n';869 var bonusEnts = window.layerStore.bonusEntities;870 if(bonusEnts.length <= 0) {871 theOutput += '<Items />\n'872 } else {873 theOutput += '<Items>\n';874 for(var i=0; i<bonusEnts.length; ++i) {875 var bonusEnt = bonusEnts[i];876 theOutput += '<Item>\n'877 theOutput += '<Simple value="' + bonusEnt[0] + '" />\n'878 theOutput += '<Simple value="' + bonusEnt[1] + '" />\n'879 theOutput += '</Item>\n';880 }881 theOutput += '</Items>\n';882 }883 theOutput += '</Dictionary>';884 return theOutput;885 }886 // Empty the layer store887 window.layerStore.bonusEntities = [];888 loadSection(889 theData,890 '<Item>',891 '</Item>',892 function(theData2) {893 var dataParts = [];894 loadSection(895 theData2,896 '<Simple value="',897 '" />',898 function(theData3) {899 // Push the data parts;900 dataParts.push(theData3);901 }, true, false902 )903 // Check if this is a valid bonus entity904 if(dataParts.length == 2) {905 window.layerStore.bonusEntities.push(dataParts);906 }907 }, true, true908 );909 // Add these bonus entities into the display910 window.rebuildBonusEntities();911 }, false, true912 );913 if(commitUpdate && res != null) {914 // Update the res915 window.activeMap.Data = res;916 }917}918// Allows entities in the level to be edited919function loadLevelEntities(commitUpdate) {920 // Find the part we need to edit921 var res = loadSection(922 window.activeMap.Data,923 '<Dictionary name="LevelEntities" keyType="System.UInt64, mscorlib" valueType="DXVision.DXEntity, DXVision">',924 /<\/Properties>[\n\r ]*<\/Complex>[\n\r ]*<\/Item>[\n\r ]*<\/Items>[\n\r ]*<\/Dictionary>/,925 function(theData) {926 // We need to break this into individual entities927 // Can we make changes?928 if(commitUpdate) {929 // We will use referenceData to build a new xmlData output930 var theOutput = '';931 theOutput += '<Dictionary name="LevelEntities" keyType="System.UInt64, mscorlib" valueType="DXVision.DXEntity, DXVision">\n';932 theOutput += '<Items>\n';933 934 for(var entityType in window.layerStore.entities) {935 var allEntitiesOfThisType = window.layerStore.entities[entityType];936 for(var i=0; i<allEntitiesOfThisType.length; ++i) {937 var thisEntity = allEntitiesOfThisType[i];938 var thisXML = thisEntity.rawXML;939 var newEntityId = ++window.totalEntities;940 // Normal properties941 for(propertyName in thisEntity) {942 // Ignore these properties943 if(hiddenFields[propertyName]) continue;944 var theValue = thisEntity[propertyName];945 if(theValue == null || theValue == "") {946 theValue = '<Null name="' + propertyName + '" />';947 } else {948 theValue = '<Simple name="' + propertyName + '" value="' + theValue + '" />';949 }950 // Replace the property951 thisXML = thisXML.replace(952 new RegExp('<Simple name="' + propertyName + '" value="[^"]*" \/>'),953 theValue954 );955 }956 // EntityId957 thisXML = replaceEntityProperty(958 thisXML,959 true,960 /<Simple value="[^"]*" \/>/,961 null,962 '<Simple value="' + newEntityId + '" />'963 );964 // EntityId again965 thisXML = replaceEntityProperty(966 thisXML,967 true,968 /<Simple name="ID" value="[^"]*" \/>/,969 null,970 '<Simple name="ID" value="' + newEntityId + '" />'971 );972 // Add the XML973 theOutput += thisXML;974 }975 }976 theOutput += '</Items>\n';977 theOutput += '</Dictionary>';978 return theOutput;979 }980 var allEntities = {};981 982 // This will edit every individual item in the map983 loadSection(984 theData,985 '<Item>',986 /<\/Properties>[\n\r ]*<\/Complex>[\n\r ]*<\/Item>/,987 function(thisItemData) {988 // Return an empty string from here to delete the entity!989 var thisEntityStore = window.extractEntityInfo(thisItemData);990 var entityType = thisEntityStore.__entityType;991 allEntities[entityType] = allEntities[entityType] || [];992 allEntities[entityType].push(thisEntityStore);993 // Hidden by default994 thisEntityStore.shouldHide = true;995 // Store a reference to the store996 thisEntityStore.__theStore = allEntities[entityType];997 }, true, true);998 // Store all the entities999 window.layerStore.entities = allEntities;1000 }, false, true1001 );1002 if(commitUpdate && res != null) {1003 // Update the res1004 window.activeMap.Data = res;1005 }1006}1007function loadLevelEvents(commitUpdate) {1008 var res = loadSection(1009 window.activeMap.Data,1010 '<Collection name="LevelEvents" elementType="ZX.GameSystems.ZXLevelEvent, TheyAreBillions">',1011 /<\/Properties>[\n\r ]*<\/Complex>[\n\r ]*<\/Items>[\n\r ]*<\/Collection>/, function(theData) {1012 // Used to change the name of events1013 var _editorName = '_editorName';1014 if(commitUpdate) {1015 var events = window.layerStore.events || [];1016 var theOutput = '';1017 theOutput += '<Collection name="LevelEvents" elementType="ZX.GameSystems.ZXLevelEvent, TheyAreBillions">\n';1018 theOutput += '<Properties>\n';1019 theOutput += '<Simple name="Capacity" value="' + events.length + '" />\n';1020 theOutput += '</Properties>\n';1021 theOutput += '<Items>\n';1022 var entProp = function(ent, prop) {1023 if(ent[prop] == null || ent[prop] == '') {1024 return '<Null name="' + prop + '" />\n';1025 } else {1026 return '<Simple name="' + prop + '" value="' + ent[prop] + '" />\n';1027 }1028 };1029 var totalEvents = 0;1030 for(var i=0; i<events.length; ++i) {1031 var thisEntity = events[i];1032 var thisXML = thisEntity.rawXML;1033 var newEntityId = ++totalEvents;1034 // Do we have the editor property?1035 if(thisXML.indexOf(_editorName) == -1) {1036 var posSearchFor = '<Simple name="ID" value="';1037 thisXML = thisXML.replace(posSearchFor, '<Simple name="' + _editorName + '" value="unnamed" />\n' + posSearchFor);1038 }1039 // Normal properties1040 for(propertyName in thisEntity) {1041 // Ignore these properties1042 if(hiddenFields[propertyName]) continue;1043 var theValue = thisEntity[propertyName];1044 if(theValue == null || theValue == "") {1045 theValue = '<Null name="' + propertyName + '" />';1046 } else {1047 theValue = '<Simple name="' + propertyName + '" value="' + theValue + '" />';1048 }1049 // Replace the property1050 thisXML = thisXML.replace(1051 new RegExp('<Simple name="' + propertyName + '" value="[^"]*" \/>'),1052 theValue1053 );1054 }1055 // EntityId again1056 thisXML = replaceEntityProperty(1057 thisXML,1058 true,1059 /<Simple name="ID" value="[^"]*" \/>/,1060 null,1061 '<Simple name="ID" value="' + newEntityId + '" />'1062 );1063 // Add the XML1064 theOutput += thisXML;1065 }1066 theOutput += '</Items>\n';1067 theOutput += '</Collection>\n';1068 return theOutput;1069 }1070 var allEvents = [];1071 loadSection(1072 theData,1073 /<Complex>[\n\r ]*<Properties>/,1074 /name="Music"( value="[^"]*")? \/>[\n\r ]*<\/Properties>[\n\r ]*<\/Complex>/,1075 function(possibleEntity) {1076 var thisEntityStore = {};1077 allEvents.push(thisEntityStore);1078 var blackListedProps = {};1079 var propertyExtractor = /<Simple name="([^"]*)" value="([^"]*)" \/>/g;1080 var theMatch;1081 while((theMatch = propertyExtractor.exec(possibleEntity)) != null) {1082 if(theMatch.length < 3) continue;1083 // Grab stuff1084 var propertyName = theMatch[1];1085 var propertyValue = theMatch[2];1086 // Is this blacklisted?1087 if(blackListedProps[propertyName]) continue;1088 // Have we already collected this prop?1089 if(thisEntityStore[propertyName] != null) {1090 // We are not touching this prop1091 delete thisEntityStore[propertyName];1092 blackListedProps[propertyName] = true;1093 continue;1094 }1095 // Store it1096 thisEntityStore[propertyName] = propertyValue;1097 }1098 // Add raw xml1099 thisEntityStore.rawXML = possibleEntity;1100 // Hide it1101 thisEntityStore.shouldHide = true;1102 // Add a reference to the store1103 thisEntityStore.__theStore = allEvents;1104 // Do we have the editor generated property?1105 if(thisEntityStore[_editorName] == null) {1106 thisEntityStore[_editorName] = 'Event ' + (thisEntityStore.ID || 'unknown');1107 }1108 },1109 true, true1110 );1111 // Store it1112 window.layerStore.events = allEvents;1113 }, false, true);1114 if(commitUpdate && res != null) {1115 window.activeMap.Data = res;1116 }1117}1118function loadMinimapIndicators(commitUpdate) {1119 // MiniMapIndicators1120 var res = loadSection(1121 window.activeMap.Data,1122 '<Collection name="MiniMapIndicators" elementType="ZX.ZXMiniMapIndicator, TheyAreBillions">',1123 '</Collection>',1124 function(theData) {1125 if(commitUpdate) {1126 // TODO: Commit the update1127 var theOutput = '';1128 theOutput += '<Collection name="MiniMapIndicators" elementType="ZX.ZXMiniMapIndicator, TheyAreBillions">';1129 1130 theOutput += '<Properties>';1131 theOutput += '<Simple name="Capacity" value="0" />';1132 theOutput += '</Properties>';1133 theOutput += '<Items>';1134 theOutput += '</Items>';1135 theOutput += '</Collection>';1136 return theOutput;1137 }1138 }, false, true1139 );1140 if(commitUpdate && res != null) {1141 // Update the res1142 window.activeMap.Data = res;1143 }1144}1145function loadFastEntities(commitUpdate) {1146 var fastEnts = {};1147 if(commitUpdate) {1148 fastEnts = window.layerStore.fastEntities;1149 }1150 // Find the part we need to edit1151 var res = loadSection(1152 window.activeMap.Data,1153 '<Dictionary name="LevelFastSerializedEntities" keyType="System.UInt64, mscorlib" valueType="System.Collections.Generic.List`1[[DXVision.DXTupla2`2[[System.UInt64, mscorlib],[System.Drawing.PointF, System.Drawing]], DXVision]], mscorlib">',1154 '</Dictionary>',1155 function(theData) {1156 if(commitUpdate) {1157 // TODO: Commit the update1158 var theOutput = '';1159 theOutput += '<Dictionary name="LevelFastSerializedEntities" keyType="System.UInt64, mscorlib" valueType="System.Collections.Generic.List`1[[DXVision.DXTupla2`2[[System.UInt64, mscorlib],[System.Drawing.PointF, System.Drawing]], DXVision]], mscorlib">\n';1160 theOutput += '<Items>\n';1161 for(var entType in fastEnts) {1162 var theseEnts = fastEnts[entType];1163 if(theseEnts.length > 0) {1164 theOutput += '<Item>\n';1165 theOutput += '<Simple value="' + entType + '" />\n';1166 theOutput += '<Collection elementType="DXVision.DXTupla2`2[[System.UInt64, mscorlib],[System.Drawing.PointF, System.Drawing]], DXVision">\n';1167 theOutput += '<Properties>\n';1168 theOutput += '<Simple name="Capacity" value="' + theseEnts.length + '" />\n';1169 theOutput += '</Properties>\n';1170 theOutput += '<Items>\n';1171 for(var i=0; i<theseEnts.length; ++i) {1172 var thisEnt = theseEnts[i];1173 var newEntityId = ++window.totalEntities;1174 theOutput += '<Complex>\n';1175 theOutput += '<Properties>\n';1176 theOutput += '<Simple name="A" value="' + newEntityId + '" />\n';1177 theOutput += '<Simple name="B" value="' + thisEnt.Position + '" />\n';1178 theOutput += '</Properties>\n';1179 theOutput += '</Complex>\n'; 1180 }1181 theOutput += '</Items>\n';1182 theOutput += '</Collection>\n';1183 theOutput += '</Item>\n';1184 }1185 }1186 theOutput += '</Items>\n';1187 theOutput += '</Dictionary>\n';1188 return theOutput;1189 }1190 // We need to break this into individual entities1191 loadSection(1192 theData,1193 '<Item>',1194 '</Item>',1195 function(theData2) {1196 var entType = (/<Simple value="([^"]*)" \/>/.exec(theData2) || [])[1] || 'Unknown';1197 // Ensure we have a store for this kind of entity1198 fastEnts[entType] = fastEnts[entType] || [];1199 loadSection(1200 theData2,1201 '<Complex>',1202 '</Complex>',1203 function(possibleFastEnt) {1204 var entId = (/<Simple name="A" value="([^"]*)" \/>/.exec(possibleFastEnt) || [])[1] || 'Unknown';1205 var position = (/<Simple name="B" value="([^"]*)" \/>/.exec(possibleFastEnt) || [])[1] || 'Unknown';1206 // Push it in1207 fastEnts[entType].push({1208 ID: entId,1209 Position: position,1210 shouldHide: true,1211 __entityType: entType,1212 __theStore: fastEnts[entType]1213 });1214 }, true, true1215 );1216 }, true, true1217 );1218 1219 }, false, true1220 );1221 // Store them1222 window.layerStore.fastEntities = fastEnts;1223 if(commitUpdate && res != null) {1224 // Update the res1225 window.activeMap.Data = res;1226 }1227}1228// Generates a map from the fast entities1229function generateFastEntitiesMap(reverse) {1230 // If it's reverse, it means we're converting from a map to fastEntities1231 if(reverse) {1232 window.layerStore.fastEntities = {};1233 var fastEnts = window.layerStore.fastEntities;1234 var zombieLayer = window.layerStore.LayerZombies;1235 var width = zombieLayer.width;1236 var height = zombieLayer.height;1237 var theData = zombieLayer.data;1238 var zombieMult = 1;1239 try {1240 zombieMult = parseInt($('#inputZombieMultiplier').val());1241 if(typeof(zombieMult) != 'number' || isNaN(zombieMult) || zombieMult < 1) {1242 zombieMult = 1;1243 }1244 } catch(e) {1245 zombieMult = 1;1246 }1247 for(var yy=0; yy<height; ++yy) {1248 for(var xx=0; xx<width; ++xx) {1249 var mapPos = width * yy + xx;1250 var entType = theData[mapPos];1251 // We don't write null zombies back into fast ents1252 if(entType == 0) continue;1253 // Ensure we have an array to store the new ent into1254 fastEnts[entType] = fastEnts[entType] || [];1255 // Add this entity1256 for(var i=0; i<zombieMult; ++i) {1257 fastEnts[entType].push({1258 Position: '' + yy + ';' + xx1259 });1260 }1261 }1262 }1263 return;1264 }1265 // We are converting from fast entities to a map1266 var width = window.layerStore.LayerTerrain.width;1267 var height = window.layerStore.LayerTerrain.height;1268 // Grab the zombie later, update it1269 var zombieLayer = window.layerStore.LayerZombies;1270 zombieLayer.width = width;1271 zombieLayer.height = height;1272 // Create an array with one slot for each square1273 var dataArray = new Array(width * height);1274 zombieLayer.data = dataArray;1275 // Fill with 0s1276 dataArray.fill(0);1277 // Grab the fast entities store1278 var fastEnts = window.layerStore.fastEntities;1279 for(var entType in fastEnts) {1280 var theseEnts = fastEnts[entType];1281 // Ensure there are some ents to play with1282 if(theseEnts == null || theseEnts.length <= 0) continue;1283 // Loop over all the ents1284 for(var i=0; i<theseEnts.length; ++i) {1285 var thisEnt = theseEnts[i];1286 var pos = thisEnt.Position.split(';');1287 if(pos.length != 2) continue;1288 var posX = Math.round(parseFloat(pos[1]));1289 var posY = Math.round(parseFloat(pos[0]));1290 // Sanity checking1291 if(isNaN(posX) || isNaN(posY)) continue;1292 if(posX < 0 || posY < 0) continue;1293 if(posX >= width || posY >= height) continue;1294 // Convert to a map pos1295 var mapPos = width * posY + posX;1296 // Store the data1297 dataArray[mapPos] = entType;1298 }1299 }...

Full Screen

Full Screen

useSubscribeToInvalidationState-test.js

Source:useSubscribeToInvalidationState-test.js Github

copy

Full Screen

...112});113const dataIDs = ['4', 'client:1'];114it('notifies when invalidation state changes due to global invalidation', () => {115 render(environment, dataIDs, callback);116 environment.commitUpdate(storeProxy => {117 storeProxy.invalidateStore();118 });119 expect(callback).toHaveBeenCalledTimes(1);120});121it('notifies when invalidation state changes due to invalidating one of the provided ids', () => {122 render(environment, dataIDs, callback);123 environment.commitUpdate(storeProxy => {124 const user = storeProxy.get('4');125 if (!user) {126 throw new Error('Expected to find record with id "4"');127 }128 user.invalidateRecord();129 });130 expect(callback).toHaveBeenCalledTimes(1);131});132it('notifies once when invalidating multiple affected records in the same update', () => {133 render(environment, dataIDs, callback);134 environment.commitUpdate(storeProxy => {135 const user = storeProxy.get('4');136 if (!user) {137 throw new Error('Expected to find record with id "4"');138 }139 user.invalidateRecord();140 const record = storeProxy.get('client:1');141 if (!record) {142 throw new Error('Expected to find record with id "client:1"');143 }144 record.invalidateRecord();145 });146 expect(callback).toHaveBeenCalledTimes(1);147});148it('notifies once per update when multiple affected records invalidated', () => {149 render(environment, dataIDs, callback);150 environment.commitUpdate(storeProxy => {151 const user = storeProxy.get('4');152 if (!user) {153 throw new Error('Expected to find record with id "4"');154 }155 user.invalidateRecord();156 });157 expect(callback).toHaveBeenCalledTimes(1);158 environment.commitUpdate(storeProxy => {159 const record = storeProxy.get('client:1');160 if (!record) {161 throw new Error('Expected to find record with id "client:1"');162 }163 record.invalidateRecord();164 });165 expect(callback).toHaveBeenCalledTimes(2);166});167it('notifies once when invalidation state changes due to both global and local invalidation in a single update', () => {168 render(environment, dataIDs, callback);169 environment.commitUpdate(storeProxy => {170 storeProxy.invalidateStore();171 const user = storeProxy.get('4');172 if (!user) {173 throw new Error('Expected to find record with id "4"');174 }175 user.invalidateRecord();176 const record = storeProxy.get('client:1');177 if (!record) {178 throw new Error('Expected to find record with id "client:1"');179 }180 record.invalidateRecord();181 });182 expect(callback).toHaveBeenCalledTimes(1);183});184it('notifies once per update when invalidation state changes due to both global and local invalidation in multiple', () => {185 render(environment, dataIDs, callback);186 environment.commitUpdate(storeProxy => {187 storeProxy.invalidateStore();188 });189 expect(callback).toHaveBeenCalledTimes(1);190 environment.commitUpdate(storeProxy => {191 const user = storeProxy.get('4');192 if (!user) {193 throw new Error('Expected to find record with id "4"');194 }195 user.invalidateRecord();196 });197 expect(callback).toHaveBeenCalledTimes(2);198 environment.commitUpdate(storeProxy => {199 const record = storeProxy.get('client:1');200 if (!record) {201 throw new Error('Expected to find record with id "client:1"');202 }203 record.invalidateRecord();204 });205 expect(callback).toHaveBeenCalledTimes(3);206});207it('does not notify if invalidated ids do not affect subscription', () => {208 render(environment, dataIDs, callback);209 environment.commitUpdate(storeProxy => {210 const user = storeProxy.get('5');211 if (!user) {212 throw new Error('Expected to find record with id "5"');213 }214 user.invalidateRecord();215 });216 expect(callback).toHaveBeenCalledTimes(0);217});218it('does not notify if subscription has been manually disposed of', () => {219 render(environment, dataIDs, callback);220 disposable.dispose();221 environment.commitUpdate(storeProxy => {222 storeProxy.invalidateStore();223 });224 expect(callback).toHaveBeenCalledTimes(0);225});226it('does not notify after component unmounts', () => {227 render(environment, dataIDs, callback);228 ReactTestRenderer.act(() => renderedInstance.unmount());229 environment.commitUpdate(storeProxy => {230 storeProxy.invalidateStore();231 });232 expect(callback).toHaveBeenCalledTimes(0);233});234it('re-establishes subscription when data ids change', () => {235 render(environment, dataIDs, callback);236 environment.commitUpdate(storeProxy => {237 const user = storeProxy.get('4');238 if (!user) {239 throw new Error('Expected to find record with id "4"');240 }241 user.invalidateRecord();242 });243 expect(callback).toHaveBeenCalledTimes(1);244 ReactTestRenderer.act(() => {245 setDataIDs(['5', 'client:2']);246 });247 // Assert that invalidating data ids from initial subscriptions248 // does not trigger callback anymore249 callback.mockClear();250 environment.commitUpdate(storeProxy => {251 const user = storeProxy.get('4');252 if (!user) {253 throw new Error('Expected to find record with id "4"');254 }255 user.invalidateRecord();256 });257 expect(callback).toHaveBeenCalledTimes(0);258 // Assert that invalidating ids from new subscription259 // trigger callback260 environment.commitUpdate(storeProxy => {261 const user = storeProxy.get('5');262 if (!user) {263 throw new Error('Expected to find record with id "5"');264 }265 user.invalidateRecord();266 });267 expect(callback).toHaveBeenCalledTimes(1);268});269it('does not re-establish subscription id data ids change but array changes', () => {270 render(environment, dataIDs, callback);271 environment.commitUpdate(storeProxy => {272 const user = storeProxy.get('4');273 if (!user) {274 throw new Error('Expected to find record with id "4"');275 }276 user.invalidateRecord();277 });278 expect(callback).toHaveBeenCalledTimes(1);279 const store = environment.getStore();280 jest.spyOn(store, 'subscribeToInvalidationState');281 ReactTestRenderer.act(() => {282 setDataIDs(['client:1', '4']);283 });284 // Assert that we didn't re-subscribe285 expect(store.subscribeToInvalidationState).toHaveBeenCalledTimes(0);286 // Assert that invalidating data ids from initial subscriptions287 // triggers callback again288 environment.commitUpdate(storeProxy => {289 const user = storeProxy.get('4');290 if (!user) {291 throw new Error('Expected to find record with id "4"');292 }293 user.invalidateRecord();294 });295 expect(callback).toHaveBeenCalledTimes(2);296});297it('re-establishes subscription when callback changes', () => {298 render(environment, dataIDs, callback);299 environment.commitUpdate(storeProxy => {300 const user = storeProxy.get('4');301 if (!user) {302 throw new Error('Expected to find record with id "4"');303 }304 user.invalidateRecord();305 });306 expect(callback).toHaveBeenCalledTimes(1);307 const newCallback = jest.fn();308 ReactTestRenderer.act(() => {309 setCallback(newCallback);310 });311 // Assert that invalidating data ids from initial subscriptions312 // does not trigger the old callback anymore, but the new one313 callback.mockClear();314 environment.commitUpdate(storeProxy => {315 const user = storeProxy.get('4');316 if (!user) {317 throw new Error('Expected to find record with id "4"');318 }319 user.invalidateRecord();320 });321 expect(callback).toHaveBeenCalledTimes(0);322 expect(newCallback).toHaveBeenCalledTimes(1);323});324it('re-establishes subscription when environment changes', () => {325 render(environment, dataIDs, callback);326 environment.commitUpdate(storeProxy => {327 const user = storeProxy.get('4');328 if (!user) {329 throw new Error('Expected to find record with id "4"');330 }331 user.invalidateRecord();332 });333 expect(callback).toHaveBeenCalledTimes(1);334 const newEnvironment = createMockEnvironment();335 ReactTestRenderer.act(() => {336 setEnvironment(newEnvironment);337 });338 // Assert that invalidating data ids from initial subscriptions339 // does not trigger callback anymore340 callback.mockClear();341 environment.commitUpdate(storeProxy => {342 const user = storeProxy.get('4');343 if (!user) {344 throw new Error('Expected to find record with id "4"');345 }346 user.invalidateRecord();347 });348 expect(callback).toHaveBeenCalledTimes(0);349 // Assert that invalidating data ids on the new environment350 // triggers the callback351 newEnvironment.commitUpdate(storeProxy => {352 const user = storeProxy.create('4', 'User');353 user.invalidateRecord();354 });355 expect(callback).toHaveBeenCalledTimes(1);...

Full Screen

Full Screen

designPanel.js

Source:designPanel.js Github

copy

Full Screen

1/*2 * Copyright 2020 Google LLC3 *4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 *8 * https://www.apache.org/licenses/LICENSE-2.09 *10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */16/**17 * External dependencies18 */19import PropTypes from 'prop-types';20import styled from 'styled-components';21import { useCallback, useMemo, useRef, useState } from 'react';22/**23 * Internal dependencies24 */25import StoryPropTypes from '../../../types';26import FormContext from '../../form/context';27import useHandlers from '../../../utils/useHandlers';28import updateProperties from './updateProperties';29const Form = styled.form`30 padding: 0;31 margin: 0;32`;33const AutoSubmitButton = styled.input.attrs({ type: 'submit' })`34 display: none;35`;36function DesignPanel({37 panelType,38 selectedElements,39 onSetProperties,40 registerSubmitHandler,41 ...rest42}) {43 const formRef = useRef(null);44 const [presubmitHandlers, registerPresubmitHandler] = useHandlers();45 const formContext = useMemo(46 () => ({47 isMultiple: selectedElements.length > 1,48 registerPresubmitHandler,49 }),50 [selectedElements, registerPresubmitHandler]51 );52 const [elementUpdates, setElementUpdates] = useState({});53 const updatedElements = useMemo(() => {54 return selectedElements.map((element) => ({55 ...element,56 ...elementUpdates[element.id],57 }));58 }, [selectedElements, elementUpdates]);59 const internalSubmit = useCallback(60 (updates) => {61 if (Object.keys(updates).length === 0) {62 return;63 }64 const commitUpdates = updates;65 if (presubmitHandlers.length > 0) {66 selectedElements.forEach((element) => {67 const precommitUpdate = updateProperties(68 element,69 updates[element.id],70 /* commitValues */ true71 );72 let commitUpdate = precommitUpdate;73 presubmitHandlers.forEach((handler) => {74 const handlerUpdate = handler(75 { ...element, ...commitUpdate },76 precommitUpdate,77 element78 );79 commitUpdate = { ...commitUpdate, ...handlerUpdate };80 });81 commitUpdates[element.id] = commitUpdate;82 });83 }84 onSetProperties((element) => commitUpdates[element.id]);85 },86 [presubmitHandlers, selectedElements, onSetProperties]87 );88 const onSubmit = registerSubmitHandler(89 useCallback(90 (evt) => {91 if (evt) {92 evt.preventDefault();93 // Reset.94 setElementUpdates({});95 }96 internalSubmit(elementUpdates);97 },98 [internalSubmit, elementUpdates]99 )100 );101 const submit = useCallback(() => {102 // The `setTimeout()` below only depends on the `ref` and thus save from103 // dismount issues.104 // eslint-disable-next-line @wordpress/react-no-unsafe-timeout105 setTimeout(() => {106 const form = formRef.current;107 if (form) {108 form.dispatchEvent(109 new window.Event('submit', { cancelable: true, bubbles: true })110 );111 }112 });113 }, []);114 const pushUpdate = useCallback(115 (update, submitArg = false) => {116 setElementUpdates((prevUpdates) => {117 const newUpdates = {};118 selectedElements.forEach((element) => {119 const prevUpdatedElement = { ...element, ...prevUpdates[element.id] };120 const newUpdate = updateProperties(prevUpdatedElement, update, false);121 newUpdates[element.id] = { ...prevUpdates[element.id], ...newUpdate };122 });123 return newUpdates;124 });125 if (submitArg) {126 submit();127 }128 },129 [selectedElements, submit]130 );131 const pushUpdateForObject = useCallback(132 (propertyName, update, defaultObject, submitArg = false) => {133 pushUpdate((prevUpdatedElement) => {134 const prevObject = prevUpdatedElement[propertyName] || defaultObject;135 return {136 [propertyName]: update137 ? {138 ...prevObject,139 ...updateProperties(prevObject, update, false),140 }141 : null,142 };143 }, submitArg);144 },145 [pushUpdate]146 );147 const Panel = panelType;148 return (149 <Form ref={formRef} onSubmit={onSubmit}>150 <AutoSubmitButton />151 <FormContext.Provider value={formContext}>152 <Panel153 selectedElements={updatedElements}154 submittedSelectedElements={selectedElements}155 pushUpdate={pushUpdate}156 pushUpdateForObject={pushUpdateForObject}157 submit={submit}158 {...rest}159 />160 </FormContext.Provider>161 </Form>162 );163}164DesignPanel.propTypes = {165 panelType: PropTypes.func.isRequired,166 selectedElements: PropTypes.arrayOf(StoryPropTypes.element).isRequired,167 onSetProperties: PropTypes.func.isRequired,168 registerSubmitHandler: PropTypes.func.isRequired,169};...

Full Screen

Full Screen

DocumentEdit.js

Source:DocumentEdit.js Github

copy

Full Screen

...53 }54 createTag = (tagInfo) => {55 const {relay} = this.props;56 return new Promise((resolve, reject) => {57 relay.commitUpdate(new CreateTagMutation(tagInfo), {58 onSuccess: ({createTag: {tag}}) => resolve(tag),59 onFailure: () => reject(),60 });61 });62 }63 addTag = (tagFromCallback) => {64 const {document, viewer, relay} = this.props;65 const tag = viewer.tags.find(tag => tag.id === tagFromCallback.id);66 relay.commitUpdate(new AddTagToDocumentMutation({document, tag}));67 }68 removeTag = (tagFromCallback) => {69 const {document, viewer, relay} = this.props;70 const tag = viewer.tags.find(tag => tag.id === tagFromCallback.id);71 relay.commitUpdate(new RemoveTagFromDocumentMutation({document, tag}));72 }73 renameDocument = (name) => {74 const {document, relay} = this.props;75 relay.commitUpdate(new RenameDocumentMutation({document, name}));76 }77 deleteDocument = () => {78 const {document, relay, router} = this.props;79 relay.commitUpdate(80 new DeleteDocumentMutation({document}),81 {onSuccess: () => router.push('/documents')},82 );83 }84}85export default Relay.createContainer(withRouter(DocumentEdit), {86 fragments: {87 document: () => Relay.QL`88 fragment on Document {89 name90 ${DocumentPartEditor.getFragment('document')}91 ${RenameDocumentMutation.getFragment('document')}92 ${DeleteDocumentMutation.getFragment('document')}93 ${AddTagToDocumentMutation.getFragment('document')}...

Full Screen

Full Screen

_utils.js

Source:_utils.js Github

copy

Full Screen

1/*2 * Copyright 2020 Google LLC3 *4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 *8 * https://www.apache.org/licenses/LICENSE-2.09 *10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */16/**17 * External dependencies18 */19import { ThemeProvider } from 'styled-components';20import PropTypes from 'prop-types';21import { render } from '@testing-library/react';22import { useMemo } from '@web-stories-wp/react';23/**24 * Internal dependencies25 */26import theme from '../../../../theme';27import useHandlers from '../../../../utils/useHandlers';28import FormContext from '../../../form/context';29import updateProperties from '../../../inspector/design/updateProperties';30function TestPanel({31 panelType,32 selectedElements,33 setPresubmitHandlers,34 wrapperComp,35 ...rest36}) {37 const [presubmitHandlers, registerPresubmitHandler] = useHandlers();38 setPresubmitHandlers(presubmitHandlers);39 const formContext = useMemo(40 () => ({41 isMultiple: selectedElements.length > 1,42 registerPresubmitHandler,43 }),44 [selectedElements, registerPresubmitHandler]45 );46 const Panel = panelType;47 const panel = (48 <FormContext.Provider value={formContext}>49 <Panel50 selectedElements={selectedElements}51 submittedSelectedElements={selectedElements}52 {...rest}53 />54 </FormContext.Provider>55 );56 const Wrapper = wrapperComp;57 if (Wrapper) {58 return <Wrapper>{panel}</Wrapper>;59 }60 return panel;61}62TestPanel.propTypes = {63 panelType: PropTypes.func.isRequired,64 selectedElements: PropTypes.array.isRequired,65 setPresubmitHandlers: PropTypes.func.isRequired,66 wrapperComp: PropTypes.func,67};68/**69 * @param {Function} panelType The panel component function.70 * @param {Array<Object>} selectedElements The array of the selected elements.71 * @param {Function} [wrapperComp] An optional wrapper component.72 * @return {Object} The result of rendering. Includes `pushUpdate`, `pushUpdateForObject`, and `submit` callbacks.73 */74export function renderPanel(panelType, selectedElements, wrapperComp) {75 const pushUpdate = jest.fn();76 const pushUpdateForObject = jest.fn();77 let presubmitHandlers = [];78 const setPresubmitHandlers = (handlers) => {79 presubmitHandlers = handlers;80 };81 const submit = (updates) => {82 const commitUpdates = {};83 selectedElements.forEach((element) => {84 const precommitUpdate = updateProperties(85 element,86 updates,87 /* commitValues */ true88 );89 let commitUpdate = precommitUpdate;90 presubmitHandlers.forEach((handler) => {91 const handlerUpdate = handler(92 { ...element, ...commitUpdate },93 precommitUpdate,94 element95 );96 commitUpdate = { ...commitUpdate, ...handlerUpdate };97 });98 commitUpdates[element.id] = commitUpdate;99 });100 return commitUpdates;101 };102 const view = render(103 <ThemeProvider theme={theme}>104 <TestPanel105 panelType={panelType}106 wrapperComp={wrapperComp}107 selectedElements={selectedElements}108 pushUpdate={pushUpdate}109 pushUpdateForObject={pushUpdateForObject}110 setPresubmitHandlers={setPresubmitHandlers}111 />112 </ThemeProvider>113 );114 return {115 ...view,116 pushUpdate,117 pushUpdateForObject,118 submit,119 };...

Full Screen

Full Screen

ReactFiberCommitWork.js

Source:ReactFiberCommitWork.js Github

copy

Full Screen

...54 var _children = child && !child.sibling ? child.output : child;55 var newProps = finishedWork.memoizedProps;56 var oldProps = current.memoizedProps;57 var instance = finishedWork.stateNode;58 commitUpdate(instance, oldProps, newProps, _children);59 return;60 }61 default:62 throw new Error('This unit of work tag should not have side-effects.');63 }64 }65 return {66 commitWork: commitWork67 };...

Full Screen

Full Screen

upgrade-store-api-0.8.output.js

Source:upgrade-store-api-0.8.output.js Github

copy

Full Screen

...11var RequiredRelay = require('relay');12var RequiredRelayStore = require('RelayStore');13var UninitializedVariable;14// Relay through a require15RequiredRelay.Store.commitUpdate(16 new DoSomethingMutation()17);18// RelayStore through a require19RequiredRelayStore.commitUpdate(20 new DoSomethingMutation()21);22// Relay through indirection23var StoredStore = RequiredRelay.Store;24StoredStore.commitUpdate(25 new DoSomethingMutation()26);27var VebatimStoredStore = Relay.Store;28VebatimStoredStore.commitUpdate(29 new DoSomethingMutation()30);31// RelayStore through indirection32var StoredStoreStore = RequiredRelayStore;33StoredStoreStore.commitUpdate(34 new DoSomethingMutation()35);36var VebatimStoredStoreStore = RelayStore;37VebatimStoredStoreStore.commitUpdate(38 new DoSomethingMutation()39);40// Verbatim Relay.Store41Relay.Store.commitUpdate(42 new DoSomethingMutation()43);44Relay.Store['commitUpdate'](45 new DoSomethingMutation()46);47// Verbatim RelayStore48RelayStore.commitUpdate(49 new DoSomethingMutation()50);51RelayStore['commitUpdate'](52 new DoSomethingMutation()53);54// Non-Relay things to leave alone55foo.update('foo');...

Full Screen

Full Screen

index.js

Source:index.js Github

copy

Full Screen

...7const minorUpdate = require('./minorUpdate');8const updateV8Clone = require('./updateV8Clone');9exports.major = function(options) {10 const tasks = new Listr(11 [updateV8Clone(), majorUpdate(), commitUpdate(), updateVersionNumbers()],12 getOptions(options)13 );14 return tasks.run(options);15};16exports.minor = function(options) {17 const tasks = new Listr(18 [updateV8Clone(), minorUpdate(), commitUpdate()],19 getOptions(options)20 );21 return tasks.run(options);22};23exports.backport = async function(options) {24 const shouldStop = await backport.checkOptions(options);25 if (shouldStop) return;26 const tasks = new Listr(27 [updateV8Clone(), backport.doBackport(options)],28 getOptions(options)29 );30 return tasks.run(options);31};32function getOptions(opts) {...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.click('text=Get started');7 await page.click('text=API reference');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.$$eval('input', (inputs) => {7 inputs.forEach((input) => {8 input.value = 'Playwright';9 });10 });11 await page.commitUpdate();12 await page.screenshot({ path: 'google.png' });13 await browser.close();14})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const {chromium} = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const element = await page.$('input[name="q"]');7 await element._page._delegate._commitUpdate(element._elementId, 'value', 'playwright');8 await browser.close();9})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2(async () => {3 const browser = await playwright.webkit.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.evaluate(() => {7 const input = document.querySelector('input');8 return input.commitUpdate((input) => {9 input.value = 'Hello World';10 });11 });12 await browser.close();13})();14const playwright = require('playwright');15(async () => {16 const browser = await playwright.webkit.launch();17 const context = await browser.newContext();18 const page = await context.newPage();19 await page.evaluate(() => {20 const input = document.querySelector('input');21 return input.commitUpdate((input) => {22 input.value = 'Hello World';23 });24 });25 await browser.close();26})();27const playwright = require('playwright');28(async () => {29 const browser = await playwright.webkit.launch();30 const context = await browser.newContext();31 const page = await context.newPage();32 await page.evaluate(() => {33 const input = document.querySelector('input');34 return input.commitUpdate((input) => {35 input.value = 'Hello World';36 });37 });38 await browser.close();39})();40const playwright = require('playwright');41(async () => {42 const browser = await playwright.webkit.launch();43 const context = await browser.newContext();44 const page = await context.newPage();45 await page.evaluate(() => {46 const input = document.querySelector('input');47 return input.commitUpdate((input) => {48 input.value = 'Hello World';49 });50 });51 await browser.close();52})();53const playwright = require('playwright');54(async () => {55 const browser = await playwright.webkit.launch();56 const context = await browser.newContext();57 const page = await context.newPage();

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderApp');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await commitUpdate(page, 'test');8 await browser.close();9})();10const { chromium } = require('playwright');11const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderApp');12(async () => {13 const browser = await chromium.launch();14 const context = await browser.newContext();15 const page = await context.newPage();16 await commitUpdate(page, 'test');17 await browser.close();18})();19commitUpdate(page, message);

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.click('text=Google apps');8 await page.click('text=Translate');9 await page.click('text=About');

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderSupplement');3(async () => {4 const browser = await playwright.chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await commitUpdate(page, {8 });9 await browser.close();10})();11const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderSupplement');12module.exports = {13 use: {14 workerInit: async ({ page }, testInfo) => {15 await commitUpdate(page, {16 });17 }18 }19};20import { commitUpdate } from 'playwright/lib/server/supplements/recorder/recorderSupplement';21export default {22 use: {23 workerInit: async ({ page }, testInfo) => {24 await commitUpdate(page, {25 });26 }27 }28};29const { commitUpdate } = require('playwright/lib/server/supplements/recorder/recorderSupplement');30module.exports = {31 use: {

Full Screen

Using AI Code Generation

copy

Full Screen

1const { commitUpdate } = require('@playwright/test/lib/server/frames');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const frame = page.mainFrame();8 await commitUpdate(frame, [9 {10 },11 ]);12 await page.screenshot({ path: `example.png` });13 await browser.close();14})();15const { test } = require('@playwright/test');16test('test', async ({ page }) => {17 await page.fill('[data-testid="search-input"]', 'hello');18 await page.screenshot({ path: `example.png` });19});20const { commitUpdate } = require('@playwright/test/lib/server/

Full Screen

Using AI Code Generation

copy

Full Screen

1const { commitUpdate } = require('@playwright/test/lib/server/frames');2const { test } = require('@playwright/test');3test('test', async ({ page }) => {4 await commitUpdate(page, 'body', 'Hello World');5});6module.exports = {7 use: {8 viewport: { width: 1280, height: 720 },9 },10};

Full Screen

Playwright tutorial

LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

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

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful