[{"data":1,"prerenderedAt":1250},["ShallowReactive",2],{"SiteFooter_cLEacilcm59lTjFJjLY6mXNWLL0ETtzOIiZwIHgDpy0":3,"doc-\u002Fdocs\u002Fconcepts\u002Fwaiting":17,"docs-nav":1136,"nav-compare":1210,"editor-94vyek":1220,"editor-1ia902h":1222,"editor-1ukites":1224,"editor-16s76n1":1226,"editor-17310rj":1228,"editor-k4fov7":1230,"editor-1x1ihyh":1232,"editor-545730":1234,"editor-1a0i1gx":1236,"editor-1cppue1":1238,"editor-9f4bxk":1240,"editor-fs0qgf":1242,"editor-8fauvo":1244,"editor-1mwv2ax":1246,"editor-yhvzzg":1248},["Island",4],{"key":5,"result":6},"SiteFooter_cLEacilcm59lTjFJjLY6mXNWLL0ETtzOIiZwIHgDpy0",{"head":7},{"style":8},[9,11,13,15],{"innerHTML":10},".site-footer[data-v-8bb1d4ae]{border-top:1px solid var(--line);background:var(--chrome);padding:var(--sp-8) var(--gutter) var(--sp-10);gap:var(--sp-6);flex-direction:column;display:flex}.links[data-v-8bb1d4ae]{gap:var(--sp-4);grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));max-width:62rem;display:grid}.links a[data-v-8bb1d4ae]{align-items:flex-start;gap:var(--sp-3);padding:var(--sp-3);border-bottom:1px solid var(--line);border-radius:var(--radius-sm);background:var(--panel);color:var(--ink);border:1px solid var(--line);display:flex}.links a[data-v-8bb1d4ae]:focus-visible,.links a[data-v-8bb1d4ae]:hover{border-color:var(--line-strong)}.links[data-v-8bb1d4ae] .mark,.links[data-v-8bb1d4ae] .rf-mark{width:1.35rem;height:1.35rem;color:var(--dim);flex:none;margin-top:.1rem}.links a[data-v-8bb1d4ae]:hover .mark,.links a[data-v-8bb1d4ae]:hover .rf-mark{color:var(--red)}.links span[data-v-8bb1d4ae]{flex-direction:column;gap:2px;min-width:0;display:flex}.links b[data-v-8bb1d4ae]{font-family:var(--font-display);font-size:.85rem;font-weight:400}.links small[data-v-8bb1d4ae]{color:var(--faint);font-size:.78rem}.colophon[data-v-8bb1d4ae]{color:var(--faint);margin:0;font-size:.75rem}@supports (corner-shape:bevel){.links a[data-v-8bb1d4ae]{corner-shape:bevel}}",{"innerHTML":12},".mark[data-v-cc0d96a5]{vertical-align:-.125em;flex:none;width:1em;height:1em}",{"innerHTML":14},".mark[data-v-26f5673d]{vertical-align:-.125em;flex:none;width:1em;height:1em}",{"innerHTML":16},".rf-mark[data-v-26317b3e]{fill:currentColor;stroke:none;flex:none}",{"id":18,"title":19,"body":20,"description":1128,"extension":1129,"meta":1130,"navigation":176,"order":180,"path":1131,"section":1132,"seo":1133,"stem":1134,"__hash__":1135},"docs\u002Fdocs\u002Fconcepts\u002Fwaiting.md","Waiting and promises",{"type":21,"value":22,"toc":1107},"minimark",[23,32,37,40,45,48,65,68,77,88,99,103,110,113,117,124,130,137,153,156,224,239,264,269,279,282,286,289,346,372,401,407,417,420,477,491,495,506,549,575,596,600,610,619,623,660,674,678,682,685,689,696,700,703,714,722,729,733,736,768,793,796,800,809,834,837,843,863,871,875,885,905,908,937,940,960,979,983,986,1025,1035,1042,1046,1103],[24,25,26,27,31],"p",{},"Most of the waiting you would write by hand is already happening. This page is\nabout the part that is not, and about the opposite problem: when you need two\nthings to happen ",[28,29,30],"em",{},"at the same time",".",[33,34,36],"h2",{"id":35},"what-waits-for-you-already","What waits for you already",[24,38,39],{},"Three separate mechanisms, and it is worth knowing which is which, because when\na test is flaky the fix depends on it.",[41,42],"doc-table",{":head":43,":rows":44},"[\"Mechanism\",\"Waits for\",\"Governed by\"]","[[\"**Actionability**\",\"An element to be attached, visible, stable and able to receive the event — before `Click`, `Fill Text` and friends act\",\"`timeout`\"],[\"**Assertion retry**\",\"A *value* to become what you asserted, re-reading it as often as needed\",\"`retry_assertions_for`, capped by `timeout`\"],[\"**Navigation**\",\"`New Page` and `Go To` to reach their load state before returning\",\"`timeout`\"]]",[24,46,47],{},"So this is already a wait, and the most common one you will write:",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-robot-repl shiki shiki-themes github-light github-dark","Get Text    .status    ==    Done\n","robot-repl","",[56,57,58],"code",{"__ignoreMap":54},[59,60,63],"span",{"class":61,"line":62},"line",1,[59,64,52],{},[24,66,67],{},"And this is not — no operator, so it reads once and returns whatever is there:",[49,69,71],{"className":51,"code":70,"language":53,"meta":54,"style":54},"${status} =    Get Text    .status\n",[56,72,73],{"__ignoreMap":54},[59,74,75],{"class":61,"line":62},[59,76,70],{},[24,78,79,80,83,84,87],{},"That difference is the single most useful thing to know about waiting in this\nlibrary. A getter ",[28,81,82],{},"with"," an assertion retries; a getter ",[28,85,86],{},"without"," one does not.",[24,89,90,91,94,95,98],{},"Both settings can be changed at import, and at runtime with ",[56,92,93],{},"Set Browser Timeout","\nand ",[56,96,97],{},"Set Retry Assertions For"," — each of which takes a scope, so you can widen\nthem for one test without widening them for the suite.",[33,100,102],{"id":101},"when-that-is-not-enough","When that is not enough",[24,104,105,106,109],{},"The built-in waiting covers the element you are about to touch and the value you\nare about to check. What it does not cover is everything else the page might be\ndoing: a spinner that has to ",[28,107,108],{},"disappear",", an animation that has to finish, a\nframework that has to declare itself ready.",[24,111,112],{},"Two keywords do that job, and they are siblings. The difference is which side of\nthe wire the condition lives on.",[41,114],{":head":115,":rows":116},"[\"Keyword\",\"Evaluates\",\"Reach for it when\"]","[[\"`Wait For Condition`\",\"A Browser **getter**, in Robot Framework\",\"The thing you are waiting for is something a Browser keyword can already read\"],[\"`Wait For Function`\",\"**JavaScript**, inside the page\",\"The thing you are waiting for is only visible to the page itself\"]]",[24,118,119,120,123],{},"If you can already write a ",[56,121,122],{},"Get …"," assertion for it, use the first. If you would\nhave to open devtools to see it, use the second.",[33,125,127],{"id":126},"wait-for-condition",[56,128,129],{},"Wait For Condition",[24,131,132,133,136],{},"The rule is simple enough that you do not have to learn anything new. Write the\nassertion as an ordinary getter first, get it passing, and then take the ",[56,134,135],{},"Get","\noff the front:",[49,138,140],{"className":51,"code":139,"language":53,"meta":54,"style":54},"Get Text              id=status_bar    contains    Done\nWait For Condition    Text    id=status_bar    contains    Done\n",[56,141,142,147],{"__ignoreMap":54},[59,143,144],{"class":61,"line":62},[59,145,146],{},"Get Text              id=status_bar    contains    Done\n",[59,148,150],{"class":61,"line":149},2,[59,151,152],{},"Wait For Condition    Text    id=status_bar    contains    Done\n",[24,154,155],{},"Everything after the condition name is that getter's own arguments, so anything\nyou already know how to assert, you already know how to wait for.",[49,157,159],{"className":51,"code":158,"language":53,"meta":54,"style":54},"# The upload finished\nWait For Condition    Text            .progress     ==    100%\n\n# The table filled in\nWait For Condition    Element Count   tbody tr       ==    ${25}\n\n# The single-page app actually navigated\nWait For Condition    Url             should end with    \u002Fcheckout\n\n# A CSS class arrived\nWait For Condition    Classes         .modal    contains    is-open\n",[56,160,161,166,171,178,184,190,195,201,207,212,218],{"__ignoreMap":54},[59,162,163],{"class":61,"line":62},[59,164,165],{},"# The upload finished\n",[59,167,168],{"class":61,"line":149},[59,169,170],{},"Wait For Condition    Text            .progress     ==    100%\n",[59,172,174],{"class":61,"line":173},3,[59,175,177],{"emptyLinePlaceholder":176},true,"\n",[59,179,181],{"class":61,"line":180},4,[59,182,183],{},"# The table filled in\n",[59,185,187],{"class":61,"line":186},5,[59,188,189],{},"Wait For Condition    Element Count   tbody tr       ==    ${25}\n",[59,191,193],{"class":61,"line":192},6,[59,194,177],{"emptyLinePlaceholder":176},[59,196,198],{"class":61,"line":197},7,[59,199,200],{},"# The single-page app actually navigated\n",[59,202,204],{"class":61,"line":203},8,[59,205,206],{},"Wait For Condition    Url             should end with    \u002Fcheckout\n",[59,208,210],{"class":61,"line":209},9,[59,211,177],{"emptyLinePlaceholder":176},[59,213,215],{"class":61,"line":214},10,[59,216,217],{},"# A CSS class arrived\n",[59,219,221],{"class":61,"line":220},11,[59,222,223],{},"Wait For Condition    Classes         .modal    contains    is-open\n",[24,225,226,227,230,231,234,235,238],{},"Most getters that take an assertion can be used, but not all — the id getters,\nthe storage getters, ",[56,228,229],{},"Console Log",", ",[56,232,233],{},"Page Errors"," and ",[56,236,237],{},"Aria Snapshot"," are not\namong them, and passing one fails with a conversion error rather than waiting.\nThe keyword documentation lists the exact set.",[240,241,242],"doc-note",{},[24,243,244,247,248,252,253,256,257,260,261,263],{},[56,245,246],{},"timeout"," here governs how long the ",[249,250,251],"strong",{},"assertion"," is retried, not how long an\nelement is looked for. It temporarily ",[28,254,255],{},"sets"," ",[56,258,259],{},"retry_assertions_for"," to that\nvalue — so a short one shortens the retry window just as a long one lengthens it\n— and additionally raises the browser ",[56,262,246],{}," if that would otherwise be the\nshorter of the two. Both are restored afterwards.",[265,266,268],"h3",{"id":267},"the-pairing-that-does-most-of-the-work","The pairing that does most of the work",[24,270,271,274,275,278],{},[56,272,273],{},"Get Element States"," returns the ",[28,276,277],{},"set"," of states an element is in right now, and\nthat is what makes it the most useful condition of the lot: one keyword covers\nevery \"wait until this element is…\" case, including the ones no dedicated\nkeyword exists for.",[24,280,281],{},"The states come in opposites, which is the part worth internalising:",[41,283],{":head":284,":rows":285},"[\"If you are waiting for\",\"Wait for the state\"]","[[\"The element to exist \u002F be gone\",\"`attached` \u002F `detached`\"],[\"It to be shown \u002F not shown\",\"`visible` \u002F `hidden`\"],[\"A control to become usable \u002F unusable\",\"`enabled` \u002F `disabled`\"],[\"A field to accept typing \u002F not\",\"`editable` \u002F `readonly`\"],[\"A box to be ticked \u002F cleared\",\"`checked` \u002F `unchecked`\"],[\"An option to be picked \u002F dropped\",\"`selected` \u002F `deselected`\"],[\"The cursor to land \u002F leave\",\"`focused` \u002F `defocused`\"]]",[24,287,288],{},"So the four cases you will actually hit:",[49,290,292],{"className":51,"code":291,"language":53,"meta":54,"style":54},"# The overlay is gone from the DOM entirely — not merely invisible\nWait For Condition    Element States    id=cdk-overlay-0    ==    detached\n\n# The submit button became usable after validation\nWait For Condition    Element States    button#submit    contains    enabled\n\n# The field is ready to type into: there, shown, and not read-only\nWait For Condition    Element States    input#search    contains    visible    editable\n\n# Focus moved away, so the blur handler has run\nWait For Condition    Element States    input#amount    contains    defocused\n",[56,293,294,299,304,308,313,318,322,327,332,336,341],{"__ignoreMap":54},[59,295,296],{"class":61,"line":62},[59,297,298],{},"# The overlay is gone from the DOM entirely — not merely invisible\n",[59,300,301],{"class":61,"line":149},[59,302,303],{},"Wait For Condition    Element States    id=cdk-overlay-0    ==    detached\n",[59,305,306],{"class":61,"line":173},[59,307,177],{"emptyLinePlaceholder":176},[59,309,310],{"class":61,"line":180},[59,311,312],{},"# The submit button became usable after validation\n",[59,314,315],{"class":61,"line":186},[59,316,317],{},"Wait For Condition    Element States    button#submit    contains    enabled\n",[59,319,320],{"class":61,"line":192},[59,321,177],{"emptyLinePlaceholder":176},[59,323,324],{"class":61,"line":197},[59,325,326],{},"# The field is ready to type into: there, shown, and not read-only\n",[59,328,329],{"class":61,"line":203},[59,330,331],{},"Wait For Condition    Element States    input#search    contains    visible    editable\n",[59,333,334],{"class":61,"line":209},[59,335,177],{"emptyLinePlaceholder":176},[59,337,338],{"class":61,"line":214},[59,339,340],{},"# Focus moved away, so the blur handler has run\n",[59,342,343],{"class":61,"line":220},[59,344,345],{},"Wait For Condition    Element States    input#amount    contains    defocused\n",[24,347,348,351,352,355,356,359,360,363,364,367,368,371],{},[56,349,350],{},"contains"," means ",[28,353,354],{},"all of these are true",", so you can list as many as you need.\n",[56,357,358],{},"=="," means the set is ",[28,361,362],{},"exactly"," that, which is why ",[56,365,366],{},"== detached"," is the honest\nway to wait for something to disappear: an element that is gone reports\n",[56,369,370],{},"detached"," and nothing else.",[240,373,375],{"kind":374},"warning",[24,376,377,380,381,384,385,387,388,391,392,395,396,398,399,31],{},[56,378,379],{},"stable"," is the exception. It is a valid state for ",[56,382,383],{},"Wait For Elements State",", but\n",[56,386,273],{}," never reports it — so ",[56,389,390],{},"Wait For Condition … contains stable","\npolls until the timeout and fails, always. Wait for ",[56,393,394],{},"visible"," instead, or use\n",[56,397,383],{}," with ",[56,400,379],{},[33,402,404],{"id":403},"wait-for-function",[56,405,406],{},"Wait For Function",[24,408,409,410,412,413,416],{},"The other half of the pair. Where ",[56,411,129],{}," asks a Browser getter,\nthis one runs JavaScript ",[249,414,415],{},"inside the page"," and keeps running it until it\nreturns something truthy.",[24,418,419],{},"Reach for it when the thing you are waiting for is not in the DOM in any way a\nselector can express — it is in the application's own state:",[49,421,423],{"className":51,"code":422,"language":53,"meta":54,"style":54},"# The framework says it has finished booting\nWait For Function    () => window.myApp.ready === true\n\n# The client-side store has data in it\nWait For Function    () => window.__STORE__.getState().cart.items.length > 0\n\n# A third-party widget has attached itself\nWait For Function    () => typeof window.Intercom === 'function'\n\n# Every image has finished loading — successfully or not\nWait For Function    () => [...document.images].every(i => i.complete)\n",[56,424,425,430,435,439,444,449,453,458,463,467,472],{"__ignoreMap":54},[59,426,427],{"class":61,"line":62},[59,428,429],{},"# The framework says it has finished booting\n",[59,431,432],{"class":61,"line":149},[59,433,434],{},"Wait For Function    () => window.myApp.ready === true\n",[59,436,437],{"class":61,"line":173},[59,438,177],{"emptyLinePlaceholder":176},[59,440,441],{"class":61,"line":180},[59,442,443],{},"# The client-side store has data in it\n",[59,445,446],{"class":61,"line":186},[59,447,448],{},"Wait For Function    () => window.__STORE__.getState().cart.items.length > 0\n",[59,450,451],{"class":61,"line":192},[59,452,177],{"emptyLinePlaceholder":176},[59,454,455],{"class":61,"line":197},[59,456,457],{},"# A third-party widget has attached itself\n",[59,459,460],{"class":61,"line":203},[59,461,462],{},"Wait For Function    () => typeof window.Intercom === 'function'\n",[59,464,465],{"class":61,"line":209},[59,466,177],{"emptyLinePlaceholder":176},[59,468,469],{"class":61,"line":214},[59,470,471],{},"# Every image has finished loading — successfully or not\n",[59,473,474],{"class":61,"line":220},[59,475,476],{},"Wait For Function    () => [...document.images].every(i => i.complete)\n",[24,478,479,480,483,484,487,488,31],{},"That last one is the flavour of problem this keyword exists for: nothing about\n\"every image has settled\" is expressible as a selector, and no getter returns\nit. Note what ",[56,481,482],{},"complete"," actually means — loading ",[28,485,486],{},"finished",", including having\nfailed. If you need them to have loaded successfully, add\n",[56,489,490],{},"&& i.naturalWidth > 0",[265,492,494],{"id":493},"waiting-on-one-element","Waiting on one element",[24,496,497,498,501,502,505],{},"Pass a ",[56,499,500],{},"selector"," and it is resolved and handed to your function as its ",[249,503,504],{},"first\nargument",". The condition then becomes a question about that element, evaluated\nin the page where the real computed values live:",[49,507,509],{"className":51,"code":508,"language":53,"meta":54,"style":54},"# The app has written an inline width of 100% onto the bar\nWait For Function    element => element.style.width === '100%'    selector=#progress_bar\n\n# The sticky header has settled at the top of the viewport\nWait For Function    el => el.getBoundingClientRect().top === 0    selector=.sticky-header\n\n# Something has actually been drawn into the canvas\nWait For Function    c => c.getContext('2d').getImageData(0,0,1,1).data[3] > 0    selector=canvas#chart\n",[56,510,511,516,521,525,530,535,539,544],{"__ignoreMap":54},[59,512,513],{"class":61,"line":62},[59,514,515],{},"# The app has written an inline width of 100% onto the bar\n",[59,517,518],{"class":61,"line":149},[59,519,520],{},"Wait For Function    element => element.style.width === '100%'    selector=#progress_bar\n",[59,522,523],{"class":61,"line":173},[59,524,177],{"emptyLinePlaceholder":176},[59,526,527],{"class":61,"line":180},[59,528,529],{},"# The sticky header has settled at the top of the viewport\n",[59,531,532],{"class":61,"line":186},[59,533,534],{},"Wait For Function    el => el.getBoundingClientRect().top === 0    selector=.sticky-header\n",[59,536,537],{"class":61,"line":192},[59,538,177],{"emptyLinePlaceholder":176},[59,540,541],{"class":61,"line":197},[59,542,543],{},"# Something has actually been drawn into the canvas\n",[59,545,546],{"class":61,"line":203},[59,547,548],{},"Wait For Function    c => c.getContext('2d').getImageData(0,0,1,1).data[3] > 0    selector=canvas#chart\n",[24,550,551,552,555,556,559,560,562,563,566,567,570,571,574],{},"Two things to know about that first one. ",[56,553,554],{},"element.style"," is the ",[28,557,558],{},"inline"," style\nattribute, not the computed value — it only sees a width the application wrote\nonto the element itself, never one that came from a stylesheet. And computed\nvalues are not out of reach for the sibling keyword either: ",[56,561,129],{},"\nwith ",[56,564,565],{},"Style"," reads ",[56,568,569],{},"getComputedStyle",", and with ",[56,572,573],{},"BoundingBox"," it reads geometry.\nCanvas pixels are the genuine case where only JavaScript will do.",[240,576,577],{"kind":374},[24,578,579,580,583,584,587,588,591,592,595],{},"The selector is resolved in ",[249,581,582],{},"strict mode",", so it has to match exactly one\nelement — which is why the canvas example says ",[56,585,586],{},"canvas#chart"," rather than\n",[56,589,590],{},"canvas",". It is also resolved ",[249,593,594],{},"once",", before polling starts, and the same\nelement is handed to every poll. If the application re-renders and replaces the\nnode, the function keeps testing the old one.",[265,597,599],{"id":598},"polling","Polling",[24,601,602,603,606,607,609],{},"By default it polls on ",[56,604,605],{},"requestAnimationFrame"," — once per frame, which is the\nright choice for anything visual, because it re-checks exactly when the browser\nrepaints. Give ",[56,608,598],{}," a time instead when you are waiting on something slow\nand want to stop burning frames on it:",[49,611,613],{"className":51,"code":612,"language":53,"meta":54,"style":54},"Wait For Function    () => window.jobStatus === 'done'    polling=2s    timeout=2min\n",[56,614,615],{"__ignoreMap":54},[59,616,617],{"class":61,"line":62},[59,618,612],{},[265,620,622],{"id":621},"two-things-that-catch-people","Two things that catch people",[24,624,625,256,628,230,631,230,634,234,637,640,641,644,645,648,649,652,653,655,656,659],{},[249,626,627],{},"Truthy is JavaScript's truthy.",[56,629,630],{},"0",[56,632,633],{},"''",[56,635,636],{},"null",[56,638,639],{},"undefined"," all read as\n\"not yet\", so ",[56,642,643],{},"element => element.children.length"," waits for a ",[28,646,647],{},"non-empty"," list\nwithout you writing the comparison, and ",[56,650,651],{},"() => document.querySelector('.x')","\nwaits for the element to exist, because a missing one is ",[56,654,636],{},". If your function\ntakes the element, declare the parameter — a bare ",[56,657,658],{},"element"," inside a zero-argument\narrow is not defined and, thanks to the next paragraph, costs you the whole\ntimeout before it says so.",[24,661,662,665,666,669,670,673],{},[249,663,664],{},"Any error is treated as \"not yet\"",", for the length of the timeout. That is\nwhat makes ",[56,667,668],{},"() => window.myApp.ready"," safe to run before ",[56,671,672],{},"myApp"," exists — but it\napplies to every error, not only the one you were expecting. A typo in your\nJavaScript, or a selector that matches two elements, is retried silently for the\nfull timeout and only then surfaces.",[33,675,677],{"id":676},"choosing-between-them","Choosing between them",[41,679],{":head":680,":rows":681},"[\"You are waiting for\",\"Use\"]","[[\"Text, a count, a class, an attribute, a URL\",\"`Wait For Condition`\"],[\"An element to appear, vanish, or become usable\",\"`Wait For Condition` with `Element States`\"],[\"A framework or widget to be ready\",\"`Wait For Function`\"],[\"An animation or transition to finish\",\"`Wait For Function`\"],[\"Application state that never reaches the DOM\",\"`Wait For Function`\"],[\"The page to finish loading\",\"`Wait For Load State`\"],[\"A request or response to happen\",\"`Wait For Response` \u002F `Wait For Request` — and promise it, see below\"],[\"A navigation caused by a click\",\"`Wait For Navigation` — promise it too\"]]",[24,683,684],{},"Prefer the first wherever it fits. A condition written against a getter fails\nwith a message naming what the value actually was; a JavaScript one that simply\nstayed false fails with a timeout and leaves you to work out why.",[33,686,688],{"id":687},"promises","Promises",[24,690,691,692,695],{},"Everything above waits for something to ",[28,693,694],{},"become"," true. Promises are the\nopposite: they let a keyword run while your test carries on doing something\nelse.",[265,697,699],{"id":698},"the-idea-if-it-is-new","The idea, if it is new",[24,701,702],{},"A promise is a placeholder for a result that does not exist yet.",[24,704,705,706,709,710,713],{},"Normally a keyword blocks: the test stops until it finishes, then continues with\nthe answer. ",[56,707,708],{},"Promise To"," breaks that in half. It ",[249,711,712],{},"starts"," the keyword and\nhands you back a token immediately. The keyword goes on running in the\nbackground while your test does the next thing. Later you present the token and\ncollect the result — waiting at that point only if it has not finished yet.",[49,715,720],{"className":716,"code":718,"language":719,"meta":54},[717],"language-text","ordinary:   ────[ Wait For Response ]────▶ result, then the next keyword\n\npromised:   ──┬─[ Wait For Response ]──┐\n              └─[ Click ]──────────────┴──▶ collect the result\n","text",[56,721,718],{"__ignoreMap":54},[24,723,724,725,728],{},"The reason this matters is that some things can only be observed ",[28,726,727],{},"while","\nsomething else happens. To catch a network response you have to be listening\nbefore the click that triggers it — but if you start listening with an ordinary\nkeyword, the test never reaches the click. The listening and the clicking have\nto overlap, and that is what a promise is for.",[265,730,732],{"id":731},"the-call-order","The call order",[24,734,735],{},"Always three steps, in this order:",[49,737,741],{"className":738,"code":739,"language":740,"meta":54,"style":54},"language-robot shiki shiki-themes github-light github-dark","*** Test Cases ***\nCatch The Response Of A Delayed Request\n    ${promise} =    Promise To    Wait For Response    matcher=    timeout=3s\n    Click           \\#delayed_request\n    ${body} =       Wait For      ${promise}\n","robot",[56,742,743,748,753,758,763],{"__ignoreMap":54},[59,744,745],{"class":61,"line":62},[59,746,747],{},"*** Test Cases ***\n",[59,749,750],{"class":61,"line":149},[59,751,752],{},"Catch The Response Of A Delayed Request\n",[59,754,755],{"class":61,"line":173},[59,756,757],{},"    ${promise} =    Promise To    Wait For Response    matcher=    timeout=3s\n",[59,759,760],{"class":61,"line":180},[59,761,762],{},"    Click           \\#delayed_request\n",[59,764,765],{"class":61,"line":186},[59,766,767],{},"    ${body} =       Wait For      ${promise}\n",[769,770,771,779,785],"ol",{},[772,773,774,778],"li",{},[249,775,776],{},[56,777,708],{}," starts the keyword and returns the promise. It waits until\nthe promised keyword's thread is actually running before returning, which\nremoves the worst of the race — though it does not wait for the browser-side\nlistener itself to be registered.",[772,780,781,784],{},[249,782,783],{},"The thing that triggers it"," — a click, a navigation, whatever.",[772,786,787,792],{},[249,788,789],{},[56,790,791],{},"Wait For"," collects the result, blocking only if it is not ready.",[24,794,795],{},"Getting the order wrong is the usual mistake: click first and the response has\ncome and gone before anything was listening.",[265,797,799],{"id":798},"collecting-several","Collecting several",[24,801,802,804,805,808],{},[56,803,791],{}," takes any number of promises and returns their results ",[249,806,807],{},"in the order\nyou passed them",", not the order they finished:",[49,810,812],{"className":51,"code":811,"language":53,"meta":54,"style":54},"${a} =    Promise To    Wait For Response    matcher=**\u002Fapi\u002Fuser\n${b} =    Promise To    Wait For Response    matcher=**\u002Fapi\u002Fcart\nClick     text=Refresh\n${results} =    Wait For    ${a}    ${b}\n",[56,813,814,819,824,829],{"__ignoreMap":54},[59,815,816],{"class":61,"line":62},[59,817,818],{},"${a} =    Promise To    Wait For Response    matcher=**\u002Fapi\u002Fuser\n",[59,820,821],{"class":61,"line":149},[59,822,823],{},"${b} =    Promise To    Wait For Response    matcher=**\u002Fapi\u002Fcart\n",[59,825,826],{"class":61,"line":173},[59,827,828],{},"Click     text=Refresh\n",[59,830,831],{"class":61,"line":180},[59,832,833],{},"${results} =    Wait For    ${a}    ${b}\n",[24,835,836],{},"With one promise you get the result itself; with several you get a list.",[24,838,839,842],{},[56,840,841],{},"Wait For All Promises"," waits for everything created and not yet collected,\nwhich is what you want when you do not need the results:",[49,844,846],{"className":51,"code":845,"language":53,"meta":54,"style":54},"Promise To    Wait For Response    matcher=**\u002Fapi\u002Flog\nClick         text=Save\nWait For All Promises\n",[56,847,848,853,858],{"__ignoreMap":54},[59,849,850],{"class":61,"line":62},[59,851,852],{},"Promise To    Wait For Response    matcher=**\u002Fapi\u002Flog\n",[59,854,855],{"class":61,"line":149},[59,856,857],{},"Click         text=Save\n",[59,859,860],{"class":61,"line":173},[59,861,862],{},"Wait For All Promises\n",[240,864,865],{},[24,866,867,868,870],{},"A promise you never wait for is not lost. At the end of the test the library\nwaits for it anyway and logs a warning that it had to. If you see that warning,\nyou have either forgotten a ",[56,869,791],{}," or you did not need the promise.",[265,872,874],{"id":873},"any-browser-keyword-can-be-promised","Any Browser keyword can be promised",[24,876,877,878,880,881,884],{},"This is the part people miss. ",[56,879,708],{}," is not limited to a handful of\n\"async\" keywords — it takes ",[249,882,883],{},"any keyword in this library",", with its normal\narguments:",[49,886,888],{"className":51,"code":887,"language":53,"meta":54,"style":54},"${title} =      Promise To    Get Title\n${clicked} =    Promise To    Click    text=Submit\n${count} =      Promise To    Get Element Count    .row    ==    ${10}\n",[56,889,890,895,900],{"__ignoreMap":54},[59,891,892],{"class":61,"line":62},[59,893,894],{},"${title} =      Promise To    Get Title\n",[59,896,897],{"class":61,"line":149},[59,898,899],{},"${clicked} =    Promise To    Click    text=Submit\n",[59,901,902],{"class":61,"line":173},[59,903,904],{},"${count} =      Promise To    Get Element Count    .row    ==    ${10}\n",[24,906,907],{},"Which means two slow waits can overlap instead of queueing:",[49,909,911],{"className":738,"code":910,"language":740,"meta":54,"style":54},"*** Test Cases ***\nTwo Slow Panels At Once\n    ${left} =     Promise To    Get Text    .left-panel     contains    Loaded\n    ${right} =    Promise To    Get Text    .right-panel    contains    Loaded\n    Wait For    ${left}    ${right}\n",[56,912,913,917,922,927,932],{"__ignoreMap":54},[59,914,915],{"class":61,"line":62},[59,916,747],{},[59,918,919],{"class":61,"line":149},[59,920,921],{},"Two Slow Panels At Once\n",[59,923,924],{"class":61,"line":173},[59,925,926],{},"    ${left} =     Promise To    Get Text    .left-panel     contains    Loaded\n",[59,928,929],{"class":61,"line":180},[59,930,931],{},"    ${right} =    Promise To    Get Text    .right-panel    contains    Loaded\n",[59,933,934],{"class":61,"line":186},[59,935,936],{},"    Wait For    ${left}    ${right}\n",[24,938,939],{},"Both getters retry independently, so the test waits about as long as the slower\none rather than the sum of the two. Promises run on a thread pool — up to 256 at\nonce — so they really are in flight together rather than taking turns.",[240,941,942],{"kind":374},[24,943,944,945,948,949,951,952,954,955,234,957,959],{},"Promise assertion getters like ",[56,946,947],{},"Get Text",", not ",[56,950,129],{},".\n",[56,953,129],{}," works by temporarily rewriting the library's\n",[56,956,259],{},[56,958,246],{}," and restoring them when it finishes — so\ntwo of them running concurrently overwrite each other's settings, and which\nvalue survives is a matter of timing.",[240,961,962,968],{"kind":374},[24,963,964,965],{},"Only Browser library keywords can be promised. Anything else — a BuiltIn\nkeyword, one of your own — fails with ",[28,966,967],{},"\"Unknown keyword … 'Promise To' can only\nbe used with Browser keywords.\"",[24,969,970,971,974,975,978],{},"And keep in mind what you are parallelising. Two promises that both act on the\nsame page are two things competing for one page; the parallelism is real and so\nare the races. Promises are for ",[28,972,973],{},"waiting"," concurrently far more often than for\n",[28,976,977],{},"acting"," concurrently.",[265,980,982],{"id":981},"the-two-purpose-built-promises","The two purpose-built promises",[24,984,985],{},"Downloads and uploads both need something in place before the click that starts\nthem, so they have dedicated keywords rather than being wrapped by hand:",[49,987,989],{"className":51,"code":988,"language":53,"meta":54,"style":54},"${dl} =      Promise To Wait For Download    saveAs=${OUTPUT_DIR}\u002Freport.pdf\nClick        text=Export\n${info} =    Wait For    ${dl}\n\n${up} =      Promise To Upload File    ${CURDIR}\u002Favatar.png\nClick        text=Choose file\nWait For     ${up}\n",[56,990,991,996,1001,1006,1010,1015,1020],{"__ignoreMap":54},[59,992,993],{"class":61,"line":62},[59,994,995],{},"${dl} =      Promise To Wait For Download    saveAs=${OUTPUT_DIR}\u002Freport.pdf\n",[59,997,998],{"class":61,"line":149},[59,999,1000],{},"Click        text=Export\n",[59,1002,1003],{"class":61,"line":173},[59,1004,1005],{},"${info} =    Wait For    ${dl}\n",[59,1007,1008],{"class":61,"line":180},[59,1009,177],{"emptyLinePlaceholder":176},[59,1011,1012],{"class":61,"line":186},[59,1013,1014],{},"${up} =      Promise To Upload File    ${CURDIR}\u002Favatar.png\n",[59,1016,1017],{"class":61,"line":192},[59,1018,1019],{},"Click        text=Choose file\n",[59,1021,1022],{"class":61,"line":197},[59,1023,1024],{},"Wait For     ${up}\n",[24,1026,1027,1030,1031,1034],{},[56,1028,1029],{},"Promise To Upload File"," waits for the ",[28,1032,1033],{},"file chooser dialog"," to appear, so the\nclick that opens it has to come after the promise — the same order as everything\nelse on this page. It fails immediately if the path is not an existing file.",[24,1036,1037,1038,1041],{},"If you can set the file directly, ",[56,1039,1040],{},"Upload File By Selector"," does it in one\nkeyword and avoids the ordering question entirely.",[33,1043,1045],{"id":1044},"in-short","In short",[1047,1048,1049,1052,1064,1071,1083,1088,1098],"ul",{},[772,1050,1051],{},"A getter with an assertion operator is a wait. Without one, it reads once.",[772,1053,1054,1055,1057,1058,1060,1061,1063],{},"Two keywords cover the rest, and they are siblings: ",[56,1056,129],{}," asks a\nBrowser getter, ",[56,1059,406],{}," asks the page. If you can write the\n",[56,1062,122],{}," assertion, use the first.",[772,1065,1066,1068,1069,31],{},[56,1067,129],{}," needs no new syntax — write the getter assertion, then\ndrop the ",[56,1070,135],{},[772,1072,1073,1075,1076,1079,1080,1082],{},[56,1074,129],{}," + ",[56,1077,1078],{},"Element States"," is the one that handles most cases;\nthe states come in opposites, and ",[56,1081,366],{}," is how you wait for something\nto be gone.",[772,1084,1085,1087],{},[56,1086,406],{}," is for what only the page knows: readiness flags,\nanimations, computed geometry, application state that never reaches the DOM.\nA throw counts as \"not yet\", so it is safe to poll for something that does not\nexist yet.",[772,1089,1090,1091,31],{},"Promises are start-now, collect-later. The order is always\n",[249,1092,1093,1095,1096],{},[56,1094,708],{}," → trigger → ",[56,1097,791],{},[772,1099,1100,1101,31],{},"Any Browser keyword can be promised, and promises really do run in parallel —\npromise the getters rather than ",[56,1102,129],{},[1104,1105,1106],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":54,"searchDepth":149,"depth":149,"links":1108},[1109,1110,1111,1114,1119,1120,1127],{"id":35,"depth":149,"text":36},{"id":101,"depth":149,"text":102},{"id":126,"depth":149,"text":129,"children":1112},[1113],{"id":267,"depth":173,"text":268},{"id":403,"depth":149,"text":406,"children":1115},[1116,1117,1118],{"id":493,"depth":173,"text":494},{"id":598,"depth":173,"text":599},{"id":621,"depth":173,"text":622},{"id":676,"depth":149,"text":677},{"id":687,"depth":149,"text":688,"children":1121},[1122,1123,1124,1125,1126],{"id":698,"depth":173,"text":699},{"id":731,"depth":173,"text":732},{"id":798,"depth":173,"text":799},{"id":873,"depth":173,"text":874},{"id":981,"depth":173,"text":982},{"id":1044,"depth":149,"text":1045},"Most waiting is already done for you. This is the rest of it — Wait For Condition, and how to run keywords in parallel with Promise To.","md",{},"\u002Fdocs\u002Fconcepts\u002Fwaiting","concepts",{"title":19,"description":1128},"docs\u002Fconcepts\u002Fwaiting","TJ0kU9EN1AeMhjnLNUPNal0k4-6Cv3v-EIdqFnx2uQU",[1137,1141,1145,1149,1153,1157,1158,1163,1167,1171,1175,1180,1184,1188,1193,1197,1201,1205],{"path":1138,"title":1139,"description":1140,"section":1132,"order":186},"\u002Fdocs\u002Fconcepts\u002Farchitecture","How Browser works","Python, a Node process and Playwright — what runs where, and which installation to choose.",{"path":1142,"title":1143,"description":1144,"section":1132,"order":173},"\u002Fdocs\u002Fconcepts\u002Fassertions","Assertions","Almost every getter can assert, every assertion retries, and each return type allows different operators. The complete AssertionEngine reference.",{"path":1146,"title":1147,"description":1148,"section":1132,"order":62},"\u002Fdocs\u002Fconcepts\u002Fbrowser-context-page","Browser, context and page","The three layers Playwright is built on — and why a new context is the cheapest clean slate you will ever get.",{"path":1150,"title":1151,"description":1152,"section":1132,"order":192},"\u002Fdocs\u002Fconcepts\u002Flogging","Logging and debugging","Four places Browser tells you what happened — log.html, the Playwright log, traces and coverage.",{"path":1154,"title":1155,"description":1156,"section":1132,"order":149},"\u002Fdocs\u002Fconcepts\u002Fselectors","Finding elements","Which selector strategy to reach for, why the order matters, and how to chain across iframes and shadow DOM.",{"path":1131,"title":19,"description":1128,"section":1132,"order":180},{"path":1159,"title":1160,"description":1161,"section":1162,"order":149},"\u002Fdocs\u002Fextending\u002Fjavascript-basics","Just enough JavaScript","The JavaScript you need to write extensions and Evaluate JavaScript, for people who write Python.","extending",{"path":1164,"title":1165,"description":1166,"section":1162,"order":173},"\u002Fdocs\u002Fextending\u002Fjavascript-extensions","JavaScript extensions","Add keywords that run on the Node side with full Playwright access — and attach a debugger to them.",{"path":1168,"title":1169,"description":1170,"section":1162,"order":62},"\u002Fdocs\u002Fextending\u002Fpython-plugins","Python plugins","Add or replace keywords from Python, with access to the library's own API and to the Node side.",{"path":1172,"title":1173,"description":1174,"section":1162,"order":180},"\u002Fdocs\u002Fextending\u002Ftranslations","Translating keywords","Using a translation package to get keyword names and documentation in your language, and writing one for a language nobody has covered yet.",{"path":1176,"title":1177,"description":1178,"section":1179,"order":149},"\u002Fdocs\u002Fmobile\u002Fdevices","Emulating a device","Device descriptors, what the six or seven properties they set actually change, and the one that emulation cannot give you.","mobile",{"path":1181,"title":1182,"description":1183,"section":1179,"order":62},"\u002Fdocs\u002Fmobile\u002Fresponsive","Testing responsive pages","Why a browser at a phone-sized viewport tests responsiveness properly, what emulation cannot tell you, and where real devices actually belong.",{"path":1185,"title":1186,"description":1187,"section":1179,"order":173},"\u002Fdocs\u002Fmobile\u002Ftouch","Touch, permissions and location","Tap and why it needs hasTouch, granting camera or notifications without a dialog, faking where the user is, and the gestures Browser has no keyword for.",{"path":1189,"title":1190,"description":1191,"section":1192,"order":173},"\u002Fdocs\u002Foperations\u002Fdocker","Running in Docker","The published Browser image, the four docker run flags that matter, and why running as root breaks Chromium.","operations",{"path":1194,"title":1195,"description":1196,"section":1192,"order":180},"\u002Fdocs\u002Foperations\u002Fdocker-images","Building your own image","Extending the published image with your dependencies, the version lock between Browser and Playwright, and using it as a CI job container.",{"path":1198,"title":1199,"description":1200,"section":1192,"order":149},"\u002Fdocs\u002Foperations\u002Fenvironment-variables","Environment variables","The variables that change how Browser behaves, which of them are safe in production, and the import parameters that should usually be preferred.",{"path":1202,"title":1203,"description":1204,"section":1192,"order":62},"\u002Fdocs\u002Foperations\u002Fnode-process","The Node process","Browser starts a Node process to talk to Playwright. What that costs, how to share one across parallel runs, and how to pass it Node flags.",{"path":1206,"title":1207,"description":1208,"section":1209,"order":62},"\u002Fdocs\u002Fstart\u002Fgetting-started","Getting started","Install Browser, write a first test, and run it.","start",[1211,1214,1217],{"to":1212,"label":1213},"\u002Fwhy\u002Fvs-cypress","vs Cypress",{"to":1215,"label":1216},"\u002Fwhy\u002Fvs-playwright","vs Playwright Test",{"to":1218,"label":1219},"\u002Fwhy\u002Fvs-seleniumlibrary","vs SeleniumLibrary",[1221],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .status    ==    Done\u003C\u002Fspan>\u003C\u002Fspan>",[1223],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">status\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .status\u003C\u002Fspan>\u003C\u002Fspan>",[1225],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">              id=status_bar    contains    Done\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Text    id=status_bar    contains    Done\u003C\u002Fspan>\u003C\u002Fspan>",[1227],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The upload finished\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Text            .progress     ==    100%\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The table filled in\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Element Count   tbody tr       ==    \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">25\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The single-page app actually navigated\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Url             should end with    \u002Fcheckout\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># A CSS class arrived\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Classes         .modal    contains    is-open\u003C\u002Fspan>\u003C\u002Fspan>",[1229],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The overlay is gone from the DOM entirely — not merely invisible\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Element States    id=cdk-overlay-0    ==    detached\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The submit button became usable after validation\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Element States    button#submit    contains    enabled\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The field is ready to type into: there, shown, and not read-only\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Element States    input#search    contains    visible    editable\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Focus moved away, so the blur handler has run\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Condition\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Element States    input#amount    contains    defocused\u003C\u002Fspan>\u003C\u002Fspan>",[1231],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The framework says it has finished booting\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    () => window.myApp.ready === true\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The client-side store has data in it\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    () => window.__STORE__.getState().cart.items.length > 0\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># A third-party widget has attached itself\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    () => typeof window.Intercom === 'function'\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Every image has finished loading — successfully or not\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    () => [...document.images].every(i => i.complete)\u003C\u002Fspan>\u003C\u002Fspan>",[1233],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The app has written an inline width of 100% onto the bar\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    element => element.style.width === '100%'    selector=#progress_bar\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The sticky header has settled at the top of the viewport\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    el => el.getBoundingClientRect().top === 0    selector=.sticky-header\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Something has actually been drawn into the canvas\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    c => c.getContext('2d').getImageData(0,0,1,1).data[3] > 0    selector=canvas#chart\u003C\u002Fspan>\u003C\u002Fspan>",[1235],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For Function\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    () => window.jobStatus === 'done'    polling=2s    timeout=2min\u003C\u002Fspan>\u003C\u002Fspan>",[1237],"\u003Cspan class=\"line\">\u003Cspan>ordinary:   ────[ Wait For Response ]────▶ result, then the next keyword\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan>promised:   ──┬─[ Wait For Response ]──┐\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan>              └─[ Click ]──────────────┴──▶ collect the result\u003C\u002Fspan>\u003C\u002Fspan>",[1239],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Test Cases ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Catch The Response Of A Delayed Request\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">promise\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Wait For Response\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    matcher=\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    timeout=3s\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Click\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">           \\#\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">delayed_request\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">body\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">       Wait For\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">      ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">promise\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",[1241],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Wait For Response    matcher=**\u002Fapi\u002Fuser\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Wait For Response    matcher=**\u002Fapi\u002Fcart\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Click\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">     text=Refresh\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">results\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Wait For\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">a\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">b\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",[1243],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Wait For Response    matcher=**\u002Fapi\u002Flog\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Click\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">         text=Save\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For All Promises\u003C\u002Fspan>\u003C\u002Fspan>",[1245],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">title\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">      Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Get Title\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">clicked\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Click    text=Submit\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">count\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">      Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Get Element Count    .row    ==    \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">10\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",[1247],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Test Cases ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Two Slow Panels At Once\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">left\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">     Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .left-panel\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">     contains\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Loaded\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">right\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Promise To\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .right-panel\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    contains\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Loaded\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Wait For\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">left\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">right\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",[1249],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">dl\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">      Promise To Wait For Download\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    saveAs=\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">OUTPUT_DIR\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">\u002Freport.pdf\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Click\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">        text=Export\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">info\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Wait For\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">dl\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">up\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">      Promise To Upload File\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">CURDIR\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">\u002Favatar.png\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Click\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">        text=Choose file\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Wait For\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">     ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">up\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",1786734138064]