[{"data":1,"prerenderedAt":813},["ShallowReactive",2],{"SiteFooter_cLEacilcm59lTjFJjLY6mXNWLL0ETtzOIiZwIHgDpy0":3,"doc-\u002Fdocs\u002Fextending\u002Fpython-libraries":17,"docs-nav":708,"nav-compare":791,"editor-1dno125":801,"editor-1sbh4qh":803,"editor-l9cc1":805,"editor-85lfvk":807,"editor-5fir7g":809,"editor-hyozq5":811},["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":700,"extension":701,"meta":702,"navigation":134,"order":131,"path":703,"section":704,"seo":705,"stem":706,"__hash__":707},"docs\u002Fdocs\u002Fextending\u002Fpython-libraries.md","Your own Python library",{"type":21,"value":22,"toc":690},"minimark",[23,43,62,65,94,99,106,178,193,219,223,246,253,306,309,396,400,411,414,418,423,440,443,502,505,610,613,633,639,643,654,657,661,674,686],[24,25,26,27,32,33,37,38,42],"p",{},"A ",[28,29,31],"a",{"href":30},"\u002Fdocs\u002Fextending\u002Fpython-plugins","plugin"," adds keywords ",[34,35,36],"em",{},"to"," Browser. This page\nis about the other move: writing ",[39,40,41],"strong",{},"your own Robot Framework library"," that uses\nBrowser — your keywords, your module, your control over what happens in Python.",[24,44,45,46,50,51,54,55,50,58,61],{},"It is what you want when the logic has outgrown Robot Framework syntax. Logging,\n",[47,48,49],"code",{},"IF","\u002F",[47,52,53],{},"ELSE",", ",[47,56,57],{},"TRY",[47,59,60],{},"EXCEPT",", parsing a response, building test data: all of it is\nordinary Python in a file you own, and none of it changes how Browser behaves.",[24,63,64],{},"Robot Framework still imports Browser. Your library sits alongside it:",[66,67,72],"pre",{"className":68,"code":69,"language":70,"meta":71,"style":71},"language-robot shiki shiki-themes github-light github-dark","*** Settings ***\nLibrary    Browser\nLibrary    MyLibraryA.py\n","robot","",[47,73,74,82,88],{"__ignoreMap":71},[75,76,79],"span",{"class":77,"line":78},"line",1,[75,80,81],{},"*** Settings ***\n",[75,83,85],{"class":77,"line":84},2,[75,86,87],{},"Library    Browser\n",[75,89,91],{"class":77,"line":90},3,[75,92,93],{},"Library    MyLibraryA.py\n",[95,96,98],"h2",{"id":97},"reach-the-instance-never-build-one","Reach the instance, never build one",[24,100,101,102,105],{},"Your library needs the Browser instance Robot Framework already made. Ask\n",[47,103,104],{},"BuiltIn"," for it:",[66,107,112],{"className":108,"code":109,"filename":110,"language":111,"meta":71,"style":71},"language-python shiki shiki-themes github-light github-dark","    @property\n    def browser(self) -> Browser:\n        \"\"\"The Browser instance that Robot Framework imported.\n\n        Looked up on first use rather than in ``__init__``, because Robot\n        Framework may not have imported Browser yet when this library is\n        constructed.\n        \"\"\"\n        if self._browser is None:\n            self._browser = BuiltIn().get_library_instance(\"Browser\")\n        return self._browser\n","MyLibraryA.py","python",[47,113,114,119,124,129,136,142,148,154,160,166,172],{"__ignoreMap":71},[75,115,116],{"class":77,"line":78},[75,117,118],{},"    @property\n",[75,120,121],{"class":77,"line":84},[75,122,123],{},"    def browser(self) -> Browser:\n",[75,125,126],{"class":77,"line":90},[75,127,128],{},"        \"\"\"The Browser instance that Robot Framework imported.\n",[75,130,132],{"class":77,"line":131},4,[75,133,135],{"emptyLinePlaceholder":134},true,"\n",[75,137,139],{"class":77,"line":138},5,[75,140,141],{},"        Looked up on first use rather than in ``__init__``, because Robot\n",[75,143,145],{"class":77,"line":144},6,[75,146,147],{},"        Framework may not have imported Browser yet when this library is\n",[75,149,151],{"class":77,"line":150},7,[75,152,153],{},"        constructed.\n",[75,155,157],{"class":77,"line":156},8,[75,158,159],{},"        \"\"\"\n",[75,161,163],{"class":77,"line":162},9,[75,164,165],{},"        if self._browser is None:\n",[75,167,169],{"class":77,"line":168},10,[75,170,171],{},"            self._browser = BuiltIn().get_library_instance(\"Browser\")\n",[75,173,175],{"class":77,"line":174},11,[75,176,177],{},"        return self._browser\n",[24,179,180,181,184,185,188,189,192],{},"The lazy lookup is not a style preference. Robot Framework may not have imported\nBrowser yet when your library is constructed, so ",[47,182,183],{},"get_library_instance"," in\n",[47,186,187],{},"__init__"," is a race you lose depending on the order of the ",[47,190,191],{},"*** Settings ***","\ntable. Looking it up on first use is always safe.",[194,195,197,211],"doc-note",{"kind":196},"warning",[24,198,199,206,207,210],{},[39,200,201,202,205],{},"Do not write ",[47,203,204],{},"Browser()"," in your library here."," It succeeds, and it hands you\nan instance Robot Framework knows nothing about. Robot Framework drives the\nlistener of the Browser ",[34,208,209],{},"it"," imported, so the automatic closing and the scope\nsettings you still think you have belong to that instance and not to yours — and\nthe two are not cleanly separate either, because Browser keeps state at class\nlevel, shared by every instance in the process.",[24,212,213,214,218],{},"Owning the instance on purpose is a different design, and it works. It is\n",[28,215,217],{"href":216},"\u002Fdocs\u002Fextending\u002Fbrowser-as-a-base","Browser as a base",".",[95,220,222],{"id":221},"calling-browser-from-python","Calling Browser from Python",[24,224,225,226,229,230,54,233,236,237,54,240,229,243,218],{},"Keywords are methods, named in snake_case. ",[47,227,228],{},"Click"," is ",[47,231,232],{},"click",[47,234,235],{},"Get Text"," is\n",[47,238,239],{},"get_text",[47,241,242],{},"Wait For Elements State",[47,244,245],{},"wait_for_elements_state",[24,247,248,252],{},[249,250],"since",{"version":251},"20.4.0"," Arguments are plain Python values — the same strings\nyou would write in a Robot Framework test, converted the same way.",[66,254,256],{"className":108,"code":255,"filename":110,"language":111,"meta":71,"style":71},"    def click_heading_with_middle_mouse_button(self):\n        \"\"\"Call Browser with plain Python values.\n\n        ``\"middle\"`` becomes a ``MouseButton``, ``\"2 seconds\"`` becomes a\n        ``timedelta``, and the ``None`` stays ``None`` instead of turning into\n        the string ``\"None\"``.\n        \"\"\"\n        self.browser.wait_for_elements_state(\"id=heading1\", \"visible\", \"2 seconds\")\n        self.browser.click(\"id=heading1\", \"middle\")\n        return self.browser.evaluate_javascript(None, \"() => 'evaluated'\")\n",[47,257,258,263,268,272,277,282,287,291,296,301],{"__ignoreMap":71},[75,259,260],{"class":77,"line":78},[75,261,262],{},"    def click_heading_with_middle_mouse_button(self):\n",[75,264,265],{"class":77,"line":84},[75,266,267],{},"        \"\"\"Call Browser with plain Python values.\n",[75,269,270],{"class":77,"line":90},[75,271,135],{"emptyLinePlaceholder":134},[75,273,274],{"class":77,"line":131},[75,275,276],{},"        ``\"middle\"`` becomes a ``MouseButton``, ``\"2 seconds\"`` becomes a\n",[75,278,279],{"class":77,"line":138},[75,280,281],{},"        ``timedelta``, and the ``None`` stays ``None`` instead of turning into\n",[75,283,284],{"class":77,"line":144},[75,285,286],{},"        the string ``\"None\"``.\n",[75,288,289],{"class":77,"line":150},[75,290,159],{},[75,292,293],{"class":77,"line":156},[75,294,295],{},"        self.browser.wait_for_elements_state(\"id=heading1\", \"visible\", \"2 seconds\")\n",[75,297,298],{"class":77,"line":162},[75,299,300],{},"        self.browser.click(\"id=heading1\", \"middle\")\n",[75,302,303],{"class":77,"line":168},[75,304,305],{},"        return self.browser.evaluate_javascript(None, \"() => 'evaluated'\")\n",[24,307,308],{},"Four things worth knowing about that, and no more:",[310,311,312,348,375,386],"ul",{},[313,314,315,318,319,322,323,54,326,329,330,54,333,336,337,54,340,343,344,347],"li",{},[39,316,317],{},"Values convert exactly as Robot Framework converts them."," ",[47,320,321],{},"\"middle\"","\nbecomes a ",[47,324,325],{},"MouseButton",[47,327,328],{},"\"2 seconds\""," a ",[47,331,332],{},"timedelta",[47,334,335],{},"\"validate\""," an\n",[47,338,339],{},"AssertionOperator",[47,341,342],{},"\"true\""," becomes ",[47,345,346],{},"True",". It is not a curated subset —\nthe same converters run, so every type Robot Framework can convert for a\nkeyword argument converts here too.",[313,349,350,359,360,363,364,367,368,370,371,374],{},[39,351,352,353,356,357,218],{},"A Python ",[47,354,355],{},"None"," stays ",[47,358,355],{}," It is never converted, so it never arrives\nas the string ",[47,361,362],{},"\"None\"",". From Python, meaning ",[34,365,366],{},"nothing"," is passing the ",[47,369,355],{},"\nobject — which is what the ",[47,372,373],{},"evaluate_javascript(None, …)"," above depends on.",[313,376,377,385],{},[39,378,379,380,382,383,218],{},"A string ",[47,381,362],{}," is not ",[47,384,355],{}," It still converts by the argument's type\nhint, exactly as it would from Robot Framework.",[313,387,388,391,392,395],{},[39,389,390],{},"Passing an already-typed value is the opt-out."," Conversion is idempotent,\nso ",[47,393,394],{},"browser.click(\"\u002F\u002Fbutton\", MouseButton.middle)"," keeps working unchanged.",[95,397,399],{"id":398},"what-stays-exactly-as-it-was","What stays exactly as it was",[24,401,402,403,406,407,410],{},"Robot Framework imported Browser, so Browser is a fully ordinary library in this\nsuite. Pages and contexts close automatically at the end of a test, ",[47,404,405],{},"scope=Test","\nsettings revert, screenshots and videos and traces land in the run's output\ndirectory, and ",[47,408,409],{},"Set Browser Timeout"," behaves the way its documentation says.",[24,412,413],{},"Nothing on this page changes any of that. That is the point of leaving the\ninstance where it is.",[95,415,417],{"id":416},"two-things-that-do-change","Two things that do change",[419,420,422],"h3",{"id":421},"failure-screenshots-stop-at-the-python-boundary","Failure screenshots stop at the Python boundary",[24,424,425,426,429,430,432,433,436,437,439],{},"Browser takes its ",[47,427,428],{},"run_on_failure"," screenshot from the dynamic library API —\nthe code path Robot Framework enters when ",[34,431,209],{}," calls a Browser keyword. A\nkeyword your Python code calls never goes through it, so a failing\n",[47,434,435],{},"self.browser.click(...)"," leaves no screenshot, even though ",[47,438,228],{}," written in\nthe suite still does.",[24,441,442],{},"The library's own tests pin the difference with two tests that differ only in\nwho makes the call:",[66,444,447],{"className":68,"code":445,"filename":446,"language":70,"meta":71,"style":71},"Run On Failure Takes Screenshot\n    [Documentation]    Expected to fail. The keyword is called by Robot Framework, so Browser\n    ...    runs its `run_on_failure` keyword and leaves a screenshot on disk.\n    Open Login Page    ${LOGIN_URL}\n    Click    id=this_element_does_not_exist\n\nPython Call Failure Takes No Screenshot\n    [Documentation]    Expected to fail. The very same Browser keyword, called from Python by\n    ...    MyLibraryA, never enters Browser's `run_on_failure` and leaves no screenshot.\n    Open Login Page    ${LOGIN_URL}\n    Click Missing Element\n","context_a.robot",[47,448,449,454,459,464,469,474,478,483,488,493,497],{"__ignoreMap":71},[75,450,451],{"class":77,"line":78},[75,452,453],{},"Run On Failure Takes Screenshot\n",[75,455,456],{"class":77,"line":84},[75,457,458],{},"    [Documentation]    Expected to fail. The keyword is called by Robot Framework, so Browser\n",[75,460,461],{"class":77,"line":90},[75,462,463],{},"    ...    runs its `run_on_failure` keyword and leaves a screenshot on disk.\n",[75,465,466],{"class":77,"line":131},[75,467,468],{},"    Open Login Page    ${LOGIN_URL}\n",[75,470,471],{"class":77,"line":138},[75,472,473],{},"    Click    id=this_element_does_not_exist\n",[75,475,476],{"class":77,"line":144},[75,477,135],{"emptyLinePlaceholder":134},[75,479,480],{"class":77,"line":150},[75,481,482],{},"Python Call Failure Takes No Screenshot\n",[75,484,485],{"class":77,"line":156},[75,486,487],{},"    [Documentation]    Expected to fail. The very same Browser keyword, called from Python by\n",[75,489,490],{"class":77,"line":162},[75,491,492],{},"    ...    MyLibraryA, never enters Browser's `run_on_failure` and leaves no screenshot.\n",[75,494,495],{"class":77,"line":168},[75,496,468],{},[75,498,499],{"class":77,"line":174},[75,500,501],{},"    Click Missing Element\n",[24,503,504],{},"You lose the screenshot for exactly the calls you moved into Python, and nothing\nwarns you. For a small library, a decorator gets it back:",[66,506,508],{"className":108,"code":507,"filename":110,"language":111,"meta":71,"style":71},"def screenshot_on_failure(keyword):\n    \"\"\"Take a screenshot when the wrapped keyword fails, then re-raise.\n\n    Browser runs its own ``run_on_failure`` from the Robot Framework dynamic\n    library API, which is not entered when a keyword is called from Python.\n    A small library can get equivalent behaviour with a decorator like this.\n    Larger libraries usually intercept in one place instead, either by being a\n    dynamic library themselves or by using PythonLibCore.\n    \"\"\"\n\n    @functools.wraps(keyword)\n    def wrapper(self, *args, **kwargs):\n        try:\n            return keyword(self, *args, **kwargs)\n        except Exception:\n            self.browser.take_screenshot(\"my-library-failure-{index}\")\n            raise\n\n    return wrapper\n",[47,509,510,515,520,524,529,534,539,544,549,554,558,563,569,575,581,587,593,599,604],{"__ignoreMap":71},[75,511,512],{"class":77,"line":78},[75,513,514],{},"def screenshot_on_failure(keyword):\n",[75,516,517],{"class":77,"line":84},[75,518,519],{},"    \"\"\"Take a screenshot when the wrapped keyword fails, then re-raise.\n",[75,521,522],{"class":77,"line":90},[75,523,135],{"emptyLinePlaceholder":134},[75,525,526],{"class":77,"line":131},[75,527,528],{},"    Browser runs its own ``run_on_failure`` from the Robot Framework dynamic\n",[75,530,531],{"class":77,"line":138},[75,532,533],{},"    library API, which is not entered when a keyword is called from Python.\n",[75,535,536],{"class":77,"line":144},[75,537,538],{},"    A small library can get equivalent behaviour with a decorator like this.\n",[75,540,541],{"class":77,"line":150},[75,542,543],{},"    Larger libraries usually intercept in one place instead, either by being a\n",[75,545,546],{"class":77,"line":156},[75,547,548],{},"    dynamic library themselves or by using PythonLibCore.\n",[75,550,551],{"class":77,"line":162},[75,552,553],{},"    \"\"\"\n",[75,555,556],{"class":77,"line":168},[75,557,135],{"emptyLinePlaceholder":134},[75,559,560],{"class":77,"line":174},[75,561,562],{},"    @functools.wraps(keyword)\n",[75,564,566],{"class":77,"line":565},12,[75,567,568],{},"    def wrapper(self, *args, **kwargs):\n",[75,570,572],{"class":77,"line":571},13,[75,573,574],{},"        try:\n",[75,576,578],{"class":77,"line":577},14,[75,579,580],{},"            return keyword(self, *args, **kwargs)\n",[75,582,584],{"class":77,"line":583},15,[75,585,586],{},"        except Exception:\n",[75,588,590],{"class":77,"line":589},16,[75,591,592],{},"            self.browser.take_screenshot(\"my-library-failure-{index}\")\n",[75,594,596],{"class":77,"line":595},17,[75,597,598],{},"            raise\n",[75,600,602],{"class":77,"line":601},18,[75,603,135],{"emptyLinePlaceholder":134},[75,605,607],{"class":77,"line":606},19,[75,608,609],{},"    return wrapper\n",[24,611,612],{},"Applied to the keywords you care about:",[66,614,616],{"className":108,"code":615,"filename":110,"language":111,"meta":71,"style":71},"    @screenshot_on_failure\n    def click_missing_element_with_screenshot(self):\n        self.browser.click(\"id=this_element_does_not_exist\")\n",[47,617,618,623,628],{"__ignoreMap":71},[75,619,620],{"class":77,"line":78},[75,621,622],{},"    @screenshot_on_failure\n",[75,624,625],{"class":77,"line":84},[75,626,627],{},"    def click_missing_element_with_screenshot(self):\n",[75,629,630],{"class":77,"line":90},[75,631,632],{},"        self.browser.click(\"id=this_element_does_not_exist\")\n",[24,634,635,636,638],{},"It costs one decorator per keyword, and nothing enforces that you remember it.\nA larger library is better served by intercepting in one place — the shapes for\nthat are on ",[28,637,217],{"href":216},", and they\napply here unchanged.",[419,640,642],{"id":641},"the-log-gets-shallower","The log gets shallower",[24,644,645,646,649,650,653],{},"A Browser keyword called from Python is not a keyword in ",[47,647,648],{},"output.xml"," — Robot\nFramework never saw a keyword call. Everything Browser logs still appears, but\nit appears ",[34,651,652],{},"inside"," the keyword of your library that made the call.",[24,655,656],{},"That is the trade every one of these libraries makes: a detailed Robot Framework\nlog, one row per Browser keyword, becomes your own keyword's log. Log the things\nthat matter to a reader of the report, because the automatic detail is gone.",[95,658,660],{"id":659},"where-these-examples-come-from","Where these examples come from",[24,662,663,664,667,668,670,671,673],{},"Every labelled block on this page is quoted from a real file, committed in this\nrepository under ",[47,665,666],{},"examples\u002Fpython-extension\u002F",", and a test fails the build if a\nquote stops matching it. ",[47,669,110],{}," is a complete worked library for this\npage and ",[47,672,446],{}," is the suite that exercises it — including the two\nfailing tests above, which are expected to fail and are the demonstration.",[24,675,676,677,685],{},"Those files are a copy. They run as acceptance tests in the library itself, in\n",[28,678,682],{"href":679,"rel":680},"https:\u002F\u002Fgithub.com\u002FMarketSquare\u002Frobotframework-browser\u002Ftree\u002Fmain\u002Fatest\u002Ftest\u002F13_Python_Extension",[681],"nofollow",[47,683,684],{},"atest\u002Ftest\u002F13_Python_Extension\u002F",",\nwhich is where a change to them belongs.",[687,688,689],"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":71,"searchDepth":84,"depth":84,"links":691},[692,693,694,695,699],{"id":97,"depth":84,"text":98},{"id":221,"depth":84,"text":222},{"id":398,"depth":84,"text":399},{"id":416,"depth":84,"text":417,"children":696},[697,698],{"id":421,"depth":90,"text":422},{"id":641,"depth":90,"text":642},{"id":659,"depth":84,"text":660},"Move business logic out of Robot Framework and keep Browser exactly as it is.","md",{},"\u002Fdocs\u002Fextending\u002Fpython-libraries","extending",{"title":19,"description":700},"docs\u002Fextending\u002Fpython-libraries","lkhUOHw5G4d4y7_XCI11XVJE7XDJbWnzYNsts8wcTR4",[709,714,718,722,726,730,734,736,740,744,745,748,752,757,761,765,769,774,778,782,786],{"path":710,"title":711,"description":712,"section":713,"order":138},"\u002Fdocs\u002Fconcepts\u002Farchitecture","How Browser works","Python, a Node process and Playwright — what runs where, and which installation to choose.","concepts",{"path":715,"title":716,"description":717,"section":713,"order":90},"\u002Fdocs\u002Fconcepts\u002Fassertions","Assertions","Almost every getter can assert, every assertion retries, and each return type allows different operators. The complete AssertionEngine reference.",{"path":719,"title":720,"description":721,"section":713,"order":78},"\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":723,"title":724,"description":725,"section":713,"order":144},"\u002Fdocs\u002Fconcepts\u002Flogging","Logging and debugging","Four places Browser tells you what happened — log.html, the Playwright log, traces and coverage.",{"path":727,"title":728,"description":729,"section":713,"order":84},"\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":731,"title":732,"description":733,"section":713,"order":131},"\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":216,"title":217,"description":735,"section":704,"order":138},"Own the Browser instance in your own library — what you get free, what one line restores, and what you build yourself.",{"path":737,"title":738,"description":739,"section":704,"order":84},"\u002Fdocs\u002Fextending\u002Fjavascript-basics","Just enough JavaScript","The JavaScript you need to write extensions and Evaluate JavaScript, for people who write Python.",{"path":741,"title":742,"description":743,"section":704,"order":90},"\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":703,"title":19,"description":700,"section":704,"order":131},{"path":30,"title":746,"description":747,"section":704,"order":78},"Python plugins","Add or replace keywords from Python, with access to the library's own API and to the Node side.",{"path":749,"title":750,"description":751,"section":704,"order":144},"\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":753,"title":754,"description":755,"section":756,"order":131},"\u002Fdocs\u002Fmobile\u002Fdevice-list","Every device descriptor","The full list of names Get Device accepts, with the viewport, pixel ratio, touch and engine each one sets — filterable and sortable.","mobile",{"path":758,"title":759,"description":760,"section":756,"order":84},"\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.",{"path":762,"title":763,"description":764,"section":756,"order":78},"\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":766,"title":767,"description":768,"section":756,"order":90},"\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":770,"title":771,"description":772,"section":773,"order":90},"\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":775,"title":776,"description":777,"section":773,"order":131},"\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":779,"title":780,"description":781,"section":773,"order":84},"\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":783,"title":784,"description":785,"section":773,"order":78},"\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":787,"title":788,"description":789,"section":790,"order":78},"\u002Fdocs\u002Fstart\u002Fgetting-started","Getting started","Install Browser, write a first test, and run it.","start",[792,795,798],{"to":793,"label":794},"\u002Fwhy\u002Fvs-cypress","vs Cypress",{"to":796,"label":797},"\u002Fwhy\u002Fvs-playwright","vs Playwright Test",{"to":799,"label":800},"\u002Fwhy\u002Fvs-seleniumlibrary","vs SeleniumLibrary",[802],"\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>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">Library\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    MyLibraryA.py\u003C\u002Fspan>\u003C\u002Fspan>",[804],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">property\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    def\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\"> ->\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\"> Browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">:\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">        \"\"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">The Browser instance that Robot Framework imported.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        Looked up on first use rather than in ``__init__``, because Robot\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        Framework may not have imported Browser yet when this library is\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        constructed.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">        \"\"\"\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\"> self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">_browser \u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\">is\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\"> None\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">:\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFB86B\">            self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">_browser \u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\">=\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> BuiltIn\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">().\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">get_library_instance\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">Browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\"> self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">_browser\u003C\u002Fspan>\u003C\u002Fspan>",[806],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    def\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> click_heading_with_middle_mouse_button\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">):\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">        \"\"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">Call Browser with plain Python values.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        ``\"middle\"`` becomes a ``MouseButton``, ``\"2 seconds\"`` becomes a\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        ``timedelta``, and the ``None`` stays ``None`` instead of turning into\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">        the string ``\"None\"``.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">        \"\"\"\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFB86B\">        self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">wait_for_elements_state\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">id=heading1\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\"> \"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">visible\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\"> \"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">2 seconds\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFB86B\">        self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">click\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">id=heading1\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\"> \"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">middle\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\"> self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">evaluate_javascript\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">None\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\"> \"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">() => 'evaluated'\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>",[808],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Run On Failure Takes Screenshot\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    [Documentation]    Expected to fail. The keyword is called by Robot Framework, so Browser\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    ...    runs its `run_on_failure` keyword and leaves a screenshot on disk.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    Open Login Page    ${LOGIN_URL}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    Click    id=this_element_does_not_exist\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Python Call Failure Takes No Screenshot\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    [Documentation]    Expected to fail. The very same Browser keyword, called from Python by\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    ...    MyLibraryA, never enters Browser's `run_on_failure` and leaves no screenshot.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    Open Login Page    ${LOGIN_URL}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">    Click Missing Element\u003C\u002Fspan>\u003C\u002Fspan>",[810],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">def\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> screenshot_on_failure\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">keyword\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">):\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    \"\"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">Take a screenshot when the wrapped keyword fails, then re-raise.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">    Browser runs its own ``run_on_failure`` from the Robot Framework dynamic\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">    library API, which is not entered when a keyword is called from Python.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">    A small library can get equivalent behaviour with a decorator like this.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">    Larger libraries usually intercept in one place instead, either by being a\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E0D3A3\">    dynamic library themselves or by using PythonLibCore.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\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:#2BD9CC\">functools\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">wraps\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">keyword\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    def\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> wrapper\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> *\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">args\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> **\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">kwargs\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">):\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">        try\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">:\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> keyword\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> *\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">args\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">,\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> **\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">kwargs\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">        except\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> Exception\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">:\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFB86B\">            self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">take_screenshot\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">my-library-failure-\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">{index}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">            raise\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#C9A6E9\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\"> wrapper\u003C\u002Fspan>\u003C\u002Fspan>",[812],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">screenshot_on_failure\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    def\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\"> click_missing_element_with_screenshot\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">):\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFB86B\">        self\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#E2E1D7\">browser\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">.\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">click\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">(\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">id=this_element_does_not_exist\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">)\u003C\u002Fspan>\u003C\u002Fspan>",1787137745013]