How to use computeThreadID method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactFiberWorkLoop.old.js

Source:ReactFiberWorkLoop.old.js Github

copy

Full Screen

...2085 }2086 }2087 }2088 }2089 function computeThreadID(root, lane) {2090 // Interaction threads are unique per root and expiration time.2091 // NOTE: Intentionally unsound cast. All that matters is that it's a number2092 // and it represents a batch of work. Could make a helper function instead,2093 // but meh this is fine for now.2094 return lane * 1000 + root.interactionThreadID;2095 }2096 function markSpawnedWork(lane) {2097 if (spawnedWorkDuringRender === null) {2098 spawnedWorkDuringRender = [lane];2099 } else {2100 spawnedWorkDuringRender.push(lane);2101 }2102 }2103 function scheduleInteractions(root, lane, interactions) {2104 if (interactions.size > 0) {2105 var pendingInteractionMap = root.pendingInteractionMap;2106 var pendingInteractions = pendingInteractionMap.get(lane);2107 if (pendingInteractions != null) {2108 interactions.forEach(function (interaction) {2109 if (!pendingInteractions.has(interaction)) {2110 // Update the pending async work count for previously unscheduled interaction.2111 interaction.__count++;2112 }2113 pendingInteractions.add(interaction);2114 });2115 } else {2116 pendingInteractionMap.set(lane, new Set(interactions)); // Update the pending async work count for the current interactions.2117 interactions.forEach(function (interaction) {2118 interaction.__count++;2119 });2120 }2121 var subscriber = __subscriberRef.current;2122 if (subscriber !== null) {2123 var threadID = computeThreadID(root, lane);2124 subscriber.onWorkScheduled(interactions, threadID);2125 }2126 }2127 }2128 function schedulePendingInteractions(root, lane) {2129 scheduleInteractions(root, lane, __interactionsRef.current);2130 }2131 function startWorkOnPendingInteractions(root, lanes) {2132 // we can accurately attribute time spent working on it, And so that cascading2133 // work triggered during the render phase will be associated with it.2134 var interactions = new Set();2135 root.pendingInteractionMap.forEach(function (scheduledInteractions, scheduledLane) {2136 if (includesSomeLane(lanes, scheduledLane)) {2137 scheduledInteractions.forEach(function (interaction) {2138 return interactions.add(interaction);2139 });2140 }2141 }); // Store the current set of interactions on the FiberRoot for a few reasons:2142 // We can re-use it in hot functions like performConcurrentWorkOnRoot()2143 // without having to recalculate it. We will also use it in commitWork() to2144 // pass to any Profiler onRender() hooks. This also provides DevTools with a2145 // way to access it when the onCommitRoot() hook is called.2146 root.memoizedInteractions = interactions;2147 if (interactions.size > 0) {2148 var subscriber = __subscriberRef.current;2149 if (subscriber !== null) {2150 var threadID = computeThreadID(root, lanes);2151 try {2152 subscriber.onWorkStarted(interactions, threadID);2153 } catch (error) {2154 // If the subscriber throws, rethrow it in a separate task2155 scheduleCallback(ImmediatePriority$1, function () {2156 throw error;2157 });2158 }2159 }2160 }2161 }2162 function finishPendingInteractions(root, committedLanes) {2163 var remainingLanesAfterCommit = root.pendingLanes;2164 var subscriber;2165 try {2166 subscriber = __subscriberRef.current;2167 if (subscriber !== null && root.memoizedInteractions.size > 0) {2168 // FIXME: More than one lane can finish in a single commit.2169 var threadID = computeThreadID(root, committedLanes);2170 subscriber.onWorkStopped(root.memoizedInteractions, threadID);2171 }2172 } catch (error) {2173 // If the subscriber throws, rethrow it in a separate task2174 scheduleCallback(ImmediatePriority$1, function () {2175 throw error;2176 });2177 } finally {2178 // Clear completed interactions from the pending Map.2179 // Unless the render was suspended or cascading work was scheduled,2180 // In which case– leave pending interactions until the subsequent render.2181 var pendingInteractionMap = root.pendingInteractionMap;2182 pendingInteractionMap.forEach(function (scheduledInteractions, lane) {2183 // Only decrement the pending interaction count if we're done....

Full Screen

Full Screen

ReactFiberScheduler.js

Source:ReactFiberScheduler.js Github

copy

Full Screen

...752 let subscriber;753 try {754 subscriber = __subscriberRef.current;755 if (subscriber !== null && root.memoizedInteractions.size > 0) {756 const threadID = computeThreadID(757 committedExpirationTime,758 root.interactionThreadID,759 );760 subscriber.onWorkStopped(root.memoizedInteractions, threadID);761 }762 } catch (error) {763 // It's not safe for commitRoot() to throw.764 // Store the error for now and we'll re-throw in finishRendering().765 if (!hasUnhandledError) {766 hasUnhandledError = true;767 unhandledError = error;768 }769 } finally {770 // Clear completed interactions from the pending Map.771 // Unless the render was suspended or cascading work was scheduled,772 // In which case– leave pending interactions until the subsequent render.773 const pendingInteractionMap = root.pendingInteractionMap;774 pendingInteractionMap.forEach(775 (scheduledInteractions, scheduledExpirationTime) => {776 // Only decrement the pending interaction count if we're done.777 // If there's still work at the current priority,778 // That indicates that we are waiting for suspense data.779 if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {780 pendingInteractionMap.delete(scheduledExpirationTime);781 scheduledInteractions.forEach(interaction => {782 interaction.__count--;783 if (subscriber !== null && interaction.__count === 0) {784 try {785 subscriber.onInteractionScheduledWorkCompleted(interaction);786 } catch (error) {787 // It's not safe for commitRoot() to throw.788 // Store the error for now and we'll re-throw in finishRendering().789 if (!hasUnhandledError) {790 hasUnhandledError = true;791 unhandledError = error;792 }793 }794 }795 });796 }797 },798 );799 }800 }801}802function resetChildExpirationTime(803 workInProgress: Fiber,804 renderTime: ExpirationTime,805) {806 if (renderTime !== Never && workInProgress.childExpirationTime === Never) {807 // The children of this component are hidden. Don't bubble their808 // expiration times.809 return;810 }811 let newChildExpirationTime = NoWork;812 // Bubble up the earliest expiration time.813 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {814 // We're in profiling mode.815 // Let's use this same traversal to update the render durations.816 let actualDuration = workInProgress.actualDuration;817 let treeBaseDuration = workInProgress.selfBaseDuration;818 // When a fiber is cloned, its actualDuration is reset to 0.819 // This value will only be updated if work is done on the fiber (i.e. it doesn't bailout).820 // When work is done, it should bubble to the parent's actualDuration.821 // If the fiber has not been cloned though, (meaning no work was done),822 // Then this value will reflect the amount of time spent working on a previous render.823 // In that case it should not bubble.824 // We determine whether it was cloned by comparing the child pointer.825 const shouldBubbleActualDurations =826 workInProgress.alternate === null ||827 workInProgress.child !== workInProgress.alternate.child;828 let child = workInProgress.child;829 while (child !== null) {830 const childUpdateExpirationTime = child.expirationTime;831 const childChildExpirationTime = child.childExpirationTime;832 if (childUpdateExpirationTime > newChildExpirationTime) {833 newChildExpirationTime = childUpdateExpirationTime;834 }835 if (childChildExpirationTime > newChildExpirationTime) {836 newChildExpirationTime = childChildExpirationTime;837 }838 if (shouldBubbleActualDurations) {839 actualDuration += child.actualDuration;840 }841 treeBaseDuration += child.treeBaseDuration;842 child = child.sibling;843 }844 workInProgress.actualDuration = actualDuration;845 workInProgress.treeBaseDuration = treeBaseDuration;846 } else {847 let child = workInProgress.child;848 while (child !== null) {849 const childUpdateExpirationTime = child.expirationTime;850 const childChildExpirationTime = child.childExpirationTime;851 if (childUpdateExpirationTime > newChildExpirationTime) {852 newChildExpirationTime = childUpdateExpirationTime;853 }854 if (childChildExpirationTime > newChildExpirationTime) {855 newChildExpirationTime = childChildExpirationTime;856 }857 child = child.sibling;858 }859 }860 workInProgress.childExpirationTime = newChildExpirationTime;861}862function completeUnitOfWork(workInProgress: Fiber): Fiber | null {863 // Attempt to complete the current unit of work, then move to the864 // next sibling. If there are no more siblings, return to the865 // parent fiber.866 while (true) {867 // The current, flushed, state of this fiber is the alternate.868 // Ideally nothing should rely on this, but relying on it here869 // means that we don't need an additional field on the work in870 // progress.871 const current = workInProgress.alternate;872 if (__DEV__) {873 setCurrentFiber(workInProgress);874 }875 const returnFiber = workInProgress.return;876 const siblingFiber = workInProgress.sibling;877 if ((workInProgress.effectTag & Incomplete) === NoEffect) {878 if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {879 // Don't replay if it fails during completion phase.880 mayReplayFailedUnitOfWork = false;881 }882 // This fiber completed.883 // Remember we're completing this unit so we can find a boundary if it fails.884 nextUnitOfWork = workInProgress;885 if (enableProfilerTimer) {886 if (workInProgress.mode & ProfileMode) {887 startProfilerTimer(workInProgress);888 }889 nextUnitOfWork = completeWork(890 current,891 workInProgress,892 nextRenderExpirationTime,893 );894 if (workInProgress.mode & ProfileMode) {895 // Update render duration assuming we didn't error.896 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);897 }898 } else {899 nextUnitOfWork = completeWork(900 current,901 workInProgress,902 nextRenderExpirationTime,903 );904 }905 if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {906 // We're out of completion phase so replaying is fine now.907 mayReplayFailedUnitOfWork = true;908 }909 stopWorkTimer(workInProgress);910 resetChildExpirationTime(workInProgress, nextRenderExpirationTime);911 if (__DEV__) {912 resetCurrentFiber();913 }914 if (nextUnitOfWork !== null) {915 // Completing this fiber spawned new work. Work on that next.916 return nextUnitOfWork;917 }918 if (919 returnFiber !== null &&920 // Do not append effects to parents if a sibling failed to complete921 (returnFiber.effectTag & Incomplete) === NoEffect922 ) {923 // Append all the effects of the subtree and this fiber onto the effect924 // list of the parent. The completion order of the children affects the925 // side-effect order.926 if (returnFiber.firstEffect === null) {927 returnFiber.firstEffect = workInProgress.firstEffect;928 }929 if (workInProgress.lastEffect !== null) {930 if (returnFiber.lastEffect !== null) {931 returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;932 }933 returnFiber.lastEffect = workInProgress.lastEffect;934 }935 // If this fiber had side-effects, we append it AFTER the children's936 // side-effects. We can perform certain side-effects earlier if937 // needed, by doing multiple passes over the effect list. We don't want938 // to schedule our own side-effect on our own list because if end up939 // reusing children we'll schedule this effect onto itself since we're940 // at the end.941 const effectTag = workInProgress.effectTag;942 // Skip both NoWork and PerformedWork tags when creating the effect list.943 // PerformedWork effect is read by React DevTools but shouldn't be committed.944 if (effectTag > PerformedWork) {945 if (returnFiber.lastEffect !== null) {946 returnFiber.lastEffect.nextEffect = workInProgress;947 } else {948 returnFiber.firstEffect = workInProgress;949 }950 returnFiber.lastEffect = workInProgress;951 }952 }953 if (__DEV__ && ReactFiberInstrumentation.debugTool) {954 ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);955 }956 if (siblingFiber !== null) {957 // If there is more work to do in this returnFiber, do that next.958 return siblingFiber;959 } else if (returnFiber !== null) {960 // If there's no more work in this returnFiber. Complete the returnFiber.961 workInProgress = returnFiber;962 continue;963 } else {964 // We've reached the root.965 return null;966 }967 } else {968 if (enableProfilerTimer && workInProgress.mode & ProfileMode) {969 // Record the render duration for the fiber that errored.970 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);971 // Include the time spent working on failed children before continuing.972 let actualDuration = workInProgress.actualDuration;973 let child = workInProgress.child;974 while (child !== null) {975 actualDuration += child.actualDuration;976 child = child.sibling;977 }978 workInProgress.actualDuration = actualDuration;979 }980 // This fiber did not complete because something threw. Pop values off981 // the stack without entering the complete phase. If this is a boundary,982 // capture values if possible.983 const next = unwindWork(workInProgress, nextRenderExpirationTime);984 // Because this fiber did not complete, don't reset its expiration time.985 if (workInProgress.effectTag & DidCapture) {986 // Restarting an error boundary987 stopFailedWorkTimer(workInProgress);988 } else {989 stopWorkTimer(workInProgress);990 }991 if (__DEV__) {992 resetCurrentFiber();993 }994 if (next !== null) {995 stopWorkTimer(workInProgress);996 if (__DEV__ && ReactFiberInstrumentation.debugTool) {997 ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);998 }999 // If completing this work spawned new work, do that next. We'll come1000 // back here again.1001 // Since we're restarting, remove anything that is not a host effect1002 // from the effect tag.1003 next.effectTag &= HostEffectMask;1004 return next;1005 }1006 if (returnFiber !== null) {1007 // Mark the parent fiber as incomplete and clear its effect list.1008 returnFiber.firstEffect = returnFiber.lastEffect = null;1009 returnFiber.effectTag |= Incomplete;1010 }1011 if (__DEV__ && ReactFiberInstrumentation.debugTool) {1012 ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);1013 }1014 if (siblingFiber !== null) {1015 // If there is more work to do in this returnFiber, do that next.1016 return siblingFiber;1017 } else if (returnFiber !== null) {1018 // If there's no more work in this returnFiber. Complete the returnFiber.1019 workInProgress = returnFiber;1020 continue;1021 } else {1022 return null;1023 }1024 }1025 }1026 // Without this explicit null return Flow complains of invalid return type1027 // TODO Remove the above while(true) loop1028 // eslint-disable-next-line no-unreachable1029 return null;1030}1031function performUnitOfWork(workInProgress: Fiber): Fiber | null {1032 // The current, flushed, state of this fiber is the alternate.1033 // Ideally nothing should rely on this, but relying on it here1034 // means that we don't need an additional field on the work in1035 // progress.1036 const current = workInProgress.alternate;1037 // See if beginning this work spawns more work.1038 startWorkTimer(workInProgress);1039 if (__DEV__) {1040 setCurrentFiber(workInProgress);1041 }1042 if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {1043 stashedWorkInProgressProperties = assignFiberPropertiesInDEV(1044 stashedWorkInProgressProperties,1045 workInProgress,1046 );1047 }1048 let next;1049 if (enableProfilerTimer) {1050 if (workInProgress.mode & ProfileMode) {1051 startProfilerTimer(workInProgress);1052 }1053 next = beginWork(current, workInProgress, nextRenderExpirationTime);1054 workInProgress.memoizedProps = workInProgress.pendingProps;1055 if (workInProgress.mode & ProfileMode) {1056 // Record the render duration assuming we didn't bailout (or error).1057 stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);1058 }1059 } else {1060 next = beginWork(current, workInProgress, nextRenderExpirationTime);1061 workInProgress.memoizedProps = workInProgress.pendingProps;1062 }1063 if (__DEV__) {1064 resetCurrentFiber();1065 if (isReplayingFailedUnitOfWork) {1066 // Currently replaying a failed unit of work. This should be unreachable,1067 // because the render phase is meant to be idempotent, and it should1068 // have thrown again. Since it didn't, rethrow the original error, so1069 // React's internal stack is not misaligned.1070 rethrowOriginalError();1071 }1072 }1073 if (__DEV__ && ReactFiberInstrumentation.debugTool) {1074 ReactFiberInstrumentation.debugTool.onBeginWork(workInProgress);1075 }1076 if (next === null) {1077 // If this doesn't spawn new work, complete the current work.1078 next = completeUnitOfWork(workInProgress);1079 }1080 ReactCurrentOwner.current = null;1081 return next;1082}1083function workLoop(isYieldy) {1084 if (!isYieldy) {1085 // Flush work without yielding1086 while (nextUnitOfWork !== null) {1087 nextUnitOfWork = performUnitOfWork(nextUnitOfWork);1088 }1089 } else {1090 // Flush asynchronous work until there's a higher priority event1091 while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {1092 nextUnitOfWork = performUnitOfWork(nextUnitOfWork);1093 }1094 }1095}1096function renderRoot(root: FiberRoot, isYieldy: boolean): void {1097 invariant(1098 !isWorking,1099 'renderRoot was called recursively. This error is likely caused ' +1100 'by a bug in React. Please file an issue.',1101 );1102 flushPassiveEffects();1103 isWorking = true;1104 if (enableHooks) {1105 ReactCurrentOwner.currentDispatcher = Dispatcher;1106 } else {1107 ReactCurrentOwner.currentDispatcher = DispatcherWithoutHooks;1108 }1109 const expirationTime = root.nextExpirationTimeToWorkOn;1110 // Check if we're starting from a fresh stack, or if we're resuming from1111 // previously yielded work.1112 if (1113 expirationTime !== nextRenderExpirationTime ||1114 root !== nextRoot ||1115 nextUnitOfWork === null1116 ) {1117 // Reset the stack and start working from the root.1118 resetStack();1119 nextRoot = root;1120 nextRenderExpirationTime = expirationTime;1121 nextUnitOfWork = createWorkInProgress(1122 nextRoot.current,1123 null,1124 nextRenderExpirationTime,1125 );1126 root.pendingCommitExpirationTime = NoWork;1127 if (enableSchedulerTracing) {1128 // Determine which interactions this batch of work currently includes,1129 // So that we can accurately attribute time spent working on it,1130 // And so that cascading work triggered during the render phase will be associated with it.1131 const interactions: Set<Interaction> = new Set();1132 root.pendingInteractionMap.forEach(1133 (scheduledInteractions, scheduledExpirationTime) => {1134 if (scheduledExpirationTime >= expirationTime) {1135 scheduledInteractions.forEach(interaction =>1136 interactions.add(interaction),1137 );1138 }1139 },1140 );1141 // Store the current set of interactions on the FiberRoot for a few reasons:1142 // We can re-use it in hot functions like renderRoot() without having to recalculate it.1143 // We will also use it in commitWork() to pass to any Profiler onRender() hooks.1144 // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.1145 root.memoizedInteractions = interactions;1146 if (interactions.size > 0) {1147 const subscriber = __subscriberRef.current;1148 if (subscriber !== null) {1149 const threadID = computeThreadID(1150 expirationTime,1151 root.interactionThreadID,1152 );1153 try {1154 subscriber.onWorkStarted(interactions, threadID);1155 } catch (error) {1156 // Work thrown by an interaction tracing subscriber should be rethrown,1157 // But only once it's safe (to avoid leaving the scheduler in an invalid state).1158 // Store the error for now and we'll re-throw in finishRendering().1159 if (!hasUnhandledError) {1160 hasUnhandledError = true;1161 unhandledError = error;1162 }1163 }1164 }1165 }1166 }1167 }1168 let prevInteractions: Set<Interaction> = (null: any);1169 if (enableSchedulerTracing) {1170 // We're about to start new traced work.1171 // Restore pending interactions so cascading work triggered during the render phase will be accounted for.1172 prevInteractions = __interactionsRef.current;1173 __interactionsRef.current = root.memoizedInteractions;1174 }1175 let didFatal = false;1176 startWorkLoopTimer(nextUnitOfWork);1177 do {1178 try {1179 workLoop(isYieldy);1180 } catch (thrownValue) {1181 resetContextDependences();1182 resetHooks();1183 // Reset in case completion throws.1184 // This is only used in DEV and when replaying is on.1185 let mayReplay;1186 if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {1187 mayReplay = mayReplayFailedUnitOfWork;1188 mayReplayFailedUnitOfWork = true;1189 }1190 if (nextUnitOfWork === null) {1191 // This is a fatal error.1192 didFatal = true;1193 onUncaughtError(thrownValue);1194 } else {1195 if (enableProfilerTimer && nextUnitOfWork.mode & ProfileMode) {1196 // Record the time spent rendering before an error was thrown.1197 // This avoids inaccurate Profiler durations in the case of a suspended render.1198 stopProfilerTimerIfRunningAndRecordDelta(nextUnitOfWork, true);1199 }1200 if (__DEV__) {1201 // Reset global debug state1202 // We assume this is defined in DEV1203 (resetCurrentlyProcessingQueue: any)();1204 }1205 if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {1206 if (mayReplay) {1207 const failedUnitOfWork: Fiber = nextUnitOfWork;1208 replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);1209 }1210 }1211 // TODO: we already know this isn't true in some cases.1212 // At least this shows a nicer error message until we figure out the cause.1213 // https://github.com/facebook/react/issues/12449#issuecomment-3867274311214 invariant(1215 nextUnitOfWork !== null,1216 'Failed to replay rendering after an error. This ' +1217 'is likely caused by a bug in React. Please file an issue ' +1218 'with a reproducing case to help us find it.',1219 );1220 const sourceFiber: Fiber = nextUnitOfWork;1221 let returnFiber = sourceFiber.return;1222 if (returnFiber === null) {1223 // This is the root. The root could capture its own errors. However,1224 // we don't know if it errors before or after we pushed the host1225 // context. This information is needed to avoid a stack mismatch.1226 // Because we're not sure, treat this as a fatal error. We could track1227 // which phase it fails in, but doesn't seem worth it. At least1228 // for now.1229 didFatal = true;1230 onUncaughtError(thrownValue);1231 } else {1232 throwException(1233 root,1234 returnFiber,1235 sourceFiber,1236 thrownValue,1237 nextRenderExpirationTime,1238 );1239 nextUnitOfWork = completeUnitOfWork(sourceFiber);1240 continue;1241 }1242 }1243 }1244 break;1245 } while (true);1246 if (enableSchedulerTracing) {1247 // Traced work is done for now; restore the previous interactions.1248 __interactionsRef.current = prevInteractions;1249 }1250 // We're done performing work. Time to clean up.1251 isWorking = false;1252 ReactCurrentOwner.currentDispatcher = null;1253 resetContextDependences();1254 resetHooks();1255 // Yield back to main thread.1256 if (didFatal) {1257 const didCompleteRoot = false;1258 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1259 interruptedBy = null;1260 // There was a fatal error.1261 if (__DEV__) {1262 resetStackAfterFatalErrorInDev();1263 }1264 // `nextRoot` points to the in-progress root. A non-null value indicates1265 // that we're in the middle of an async render. Set it to null to indicate1266 // there's no more work to be done in the current batch.1267 nextRoot = null;1268 onFatal(root);1269 return;1270 }1271 if (nextUnitOfWork !== null) {1272 // There's still remaining async work in this tree, but we ran out of time1273 // in the current frame. Yield back to the renderer. Unless we're1274 // interrupted by a higher priority update, we'll continue later from where1275 // we left off.1276 const didCompleteRoot = false;1277 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1278 interruptedBy = null;1279 onYield(root);1280 return;1281 }1282 // We completed the whole tree.1283 const didCompleteRoot = true;1284 stopWorkLoopTimer(interruptedBy, didCompleteRoot);1285 const rootWorkInProgress = root.current.alternate;1286 invariant(1287 rootWorkInProgress !== null,1288 'Finished root should have a work-in-progress. This error is likely ' +1289 'caused by a bug in React. Please file an issue.',1290 );1291 // `nextRoot` points to the in-progress root. A non-null value indicates1292 // that we're in the middle of an async render. Set it to null to indicate1293 // there's no more work to be done in the current batch.1294 nextRoot = null;1295 interruptedBy = null;1296 if (nextRenderDidError) {1297 // There was an error1298 if (hasLowerPriorityWork(root, expirationTime)) {1299 // There's lower priority work. If so, it may have the effect of fixing1300 // the exception that was just thrown. Exit without committing. This is1301 // similar to a suspend, but without a timeout because we're not waiting1302 // for a promise to resolve. React will restart at the lower1303 // priority level.1304 markSuspendedPriorityLevel(root, expirationTime);1305 const suspendedExpirationTime = expirationTime;1306 const rootExpirationTime = root.expirationTime;1307 onSuspend(1308 root,1309 rootWorkInProgress,1310 suspendedExpirationTime,1311 rootExpirationTime,1312 -1, // Indicates no timeout1313 );1314 return;1315 } else if (1316 // There's no lower priority work, but we're rendering asynchronously.1317 // Synchronsouly attempt to render the same level one more time. This is1318 // similar to a suspend, but without a timeout because we're not waiting1319 // for a promise to resolve.1320 !root.didError &&1321 isYieldy1322 ) {1323 root.didError = true;1324 const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);1325 const rootExpirationTime = (root.expirationTime = Sync);1326 onSuspend(1327 root,1328 rootWorkInProgress,1329 suspendedExpirationTime,1330 rootExpirationTime,1331 -1, // Indicates no timeout1332 );1333 return;1334 }1335 }1336 if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {1337 // The tree was suspended.1338 const suspendedExpirationTime = expirationTime;1339 markSuspendedPriorityLevel(root, suspendedExpirationTime);1340 // Find the earliest uncommitted expiration time in the tree, including1341 // work that is suspended. The timeout threshold cannot be longer than1342 // the overall expiration.1343 const earliestExpirationTime = findEarliestOutstandingPriorityLevel(1344 root,1345 expirationTime,1346 );1347 const earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);1348 if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {1349 nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;1350 }1351 // Subtract the current time from the absolute timeout to get the number1352 // of milliseconds until the timeout. In other words, convert an absolute1353 // timestamp to a relative time. This is the value that is passed1354 // to `setTimeout`.1355 const currentTimeMs = expirationTimeToMs(requestCurrentTime());1356 let msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;1357 msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;1358 // TODO: Account for the Just Noticeable Difference1359 const rootExpirationTime = root.expirationTime;1360 onSuspend(1361 root,1362 rootWorkInProgress,1363 suspendedExpirationTime,1364 rootExpirationTime,1365 msUntilTimeout,1366 );1367 return;1368 }1369 // Ready to commit.1370 onComplete(root, rootWorkInProgress, expirationTime);1371}1372function captureCommitPhaseError(sourceFiber: Fiber, value: mixed) {1373 const expirationTime = Sync;1374 let fiber = sourceFiber.return;1375 while (fiber !== null) {1376 switch (fiber.tag) {1377 case ClassComponent:1378 const ctor = fiber.type;1379 const instance = fiber.stateNode;1380 if (1381 typeof ctor.getDerivedStateFromError === 'function' ||1382 (typeof instance.componentDidCatch === 'function' &&1383 !isAlreadyFailedLegacyErrorBoundary(instance))1384 ) {1385 const errorInfo = createCapturedValue(value, sourceFiber);1386 const update = createClassErrorUpdate(1387 fiber,1388 errorInfo,1389 expirationTime,1390 );1391 enqueueUpdate(fiber, update);1392 scheduleWork(fiber, expirationTime);1393 return;1394 }1395 break;1396 case HostRoot: {1397 const errorInfo = createCapturedValue(value, sourceFiber);1398 const update = createRootErrorUpdate(fiber, errorInfo, expirationTime);1399 enqueueUpdate(fiber, update);1400 scheduleWork(fiber, expirationTime);1401 return;1402 }1403 }1404 fiber = fiber.return;1405 }1406 if (sourceFiber.tag === HostRoot) {1407 // Error was thrown at the root. There is no parent, so the root1408 // itself should capture it.1409 const rootFiber = sourceFiber;1410 const errorInfo = createCapturedValue(value, rootFiber);1411 const update = createRootErrorUpdate(rootFiber, errorInfo, expirationTime);1412 enqueueUpdate(rootFiber, update);1413 scheduleWork(rootFiber, expirationTime);1414 }1415}1416function computeThreadID(1417 expirationTime: ExpirationTime,1418 interactionThreadID: number,1419): number {1420 // Interaction threads are unique per root and expiration time.1421 return expirationTime * 1000 + interactionThreadID;1422}1423// Creates a unique async expiration time.1424function computeUniqueAsyncExpiration(): ExpirationTime {1425 const currentTime = requestCurrentTime();1426 let result = computeAsyncExpiration(currentTime);1427 if (result >= lastUniqueAsyncExpiration) {1428 // Since we assume the current time monotonically increases, we only hit1429 // this branch when computeUniqueAsyncExpiration is fired multiple times1430 // within a 200ms window (or whatever the async bucket size is).1431 result = lastUniqueAsyncExpiration - 1;1432 }1433 lastUniqueAsyncExpiration = result;1434 return lastUniqueAsyncExpiration;1435}1436function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {1437 let expirationTime;1438 if (expirationContext !== NoWork) {1439 // An explicit expiration context was set;1440 expirationTime = expirationContext;1441 } else if (isWorking) {1442 if (isCommitting) {1443 // Updates that occur during the commit phase should have sync priority1444 // by default.1445 expirationTime = Sync;1446 } else {1447 // Updates during the render phase should expire at the same time as1448 // the work that is being rendered.1449 expirationTime = nextRenderExpirationTime;1450 }1451 } else {1452 // No explicit expiration context was set, and we're not currently1453 // performing work. Calculate a new expiration time.1454 if (fiber.mode & ConcurrentMode) {1455 if (isBatchingInteractiveUpdates) {1456 // This is an interactive update1457 expirationTime = computeInteractiveExpiration(currentTime);1458 } else {1459 // This is an async update1460 expirationTime = computeAsyncExpiration(currentTime);1461 }1462 // If we're in the middle of rendering a tree, do not update at the same1463 // expiration time that is already rendering.1464 if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {1465 expirationTime -= 1;1466 }1467 } else {1468 // This is a sync update1469 expirationTime = Sync;1470 }1471 }1472 if (isBatchingInteractiveUpdates) {1473 // This is an interactive update. Keep track of the lowest pending1474 // interactive expiration time. This allows us to synchronously flush1475 // all interactive updates when needed.1476 if (1477 lowestPriorityPendingInteractiveExpirationTime === NoWork ||1478 expirationTime < lowestPriorityPendingInteractiveExpirationTime1479 ) {1480 lowestPriorityPendingInteractiveExpirationTime = expirationTime;1481 }1482 }1483 return expirationTime;1484}1485function renderDidSuspend(1486 root: FiberRoot,1487 absoluteTimeoutMs: number,1488 suspendedTime: ExpirationTime,1489) {1490 // Schedule the timeout.1491 if (1492 absoluteTimeoutMs >= 0 &&1493 nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs1494 ) {1495 nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;1496 }1497}1498function renderDidError() {1499 nextRenderDidError = true;1500}1501function pingSuspendedRoot(1502 root: FiberRoot,1503 thenable: Thenable,1504 pingTime: ExpirationTime,1505) {1506 // A promise that previously suspended React from committing has resolved.1507 // If React is still suspended, try again at the previous level (pingTime).1508 const pingCache = root.pingCache;1509 if (pingCache !== null) {1510 // The thenable resolved, so we no longer need to memoize, because it will1511 // never be thrown again.1512 pingCache.delete(thenable);1513 }1514 if (nextRoot !== null && nextRenderExpirationTime === pingTime) {1515 // Received a ping at the same priority level at which we're currently1516 // rendering. Restart from the root.1517 nextRoot = null;1518 } else {1519 // Confirm that the root is still suspended at this level. Otherwise exit.1520 if (isPriorityLevelSuspended(root, pingTime)) {1521 // Ping at the original level1522 markPingedPriorityLevel(root, pingTime);1523 const rootExpirationTime = root.expirationTime;1524 if (rootExpirationTime !== NoWork) {1525 requestWork(root, rootExpirationTime);1526 }1527 }1528 }1529}1530function retryTimedOutBoundary(boundaryFiber: Fiber, thenable: Thenable) {1531 // The boundary fiber (a Suspense component) previously timed out and was1532 // rendered in its fallback state. One of the promises that suspended it has1533 // resolved, which means at least part of the tree was likely unblocked. Try1534 // rendering again, at a new expiration time.1535 const retryCache: WeakSet<Thenable> | Set<Thenable> | null =1536 boundaryFiber.stateNode;1537 if (retryCache !== null) {1538 // The thenable resolved, so we no longer need to memoize, because it will1539 // never be thrown again.1540 retryCache.delete(thenable);1541 }1542 const currentTime = requestCurrentTime();1543 const retryTime = computeExpirationForFiber(currentTime, boundaryFiber);1544 const root = scheduleWorkToRoot(boundaryFiber, retryTime);1545 if (root !== null) {1546 markPendingPriorityLevel(root, retryTime);1547 const rootExpirationTime = root.expirationTime;1548 if (rootExpirationTime !== NoWork) {1549 requestWork(root, rootExpirationTime);1550 }1551 }1552}1553function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {1554 recordScheduleUpdate();1555 if (__DEV__) {1556 if (fiber.tag === ClassComponent) {1557 const instance = fiber.stateNode;1558 warnAboutInvalidUpdates(instance);1559 }1560 }1561 // Update the source fiber's expiration time1562 if (fiber.expirationTime < expirationTime) {1563 fiber.expirationTime = expirationTime;1564 }1565 let alternate = fiber.alternate;1566 if (alternate !== null && alternate.expirationTime < expirationTime) {1567 alternate.expirationTime = expirationTime;1568 }1569 // Walk the parent path to the root and update the child expiration time.1570 let node = fiber.return;1571 let root = null;1572 if (node === null && fiber.tag === HostRoot) {1573 root = fiber.stateNode;1574 } else {1575 while (node !== null) {1576 alternate = node.alternate;1577 if (node.childExpirationTime < expirationTime) {1578 node.childExpirationTime = expirationTime;1579 if (1580 alternate !== null &&1581 alternate.childExpirationTime < expirationTime1582 ) {1583 alternate.childExpirationTime = expirationTime;1584 }1585 } else if (1586 alternate !== null &&1587 alternate.childExpirationTime < expirationTime1588 ) {1589 alternate.childExpirationTime = expirationTime;1590 }1591 if (node.return === null && node.tag === HostRoot) {1592 root = node.stateNode;1593 break;1594 }1595 node = node.return;1596 }1597 }1598 if (enableSchedulerTracing) {1599 if (root !== null) {1600 const interactions = __interactionsRef.current;1601 if (interactions.size > 0) {1602 const pendingInteractionMap = root.pendingInteractionMap;1603 const pendingInteractions = pendingInteractionMap.get(expirationTime);1604 if (pendingInteractions != null) {1605 interactions.forEach(interaction => {1606 if (!pendingInteractions.has(interaction)) {1607 // Update the pending async work count for previously unscheduled interaction.1608 interaction.__count++;1609 }1610 pendingInteractions.add(interaction);1611 });1612 } else {1613 pendingInteractionMap.set(expirationTime, new Set(interactions));1614 // Update the pending async work count for the current interactions.1615 interactions.forEach(interaction => {1616 interaction.__count++;1617 });1618 }1619 const subscriber = __subscriberRef.current;1620 if (subscriber !== null) {1621 const threadID = computeThreadID(1622 expirationTime,1623 root.interactionThreadID,1624 );1625 subscriber.onWorkScheduled(interactions, threadID);1626 }1627 }1628 }1629 }1630 return root;1631}1632function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {1633 const root = scheduleWorkToRoot(fiber, expirationTime);1634 if (root === null) {1635 if (__DEV__) {...

Full Screen

Full Screen

ReactFiberScheduler.new.js

Source:ReactFiberScheduler.new.js Github

copy

Full Screen

...1876 }1877 }1878}1879export const warnIfNotCurrentlyActingUpdatesInDev = warnIfNotCurrentlyActingUpdatesInDEV;1880function computeThreadID(root, expirationTime) {1881 // Interaction threads are unique per root and expiration time.1882 return expirationTime * 1000 + root.interactionThreadID;1883}1884function schedulePendingInteraction(root, expirationTime) {1885 // This is called when work is scheduled on a root. It sets up a pending1886 // interaction, which is completed once the work commits.1887 if (!enableSchedulerTracing) {1888 return;1889 }1890 const interactions = __interactionsRef.current;1891 if (interactions.size > 0) {1892 const pendingInteractionMap = root.pendingInteractionMap;1893 const pendingInteractions = pendingInteractionMap.get(expirationTime);1894 if (pendingInteractions != null) {1895 interactions.forEach(interaction => {1896 if (!pendingInteractions.has(interaction)) {1897 // Update the pending async work count for previously unscheduled interaction.1898 interaction.__count++;1899 }1900 pendingInteractions.add(interaction);1901 });1902 } else {1903 pendingInteractionMap.set(expirationTime, new Set(interactions));1904 // Update the pending async work count for the current interactions.1905 interactions.forEach(interaction => {1906 interaction.__count++;1907 });1908 }1909 const subscriber = __subscriberRef.current;1910 if (subscriber !== null) {1911 const threadID = computeThreadID(root, expirationTime);1912 subscriber.onWorkScheduled(interactions, threadID);1913 }1914 }1915}1916function startWorkOnPendingInteraction(root, expirationTime) {1917 // This is called when new work is started on a root.1918 if (!enableSchedulerTracing) {1919 return;1920 }1921 // Determine which interactions this batch of work currently includes, So that1922 // we can accurately attribute time spent working on it, And so that cascading1923 // work triggered during the render phase will be associated with it.1924 const interactions: Set<Interaction> = new Set();1925 root.pendingInteractionMap.forEach(1926 (scheduledInteractions, scheduledExpirationTime) => {1927 if (scheduledExpirationTime >= expirationTime) {1928 scheduledInteractions.forEach(interaction =>1929 interactions.add(interaction),1930 );1931 }1932 },1933 );1934 // Store the current set of interactions on the FiberRoot for a few reasons:1935 // We can re-use it in hot functions like renderRoot() without having to1936 // recalculate it. We will also use it in commitWork() to pass to any Profiler1937 // onRender() hooks. This also provides DevTools with a way to access it when1938 // the onCommitRoot() hook is called.1939 root.memoizedInteractions = interactions;1940 if (interactions.size > 0) {1941 const subscriber = __subscriberRef.current;1942 if (subscriber !== null) {1943 const threadID = computeThreadID(root, expirationTime);1944 try {1945 subscriber.onWorkStarted(interactions, threadID);1946 } catch (error) {1947 // If the subscriber throws, rethrow it in a separate task1948 scheduleCallback(ImmediatePriority, () => {1949 throw error;1950 });1951 }1952 }1953 }1954}1955function finishPendingInteractions(root, committedExpirationTime) {1956 if (!enableSchedulerTracing) {1957 return;1958 }1959 const earliestRemainingTimeAfterCommit = root.firstPendingTime;1960 let subscriber;1961 try {1962 subscriber = __subscriberRef.current;1963 if (subscriber !== null && root.memoizedInteractions.size > 0) {1964 const threadID = computeThreadID(root, committedExpirationTime);1965 subscriber.onWorkStopped(root.memoizedInteractions, threadID);1966 }1967 } catch (error) {1968 // If the subscriber throws, rethrow it in a separate task1969 scheduleCallback(ImmediatePriority, () => {1970 throw error;1971 });1972 } finally {1973 // Clear completed interactions from the pending Map.1974 // Unless the render was suspended or cascading work was scheduled,1975 // In which case– leave pending interactions until the subsequent render.1976 const pendingInteractionMap = root.pendingInteractionMap;1977 pendingInteractionMap.forEach(1978 (scheduledInteractions, scheduledExpirationTime) => {...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2const { computeThreadID } = require('playwright/lib/server/worker');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const threadID = await computeThreadID(page);8 console.log(threadID);9 await browser.close();10})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2const { computeThreadID } = require('playwright/lib/server/workerRunner');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const threadID = await computeThreadID(page);8 console.log(threadID);9 await browser.close();10})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2const { computeThreadID } = playwright._internal;3const playwright = require('playwright');4const { computeThreadID } = playwright._internal;5const { chromium } = playwright;6(async () => {7 const browser = await chromium.launch();8 const page = await browser.newPage();9 await page.screenshot({ path: `example-${computeThreadID()}.png` });10 await browser.close();11})();12const playwright = require('playwright');13const { computeThreadID } = playwright._internal;14const { chromium } = playwright;15(async () => {16 const browser = await chromium.launch();17 const page = await browser.newPage();18 await page.screenshot({ path: `example-${computeThreadID()}.png` });19 await browser.close();20})();21const playwright = require('playwright');22const { computeThreadID } = playwright._internal;23const { chromium } = playwright;24(async () => {25 const browser = await chromium.launch();26 const page = await browser.newPage();27 await page.screenshot({ path: `example-${computeThreadID()}.png` });28 await browser.close();29})();30const playwright = require('playwright');31const { computeThreadID } = playwright._internal;32const { chromium } = playwright;33(async () => {34 const browser = await chromium.launch();35 const page = await browser.newPage();36 await page.screenshot({ path: `example-${computeThreadID()}.png` });37 await browser.close();38})();39const playwright = require('playwright');40const { computeThreadID } = playwright._internal;41const { chromium } = playwright;42(async () => {43 const browser = await chromium.launch();44 const page = await browser.newPage();45 await page.goto('

Full Screen

Using AI Code Generation

copy

Full Screen

1const { computeThreadID } = require('playwright/lib/utils/worker');2const { Worker } = require('worker_threads');3const worker = new Worker(`4const { computeThreadID } = require('playwright/lib/utils/worker');5console.log(computeThreadID());6`, { eval: true });7worker.on('message', (message) => {8 console.log(message);9});10worker.on('error', (error) => {11 console.error(error);12});13worker.on('exit', (code) => {14 if (code !== 0)15 console.error(new Error(`Worker stopped with exit code ${code}`));16});17worker.postMessage('computeThreadID');18const { threadId } = require('worker_threads');19console.log(threadId);

Full Screen

Using AI Code Generation

copy

Full Screen

1const { computeThreadID } = require('playwright/lib/server/browserContext');2const threadID = computeThreadID({ browserName: 'chromium', deviceScaleFactor: 1, isMobile: false, hasTouch: false, viewport: { width: 800, height: 600 } });3console.log(threadID);4const { computeThreadID } = require('playwright/lib/server/browserContext');5const threadID = computeThreadID({ browserName: 'chromium', deviceScaleFactor: 1, isMobile: false, hasTouch: false, viewport: { width: 800, height: 600 } });6console.log(threadID);7const { computeThreadID } = require('playwright/lib/server/browserContext'); const threadID = computeThreadID({ browserName: 'chromium', deviceScaleFactor: 1, isMobile: false, hasTouch: false, viewport: { width: 800, height: 600 } }); console.log(threadID);

Full Screen

Using AI Code Generation

copy

Full Screen

1const { computeThreadID } = require('playwright/lib/utils/utils');2const threadID = computeThreadID();3console.log(threadID);4const { computeThreadID } = require('playwright/lib/utils/utils');5const threadID = computeThreadID();6console.log(threadID);7const { computeThreadID } = require('playwright/lib/utils/utils');8const threadID = computeThreadID();9console.log(threadID);10const { computeThreadID } = require('playwright/lib/utils/utils');11const threadID = computeThreadID();12console.log(threadID);13const { computeThreadID } = require('playwright/lib/utils/utils');14const threadID = computeThreadID();15console.log(threadID);16const { computeThreadID } = require('playwright/lib/utils/utils');17const threadID = computeThreadID();18console.log(threadID);19const { computeThreadID } = require('playwright/lib/utils/utils');20const threadID = computeThreadID();21console.log(threadID);22const { computeThreadID } = require('playwright/lib/utils/utils');23const threadID = computeThreadID();24console.log(threadID);25const { computeThreadID } = require('playwright/lib/utils/utils');26const threadID = computeThreadID();27console.log(threadID);28const { computeThreadID } = require('playwright/lib/utils/utils');29const threadID = computeThreadID();30console.log(threadID);31const { computeThreadID } = require('playwright/lib/utils/utils');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const page = await browser.newPage();5 const threadID = await page.computeThreadID();6 console.log(threadID);7 await browser.close();8})();

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