[{"data":1,"prerenderedAt":1178},["ShallowReactive",2],{"SiteFooter_cLEacilcm59lTjFJjLY6mXNWLL0ETtzOIiZwIHgDpy0":3,"doc-\u002Fdocs\u002Fconcepts\u002Fassertions":17,"docs-nav":1056,"nav-compare":1130,"editor-1g1zs67":1140,"editor-19j3acw":1142,"editor-134urvm":1144,"editor-e268nr":1146,"editor-113ob86":1148,"editor-12vlqvs":1150,"editor-9e8pwo":1152,"editor-tls1rl":1154,"editor-pa28a1":1156,"editor-l7f4p":1158,"editor-1sij0dg":1160,"editor-6yrvcr":1162,"editor-ij4mx":1164,"editor-9oactq":1166,"editor-1xyjbf6":1168,"editor-g9xfyx":1170,"editor-1d28yvi":1172,"editor-66ug88":1174,"editor-kr6hud":1176},["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":1048,"extension":1049,"meta":1050,"navigation":313,"order":89,"path":1051,"section":1052,"seo":1053,"stem":1054,"__hash__":1055},"docs\u002Fdocs\u002Fconcepts\u002Fassertions.md","Assertions",{"type":21,"value":22,"toc":1024},"minimark",[23,32,49,52,61,66,69,99,102,107,112,127,153,157,161,166,186,190,193,218,235,242,281,290,294,355,364,370,380,405,441,447,456,476,487,491,494,517,521,524,537,546,557,566,618,622,625,659,670,687,697,700,708,734,738,756,761,764,789,800,807,827,834,849,852,872,875,879,886,890,904,907,927,931,935,939,1020],[24,25,26,27,31],"p",{},"Almost every keyword that gets something can also check it. There is no separate\nassertion library, and — this is the part that matters — ",[28,29,30],"strong",{},"the check retries",".",[33,34,39],"pre",{"className":35,"code":36,"language":37,"meta":38,"style":38},"language-robot-repl shiki shiki-themes github-light github-dark","Get Text    h1    ==    Welcome\n","robot-repl","",[40,41,42],"code",{"__ignoreMap":38},[43,44,47],"span",{"class":45,"line":46},"line",1,[43,48,36],{},[24,50,51],{},"Three things happen in that one line: the text is read, compared, and if it does\nnot match yet, read again until it does or the retry window expires. The value is\nreturned either way, so you can assert and capture at once:",[33,53,55],{"className":35,"code":54,"language":37,"meta":38,"style":38},"${title} =    Get Text    h1    ==    Welcome\n",[40,56,57],{"__ignoreMap":38},[43,58,59],{"class":45,"line":46},[43,60,54],{},[62,63,65],"h2",{"id":64},"why-this-replaces-the-waiting-code","Why this replaces the waiting code",[24,67,68],{},"The classic flaky test looks once, too early:",[33,70,74],{"className":71,"code":72,"language":73,"meta":38,"style":38},"language-robot shiki shiki-themes github-light github-dark","# The old shape: look, then hope\nWait Until Element Is Visible    h1\n${text} =    Get Text    h1\nShould Be Equal    ${text}    Welcome\n","robot",[40,75,76,81,87,93],{"__ignoreMap":38},[43,77,78],{"class":45,"line":46},[43,79,80],{},"# The old shape: look, then hope\n",[43,82,84],{"class":45,"line":83},2,[43,85,86],{},"Wait Until Element Is Visible    h1\n",[43,88,90],{"class":45,"line":89},3,[43,91,92],{},"${text} =    Get Text    h1\n",[43,94,96],{"class":45,"line":95},4,[43,97,98],{},"Should Be Equal    ${text}    Welcome\n",[24,100,101],{},"Three keywords, and still a race — the element became visible, then the text\narrived a moment later. The retrying form has no gap between the look and the\ncheck, because they are one operation.",[103,104,106],"h3",{"id":105},"two-timeouts-and-they-are-not-the-same","Two timeouts, and they are not the same",[108,109],"doc-table",{":head":110,":rows":111},"[\"Setting\",\"Governs\",\"Default\"]","[[\"`timeout`\",\"How long Playwright waits for the **element** to exist\",\"10s\"],[\"`retry_assertions_for`\",\"How long the **value** is re-read while the assertion fails — never past `timeout`\",\"1s\"]]",[33,113,115],{"className":71,"code":114,"language":73,"meta":38,"style":38},"*** Settings ***\nLibrary    Browser    timeout=15s    retry_assertions_for=5s\n",[40,116,117,122],{"__ignoreMap":38},[43,118,119],{"class":45,"line":46},[43,120,121],{},"*** Settings ***\n",[43,123,124],{"class":45,"line":83},[43,125,126],{},"Library    Browser    timeout=15s    retry_assertions_for=5s\n",[24,128,129,130,133,134,137,138,141,142,144,145,148,149,152],{},"Element never appears → ",[40,131,132],{},"timeout",". Element is there but the text is still\n",[40,135,136],{},"Loading…"," → ",[40,139,140],{},"retry_assertions_for",". Raising the wrong one is the most common\nreason a fix does not help — and note that ",[40,143,132],{}," bounds the whole keyword,\nso ",[40,146,147],{},"retry_assertions_for=30s"," against the default ",[40,150,151],{},"timeout=10s"," still gives you\nten seconds of retrying.",[62,154,156],{"id":155},"the-operators","The operators",[103,158,160],{"id":159},"comparison","Comparison",[108,162],{":head":163,":rows":164,":nowrap":165},"[\"Operator\",\"Also written\",\"True when\"]","[[\"`==`\",\"`equal`, `equals`, `should be`\",\"`value == expected`\"],[\"`!=`\",\"`inequal`, `should not be`\",\"`value != expected`\"],[\"`>`\",\"`greater than`\",\"`value > expected`\"],[\"`>=`\",\"— none —\",\"`value >= expected`\"],[\"`\u003C`\",\"`less than`\",\"`value \u003C expected`\"],[\"`\u003C=`\",\"— none —\",\"`value \u003C= expected`\"]]","[0,1]",[33,167,169],{"className":35,"code":168,"language":37,"meta":38,"style":38},"Get Text             h1        ==    Welcome\nGet Element Count    .row      >     ${5}\nGet Style            body      width    !=    0px\n",[40,170,171,176,181],{"__ignoreMap":38},[43,172,173],{"class":45,"line":46},[43,174,175],{},"Get Text             h1        ==    Welcome\n",[43,177,178],{"class":45,"line":83},[43,179,180],{},"Get Element Count    .row      >     ${5}\n",[43,182,183],{"class":45,"line":89},[43,184,185],{},"Get Style            body      width    !=    0px\n",[103,187,189],{"id":188},"substring-and-edges","Substring and edges",[108,191],{":head":163,":rows":192,":nowrap":165},"[[\"`*=`\",\"`contains`\",\"`expected in value`\"],[\"`not contains`\",\"— none —\",\"`expected not in value`\"],[\"`^=`\",\"`starts`, `should start with`\",\"Value starts with expected — a literal, not a pattern\"],[\"`$=`\",\"`ends`, `should end with`\",\"Value ends with expected — literal too\"]]",[33,194,196],{"className":35,"code":195,"language":37,"meta":38,"style":38},"Get Text    .banner    *=              Welcome\nGet Text    .banner    not contains    Error\nGet Url     ^=         https:\u002F\u002F\nGet Url     $=         \u002Fcheckout\n",[40,197,198,203,208,213],{"__ignoreMap":38},[43,199,200],{"class":45,"line":46},[43,201,202],{},"Get Text    .banner    *=              Welcome\n",[43,204,205],{"class":45,"line":83},[43,206,207],{},"Get Text    .banner    not contains    Error\n",[43,209,210],{"class":45,"line":89},[43,211,212],{},"Get Url     ^=         https:\u002F\u002F\n",[43,214,215],{"class":45,"line":95},[43,216,217],{},"Get Url     $=         \u002Fcheckout\n",[24,219,220,223,224,227,228,230,231,234],{},[40,221,222],{},"^="," and ",[40,225,226],{},"$="," escape what you give them, so ",[40,229,226],{}," with ",[40,232,233],{},".html"," matches a literal\ndot, not any character.",[103,236,238,241],{"id":237},"matches-regular-expressions",[40,239,240],{},"matches"," — regular expressions",[24,243,244,246,247,250,251,253,254,257,258,223,261,264,265,268,269,272,273,275,276,280],{},[40,245,240],{}," runs ",[40,248,249],{},"re.search"," over the value. It is spelled ",[40,252,240],{}," and nothing\nelse — it has no symbolic alias, as ",[40,255,256],{},"validate",", ",[40,259,260],{},"then",[40,262,263],{},"not contains"," also\ndo not. In particular it is\n",[28,266,267],{},"not"," ",[40,270,271],{},"$",": that character is the operator's internal value, Robot Framework\nrejects it as input, and ",[40,274,226],{}," is a different operator entirely (",[277,278,279],"em",{},"ends with",").",[24,282,283,284,268,286,289],{},"What ",[40,285,240],{},[277,287,288],{},"returns"," depends on the groups in your pattern, and this catches\npeople out:",[108,291],{":head":292,":rows":293},"[\"Pattern has\",\"Returns\"]","[[\"No groups\",\"The whole value, as a string\"],[\"Unnamed groups\",\"A **tuple** of the groups\"],[\"All groups named\",\"A **dictionary** of the groups\"]]",[33,295,297],{"className":35,"code":296,"language":37,"meta":38,"style":38},"# No groups — returns the whole string\nGet Text    .id    matches    ^ORDER-\\\\d+$\n\n# Unnamed groups — returns a tuple\n${parts} =    Get Text    .date    matches    (\\\\d+)-(\\\\d+)-(\\\\d+)\nShould Be Equal    ${parts}[0]    2026\n\n# Named groups — returns a dict\n${d} =    Get Text    .date    matches    (?P\u003Cyear>\\\\d+)-(?P\u003Cmonth>\\\\d+)-(?P\u003Cday>\\\\d+)\nShould Be Equal    ${d}[year]    2026\n",[40,298,299,304,309,315,320,326,332,337,343,349],{"__ignoreMap":38},[43,300,301],{"class":45,"line":46},[43,302,303],{},"# No groups — returns the whole string\n",[43,305,306],{"class":45,"line":83},[43,307,308],{},"Get Text    .id    matches    ^ORDER-\\\\d+$\n",[43,310,311],{"class":45,"line":89},[43,312,314],{"emptyLinePlaceholder":313},true,"\n",[43,316,317],{"class":45,"line":95},[43,318,319],{},"# Unnamed groups — returns a tuple\n",[43,321,323],{"class":45,"line":322},5,[43,324,325],{},"${parts} =    Get Text    .date    matches    (\\\\d+)-(\\\\d+)-(\\\\d+)\n",[43,327,329],{"class":45,"line":328},6,[43,330,331],{},"Should Be Equal    ${parts}[0]    2026\n",[43,333,335],{"class":45,"line":334},7,[43,336,314],{"emptyLinePlaceholder":313},[43,338,340],{"class":45,"line":339},8,[43,341,342],{},"# Named groups — returns a dict\n",[43,344,346],{"class":45,"line":345},9,[43,347,348],{},"${d} =    Get Text    .date    matches    (?P\u003Cyear>\\\\d+)-(?P\u003Cmonth>\\\\d+)-(?P\u003Cday>\\\\d+)\n",[43,350,352],{"class":45,"line":351},10,[43,353,354],{},"Should Be Equal    ${d}[year]    2026\n",[24,356,357,358,360,361,363],{},"So ",[40,359,240],{}," is both an assertion and an extractor. Use ",[40,362,249],{}," semantics:\nit is not anchored unless you anchor it.",[103,365,367,369],{"id":366},"validate-any-python-expression",[40,368,256],{}," — any Python expression",[24,371,372,373,375,376,379],{},"When no operator says it, ",[40,374,256],{}," gives you one expression with the result\nbound to ",[40,377,378],{},"value",":",[33,381,383],{"className":35,"code":382,"language":37,"meta":38,"style":38},"Get Text             h1       validate    value.startswith(\"Welcome\")\nGet Element Count    .row     validate    value % 2 == 0\nGet Text             .price   validate    float(value.strip(\"€\")) \u003C 100\nGet BoundingBox      #card    ALL         validate    value['width'] > 40\n",[40,384,385,390,395,400],{"__ignoreMap":38},[43,386,387],{"class":45,"line":46},[43,388,389],{},"Get Text             h1       validate    value.startswith(\"Welcome\")\n",[43,391,392],{"class":45,"line":83},[43,393,394],{},"Get Element Count    .row     validate    value % 2 == 0\n",[43,396,397],{"class":45,"line":89},[43,398,399],{},"Get Text             .price   validate    float(value.strip(\"€\")) \u003C 100\n",[43,401,402],{"class":45,"line":95},[43,403,404],{},"Get BoundingBox      #card    ALL         validate    value['width'] > 40\n",[24,406,407,408,230,411,257,414,417,418,420,421,424,425,257,427,257,429,431,432,435,436,223,438,440],{},"Getters that return a dictionary — ",[40,409,410],{},"Get BoundingBox",[40,412,413],{},"ALL",[40,415,416],{},"Get Viewport Size","\n— are usually asserted this way, indexing into ",[40,419,378],{}," directly. So is\n",[40,422,423],{},"Get Browser Catalog",", which returns a list of dictionaries and is not restricted\nto the list operators — though the string ones (",[40,426,222],{},[40,428,226],{},[40,430,240],{},") raise a\n",[40,433,434],{},"TypeError"," on a list, so ",[40,437,256],{},[40,439,260],{}," are what you actually use.",[103,442,444,446],{"id":443},"then-derive-instead-of-check",[40,445,260],{}," — derive instead of check",[24,448,449,451,452,455],{},[40,450,260],{}," (also ",[40,453,454],{},"evaluate",") asserts nothing. It evaluates an expression and returns\nthe result, so a getter hands back exactly the piece you want:",[33,457,459],{"className":35,"code":458,"language":37,"meta":38,"style":38},"${id} =       Get Property    a#link    href     then    value.split(\"\u002F\")[-1]\n${count} =    Get Text        .total            then    int(value.strip(\" items\"))\n${upper} =    Get Text        h1                then    value.upper()\n",[40,460,461,466,471],{"__ignoreMap":38},[43,462,463],{"class":45,"line":46},[43,464,465],{},"${id} =       Get Property    a#link    href     then    value.split(\"\u002F\")[-1]\n",[43,467,468],{"class":45,"line":83},[43,469,470],{},"${count} =    Get Text        .total            then    int(value.strip(\" items\"))\n",[43,472,473],{"class":45,"line":89},[43,474,475],{},"${upper} =    Get Text        h1                then    value.upper()\n",[24,477,478,480,481,483,484,486],{},[40,479,260],{}," never asserts, so it never retries — if the expression raises, the\nkeyword fails immediately. Use ",[40,482,256],{}," when you need the retry and ",[40,485,260],{},"\nonly to reshape a value that is already there.",[62,488,490],{"id":489},"which-operators-a-keyword-allows","Which operators a keyword allows",[24,492,493],{},"Not every operator works everywhere. The engine picks a rule set from the type\nthe keyword returns, and using the wrong one is an error, not a failed assertion.",[24,495,496,497,257,500,257,503,506,507,257,510,257,513,516],{},"Not every getter asserts, either. ",[40,498,499],{},"Get Element",[40,501,502],{},"Get Elements",[40,504,505],{},"Get Cookie",",\n",[40,508,509],{},"Get Cookies",[40,511,512],{},"Get Device",[40,514,515],{},"Get Devices"," and a few others take no assertion\narguments at all — they only return.",[108,518],{":head":519,":rows":520},"[\"Return type\",\"Allowed\",\"Example keyword\"]","[[\"**String**\",\"All of them\",\"`Get Text`, `Get Url`, `Get Property`\"],[\"**Number**\",\"`==` `!=` `>` `>=` `\u003C` `\u003C=` `validate` `then`\",\"`Get Element Count`\"],[\"**List**\",\"`==` `!=` `contains` `validate` `then`\",\"`Get Classes`, `Get Selected Options`, `Get Attribute Names`\"],[\"**Dictionary** (numeric)\",\"The list set, plus `>` `>=` `\u003C` `\u003C=` per key — those need a real dictionary, `${{ {...} }}`, not a string\",\"`Get BoundingBox`, `Get Viewport Size`\"],[\"**Dictionary** (strict)\",\"`==` `!=` `contains` `validate` `then`\",\"`Get Style` with `ALL`\"],[\"**Boolean**\",\"`==` `!=` only\",\"`Get Checkbox State`\"],[\"**Element states**\",\"Set operators, including `not contains`\",\"`Get Element States`\"]]",[24,522,523],{},"Two consequences worth knowing:",[24,525,526,529,530,223,533,536],{},[28,527,528],{},"Lists are compared unordered."," With ",[40,531,532],{},"==",[40,534,535],{},"!="," the engine sorts both sides\nfirst, so this passes:",[33,538,540],{"className":35,"code":539,"language":37,"meta":38,"style":38},"Get Classes    .btn    ==    primary    large    # matches \"large primary\" too\n",[40,541,542],{"__ignoreMap":38},[43,543,544],{"class":45,"line":46},[43,545,539],{},[24,547,548,553,554,556],{},[28,549,550,552],{},[40,551,263],{}," does not work on lists"," — it is absent from the sequence\noperators. Use ",[40,555,256],{}," instead:",[33,558,560],{"className":35,"code":559,"language":37,"meta":38,"style":38},"Get Classes    .btn    validate    \"disabled\" not in value\n",[40,561,562],{"__ignoreMap":38},[43,563,564],{"class":45,"line":46},[43,565,559],{},[24,567,568,571,572,257,575,257,578,257,581,257,584,257,587,590,591,257,594,223,597,600,601,257,604,223,607,610,611,613,614,617],{},[28,569,570],{},"Booleans use AssertionEngine's own truthiness",", which is not quite Robot's.\nEverything is true except ",[40,573,574],{},"FALSE",[40,576,577],{},"NO",[40,579,580],{},"OFF",[40,582,583],{},"0",[40,585,586],{},"UNCHECKED",[40,588,589],{},"NONE"," and the\nempty string, case-insensitively. So ",[40,592,593],{},"checked",[40,595,596],{},"yes",[40,598,599],{},"true"," are True;\n",[40,602,603],{},"unchecked",[40,605,606],{},"no",[40,608,609],{},"false"," are False. (",[40,612,603],{}," is the one that differs\nfrom Robot Framework's own ",[40,615,616],{},"is_truthy",".)",[62,619,621],{"id":620},"types-must-match","Types must match",[24,623,624],{},"The expected value is used exactly as written — the library does not convert it.\nSo it has to already be the type the keyword returns.",[33,626,628],{"className":35,"code":627,"language":37,"meta":38,"style":38},"# Get Text returns a string, even when it looks like a number\nGet Text             #price    ==    ${99}    # fails: int vs str\nGet Text             #price    ==    99       # passes\n\n# Get Element Count returns an integer\nGet Element Count    .row      ==    ${3}     # passes\n",[40,629,630,635,640,645,649,654],{"__ignoreMap":38},[43,631,632],{"class":45,"line":46},[43,633,634],{},"# Get Text returns a string, even when it looks like a number\n",[43,636,637],{"class":45,"line":83},[43,638,639],{},"Get Text             #price    ==    ${99}    # fails: int vs str\n",[43,641,642],{"class":45,"line":89},[43,643,644],{},"Get Text             #price    ==    99       # passes\n",[43,646,647],{"class":45,"line":95},[43,648,314],{"emptyLinePlaceholder":313},[43,650,651],{"class":45,"line":322},[43,652,653],{},"# Get Element Count returns an integer\n",[43,655,656],{"class":45,"line":328},[43,657,658],{},"Get Element Count    .row      ==    ${3}     # passes\n",[24,660,661,662,665,666,669],{},"Keywords returning numbers ",[277,663,664],{},"do"," convert the expected value for you. Keywords\nreturning strings do not, and ",[40,667,668],{},"Get Text"," is the one everybody trips over.",[24,671,672,673,676,677,680,681,686],{},"Numbers also refuse the text operators outright — ",[40,674,675],{},"Get Element Count    .row *=    2"," raises ",[40,678,679],{},"ValueError: Operator 'contains' is not allowed."," rather than\nfailing the assertion. When a\nfailure shows two values that look identical, print the types — see\n",[682,683,685],"a",{"href":684},"#custom-messages","messages"," below.",[103,688,690,691,223,694],{"id":689},"comparing-strings-with-and","Comparing strings with ",[40,692,693],{},"\u003C",[40,695,696],{},">",[24,698,699],{},"Character by character, by code point, stopping at the first difference. Length\nnever enters into it:",[33,701,706],{"className":702,"code":704,"language":705},[703],"language-text","A \u003C Z        Z \u003C a        ac \u003C dc\n'abcde' \u003C 'abd'           '100.000' \u003C '2'\n","text",[40,707,704],{"__ignoreMap":38},[24,709,710,711,714,715,718,719,722,723,726,727,718,730,733],{},"Both of the last two surprise people. ",[40,712,713],{},"'abcde' \u003C 'abd'"," because ",[40,716,717],{},"c"," precedes ",[40,720,721],{},"d","\nat the third character and the comparison stops there. ",[40,724,725],{},"'100.000' \u003C '2'"," because\nthese are strings and ",[40,728,729],{},"'1'",[40,731,732],{},"'2'"," — nothing numeric is involved. You\ncannot compare a number with a string in Python, and so not here either.",[62,735,737],{"id":736},"formatters","Formatters",[24,739,740,741,744,745,747,748,751,752,755],{},"A formatter normalises the value ",[277,742,743],{},"before"," it is compared, so a test does not fail\non whitespace nobody can see. Ordinary source indentation is not the problem —\n",[40,746,668],{}," reads rendered text, so the browser has already collapsed that. What\nsurvives rendering is what bites: non-breaking spaces (",[40,749,750],{},"&nbsp;&nbsp;two spaces&nbsp;&nbsp;","), ",[40,753,754],{},"\u003Cpre>"," blocks, and text assembled from several inline\nelements.",[108,757],{":head":758,":rows":759,":nowrap":760},"[\"Formatter\",\"Does\"]","[[\"`strip`\",\"Removes leading and trailing whitespace\"],[\"`normalize spaces`\",\"Collapses every run of whitespace to one space\"],[\"`case insensitive`\",\"Lower-cases the value\"],[\"`apply to expected`\",\"Not a transform — makes all the others apply to your expected value too\"]]","[0]",[24,762,763],{},"Set them per keyword:",[33,765,767],{"className":71,"code":766,"language":73,"meta":38,"style":38},"*** Test Cases ***\nWhitespace Does Not Matter\n    Set Assertion Formatters    {\"Get Text\": [\"strip\", \"normalize spaces\"]}\n    Get Text    .greeting    ==    Hello World\n",[40,768,769,774,779,784],{"__ignoreMap":38},[43,770,771],{"class":45,"line":46},[43,772,773],{},"*** Test Cases ***\n",[43,775,776],{"class":45,"line":83},[43,777,778],{},"Whitespace Does Not Matter\n",[43,780,781],{"class":45,"line":89},[43,782,783],{},"    Set Assertion Formatters    {\"Get Text\": [\"strip\", \"normalize spaces\"]}\n",[43,785,786],{"class":45,"line":95},[43,787,788],{},"    Get Text    .greeting    ==    Hello World\n",[24,790,791,792,795,796,799],{},"That works on markup like ",[40,793,794],{},"\u003Cp>Hello&nbsp;&nbsp;World\u003C\u002Fp>",". Without the\nformatters the comparison sees ",[40,797,798],{},"\"Hello\\xa0\\xa0World\""," — the browser collapses\nordinary whitespace but not non-breaking spaces — and fails.",[24,801,802,803,806],{},"They apply ",[28,804,805],{},"in the order given",", and only to the value:",[33,808,810],{"className":35,"code":809,"language":37,"meta":38,"style":38},"# value \"  HELLO  \" becomes \"hello\", expected stays \"  Hello  \" -> fails\nSet Assertion Formatters    {\"Get Text\": [\"strip\", \"case insensitive\"]}\nGet Text    h1    ==    ${SPACE}${SPACE}Hello${SPACE}${SPACE}\n",[40,811,812,817,822],{"__ignoreMap":38},[43,813,814],{"class":45,"line":46},[43,815,816],{},"# value \"  HELLO  \" becomes \"hello\", expected stays \"  Hello  \" -> fails\n",[43,818,819],{"class":45,"line":83},[43,820,821],{},"Set Assertion Formatters    {\"Get Text\": [\"strip\", \"case insensitive\"]}\n",[43,823,824],{"class":45,"line":89},[43,825,826],{},"Get Text    h1    ==    ${SPACE}${SPACE}Hello${SPACE}${SPACE}\n",[24,828,829,830,833],{},"Add ",[40,831,832],{},"apply to expected"," and both sides get the same treatment, which is usually\nwhat you meant:",[33,835,837],{"className":35,"code":836,"language":37,"meta":38,"style":38},"Set Assertion Formatters    {\"Get Text\": [\"strip\", \"case insensitive\", \"apply to expected\"]}\nGet Text    h1    ==    ${SPACE}${SPACE}Hello${SPACE}${SPACE}    # passes\n",[40,838,839,844],{"__ignoreMap":38},[43,840,841],{"class":45,"line":46},[43,842,843],{},"Set Assertion Formatters    {\"Get Text\": [\"strip\", \"case insensitive\", \"apply to expected\"]}\n",[43,845,846],{"class":45,"line":83},[43,847,848],{},"Get Text    h1    ==    ${SPACE}${SPACE}Hello${SPACE}${SPACE}    # passes\n",[24,850,851],{},"Several keywords at once, and a scope:",[33,853,855],{"className":71,"code":854,"language":73,"meta":38,"style":38},"Set Assertion Formatters\n...    {\"Get Text\": [\"strip\", \"normalize spaces\"], \"Get Property\": [\"case insensitive\"]}\n...    scope=Suite\n",[40,856,857,862,867],{"__ignoreMap":38},[43,858,859],{"class":45,"line":46},[43,860,861],{},"Set Assertion Formatters\n",[43,863,864],{"class":45,"line":83},[43,865,866],{},"...    {\"Get Text\": [\"strip\", \"normalize spaces\"], \"Get Property\": [\"case insensitive\"]}\n",[43,868,869],{"class":45,"line":89},[43,870,871],{},"...    scope=Suite\n",[24,873,874],{},"The keyword returns the formatters that were set before, so a test can put them\nback — normalised to a canonical order rather than the order you supplied. A\nformatter written as a lambda is not included at all; only the named rules\nsurvive the round trip.",[62,876,878],{"id":877},"custom-messages","Custom messages",[24,880,881,882,885],{},"A failed assertion has a readable default. To replace it, ",[40,883,884],{},"message"," accepts four\nplaceholders:",[108,887],{":head":888,":rows":889,":nowrap":760},"[\"Placeholder\",\"Is\"]","[[\"`{value}`\",\"What the keyword returned\"],[\"`{expected}`\",\"What you asserted\"],[\"`{value_type}`\",\"The type of the returned value\"],[\"`{expected_type}`\",\"The type of the expected value\"]]",[33,891,893],{"className":35,"code":892,"language":37,"meta":38,"style":38},"Get Text    h1    ==    Welcome\n...    message=Landing headline was \"{value}\", expected \"{expected}\"\n",[40,894,895,899],{"__ignoreMap":38},[43,896,897],{"class":45,"line":46},[43,898,36],{},[43,900,901],{"class":45,"line":83},[43,902,903],{},"...    message=Landing headline was \"{value}\", expected \"{expected}\"\n",[24,905,906],{},"The two type placeholders exist precisely for the mismatch above — when both\nsides look the same and it still fails, printing the types shows why:",[33,908,910],{"className":35,"code":909,"language":37,"meta":38,"style":38},"Get Text    #price    ==    ${99}\n...    message=Got {value} ({value_type}), expected {expected} ({expected_type})\n# Got '99' (str), expected 99 (int)\n",[40,911,912,917,922],{"__ignoreMap":38},[43,913,914],{"class":45,"line":46},[43,915,916],{},"Get Text    #price    ==    ${99}\n",[43,918,919],{"class":45,"line":83},[43,920,921],{},"...    message=Got {value} ({value_type}), expected {expected} ({expected_type})\n",[43,923,924],{"class":45,"line":89},[43,925,926],{},"# Got '99' (str), expected 99 (int)\n",[62,928,930],{"id":929},"common-mistakes","Common mistakes",[108,932],{":head":933,":rows":934},"[\"Symptom\",\"Cause\"]","[[\"`... got value 'Welcome' that cannot be converted to AssertionOperator`\",\"An expected value written where the operator belongs\"],[\"`Assertion operator is mandatory when specifying expected value`\",\"`assertion_expected=` passed by name, with no operator\"],[\"Two identical-looking values, still fails\",\"String against number. Print `{value_type}`\"],[\"`Operator ... is not allowed in this Keyword`\",\"A list or dict getter given something outside `==` `!=` `contains` `validate` `then`\"],[\"Fails on invisible whitespace\",\"No formatter. Add `strip` and `normalize spaces`\"],[\"Passes when it should not, on a list\",\"`==` sorts both sides — order is not checked\"],[\"Assertion never retries long enough\",\"Raised `timeout` instead of `retry_assertions_for`\"]]",[62,936,938],{"id":937},"in-short","In short",[940,941,942,946,962,970,976,1004,1017],"ul",{},[943,944,945],"li",{},"Assert inside the getter, not after it — the retry is the whole point.",[943,947,948,949,223,952,954,955,957,958,223,960,31],{},"Match the operator to the return type: numbers refuse ",[40,950,951],{},"contains",[40,953,240],{},",\nlists refuse ",[40,956,263],{},", booleans take only ",[40,959,532],{},[40,961,535],{},[943,963,964,966,967,969],{},[40,965,140],{}," is for values, ",[40,968,132],{}," is for elements.",[943,971,972,973,975],{},"Match the type. ",[40,974,668],{}," returns a string.",[943,977,978,979,268,981,268,983,268,985,268,987,989,990,992,993,268,995,268,998,268,1000,1003],{},"Lists take ",[40,980,532],{},[40,982,535],{},[40,984,951],{},[40,986,256],{},[40,988,260],{},", and ",[40,991,532],{}," ignores order.\nDictionaries take the same set; the numeric ones also take ",[40,994,696],{},[40,996,997],{},">=",[40,999,693],{},[40,1001,1002],{},"\u003C=",",\napplied per key against a real dictionary.",[943,1005,1006,1008,1009,1011,1012,1014,1015,31],{},[40,1007,256],{}," for whatever the operators do not cover, ",[40,1010,260],{}," when you want a\nvalue rather than a check, ",[40,1013,240],{}," when you want both — and it is only ever\nspelled ",[40,1016,240],{},[943,1018,1019],{},"Reach for formatters before you reach for a regular expression.",[1021,1022,1023],"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":38,"searchDepth":83,"depth":83,"links":1025},[1026,1029,1039,1040,1044,1045,1046,1047],{"id":64,"depth":83,"text":65,"children":1027},[1028],{"id":105,"depth":89,"text":106},{"id":155,"depth":83,"text":156,"children":1030},[1031,1032,1033,1035,1037],{"id":159,"depth":89,"text":160},{"id":188,"depth":89,"text":189},{"id":237,"depth":89,"text":1034},"matches — regular expressions",{"id":366,"depth":89,"text":1036},"validate — any Python expression",{"id":443,"depth":89,"text":1038},"then — derive instead of check",{"id":489,"depth":83,"text":490},{"id":620,"depth":83,"text":621,"children":1041},[1042],{"id":689,"depth":89,"text":1043},"Comparing strings with \u003C and >",{"id":736,"depth":83,"text":737},{"id":877,"depth":83,"text":878},{"id":929,"depth":83,"text":930},{"id":937,"depth":83,"text":938},"Almost every getter can assert, every assertion retries, and each return type allows different operators. The complete AssertionEngine reference.","md",{},"\u002Fdocs\u002Fconcepts\u002Fassertions","concepts",{"title":19,"description":1048},"docs\u002Fconcepts\u002Fassertions","Dq5FVSb7VPcQi_64fh4nmSL2f-DRkceGfh9_uDibiAc",[1057,1061,1062,1066,1070,1074,1078,1083,1087,1091,1095,1100,1104,1108,1113,1117,1121,1125],{"path":1058,"title":1059,"description":1060,"section":1052,"order":322},"\u002Fdocs\u002Fconcepts\u002Farchitecture","How Browser works","Python, a Node process and Playwright — what runs where, and which installation to choose.",{"path":1051,"title":19,"description":1048,"section":1052,"order":89},{"path":1063,"title":1064,"description":1065,"section":1052,"order":46},"\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":1067,"title":1068,"description":1069,"section":1052,"order":328},"\u002Fdocs\u002Fconcepts\u002Flogging","Logging and debugging","Four places Browser tells you what happened — log.html, the Playwright log, traces and coverage.",{"path":1071,"title":1072,"description":1073,"section":1052,"order":83},"\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":1075,"title":1076,"description":1077,"section":1052,"order":95},"\u002Fdocs\u002Fconcepts\u002Fwaiting","Waiting and promises","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.",{"path":1079,"title":1080,"description":1081,"section":1082,"order":83},"\u002Fdocs\u002Fextending\u002Fjavascript-basics","Just enough JavaScript","The JavaScript you need to write extensions and Evaluate JavaScript, for people who write Python.","extending",{"path":1084,"title":1085,"description":1086,"section":1082,"order":89},"\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":1088,"title":1089,"description":1090,"section":1082,"order":46},"\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":1092,"title":1093,"description":1094,"section":1082,"order":95},"\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":1096,"title":1097,"description":1098,"section":1099,"order":83},"\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":1101,"title":1102,"description":1103,"section":1099,"order":46},"\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":1105,"title":1106,"description":1107,"section":1099,"order":89},"\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":1109,"title":1110,"description":1111,"section":1112,"order":89},"\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":1114,"title":1115,"description":1116,"section":1112,"order":95},"\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":1118,"title":1119,"description":1120,"section":1112,"order":83},"\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":1122,"title":1123,"description":1124,"section":1112,"order":46},"\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":1126,"title":1127,"description":1128,"section":1129,"order":46},"\u002Fdocs\u002Fstart\u002Fgetting-started","Getting started","Install Browser, write a first test, and run it.","start",[1131,1134,1137],{"to":1132,"label":1133},"\u002Fwhy\u002Fvs-cypress","vs Cypress",{"to":1135,"label":1136},"\u002Fwhy\u002Fvs-playwright","vs Playwright Test",{"to":1138,"label":1139},"\u002Fwhy\u002Fvs-seleniumlibrary","vs SeleniumLibrary",[1141],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    h1    ==    Welcome\u003C\u002Fspan>\u003C\u002Fspan>",[1143],"\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\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    h1    ==    Welcome\u003C\u002Fspan>\u003C\u002Fspan>",[1145],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># The old shape: look, then hope\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Wait Until Element Is Visible    h1\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">${text} =    Get Text    h1\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Should Be Equal    ${text}    Welcome\u003C\u002Fspan>\u003C\u002Fspan>",[1147],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Settings ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">Library\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Browser\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    timeout=15s\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    retry_assertions_for=5s\u003C\u002Fspan>\u003C\u002Fspan>",[1149],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">             h1        ==    Welcome\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Element Count\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .row      >     \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">5\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Style\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">            body      width    !=    0px\u003C\u002Fspan>\u003C\u002Fspan>",[1151],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .banner    *=              Welcome\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .banner    not contains    Error\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Url\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">     ^=         https:\u002F\u002F\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Url\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">     $=         \u002Fcheckout\u003C\u002Fspan>\u003C\u002Fspan>",[1153],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># No groups — returns the whole string\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .id    matches    ^ORDER-\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+$\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Unnamed groups — returns a tuple\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">parts\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\">    .date    matches    (\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)-(\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)-(\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Should Be Equal\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">parts\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">[0]    2026\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Named groups — returns a dict\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">d\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\">    .date    matches    (?P&#x3C;year>\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)-(?P&#x3C;month>\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)-(?P&#x3C;day>\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">\\\\\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">d+)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Should Be Equal\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">d\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">[year]    2026\u003C\u002Fspan>\u003C\u002Fspan>",[1155],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">             h1       validate    value.startswith(\"Welcome\")\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Element Count\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .row     validate    value % 2 == 0\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">             .price   validate    float(value.strip(\"€\")) &#x3C; 100\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get BoundingBox\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">      #card    ALL         validate    value['width'] > 40\u003C\u002Fspan>\u003C\u002Fspan>",[1157],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">id\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">       Get Property\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    a#link    href     then    value.split(\"\u002F\")[-1]\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\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">        .total            then    int(value.strip(\" items\"))\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">upper\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\">        h1                then    value.upper()\u003C\u002Fspan>\u003C\u002Fspan>",[1159],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Classes\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .btn    ==    primary    large\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">    # matches \"large primary\" too\u003C\u002Fspan>\u003C\u002Fspan>",[1161],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Classes\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .btn    validate    \"disabled\" not in value\u003C\u002Fspan>\u003C\u002Fspan>",[1163],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Get Text returns a string, even when it looks like a number\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">             #price    ==    ${99}    # fails: int vs str\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">             #price    ==    99       # passes\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Get Element Count returns an integer\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Element Count\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .row      ==    \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">3\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">     # passes\u003C\u002Fspan>\u003C\u002Fspan>",[1165],"\u003Cspan class=\"line\">\u003Cspan>A &#x3C; Z        Z &#x3C; a        ac &#x3C; dc\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan>'abcde' &#x3C; 'abd'           '100.000' &#x3C; '2'\u003C\u002Fspan>\u003C\u002Fspan>",[1167],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Test Cases ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Whitespace Does Not Matter\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Set Assertion Formatters\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    {\"Get Text\": [\"strip\", \"normalize spaces\"]}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    .greeting\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    ==\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Hello World\u003C\u002Fspan>\u003C\u002Fspan>",[1169],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># value \"  HELLO  \" becomes \"hello\", expected stays \"  Hello  \" -> fails\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Set Assertion Formatters\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    {\"Get Text\": [\"strip\", \"case insensitive\"]}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    h1    ==    \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">Hello\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>",[1171],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Set Assertion Formatters\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    {\"Get Text\": [\"strip\", \"case insensitive\", \"apply to expected\"]}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    h1    ==    \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">Hello\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">SPACE\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">    # passes\u003C\u002Fspan>\u003C\u002Fspan>",[1173],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Set Assertion Formatters\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    {\"Get Text\": [\"strip\", \"normalize spaces\"], \"Get Property\": [\"case insensitive\"]}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    scope=Suite\u003C\u002Fspan>\u003C\u002Fspan>",[1175],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    h1    ==    Welcome\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    message=Landing headline was \"{value}\", expected \"{expected}\"\u003C\u002Fspan>\u003C\u002Fspan>",[1177],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA;font-style:italic\">    #price    ==    ${99}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    message=Got {value} ({value_type}), expected {expected} ({expected_type})\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Got '99' (str), expected 99 (int)\u003C\u002Fspan>\u003C\u002Fspan>",1786734137767]