[{"data":1,"prerenderedAt":726},["ShallowReactive",2],{"SiteFooter_cLEacilcm59lTjFJjLY6mXNWLL0ETtzOIiZwIHgDpy0":3,"doc-\u002Fdocs\u002Fmobile\u002Ftouch":17,"docs-nav":627,"nav-compare":700,"editor-24w959":710,"editor-1uchep9":712,"editor-9u8fsd":714,"editor-10benum":716,"editor-cff65l":718,"editor-1d3ocae":720,"editor-9ubwuu":722,"editor-10jksj3":724},["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":619,"extension":620,"meta":621,"navigation":114,"order":105,"path":622,"section":623,"seo":624,"stem":625,"__hash__":626},"docs\u002Fdocs\u002Fmobile\u002Ftouch.md","Touch, permissions and location",{"type":21,"value":22,"toc":609},"minimark",[23,27,32,49,58,87,133,138,148,156,160,167,170,175,183,187,190,199,202,222,267,271,277,317,322,326,332,366,372,380,384,387,416,433,437,574,577,592,605],[24,25,26],"p",{},"A phone is not only a small screen. It is a touch screen that knows where it is\nand can be asked for the camera. All three are context options, and all three\nare testable without a device.",[28,29,31],"h2",{"id":30},"tap","Tap",[33,34,39],"pre",{"className":35,"code":36,"language":37,"meta":38,"style":38},"language-robot shiki shiki-themes github-light github-dark","Tap    id=filter-toggle\n","robot","",[40,41,42],"code",{"__ignoreMap":38},[43,44,47],"span",{"class":45,"line":46},"line",1,[43,48,36],{},[24,50,51,53,54,57],{},[40,52,31],{}," dispatches a real touch event through the browser's touchscreen, rather\nthan a mouse click. It runs the same actionability checks as ",[40,55,56],{},"Click"," — the\nelement must be visible, stable, receiving events and enabled — and scrolls it\ninto view first.",[59,60,62],"doc-note",{"kind":61},"warning",[24,63,64,74,75,78,79,82,83,86],{},[65,66,67,69,70,73],"strong",{},[40,68,31],{}," requires ",[40,71,72],{},"hasTouch=True"," on the context."," Without it, Playwright\nrefuses. This is the first thing that goes wrong for everybody, and the error is\nclear once you know to look for it. A mobile device descriptor sets ",[40,76,77],{},"hasTouch"," for you — the ",[40,80,81],{},"Desktop *"," ones do not;\na bare ",[40,84,85],{},"viewport="," does not.",[33,88,90],{"className":35,"code":89,"language":37,"meta":38,"style":38},"# Works.\nNew Context    viewport={'width': 390, 'height': 844}    hasTouch=True\nTap    text=Menu\n\n# Fails: no touch support on this context.\nNew Context    viewport={'width': 390, 'height': 844}\nTap    text=Menu\n",[40,91,92,97,103,109,116,122,128],{"__ignoreMap":38},[43,93,94],{"class":45,"line":46},[43,95,96],{},"# Works.\n",[43,98,100],{"class":45,"line":99},2,[43,101,102],{},"New Context    viewport={'width': 390, 'height': 844}    hasTouch=True\n",[43,104,106],{"class":45,"line":105},3,[43,107,108],{},"Tap    text=Menu\n",[43,110,112],{"class":45,"line":111},4,[43,113,115],{"emptyLinePlaceholder":114},true,"\n",[43,117,119],{"class":45,"line":118},5,[43,120,121],{},"# Fails: no touch support on this context.\n",[43,123,125],{"class":45,"line":124},6,[43,126,127],{},"New Context    viewport={'width': 390, 'height': 844}\n",[43,129,131],{"class":45,"line":130},7,[43,132,108],{},[134,135,137],"h3",{"id":136},"tap-or-click","Tap or Click?",[24,139,140,141,143,144,147],{},"Use ",[40,142,31],{}," when the touch event itself is the thing under test — a handler bound\nto ",[40,145,146],{},"touchstart",", a control that behaves differently for touch, a tap target you\nwant to prove is reachable.",[24,149,140,150,152,153,155],{},[40,151,56],{}," for everything else, including on a mobile-sized context. ",[40,154,56],{},"\nworks on a touch context and is what most of your suite should say, because most\nof your suite is testing the application rather than the input method.",[28,157,159],{"id":158},"gestures-beyond-a-tap","Gestures beyond a tap",[24,161,162,163,166],{},"Being straight about a gap: ",[65,164,165],{},"there is no swipe keyword, and no pinch or\nmulti-touch."," Playwright's touchscreen API is a single tap.",[24,168,169],{},"What you can do instead, in rough order of preference:",[171,172],"doc-table",{":head":173,":rows":174},"[\"You want\",\"Use\"]","[[\"A carousel moved along\",\"Its next\u002Fprevious control. It has one, because it needs one for keyboard users too.\"],[\"Scrolling\",\"`Scroll To`, or `Mouse Wheel`. Neither needs touch.\"],[\"Drag-style reordering\",\"`Drag And Drop`, which uses mouse events\"],[\"A swipe-like drag\",\"`Drag And Drop Relative To` — mouse-based, so it fires pointer events, not touch\"],[\"A genuine touch swipe\",\"`Evaluate JavaScript`, dispatching TouchEvents yourself\"],[\"Pinch to zoom\",\"Nothing. This is a real-device test.\"]]",[24,176,177,178,182],{},"If a feature is ",[179,180,181],"em",{},"only"," reachable by swiping, that is worth raising with the team\nregardless of testing: it is unreachable by keyboard too, which is an\naccessibility defect rather than a test-automation inconvenience.",[28,184,186],{"id":185},"permissions","Permissions",[24,188,189],{},"Browsers ask the user before handing over the camera, the microphone or the\nlocation. That dialog is browser chrome, not page content, so it is not\nsomething a test can click. Grant it up front instead:",[33,191,193],{"className":35,"code":192,"language":37,"meta":38,"style":38},"New Context    permissions=['geolocation', 'notifications']\n",[40,194,195],{"__ignoreMap":38},[43,196,197],{"class":45,"line":46},[43,198,192],{},[24,200,201],{},"or during the test, when the point is to check the before-and-after:",[33,203,205],{"className":35,"code":204,"language":37,"meta":38,"style":38},"Grant Permissions    geolocation\nGrant Permissions    camera    microphone    origin=https:\u002F\u002Fexample.com\nClear Permissions\n",[40,206,207,212,217],{"__ignoreMap":38},[43,208,209],{"class":45,"line":46},[43,210,211],{},"Grant Permissions    geolocation\n",[43,213,214],{"class":45,"line":99},[43,215,216],{},"Grant Permissions    camera    microphone    origin=https:\u002F\u002Fexample.com\n",[43,218,219],{"class":45,"line":105},[43,220,221],{},"Clear Permissions\n",[24,223,224,225,228,229,232,233,232,236,232,239,242,243,246,247,232,249,242,251,254,255,258,259,262,263,266],{},"Available permissions — though only ",[65,226,227],{},"Chromium accepts all of them",". WebKit\naccepts ",[40,230,231],{},"geolocation",", ",[40,234,235],{},"notifications",[40,237,238],{},"camera",[40,240,241],{},"microphone"," and\n",[40,244,245],{},"clipboard-read","; Firefox accepts ",[40,248,231],{},[40,250,235],{},[40,252,253],{},"local-network-access",". Anything else fails — from ",[40,256,257],{},"New Context"," or\n",[40,260,261],{},"Grant Permissions"," on Chromium and Firefox, but on WebKit not until the next\n",[40,264,265],{},"New Page",", which makes it look like a navigation problem.",[171,268],{":head":269,":rows":270},"[\"Group\",\"Values\"]","[[\"Location\",\"`geolocation`\"],[\"Media\",\"`camera`, `microphone`\"],[\"Clipboard\",\"`clipboard-read`, `clipboard-write`\"],[\"Notifications and payments\",\"`notifications`, `payment-handler`\"],[\"Sensors\",\"`accelerometer`, `gyroscope`, `magnetometer`, `ambient-light-sensor`\"],[\"Other\",\"`background-sync`, `midi`, `midi-sysex`, `local-network-access`\"]]",[24,272,273,276],{},[40,274,275],{},"Clear Permissions"," resets every grant on the context — useful for testing the\ndenied path, which is the one that actually breaks in production:",[33,278,280],{"className":35,"code":279,"language":37,"meta":38,"style":38},"*** Test Cases ***\nStore Finder Degrades Without Location\n    New Context    viewport={'width': 390, 'height': 844}    hasTouch=True\n    New Page    ${URL}\n    Clear Permissions\n    Tap    id=find-near-me\n    Get Text    id=message    ==    Enter a postcode instead\n",[40,281,282,287,292,297,302,307,312],{"__ignoreMap":38},[43,283,284],{"class":45,"line":46},[43,285,286],{},"*** Test Cases ***\n",[43,288,289],{"class":45,"line":99},[43,290,291],{},"Store Finder Degrades Without Location\n",[43,293,294],{"class":45,"line":105},[43,295,296],{},"    New Context    viewport={'width': 390, 'height': 844}    hasTouch=True\n",[43,298,299],{"class":45,"line":111},[43,300,301],{},"    New Page    ${URL}\n",[43,303,304],{"class":45,"line":118},[43,305,306],{},"    Clear Permissions\n",[43,308,309],{"class":45,"line":124},[43,310,311],{},"    Tap    id=find-near-me\n",[43,313,314],{"class":45,"line":130},[43,315,316],{},"    Get Text    id=message    ==    Enter a postcode instead\n",[59,318,319],{},[24,320,321],{},"Permissions belong to the context, not the page, so a new context starts with\nnothing granted. That is usually what you want — it is what a first-time visitor\ngets.",[28,323,325],{"id":324},"location","Location",[24,327,328,329,331],{},"With ",[40,330,231],{}," granted, set the coordinates:",[33,333,335],{"className":35,"code":334,"language":37,"meta":38,"style":38},"New Context\n...    permissions=['geolocation']\n...    geolocation={'latitude': 60.17, 'longitude': 24.94}\n\n# or later, to simulate movement\nSet Geolocation    60.17    24.94    accuracy=50\n",[40,336,337,342,347,352,356,361],{"__ignoreMap":38},[43,338,339],{"class":45,"line":46},[43,340,341],{},"New Context\n",[43,343,344],{"class":45,"line":99},[43,345,346],{},"...    permissions=['geolocation']\n",[43,348,349],{"class":45,"line":105},[43,350,351],{},"...    geolocation={'latitude': 60.17, 'longitude': 24.94}\n",[43,353,354],{"class":45,"line":111},[43,355,115],{"emptyLinePlaceholder":114},[43,357,358],{"class":45,"line":118},[43,359,360],{},"# or later, to simulate movement\n",[43,362,363],{"class":45,"line":124},[43,364,365],{},"Set Geolocation    60.17    24.94    accuracy=50\n",[24,367,368,371],{},[40,369,370],{},"accuracy"," is in metres, and it is worth exercising: an app that draws a\nconfidence circle, or refuses to act on a vague fix, behaves differently at 5 m\nand at 5000 m.",[59,373,374],{"kind":61},[24,375,376,377,379],{},"Setting a location without granting ",[40,378,231],{}," gets you a page whose location\nrequest is denied — which looks exactly like a broken test. If your store finder\n\"cannot find you\", check the permission before you check the coordinates.",[28,381,383],{"id":382},"locale-timezone-and-colour-scheme","Locale, timezone and colour scheme",[24,385,386],{},"Context options too, and worth pairing with a device when you are testing what a\ntraveller sees, or what someone with dark mode enabled sees:",[33,388,390],{"className":35,"code":389,"language":37,"meta":38,"style":38},"New Context\n...    locale=de-DE\n...    timezoneId=Europe\u002FBerlin\n...    colorScheme=dark\n...    reducedMotion=reduce\n",[40,391,392,396,401,406,411],{"__ignoreMap":38},[43,393,394],{"class":45,"line":46},[43,395,341],{},[43,397,398],{"class":45,"line":99},[43,399,400],{},"...    locale=de-DE\n",[43,402,403],{"class":45,"line":105},[43,404,405],{},"...    timezoneId=Europe\u002FBerlin\n",[43,407,408],{"class":45,"line":111},[43,409,410],{},"...    colorScheme=dark\n",[43,412,413],{"class":45,"line":118},[43,414,415],{},"...    reducedMotion=reduce\n",[24,417,418,421,422,425,426,242,429,432],{},[40,419,420],{},"colorScheme"," and ",[40,423,424],{},"reducedMotion"," drive ",[40,427,428],{},"@media (prefers-color-scheme)",[40,430,431],{},"@media (prefers-reduced-motion)",". Both are one line to test and are routinely\nshipped broken, because the person who built the feature had the other setting.",[28,434,436],{"id":435},"putting-it-together","Putting it together",[33,438,441],{"className":35,"code":439,"filename":440,"language":37,"meta":38,"style":38},"*** Settings ***\nLibrary     Browser\n\n\n*** Variables ***\n# Robot Framework ry, Kampinkuja 2, 00100 Helsinki.\n${LAT}      ${60.16829}\n${LON}      ${24.93051}\n\n\n*** Test Cases ***\nThe Map Finds The Phone, In Helsinki\n    ${device} =    Get Device    iPhone 13\n    New Browser    webkit    headless=True\n    New Context\n    ...    &{device}\n    ...    permissions=['geolocation']\n    ...    geolocation={'latitude': ${LAT}, 'longitude': ${LON}}\n    ...    locale=fi-FI\n    ...    timezoneId=Europe\u002FHelsinki\n    New Page    https:\u002F\u002Fwww.openstreetmap.org\n\n    Tap        role=button[name=\"Näytä oma sijaintini\"]\n    Get Url    *=    60.168\n","locate-me.robot",[40,442,443,448,453,457,461,466,471,476,482,487,492,497,503,509,515,521,527,533,539,545,551,557,562,568],{"__ignoreMap":38},[43,444,445],{"class":45,"line":46},[43,446,447],{},"*** Settings ***\n",[43,449,450],{"class":45,"line":99},[43,451,452],{},"Library     Browser\n",[43,454,455],{"class":45,"line":105},[43,456,115],{"emptyLinePlaceholder":114},[43,458,459],{"class":45,"line":111},[43,460,115],{"emptyLinePlaceholder":114},[43,462,463],{"class":45,"line":118},[43,464,465],{},"*** Variables ***\n",[43,467,468],{"class":45,"line":124},[43,469,470],{},"# Robot Framework ry, Kampinkuja 2, 00100 Helsinki.\n",[43,472,473],{"class":45,"line":130},[43,474,475],{},"${LAT}      ${60.16829}\n",[43,477,479],{"class":45,"line":478},8,[43,480,481],{},"${LON}      ${24.93051}\n",[43,483,485],{"class":45,"line":484},9,[43,486,115],{"emptyLinePlaceholder":114},[43,488,490],{"class":45,"line":489},10,[43,491,115],{"emptyLinePlaceholder":114},[43,493,495],{"class":45,"line":494},11,[43,496,286],{},[43,498,500],{"class":45,"line":499},12,[43,501,502],{},"The Map Finds The Phone, In Helsinki\n",[43,504,506],{"class":45,"line":505},13,[43,507,508],{},"    ${device} =    Get Device    iPhone 13\n",[43,510,512],{"class":45,"line":511},14,[43,513,514],{},"    New Browser    webkit    headless=True\n",[43,516,518],{"class":45,"line":517},15,[43,519,520],{},"    New Context\n",[43,522,524],{"class":45,"line":523},16,[43,525,526],{},"    ...    &{device}\n",[43,528,530],{"class":45,"line":529},17,[43,531,532],{},"    ...    permissions=['geolocation']\n",[43,534,536],{"class":45,"line":535},18,[43,537,538],{},"    ...    geolocation={'latitude': ${LAT}, 'longitude': ${LON}}\n",[43,540,542],{"class":45,"line":541},19,[43,543,544],{},"    ...    locale=fi-FI\n",[43,546,548],{"class":45,"line":547},20,[43,549,550],{},"    ...    timezoneId=Europe\u002FHelsinki\n",[43,552,554],{"class":45,"line":553},21,[43,555,556],{},"    New Page    https:\u002F\u002Fwww.openstreetmap.org\n",[43,558,560],{"class":45,"line":559},22,[43,561,115],{"emptyLinePlaceholder":114},[43,563,565],{"class":45,"line":564},23,[43,566,567],{},"    Tap        role=button[name=\"Näytä oma sijaintini\"]\n",[43,569,571],{"class":45,"line":570},24,[43,572,573],{},"    Get Url    *=    60.168\n",[24,575,576],{},"That runs against the real OpenStreetMap, and the coordinates it proves are the\nFoundation's own front door.",[24,578,579,580,583,584,587,588,591],{},"The Finnish in that selector is not decoration, and it is the trap in this\nchapter: ",[40,581,582],{},"locale"," changes the page, including the accessible names that\n",[40,585,586],{},"role=…[name=…]"," matches on. Set ",[40,589,590],{},"locale=fi-FI"," and \"Show My Location\" becomes\n\"Näytä oma sijaintini\". A suite that sets a locale and selects by visible name\nhas to pick one — either select on something the translation does not touch, or\naccept that the selector belongs to that locale.",[24,593,594,595,598,599,604],{},"One context, one device, no hardware. Everything a real phone would have told\nyou about ",[179,596,597],{},"behaviour"," — and none of what it would have told you about how the\nthing feels in a hand, which is\n",[600,601,603],"a",{"href":602},"\u002Fdocs\u002Fmobile\u002Fresponsive","a job for a person",".",[606,607,608],"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":99,"depth":99,"links":610},[611,614,615,616,617,618],{"id":30,"depth":99,"text":31,"children":612},[613],{"id":136,"depth":105,"text":137},{"id":158,"depth":99,"text":159},{"id":185,"depth":99,"text":186},{"id":324,"depth":99,"text":325},{"id":382,"depth":99,"text":383},{"id":435,"depth":99,"text":436},"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.","md",{},"\u002Fdocs\u002Fmobile\u002Ftouch","mobile",{"title":19,"description":619},"docs\u002Fmobile\u002Ftouch","vuwfk0Tk7IXyCAXUVOQKU_n4j0BTX2vP3Z-uNotxGIU",[628,633,637,641,645,649,653,658,662,666,670,674,677,678,683,687,691,695],{"path":629,"title":630,"description":631,"section":632,"order":118},"\u002Fdocs\u002Fconcepts\u002Farchitecture","How Browser works","Python, a Node process and Playwright — what runs where, and which installation to choose.","concepts",{"path":634,"title":635,"description":636,"section":632,"order":105},"\u002Fdocs\u002Fconcepts\u002Fassertions","Assertions","Almost every getter can assert, every assertion retries, and each return type allows different operators. The complete AssertionEngine reference.",{"path":638,"title":639,"description":640,"section":632,"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":642,"title":643,"description":644,"section":632,"order":124},"\u002Fdocs\u002Fconcepts\u002Flogging","Logging and debugging","Four places Browser tells you what happened — log.html, the Playwright log, traces and coverage.",{"path":646,"title":647,"description":648,"section":632,"order":99},"\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":650,"title":651,"description":652,"section":632,"order":111},"\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":654,"title":655,"description":656,"section":657,"order":99},"\u002Fdocs\u002Fextending\u002Fjavascript-basics","Just enough JavaScript","The JavaScript you need to write extensions and Evaluate JavaScript, for people who write Python.","extending",{"path":659,"title":660,"description":661,"section":657,"order":105},"\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":663,"title":664,"description":665,"section":657,"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":667,"title":668,"description":669,"section":657,"order":111},"\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":671,"title":672,"description":673,"section":623,"order":99},"\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":602,"title":675,"description":676,"section":623,"order":46},"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":622,"title":19,"description":619,"section":623,"order":105},{"path":679,"title":680,"description":681,"section":682,"order":105},"\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":684,"title":685,"description":686,"section":682,"order":111},"\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":688,"title":689,"description":690,"section":682,"order":99},"\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":692,"title":693,"description":694,"section":682,"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":696,"title":697,"description":698,"section":699,"order":46},"\u002Fdocs\u002Fstart\u002Fgetting-started","Getting started","Install Browser, write a first test, and run it.","start",[701,704,707],{"to":702,"label":703},"\u002Fwhy\u002Fvs-cypress","vs Cypress",{"to":705,"label":706},"\u002Fwhy\u002Fvs-playwright","vs Playwright Test",{"to":708,"label":709},"\u002Fwhy\u002Fvs-seleniumlibrary","vs SeleniumLibrary",[711],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Tap    id=filter-toggle\u003C\u002Fspan>\u003C\u002Fspan>",[713],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Works.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">New Context    viewport={'width': 390, 'height': 844}    hasTouch=True\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Tap    text=Menu\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Fails: no touch support on this context.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">New Context    viewport={'width': 390, 'height': 844}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Tap    text=Menu\u003C\u002Fspan>\u003C\u002Fspan>",[715],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">New Context    permissions=['geolocation', 'notifications']\u003C\u002Fspan>\u003C\u002Fspan>",[717],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Grant Permissions    geolocation\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Grant Permissions    camera    microphone    origin=https:\u002F\u002Fexample.com\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Clear Permissions\u003C\u002Fspan>\u003C\u002Fspan>",[719],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Test Cases ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">Store Finder Degrades Without Location\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    New Context\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    viewport={'width': 390, 'height': 844}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    hasTouch=True\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    New Page\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">URL\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Clear Permissions\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Tap\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    id=find-near-me\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Get Text\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    id=message\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    ==\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    Enter a postcode instead\u003C\u002Fspan>\u003C\u002Fspan>",[721],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">New Context\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    permissions=['geolocation']\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    geolocation={'latitude': 60.17, 'longitude': 24.94}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># or later, to simulate movement\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">Set Geolocation    60.17    24.94    accuracy=50\u003C\u002Fspan>\u003C\u002Fspan>",[723],"\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">New Context\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    locale=de-DE\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    timezoneId=Europe\u002FBerlin\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    colorScheme=dark\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\">...    reducedMotion=reduce\u003C\u002Fspan>\u003C\u002Fspan>",[725],"\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\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Variables ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#96A2A8;font-style:italic\"># Robot Framework ry, Kampinkuja 2, 00100 Helsinki.\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">LAT\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">      ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">60.16829\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">LON\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">      ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">24.93051\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E;font-weight:bold\">*** Test Cases ***\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">The Map Finds The Phone, In Helsinki\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#A9B4BA\">    ${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">device\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#FF6B5E\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#2BD9CC\">    Get Device\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    iPhone 13\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    New Browser\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    webkit\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    headless=True\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    New Context\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">    ...\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">    &#x26;{\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">device\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">    ...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    permissions=['geolocation']\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">    ...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    geolocation={'latitude': \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">LAT\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">, 'longitude': \u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">${\u003C\u002Fspan>\u003Cspan style=\"color:#FFB86B\">LON\u003C\u002Fspan>\u003Cspan style=\"color:#A9B4BA\">}\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">    ...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    locale=fi-FI\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#FF6B5E\">    ...\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    timezoneId=Europe\u002FHelsinki\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    New Page\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    https:\u002F\u002Fwww.openstreetmap.org\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Tap\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">        role=button[name=\"Näytä oma sijaintini\"]\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan class=\"line\">\u003Cspan style=\"color:#2BD9CC\">    Get Url\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    *=\u003C\u002Fspan>\u003Cspan style=\"color:#E0D3A3\">    60.168\u003C\u002Fspan>\u003C\u002Fspan>",1786734140322]