{"id":1169,"date":"2026-07-27T22:50:00","date_gmt":"2026-07-27T17:20:00","guid":{"rendered":"https:\/\/lastroundai.com\/blog\/?post_type=iq&#038;p=1169"},"modified":"2026-07-21T22:32:44","modified_gmt":"2026-07-21T17:02:44","slug":"selenium-automation","status":"publish","type":"iq","link":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation","title":{"rendered":"Selenium Automation Interview Questions (2026): WebDriver to Frameworks"},"content":{"rendered":"<p>A QA lead at a 60-person logistics startup rejected a Selenium candidate in April 2026 over one follow-up question: what happens if you set a 10-second implicit wait and also wrap a click in a 15-second explicit wait on the same test. The candidate had built a clean Page Object Model and recited the difference between implicit and explicit waits correctly on the first pass. Then the follow-up landed, and the answer fell apart. That&#8217;s usually where Selenium interviews actually live, not in the definitions, in what happens when two of them collide.<\/p>\n<p>Selenium&#8217;s overall popularity has slipped. Stack Overflow&#8217;s own 2026 comparison of the major test automation frameworks put Selenium&#8217;s adoption among QA professionals at 22.1 percent, well behind Playwright&#8217;s 45.1 percent (<a href=\"https:\/\/stackoverflow.blog\/2026\/06\/15\/selenium-vs-cypress-vs-playwright-choosing-your-test-automation-framework\/\" target=\"_blank\" rel=\"noopener noreferrer\">Stack Overflow, 2026<\/a>). Here&#8217;s a take that might be wrong: I don&#8217;t think that decline makes Selenium interviews easier. If anything, the teams still running it in 2026 tend to be the ones maintaining genuinely large, years-old regression suites, not greenfield projects, and those interviews probe deeper into framework design and failure modes than a fresh Playwright shop&#8217;s loop usually does. Nobody&#8217;s proven that to me with a survey. It&#8217;s just what I keep seeing.<\/p>\n<p>This page covers the Selenium interview questions that come up across Java-based automation loops in 2026: WebDriver architecture, locator strategy, the three wait types, dynamic UI handling, Page Object Model, TestNG, and framework design. Java is the default here since it&#8217;s still the dominant Selenium binding at most enterprise QA shops, with a few notes on where Python or C# differ along the way.<\/p>\n<div class=\"iq-stats not-prose\"><div class=\"iq-stat\"><span class=\"iq-stat__value\">52<\/span><span class=\"iq-stat__label\">Questions<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">Java<\/span><span class=\"iq-stat__label\">Core Language<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">3<\/span><span class=\"iq-stat__label\">Wait Types<\/span><\/div><div class=\"iq-stat\"><span class=\"iq-stat__value\">22%<\/span><span class=\"iq-stat__label\">2026 Selenium share<\/span><\/div><\/div>\n<h2>Selenium WebDriver basics: what&#8217;s actually happening when you call a command<\/h2>\n<p>This is the warm-up section, but a shaky answer here sets a bad tone for the rest of the loop. It&#8217;s also the first place interviewers check whether you understand the tool, not just the syntax you copy-pasted off a blog.<\/p>\n<div class=\"iq-dsec iq-dsec--easy\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"easy\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Easy questions<\/h2><span class=\"iq-dsec__n\">15<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is Selenium WebDriver, and how is it different from the old Selenium RC?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>WebDriver talks to the browser directly, through each browser&#8217;s native automation API, or through the W3C WebDriver protocol layered on top of that. Selenium RC, the tool WebDriver replaced, injected a JavaScript layer into the page and routed everything through a separate server process, which was slower and fought constantly with the browser&#8217;s same-origin policy.<\/p>\n<p>RC has been deprecated since Selenium 3. Nobody should be shipping it in a 2026 codebase. Interviewers still ask this to check whether a candidate understands why the architecture changed, not because anyone expects you to have touched RC.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the difference between driver.get() and driver.navigate().to()?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Fundamentals<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Functionally, almost nothing. Both load a URL and wait for the page load event to fire. navigate() is part of a larger interface that also exposes back(), forward(), and refresh(), so it reads as browser-history navigation, while get() is a simpler one-shot call.<\/p>\n<p>Under the hood, get() actually calls navigate().to() internally, so there&#8217;s no real performance difference between them. Interviewers ask this to see if you know the interface exists at all, not because your test would behave any differently either way.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What locator strategies does Selenium support, and which should you reach for first?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Locators<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>id, name, className, tagName, linkText, partialLinkText, cssSelector, and xpath, roughly in that order of preference. ID is fastest and most stable when the app actually assigns a meaningful one. CSS selectors are usually the next-best choice because the browser parses them natively and they stay readable. XPath is the fallback for what CSS genuinely can&#8217;t express.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the difference between implicit and explicit waits in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Waits<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>An implicit wait is one global timeout, set once per driver session, that applies to every findElement call for the life of that session. It tells WebDriver to poll for up to N seconds before throwing NoSuchElementException. An explicit wait is scoped to one specific condition: WebDriverWait paired with ExpectedConditions polls until a particular thing happens, an element becomes clickable, some text appears, or the timeout hits, whichever comes first.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you switch control to an iframe, and how do you get back out?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Frames<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>driver.switchTo().frame() accepts an index, a name or ID string, or a WebElement reference, and the WebElement version is the most reliable since names and indices can shift between builds. To return, driver.switchTo().defaultContent() jumps back to the main document, or parentFrame() if you&#8217;re nested several frames deep and only need to pop up one level.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you handle a native JavaScript alert, confirm, or prompt box in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Alerts<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>driver.switchTo().alert() returns an Alert object. Call .accept() for OK, .dismiss() for Cancel, .getText() to read the message, and .sendKeys() if it&#8217;s a prompt() with an input field. A native browser alert isn&#8217;t part of the DOM at all, so normal element locators can&#8217;t touch it, this dedicated interface is the only path in.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is the Page Object Model, and why do teams bother with it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Page Object Model<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>POM wraps each page, or major component, of the application in a class that exposes its elements as fields and its user actions as methods. Test code calls loginPage.login(&#8220;user&#8221;, &#8220;pass&#8221;) instead of repeating four findElement calls and a click chain in every single test that touches login.<\/p>\n<p>The payoff shows up when the UI changes. You fix one locator in one class instead of hunting through dozens of test files that all copy-pasted the same broken selector.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What are the core TestNG annotations, and what order do they actually run in?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">TestNG<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>@BeforeSuite, @BeforeTest, @BeforeClass, and @BeforeMethod run, in that nesting order, before each @Test method, and the matching @After annotations unwind in reverse. @BeforeMethod fires before every single @Test in the class. @BeforeClass fires once per class, no matter how many @Test methods the class has. Mixing those two up is probably the single most common TestNG mistake that shows up in code review.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">NoSuchElementException vs. TimeoutException, when do you actually get each one?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Exceptions<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>NoSuchElementException means findElement searched and the element genuinely isn&#8217;t in the DOM at that moment, thrown immediately, or after the implicit wait expires, with no further waiting involved. TimeoutException comes from an explicit wait whose condition never became true within the configured duration. It&#8217;s the &#8220;I waited the full 15 seconds and it still didn&#8217;t happen&#8221; exception, distinct from &#8220;it&#8217;s just not there at all.&#8221;<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the difference between findElement() and findElements() in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Locator Methods<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>findElement() returns the first matching WebElement and throws NoSuchElementException the moment nothing matches. findElements() returns a List of WebElements, and if nothing matches you just get an empty list back, no exception at all.<\/p>\n<p>That difference matters for how you write existence checks. A common junior habit is calling findElements(locator).size() greater than 0 to check if something exists, then immediately calling findElement() again right after to actually grab it. That&#8217;s two DOM queries for one check. Store the list once, check its size, and if it&#8217;s non-empty just pull index zero from the same list instead of querying twice.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is JavascriptExecutor, and when do you actually need it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">JavaScript Executor<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>JavascriptExecutor is the WebDriver interface that lets you run arbitrary JavaScript inside the current page&#8217;s context and get a value back. You cast the driver to it and call executeScript() with a script string and optional arguments.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nJavascriptExecutor js = (JavascriptExecutor) driver;\n\njs.executeScript(\u201carguments[0].click();\u201d, element);\n\nObject title = js.executeScript(\u201creturn document.title;\u201d);\n<\/code><\/pre><\/div><\/p>\n<p>You reach for it when a normal WebDriver action can&#8217;t do the job cleanly: forcing a click on an element that&#8217;s technically covered by something else, reading a value out of a JS variable the page doesn&#8217;t render to the DOM, or scrolling. It&#8217;s also an escape hatch when a genuine bug in the site (an invisible overlay, a mistimed animation) is blocking a real click, but be careful, a JS click can succeed even when a real user&#8217;s mouse click would fail, so overusing it can hide actual UI bugs from your tests.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does running Selenium in headless mode mean, and how do you enable it in Chrome?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Headless Execution<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Headless means the browser runs with no visible window, no GPU-rendered UI you can watch, just the rendering engine executing in memory. It&#8217;s faster to spin up and uses less memory per instance, which is why almost every CI pipeline runs Selenium headless by default.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nChromeOptions options = new ChromeOptions();\n\noptions.addArguments(\u201c\u2013headless=new\u201d, \u201c\u2013window-size=1920,1080\u201d);\n\nWebDriver driver = new ChromeDriver(options);\n<\/code><\/pre><\/div><\/p>\n<p>Always set an explicit window size when running headless. Chrome&#8217;s default headless viewport is smaller than a typical headed window, and that difference alone can shift where responsive elements land or trigger a different CSS breakpoint, which then breaks locators that worked fine on your local headed run.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you capture a screenshot in Selenium when a test fails?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Screenshot Capture<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>WebDriver implements the TakesScreenshot interface, so you cast the driver and call getScreenshotAs(OutputType.FILE), then copy that temp file to wherever you want it saved.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nFile src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);\n\nFileUtils.copyFile(src, new File(\u201cscreenshots\/\u201d + testName + \u201c.png\u201d));\n<\/code><\/pre><\/div><\/p>\n<p>The useful place to wire this in is a TestNG @AfterMethod that checks ITestResult.getStatus() and only saves a screenshot on ITestResult.FAILURE, so you&#8217;re not littering disk with a screenshot of every passing test. Attach that file path to your report (Extent, Allure) so a failure is visible without anyone re-running the suite locally to see what actually happened.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you get, add, or delete cookies in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Cookie Management<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>driver.manage().getCookies() returns the full cookie set for the current page, getCookieNamed(name) returns one, addCookie(new Cookie(name, value)) sets one, and deleteCookie() or deleteAllCookies() removes them.<\/p>\n<p>The gotcha is that a cookie is scoped to a domain, and Selenium&#8217;s addCookie() call will throw if you try to set one before navigating to that domain first. A common pattern is to skip a slow UI login by driver.get()-ing the target site once, adding a pre-obtained session cookie, then refreshing the page, which lands you logged in without ever touching the login form. It only works if the session cookie itself is still valid server-side, so this approach breaks silently if your test data setup issues a stale token.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the difference between driver.close() and driver.quit()?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Close vs Quit<\/span><span class=\"iq-badge iq-badge--easy\">Easy<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>close() closes only the current browser window or tab. If that was the last window open, the WebDriver session ends as a side effect, but if other windows are still open, the session and the driver process stay alive.<\/p>\n<p>quit() closes every window associated with that session and ends the WebDriver session outright, which also terminates the underlying driver executable (chromedriver, geckodriver) and the browser process itself. If a test opens multiple windows and you only ever call close() at the end instead of quit(), you leave orphaned browser and driver processes running on whatever machine executed the test. Do that across a few hundred CI runs and you&#8217;ll eventually watch a build agent run out of memory for reasons nobody can explain from the test logs alone.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-dsec iq-dsec--medium\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"medium\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Medium questions<\/h2><span class=\"iq-dsec__n\">25<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Explain the Selenium WebDriver architecture. What actually happens when you call driver.click()?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Architecture<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Four pieces are involved: your test script calling the WebDriver client API, the W3C WebDriver protocol (JSON over HTTP), the browser driver executable (chromedriver, geckodriver), and the browser itself. Calling click() sends an HTTP request from the client binding to the driver&#8217;s local server, the driver translates that into the browser&#8217;s native automation commands, executes it, and returns the result as an HTTP response.<\/p>\n<p>Selenium 4 dropped the older JSON Wire Protocol translation step for most browsers, since Chrome, Firefox, and Edge had already implemented the W3C spec natively by then. That cut out a whole translation layer and quietly fixed a batch of flaky-command bugs that had nothing to do with anyone&#8217;s test code.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s the Actions class for, and when do you actually need it instead of a plain .click()?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">WebDriver API<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Actions handles multi-step gestures a single WebElement method can&#8217;t express on its own: mouse hover, drag and drop, right-click (context click), holding a modifier key while clicking, or click-and-hold sequences. You chain steps like moveToElement(), clickAndHold(), and moveByOffset(), then call .build().perform() once to fire the whole sequence at once.<\/p>\n<p>The common trap is reaching for Actions when a plain .click() would&#8217;ve worked fine, which just adds moving parts for no reason. Save it for gestures a single interaction genuinely can&#8217;t do. Hover-to-reveal navigation menus are the case that comes up most in real applications.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">CSS selector vs. XPath: what can XPath do that CSS can&#039;t, and does it actually run slower?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Locators<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>XPath can traverse upward in the DOM, selecting a parent or ancestor of an element you&#8217;ve matched, and it can match on visible text content. CSS selectors can&#8217;t do either natively. The &#8220;XPath is slower&#8221; claim is mostly leftover folklore from older IE-era browsers; in modern Chromium and Firefox both use native DOM query engines, and the real-world speed gap is negligible.<\/p>\n<p>What actually costs you with XPath is maintainability, not speed. A long absolute path like \/html\/body\/div[3]\/div\/table\/tr[2]\/td[1] breaks the instant a developer wraps the table in a new div. That&#8217;s the real objection, and it&#8217;s the one worth raising in an interview instead of the outdated speed argument.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What are Selenium 4&#039;s relative locators, and when would you actually reach for one?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Selenium 4<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Relative locators, withTagName(&#8220;button&#8221;).above(el), .below(el), .toLeftOf(el), .toRightOf(el), and .near(el), find an element by its rendered position relative to another element you can already locate reliably. They&#8217;re genuinely useful on forms where a label has a stable ID but the input sitting next to it doesn&#8217;t, which happens constantly in auto-generated admin panels.<\/p>\n<p>The catch: they&#8217;re calculated from rendered coordinates, so a responsive layout change, a hidden element, or a reflow can silently break one without any error until the test runs against the new layout. This is a comparatively recent feature (Selenium 4, first released in 2021), and interviewers sometimes ask about it specifically to check whether you&#8217;ve kept up with the framework at all.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">An element&#039;s id changes on every page load, something like user-row-48291. How do you write a locator that survives that?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Locators<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Stop matching the whole ID and match the stable prefix instead. A CSS attribute-contains selector like [id^=&#8217;user-row-&#8216;], or an XPath using contains(@id, &#8216;user-row-&#8216;), both work off the fixed part and ignore the random suffix.<\/p>\n<p>The more durable fix, if you can influence the app, is asking the dev team to add a data-testid attribute that never changes. Interviews usually just want the CSS or XPath workaround, but naming the better long-term fix unprompted is what separates a strong answer from an adequate one.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does FluentWait differ from a standard WebDriverWait, and when would you actually use one over the other?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Waits<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>WebDriverWait is really a simpler, preset case of FluentWait. FluentWait lets you configure the polling interval directly and choose which exceptions to ignore mid-poll, instead of accepting the library defaults. You&#8217;d reach for it when an element&#8217;s momentary absence should be tolerated during polling, a loading spinner that briefly detaches and reattaches to the DOM, say, where a plain WebDriverWait would blow up on a transient StaleElementReferenceException instead of continuing to poll through it.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nWait&lt;WebDriver&gt; wait = new FluentWait&lt;&gt;(driver)\n\n    .withTimeout(Duration.ofSeconds(20))\n\n    .pollingEvery(Duration.ofMillis(500))\n\n    .ignoring(NoSuchElementException.class, StaleElementReferenceException.class);\nWebElement submitButton = wait.until(\n\n    driver -&gt; driver.findElement(By.id(\u201csubmit-btn\u201d))\n\n);\n<\/code><\/pre><\/div><br \/>\n<\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">A test action opens a new browser tab. How do you interact with it, and get back to the original one?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Windows<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>driver.getWindowHandles() returns the set of every open window and tab handle. Store the original handle before triggering whatever opens the new tab, iterate the updated set, and switch to whichever handle isn&#8217;t the one you started with, using driver.switchTo().window(newHandle).<\/p>\n<p>Handles are opaque strings with no guaranteed order, so assuming &#8220;index 1 is always the new tab&#8221; is a common bug. Diff the before-and-after handle sets instead of trusting position.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you interact with a native HTML select dropdown in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Dropdowns<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Wrap the WebElement in Selenium&#8217;s Select class, which gives you selectByVisibleText(), selectByValue(), and selectByIndex() instead of manually clicking the dropdown open and clicking an option. For a multi-select, deselectAll() and getAllSelectedOptions() are also available.<\/p>\n<p>The Select class only works against a genuine &lt;select&gt; element. Plenty of modern UI kits render a fake dropdown out of styled divs and spans, and Select throws UnexpectedTagNameException against those. You&#8217;re back to plain clicks and visible-text matching in that case.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What does PageFactory.initElements() actually do, and what&#039;s the @FindBy annotation for?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Page Object Model<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>@FindBy declares how to locate a field, @FindBy(id = &#8220;username&#8221;), without you calling findElement yourself. PageFactory.initElements(driver, this) then reflects over the class, reads those annotations, and builds lazy-initialized proxy objects for each WebElement field.<\/p>\n<p>&#8220;Lazy&#8221; is the key word. The actual findElement call doesn&#8217;t fire until you first interact with the element, not the moment the page object gets constructed, which matters for elements that don&#8217;t exist on the page yet at construction time.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Assert vs. SoftAssert in TestNG, when would you actually pick SoftAssert?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">TestNG<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A regular Assert.assertEquals() throws immediately on failure and the test method stops right there. SoftAssert collects every failure without stopping execution, and you call assertAll() at the end to report everything that failed in one pass.<\/p>\n<p>Pick SoftAssert when one test method checks several independent things on a page, five field values after a form submit, say, and you want the full failure report in a single run instead of fixing one assertion, rerunning, hitting the next failure, fixing that, and rerunning again.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is @DataProvider used for, and how is it different from a parameter in the TestNG XML suite file?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">TestNG<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>@DataProvider marks a method that returns an Object[][] (or an Iterator), and TestNG runs the annotated test once per row, feeding each row&#8217;s values in as parameters. That&#8217;s how you get data-driven testing without writing a separate loop inside the test body, and it&#8217;s the mechanism TestNG&#8217;s own documentation describes as the standard way to parameterize a test across multiple data sets (<a href=\"https:\/\/testng.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">TestNG documentation<\/a>).<\/p>\n<p>An XML suite parameter, &lt;parameter name=&#8221;browser&#8221; value=&#8221;chrome&#8221;\/&gt;, is simpler and static: one value per run, useful for environment config like browser or base URL, not for iterating a dataset.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\n@DataProvider(name = \u201cloginData\u201d)\n\npublic Object[][] loginData() {\n\n    return new Object[][] {\n\n        {\u201cuser1\u201d, \u201cpass123\u201d, true},\n\n        {\u201cuser2\u201d, \u201cwrongpass\u201d, false},\n\n        {\u201c\u201d, \u201cpass123\u201d, false}\n\n    };\n\n}\n@Test(dataProvider = \u201cloginData\u201d)\n\npublic void testLogin(String username, String password, boolean expected) {\n\n    LoginPage loginPage = new LoginPage(driver);\n\n    loginPage.login(username, password);\n\n    Assert.assertEquals(loginPage.isLoginSuccessful(), expected);\n\n}\n<\/code><\/pre><\/div><br \/>\n<\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Data-driven, keyword-driven, and hybrid frameworks, what&#039;s the actual difference?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Framework Design<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A data-driven framework separates test data from test logic, the DataProvider example above is data-driven in miniature, so the same test method runs repeatedly against different rows pulled from a CSV, an Excel file, or a database. A keyword-driven framework goes further and separates the actions themselves into a table (Click, Type, Verify) that non-programmers can author, with an engine that interprets each keyword row and calls the matching Selenium method underneath.<\/p>\n<p>Hybrid frameworks combine both: keywords for the action vocabulary, external data for the values, POM underneath for element management. That&#8217;s what most mature enterprise Selenium suites actually run once the test count passes a few hundred and one person can no longer hold the whole thing in their head.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does Cucumber and BDD change a Selenium framework, and what&#039;s the real role of step definitions?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">BDD<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Cucumber lets you write scenarios in Gherkin (Given\/When\/Then, close to plain English) that business stakeholders can read and sign off on without touching code. Step definitions are the Java methods that map each Gherkin line to actual Selenium calls, so &#8220;When the user logs in with valid credentials&#8221; becomes a method annotated @When(&#8220;the user logs in with valid credentials&#8221;) that calls loginPage.login() underneath.<\/p>\n<p>The honest tradeoff interviewers want named: BDD adds a real maintenance layer, feature files, step definitions, and the mapping between them, for teams that don&#8217;t actually have non-technical stakeholders reading the scenarios. I&#8217;ve seen more of those wasted-effort setups than genuinely collaborative ones, which isn&#8217;t the answer most BDD advocates want to hear.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What causes StaleElementReferenceException, and what&#039;s the actual fix, not just &#039;re-locate the element&#039;?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Exceptions<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>The exception fires when the DOM node your WebElement points to has been removed or replaced, most often because part of the page re-rendered, a React state update or an AJAX refresh, after you located the element but before you interacted with it. &#8220;Re-locate it&#8221; is the right instinct, but the reliable version wraps the interaction in a small retry loop, or a FluentWait ignoring that specific exception, since a single re-locate can itself land mid-re-render and go stale again.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is Selenium Manager, and how did driver binary management change in Selenium 4?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Selenium Manager<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Before Selenium 4.6, you had to manually download the right chromedriver or geckodriver version, match it to your installed browser version, and either set it on the system PATH or point to it with a system property like webdriver.chrome.driver. Most teams offloaded that to a third-party library, usually WebDriverManager by Boni Garcia, which detected the installed browser and pulled the matching driver automatically.<\/p>\n<p>Selenium 4.6 folded that capability directly into Selenium itself as Selenium Manager, a small binary bundled with the language bindings that runs before your test starts, detects the installed browser and its version, and resolves or downloads a matching driver without you writing any setup code at all. You can still set webdriver.chrome.driver manually if you want to pin an exact driver version.<\/p>\n<p>The failure mode to watch for is browser auto-update. If Chrome silently updates itself to a new major version mid-CI-run while Selenium Manager&#8217;s cached driver mapping is stale, you get a driver-to-browser version mismatch error that looks like a Selenium bug but is really an environment drift problem. Pinning both browser and driver versions in your CI base image avoids it entirely.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you automate a file upload when the browser would normally open a native OS file picker?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">File Upload<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>You don&#8217;t click the button that opens the picker at all. If the upload control is backed by a genuine input element with type set to file, you can call sendKeys() on that input directly with the absolute file path, and the browser accepts the file without ever opening the native OS dialog.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nWebElement upload = driver.findElement(By.id(\u201cresume-upload\u201d));\n\nupload.sendKeys(\u201c\/Users\/qa\/testdata\/resume.pdf\u201d);\n<\/code><\/pre><\/div><\/p>\n<p>The trick only breaks down when the real input is hidden with something like display none or visibility hidden to let a styled button sit on top of it, because W3C interactability rules reject sendKeys on an element the driver considers not visible. In that case, use JavascriptExecutor to briefly strip the hiding style, or set the value directly through the DOM and dispatch a change event, then continue with the rest of the flow as normal.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you verify a file actually finished downloading during a Selenium test, instead of just checking it exists?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">File Download<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>First you have to make Chrome download to a predictable folder without prompting, which you set through ChromeOptions preferences rather than anything in the DOM.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nMap&lt;String, Object&gt; prefs = new HashMap&lt;&gt;();\n\nprefs.put(\u201cdownload.default_directory\u201d, \u201c\/tmp\/downloads\u201d);\n\nprefs.put(\u201cdownload.prompt_for_download\u201d, false);\n\noptions.setExperimentalOption(\u201cprefs\u201d, prefs);\n<\/code><\/pre><\/div><\/p>\n<p>Checking for existence right after the click is where tests get flaky, because a large file is still mid-write as a temporary file with a suffix like .crdownload, and File.exists() on the final filename can return true before the bytes finish landing. The reliable check is a small poll loop: wait for a file with the expected extension to appear, then confirm its size has stopped changing across two checks a second or two apart, or simply confirm the .crdownload temp file is gone, since Chrome renames it to the final name only once the write completes.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Selenium&#039;s Actions class drag-and-drop doesn&#039;t work on a page built with a library like React DnD or SortableJS. Why not, and how do you fix it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Drag and Drop<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Actions.dragAndDrop(), or the manual clickAndHold, moveToElement, release sequence, simulates native mouse events, mousedown, mousemove, mouseup, at roughly the pointer level. Plenty of modern drag-and-drop libraries don&#8217;t listen for those at all. They bind to the HTML5 drag events, dragstart, dragenter, dragover, drop, which carry their own DataTransfer object, and WebDriver&#8217;s synthetic mouse movement never fires those events because it&#8217;s simulating a different input model entirely.<\/p>\n<p>The fix is to fire the events the library actually listens for. Build a small JavaScript helper that constructs a DragEvent with a DataTransfer payload and dispatches dragstart on the source element, then dragenter, dragover, and drop on the target in sequence, calling it through JavascriptExecutor. There&#8217;s no single universal snippet that works everywhere, since some libraries use HTML5 drag events, others use pointer events with their own gesture detection, so the first debugging step is always opening devtools and checking which event listener actually reacts when a real mouse drags the element.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">You scroll an element into view with JavaScript before clicking it, but the click still fails because a sticky header covers the element. What&#039;s going on, and how do you fix it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Scroll Into View<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>scrollIntoView(true), or its equivalent block: &#8216;start&#8217; option, aligns the target element to the top edge of the viewport. On a page with a fixed or sticky header, the top edge of the viewport is exactly where that header sits, so the element you scrolled to ends up hidden underneath it, and the click gets blocked or lands on the header instead.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">javascript<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-javascript\">\n\narguments[0].scrollIntoView({block: \u201ccenter\u201d, inline: \u201cnearest\u201d});\n<\/code><\/pre><\/div><\/p>\n<p>Passing block: &#8216;center&#8217; instead scrolls the element to the vertical middle of the viewport, clear of a header pinned to the top. If you need block: &#8216;start&#8217; for another reason, an alternative is scrolling normally and then nudging the window up afterward with window.scrollBy(0, -headerHeightInPixels) so the header&#8217;s own height gets accounted for. Some sites already solve this with a scroll-padding-top CSS rule on the html element, in which case a plain scrollIntoView call respects that padding automatically and you don&#8217;t need any of this.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you locate an element that&#039;s inside a Shadow DOM with Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Shadow DOM<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A shadow root is a separate DOM subtree attached to a host element through element.attachShadow(), and ordinary CSS or XPath locators can&#8217;t cross that boundary because the shadow tree isn&#8217;t part of the main document tree those locators search.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nWebElement host = driver.findElement(By.cssSelector(\u201cmy-custom-widget\u201d));\n\nSearchContext shadowRoot = host.getShadowRoot();\n\nWebElement inner = shadowRoot.findElement(By.cssSelector(\u201c.label\u201d));\n<\/code><\/pre><\/div><\/p>\n<p>Selenium 4 exposes getShadowRoot() directly on WebElement, but only for open shadow roots. A closed shadow root is inaccessible to Selenium the same way it&#8217;s inaccessible to plain JavaScript, since the browser itself doesn&#8217;t expose a reference to it, and no automation tool including CDP-based ones can reach inside a genuinely closed one. Before Selenium 4 added native support, people did the same thing purely through JavascriptExecutor calling element.shadowRoot.querySelector() and returning the resulting element reference, which is still a reasonable fallback for deeply nested shadow trees, like a component inside another component inside a design system wrapper.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">You run tests in parallel with TestNG and intermittently see one test interacting with the wrong browser window. What&#039;s the root cause?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Parallel Execution<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Almost always it&#8217;s a WebDriver reference declared as a shared static or instance field being read and written by multiple threads at once. TestNG&#8217;s parallel execution spins up several threads, and if they all touch the same driver variable, one thread&#8217;s setup can overwrite another thread&#8217;s reference mid-test, so a test on thread A ends up issuing commands against the browser thread B just created.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nprivate static ThreadLocal&lt;WebDriver&gt; driver = new ThreadLocal&lt;&gt;();\n@BeforeMethod\n\npublic void setUp() {\n\n    driver.set(new ChromeDriver());\n\n}\n@AfterMethod\n\npublic void tearDown() {\n\n    driver.get().quit();\n\n    driver.remove();\n\n}\n<\/code><\/pre><\/div><\/p>\n<p>Wrapping the driver in ThreadLocal gives each thread its own isolated reference, so threads can never see or mutate each other&#8217;s browser instance. Also double check the parallel attribute in your testng.xml, methods versus classes versus tests changes the granularity at which new threads spin up, and if you set parallel=&#8221;methods&#8221; you need the ThreadLocal initialized fresh per test method, not just once per class instance.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you configure browser-specific capabilities for cross-browser testing, like disabling notification popups in Chrome versus Firefox?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Cross Browser Options<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Each browser has its own Options class, ChromeOptions, FirefoxOptions, EdgeOptions, all implementing MutableCapabilities. A handful of capabilities are shared across all of them, browserName and platformName for instance, but most of the flags that actually control browser behavior are vendor-specific and don&#8217;t transfer.<\/p>\n<p>Disabling the notification permission prompt in Chrome is a Chrome preference, set through setExperimentalOption with a prefs map containing profile.default_content_setting_values.notifications set to 2. Firefox does the equivalent thing through a completely different mechanism, FirefoxProfile.setPreference with dom.webnotifications.enabled set to false. A common mistake on multi-browser frameworks is building one shared capabilities object and passing Chrome-specific arguments into FirefoxOptions, which Selenium and the browser driver just silently ignore rather than erroring on, so the test suite runs and passes while the notification popup you thought you suppressed is still showing up in Firefox and just never got clicked on, or got clicked on by luck through a broad wait timeout.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you read data out of an HTML table with Selenium, like validating a row for a specific order ID?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Table Data<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Locate the table, get all the row elements inside its body, and loop through them, pulling the cell elements for each row as you go.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nList&lt;WebElement&gt; rows = driver.findElements(By.cssSelector(\u201ctable#orders tbody tr\u201d));\n\nfor (WebElement row : rows) {\n\n    List&lt;WebElement&gt; cells = row.findElements(By.tagName(\u201ctd\u201d));\n\n    if (cells.get(0).getText().equals(\u201cORD-12345\u201d)) {\n\n        assertThat(cells.get(2).getText()).isEqualTo(\u201cShipped\u201d);\n\n    }\n\n}\n<\/code><\/pre><\/div><\/p>\n<p>Avoid hardcoding a fixed row index like row three to find your record, since sort order, pagination, and inserted test data from other tests can shift exactly where a given row lands, which is a classic source of flaky table assertions. For a single lookup, a more direct XPath skips the manual loop entirely: \/\/tr[td[contains(text(),&#8217;ORD-12345&#8242;)]], which returns the whole matching row in one call and cuts your locator round trips roughly in half.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you implement an automatic retry for flaky tests in a TestNG and Selenium framework?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Flaky Test Retry<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>TestNG exposes an IRetryAnalyzer interface with one method, retry(ITestResult), that you implement to return true up to some maximum attempt count you track yourself with a counter field. You can attach it to a single test through the retryAnalyzer attribute on @Test, but the more practical setup wires it in globally through an IAnnotationTransformer that injects the retry analyzer onto every test method automatically, so nobody on the team has to remember to add it by hand to a new test class.<\/p>\n<p>The part teams miss is that retries by themselves just paper over instability if you don&#8217;t also track which tests needed a retry and how often. A test that only passes on its second attempt every single run isn&#8217;t stable, it&#8217;s failing consistently and getting bailed out by the retry logic. That data needs to feed back into an actual investigation, usually a missing explicit wait or a race condition in test setup, rather than becoming a permanent workaround that quietly hides a real bug from the team.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">A page is protected by HTTP Basic Authentication and a native browser credentials popup blocks it from loading. How do you get past that in Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Basic Auth Popup<\/span><span class=\"iq-badge iq-badge--medium\">Medium<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>The native OS-level basic auth prompt isn&#8217;t part of the page&#8217;s DOM, so no Selenium locator can see it or interact with it. The simplest workaround is embedding credentials directly in the URL, https:\/\/username:password@hostname\/path, which Chrome and Firefox both accept and will use to satisfy the prompt without ever displaying it. It only covers Basic and Digest auth, not SSO or OAuth flows, since those need an actual form to be submitted somewhere.<\/p>\n<p>On Chrome, Selenium 4 also gives you a network-level path through CDP: enabling the Fetch or Network domain and intercepting the auth challenge to supply credentials programmatically before the page even renders the prompt. It&#8217;s more setup than the URL trick, but it&#8217;s the option to reach for on sites that specifically block credentials embedded in the URL for security reasons, since some servers now reject that format outright.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-dsec iq-dsec--hard\"><div class=\"iq-dsec__row\"><h2 class=\"iq-dsec__h\" id=\"hard\"><span class=\"iq-dsec__dot\" aria-hidden=\"true\"><\/span>Hard questions<\/h2><span class=\"iq-dsec__n\">12<\/span><\/div><div class=\"iq-dsec__bar\" aria-hidden=\"true\"><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Why is mixing implicit and explicit waits in the same test considered bad practice, and what actually happens if you do it?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Waits<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Selenium&#8217;s own documentation calls this out directly: mixing the two can produce unpredictable, longer-than-expected wait times, because both timers can effectively stack (<a href=\"https:\/\/www.selenium.dev\/documentation\/webdriver\/waits\/\" target=\"_blank\" rel=\"noopener noreferrer\">Selenium&#8217;s waiting-strategies docs<\/a>). The textbook example, and the one from this page&#8217;s opening scene, is a 10-second implicit wait combined with a 15-second explicit wait producing something closer to 20 seconds in the worst case, not 15, because the driver keeps re-polling under the implicit wait&#8217;s rules while the explicit wait is separately counting down.<\/p>\n<p>The safe pattern most teams settle on: pick one strategy globally, usually explicit waits (with FluentWait for the handful of genuinely flaky elements), and set the implicit wait to zero everywhere else. Not both. Ever.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What&#039;s a real drawback of PageFactory, and how would you structure Page Objects for a parallel test run?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Page Object Model<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>PageFactory&#8217;s proxy elements cache the underlying WebElement after first use by default. That&#8217;s a real problem on pages with dynamic content: if the element gets removed and re-rendered, a common React or Angular pattern, the cached reference goes stale and throws StaleElementReferenceException on the next interaction. Most teams either re-run initElements() at the top of each test method or skip field caching entirely and call findElement inside plain methods instead.<\/p>\n<p>For parallel runs, the bigger risk is Page Objects unintentionally sharing one WebDriver instance across threads. The standard fix is a ThreadLocal&lt;WebDriver&gt;, with each Page Object constructor pulling the driver for its own thread rather than reading from a shared static field.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\npublic class LoginPage {\n\n    private WebDriver driver;\n    @FindBy(id = \u201cusername\u201d)\n\n    private WebElement usernameField;\n    @FindBy(id = \u201cpassword\u201d)\n\n    private WebElement passwordField;\n    @FindBy(css = \u201cbutton[type=\u2019submit\u2019]\u201d)\n\n    private WebElement loginButton;\n    public LoginPage(WebDriver driver) {\n\n        this.driver = driver;\n\n        PageFactory.initElements(driver, this);\n\n    }\n    public void login(String username, String password) {\n\n        usernameField.sendKeys(username);\n\n        passwordField.sendKeys(password);\n\n        loginButton.click();\n\n    }\n\n}\n<\/code><\/pre><\/div><br \/>\n<\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">ElementClickInterceptedException, what&#039;s actually happening, and how do you fix it properly?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Exceptions<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Another element, an overlay, a sticky header, a cookie banner, or a spinner still animating out, is sitting visually on top of the element you&#8217;re trying to click, even though your locator found the right node. The lazy fix everyone reaches for first is a JavaScript executor click, ((JavascriptExecutor) driver).executeScript(&#8220;arguments[0].click();&#8221;, element), which bypasses the visibility check entirely. That can mask a real bug where an actual user couldn&#8217;t click that button either.<\/p>\n<p>The more defensible fix waits for the obstructing element to actually disappear, ExpectedConditions.invisibilityOfElementLocated(), before clicking normally. Not always possible. Usually worth the extra line.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How does Selenium Grid&#039;s architecture work in Selenium 4, and how is it different from the old Hub and Node model in Grid 3?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Selenium Grid Architecture<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Grid 3 was a simple hub-and-node setup: one hub process accepted incoming session requests and queued them against whatever nodes had registered capacity, and if no node was free the request generally just failed rather than waiting.<\/p>\n<p>Grid 4 breaks that single hub process into separate components: a Router that receives incoming requests and forwards them onward, a Distributor that tracks live capacity across registered Nodes and assigns new sessions to one with room, a Session Map that records which physical Node an active session is running on so every subsequent command for that session gets routed correctly, and a Session Queue that holds new session requests when capacity is temporarily exhausted instead of failing them outright. All of that can run bundled together as a single standalone process for local use, or scaled out as genuinely separate services behind a load balancer for a real production Grid, which the old Grid 3 architecture had no clean way to do since the hub itself was a single point of both routing and bottleneck.<\/p>\n<p>Practically, this means Grid 4 degrades more gracefully under load, queueing requests rather than erroring immediately, and lets you scale the piece that&#8217;s actually your bottleneck, usually the Distributor or the number of registered Nodes, independently of the rest of the system.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What can you do with Selenium 4&#039;s Chrome DevTools Protocol integration that you can&#039;t do with the plain WebDriver API?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">CDP Integration<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Plain WebDriver only exposes what the W3C spec defines: navigation, element interaction, script execution, cookie and window management. CDP is Chrome&#8217;s own internal protocol and sits underneath that, giving you access to things WebDriver was never designed to expose.<\/p>\n<p><div class=\"iq-code not-prose\"><div class=\"iq-code__bar\"><span class=\"iq-code__lang\">java<\/span><button class=\"iq-code__copy\" type=\"button\">Copy<\/button><\/div><pre><code class=\"language-java\">\n\nDevTools devTools = ((HasDevTools) driver).getDevTools();\n\ndevTools.createSession();\n\ndevTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));\n\ndevTools.addListener(Network.responseReceived(), response -&gt;\n\n    System.out.println(response.getResponse().getStatus()));\n<\/code><\/pre><\/div><\/p>\n<p>With CDP you can intercept and rewrite network requests and responses, blocking third-party trackers or forcing a 500 to test how the UI handles a real error state, capture the browser&#8217;s own console.log and console.error output instead of guessing why something failed silently, override geolocation or device metrics for responsive and location-aware testing, throttle the network to simulate a slow connection, and pull real performance timing data off the page. The tradeoff is that CDP is Chromium-specific, so Edge gets it for free since it&#8217;s Chromium-based, but any framework logic built directly on CDP calls has to be reimplemented or skipped for Firefox and Safari, which is a large part of why the WebDriver BiDi effort exists, to standardize a version of this same capability set across browsers.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Selenium 4 removed the JSON Wire Protocol translation layer. What was that layer actually doing, and what broke when it was removed?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">W3C Protocol<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>In Selenium 3 and earlier, the client bindings spoke Selenium&#8217;s own JSON Wire Protocol, and browser vendor drivers either had to natively understand that protocol or Selenium had to translate every command into whatever format that specific driver actually expected underneath. Selenium 4 dropped that translation layer entirely and speaks the official W3C WebDriver specification end to end, a spec browser vendors themselves helped author and now implement natively in chromedriver and geckodriver.<\/p>\n<p>The visible fallout landed mostly in capabilities. The old flat DesiredCapabilities map with keys like chromeOptions got replaced by vendor-namespaced capabilities like goog:chromeOptions, and drivers built on the W3C spec reject capabilities they don&#8217;t recognize in that namespaced format instead of silently accepting whatever you hand them. Teams that had hand-rolled a DesiredCapabilities map from an old Selenium 3 framework and upgraded the client library without updating that code hit driver instantiation failures that looked like Selenium bugs but were really stale capability formats.<\/p>\n<p>The W3C spec also formalizes input actions differently, defining a more precise pointer and keyboard input state model for the Actions class than Selenium&#8217;s own older simplified implementation used, which occasionally changed the exact timing and coordinates of complex multi-step Actions chains between Selenium 3 and 4. The fix across all of this is migrating fully onto the ChromeOptions and FirefoxOptions builder classes instead of any hand-built capabilities map, since those builders always emit the correct namespaced format for whichever driver version you&#8217;re targeting.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">How do you test a UI built on an HTML5 canvas element, like a chart or drawing tool, with Selenium?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Canvas Testing<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A canvas is a single DOM element, and everything drawn inside it, bars, lines, shapes, exists only as pixels the browser painted, not as separate nodes Selenium can find with a locator. There is no findElement() call that gets you to the third bar in a chart, because that bar isn&#8217;t a DOM node at all.<\/p>\n<p>For verifying rendered output, take a screenshot of just the canvas element&#8217;s bounding box and run pixel or perceptual image comparison against a stored baseline, using a visual regression tool or a simpler pixel-diff library, and accept some tolerance for anti-aliasing and font rendering differences across machines. For verifying interaction, you either calculate the exact pixel coordinates you need to click, based on a chart library&#8217;s documented data-to-pixel mapping, or reach into the library&#8217;s own internal state object through JavascriptExecutor if it exposes one globally, then use Actions to click at that calculated point rather than relying on a locator that doesn&#8217;t exist. If the library renders an accessible SVG or table alongside the canvas for screen readers, which some charting libraries do, testing against that hidden accessible layer instead is far more stable than any coordinate math.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">You&#039;re setting up Selenium Grid in Docker for CI. What does a realistic setup need beyond just a hub and a node image?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Docker Grid Setup<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>A single standalone Chrome image is fine for running things locally, but a real CI setup typically composes a selenium\/hub container alongside one or more node-chrome and node-firefox containers, each pointed at the hub&#8217;s internal Docker network address through the SE_EVENT_BUS_HOST and SE_EVENT_BUS_PORT environment variables so nodes actually register.<\/p>\n<p>Shared memory size matters more than people expect. Docker&#8217;s default \/dev\/shm is tiny, and Chrome under that default crashes with session not created errors or random renderer crashes, so you need either &#8211;shm-size=2g on the container or a mounted \/dev\/shm volume sized properly. For debugging, node images ship a VNC server on port 5900 by default, and turning that on lets you actually watch what a failing test is doing live in the browser instead of reading through logs blind trying to reconstruct what happened.<\/p>\n<p>Node scaling needs planning too, either a fixed replica count per browser type sized to match your parallel thread count, or dynamic node registration if you&#8217;re autoscaling based on queue depth. And healthchecks matter more than they look like they should, because a node container that&#8217;s up and running but hasn&#8217;t finished registering with the hub yet will make Selenium fail with no such session errors if tests start hitting the hub too early, so CI pipelines need an actual wait-for-hub-ready step rather than a fixed sleep that either wastes time or isn&#8217;t long enough.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">What is the WebDriver BiDi protocol, and how is it different from what CDP already gives you in Selenium 4?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">WebDriver BiDi<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>CDP is Chrome&#8217;s own internal protocol, powerful but tied to Chromium, so anything built on top of it, network interception, console log capture, geolocation overrides, works on Chrome and Edge and simply isn&#8217;t available at all on Firefox or Safari without a completely separate implementation.<\/p>\n<p>WebDriver BiDi is a newer W3C standard, still landing across browsers as implementations mature, that aims to standardize a browser-agnostic version of that same category of capability. The name refers to bidirectional communication: the classic WebDriver protocol is strictly request and response, your client asks a question and the browser answers, while BiDi lets the browser push events to the client on its own, like a console message or a network request firing, without your test polling for it. That covers console log streaming and network interception specified once, the same way, for every vendor to implement, instead of each browser needing its own bespoke integration.<\/p>\n<p>As of right now, Selenium 4 still leans on CDP for Chrome and Edge-specific features and is gradually shifting equivalent functionality onto BiDi as browser support catches up, which means framework code sometimes has to check driver capabilities first to decide which API path to use depending on what the target browser actually implements at that point in time.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">A custom upload widget triggers a native OS file picker directly, and sendKeys() on the underlying input doesn&#039;t work. What are your real options?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Native File Dialogs<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>If there genuinely is no underlying input element with type file anywhere in the DOM, Selenium fundamentally cannot help you, because WebDriver&#8217;s entire command surface operates inside the browser&#8217;s rendering context, not the operating system&#8217;s own desktop. A native file picker window is outside that boundary entirely, the same way a print dialog or an OS-level download prompt is.<\/p>\n<p>Start by checking devtools more carefully rather than assuming the widget is truly custom. In the large majority of cases there&#8217;s still a real input element with type file sitting underneath a styled button, just hidden visually, and you can call sendKeys() on it directly once you strip whatever style is making the driver consider it non-interactable, usually through JavascriptExecutor removing a display none or visibility hidden rule first. If the page truly opens the picker through some non-standard mechanism with no input element backing it at all, you&#8217;re into OS-level automation entirely outside Selenium&#8217;s scope, tools like AutoIT on Windows or Java&#8217;s Robot class typing a file path and hitting enter blind, which is fragile because it depends on dialog focus and OS window behavior and simply can&#8217;t run headless. Some teams sidestep the whole problem by calling the backend upload API directly for test data setup and only using the UI afterward to verify the file shows up, treating the actual native dialog interaction as out of scope for browser automation entirely.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">Architecturally, why do Selenium tests tend to run slower or flake more than an equivalent Playwright test doing the same thing?<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">Selenium vs Playwright<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Selenium&#8217;s WebDriver protocol is a synchronous HTTP request and response protocol between your test process and a separate driver process, chromedriver or geckodriver, which then talks to the actual browser. Every single command, a click, a find, reading text, is a full round trip: serialize the command to JSON, send it over HTTP, the driver parses it and executes it against the browser, then serializes a response back the same way.<\/p>\n<p>Playwright talks to the browser over a persistent WebSocket connection using the browser&#8217;s own remote debugging protocol directly, with its own driver process bundled into the library rather than a separate binary you manage yourself, which cuts out a layer and lets it batch calls more efficiently instead of paying a full round trip per action. Playwright also bakes actionability checks into every action by default, auto-waiting for an element to be visible, stable, and not obscured before acting on it, where Selenium leaves all of that entirely up to you. A lot of what gets blamed as Selenium flakiness is really a team not writing proper explicit waits, not some fundamental protocol limitation Selenium can&#8217;t overcome.<\/p>\n<p>Selenium&#8217;s real advantage is breadth rather than speed. It supports more browser and language combinations across a much longer history, real SafariDriver support, legacy IEDriverServer for enterprise teams still stuck supporting old internal tools, and it sits underneath a much larger existing ecosystem of Grid infrastructure and cloud vendor support like BrowserStack and Sauce Labs. Plenty of large legacy suites simply can&#8217;t justify the cost of rewriting away from that ecosystem, even knowing Playwright&#8217;s underlying protocol is architecturally leaner.<\/p>\n<p><\/div><\/div><\/div>\n<div class=\"iq-qa\"><button class=\"iq-qa__q\" type=\"button\" aria-expanded=\"false\"><span class=\"iq-qa__qtext\">A Selenium test passes reliably on your machine but fails intermittently in CI. Walk through how you&#039;d actually debug that.<\/span><span class=\"iq-qa__meta\"><span class=\"iq-qa__tag\">CI Flakiness Debugging<\/span><span class=\"iq-badge iq-badge--hard\">Hard<\/span><span class=\"iq-qa__chev\" aria-hidden=\"true\"><\/span><\/span><\/button><div class=\"iq-qa__a\"><div class=\"iq-qa__a-inner\"><\/p>\n<p>Start by ruling out environment differences before touching the test code. CI almost always runs headless while local development often runs headed, and headless Chrome can render fonts, scrollbars, and viewport dimensions slightly differently, which is enough to shift element positions or trip a different CSS breakpoint than the one your local headed run hits. Reproduce headless locally with the exact window size CI uses before assuming the test logic itself is wrong.<\/p>\n<p>CI agents also tend to be resource-constrained compared to a developer laptop, shared CPU, less memory, more noisy neighbors, so real page load and animation timing runs slower there, and that exposes waits that were only ever passing locally because your machine was fast enough to beat the race, not because the wait was actually correct. Capture the browser console log and a screenshot at the exact moment of failure, through the CDP Log domain or driver.manage().logs(), and save both as CI artifacts rather than trusting the stack trace alone to explain what happened.<\/p>\n<p>Check specifically for animation and transition timing next, since a wait for element to be clickable can be satisfied while a CSS transition is still sliding that element toward its final resting position, so a click lands at coordinates the element hasn&#8217;t reached yet. Also look for test order dependencies or shared test data colliding across parallel workers in CI, something that never surfaces when you run one test at a time locally. If your Grid setup supports it, recording video of the failing run, which most Docker-based Selenium Grid images with VNC enabled do, catches the full sequence leading to failure in a way a single screenshot never can.<\/p>\n<p><\/div><\/div><\/div>\n<h2>How to prepare for a Selenium interview in 2026<\/h2>\n<p>Most Selenium interview questions reward hands-on practice far more than passive reading, so skip another locator cheat sheet and build one small framework end to end instead: three or four Page Object classes, a TestNG suite with a DataProvider-fed test, and one real FluentWait usage against something you deliberately made flaky. Interviewers can tell within two questions whether you&#8217;ve actually wired a framework together or only read about the pieces separately.<\/p>\n<p>Across mock interview sessions run through LastRoundAI tagged QA, SDET, or automation testing, the most common stumble isn&#8217;t a locator question or a Page Object question. It&#8217;s the wait-strategy follow-up. Candidates recite &#8220;implicit versus explicit&#8221; correctly on the first pass, then contradict themselves the moment an interviewer asks what happens if both are set at once, the exact scenario this page opened with. That single follow-up catches more candidates in our practice sessions than any other topic on this list. We don&#8217;t have a clean percentage to put on that claim, only that it comes up often enough in review to be worth flagging here.<\/p>\n<p>Demand for the underlying role isn&#8217;t shrinking, even as the specific tool shifts underneath it. Software quality assurance analysts and testers held about 201,700 jobs in 2024, and the broader category of software developers, QA analysts, and testers is projected to grow 15 percent through 2034, according to the <a href=\"https:\/\/www.bls.gov\/ooh\/computer-and-information-technology\/software-developers.htm\" target=\"_blank\" rel=\"noopener noreferrer\">BLS Occupational Outlook Handbook<\/a>. Whether that growth favors Selenium specifically or its faster-moving successors is a separate argument. Knowing both the framework your target company already runs and where the tooling is heading next is probably the safer bet than betting everything on just one.<\/p>\n<h2>Practice the follow-up, not just the first answer<\/h2>\n<p>Reading the answer to a stale-element question is not the same as defending it live once an interviewer changes one variable on you. <a href=\"\/products\/mock-interviews\">LastRoundAI&#8217;s mock interview mode<\/a> runs Selenium and QA-focused rounds with real-time follow-up questions instead of a static bank, and the free plan includes 15 credits a month that reset monthly. Starter is $19\/mo if you want more sessions than that covers.<\/p>\n<p>If the harder part right now is finding enough QA, SDET, or automation engineer openings rather than passing the interview once you land one, <a href=\"\/products\/auto-apply\">Auto-Apply<\/a> matches and applies to roles for you, 10 a month on the free plan, up to 400 a month on the Ultimate plan, with every application held in a review queue until you approve it. Nothing goes out on your behalf without you seeing it first.<\/p>\n<p>Questions about either product go to contact@lastroundai.com. That&#8217;s the only inbox we check.<\/p>\n<div class=\"iq-sources not-prose\"><div class=\"iq-sources__title\">Sources &amp; further reading<\/div><ul class=\"iq-sources__list\"><li><a href=\"https:\/\/www.selenium.dev\/documentation\/webdriver\/waits\/\" target=\"_blank\" rel=\"nofollow noopener\">Selenium official docs: Waits<\/a><\/li><li><a href=\"https:\/\/stackoverflow.blog\/2026\/06\/15\/selenium-vs-cypress-vs-playwright-choosing-your-test-automation-framework\/\" target=\"_blank\" rel=\"nofollow noopener\">Stack Overflow: Selenium vs. Cypress vs. Playwright (2026)<\/a><\/li><li><a href=\"https:\/\/testng.org\/\" target=\"_blank\" rel=\"nofollow noopener\">TestNG Documentation<\/a><\/li><li><a href=\"https:\/\/www.bls.gov\/ooh\/computer-and-information-technology\/software-developers.htm\" target=\"_blank\" rel=\"nofollow noopener\">BLS Occupational Outlook Handbook: Software Developers, QA Analysts, and Testers<\/a><\/li><\/ul><\/div>\n","protected":false},"excerpt":{"rendered":"<p>A QA lead at a 60-person logistics startup rejected a Selenium candidate in April 2026 over one follow-up question: what happens if you set a 10-second implicit wait and also wrap a click in a 15-second explicit wait on the same test. The candidate had built a clean Page Object Model and recited the difference&#8230;<\/p>\n","protected":false},"author":3,"featured_media":1662,"comment_status":"open","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"tags":[],"class_list":["post-1169","iq","type-iq","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Selenium Interview Questions (2026) Guide | LastRoundAI<\/title>\n<meta name=\"description\" content=\"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/lastroundai.com\/interview-questions\/selenium-automation\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Selenium Interview Questions (2026) Guide | LastRoundAI\" \/>\n<meta property=\"og:description\" content=\"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/lastroundai.com\/interview-questions\/selenium-automation\" \/>\n<meta property=\"og:site_name\" content=\"LastRound AI\" \/>\n<meta property=\"og:image\" content=\"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-selenium-automation-og.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"46 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation\",\"name\":\"Selenium Interview Questions (2026) Guide | LastRoundAI\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-selenium-automation-og.png\",\"datePublished\":\"2026-07-27T17:20:00+00:00\",\"description\":\"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation#primaryimage\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-selenium-automation-og.png\",\"contentUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/iq-selenium-automation-og.png\",\"width\":1200,\"height\":630,\"caption\":\"Selenium Automation interview questions \u2014 LastRoundAI\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\\\/selenium-automation#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/lastroundai.com\\\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Interview Questions\",\"item\":\"https:\\\/\\\/lastroundai.com\\\/interview-questions\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Selenium Automation Interview Questions (2026): WebDriver to Frameworks\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/\",\"name\":\"LastRound AI\",\"description\":\"Interview Assistant prep, tech careers and AI tools\",\"publisher\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#organization\",\"name\":\"LastRound AI\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png\",\"contentUrl\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png\",\"width\":400,\"height\":400,\"caption\":\"LastRound AI\"},\"image\":{\"@id\":\"https:\\\/\\\/lastroundai.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Selenium Interview Questions (2026) Guide | LastRoundAI","description":"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation","og_locale":"en_US","og_type":"article","og_title":"Selenium Interview Questions (2026) Guide | LastRoundAI","og_description":"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.","og_url":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation","og_site_name":"LastRound AI","og_image":[{"width":1200,"height":630,"url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-selenium-automation-og.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"46 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation","url":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation","name":"Selenium Interview Questions (2026) Guide | LastRoundAI","isPartOf":{"@id":"https:\/\/lastroundai.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation#primaryimage"},"image":{"@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation#primaryimage"},"thumbnailUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-selenium-automation-og.png","datePublished":"2026-07-27T17:20:00+00:00","description":"Selenium interview questions for 2026: WebDriver, locators, waits, Page Object Model, TestNG, and framework design, with real Java code and prep tips.","breadcrumb":{"@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/lastroundai.com\/interview-questions\/selenium-automation"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation#primaryimage","url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-selenium-automation-og.png","contentUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/07\/iq-selenium-automation-og.png","width":1200,"height":630,"caption":"Selenium Automation interview questions \u2014 LastRoundAI"},{"@type":"BreadcrumbList","@id":"https:\/\/lastroundai.com\/interview-questions\/selenium-automation#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/lastroundai.com\/blog"},{"@type":"ListItem","position":2,"name":"Interview Questions","item":"https:\/\/lastroundai.com\/interview-questions"},{"@type":"ListItem","position":3,"name":"Selenium Automation Interview Questions (2026): WebDriver to Frameworks"}]},{"@type":"WebSite","@id":"https:\/\/lastroundai.com\/blog\/#website","url":"https:\/\/lastroundai.com\/blog\/","name":"LastRound AI","description":"Interview Assistant prep, tech careers and AI tools","publisher":{"@id":"https:\/\/lastroundai.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/lastroundai.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/lastroundai.com\/blog\/#organization","name":"LastRound AI","url":"https:\/\/lastroundai.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/lastroundai.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/06\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png","contentUrl":"https:\/\/lastroundai.com\/blog\/wp-content\/uploads\/2026\/06\/lastroundai-transprant-logo-optimized-BxEo2Wtq.png","width":400,"height":400,"caption":"LastRound AI"},"image":{"@id":"https:\/\/lastroundai.com\/blog\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1169","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq"}],"about":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/types\/iq"}],"author":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/comments?post=1169"}],"version-history":[{"count":3,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1169\/revisions"}],"predecessor-version":[{"id":1760,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/iq\/1169\/revisions\/1760"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/media\/1662"}],"wp:attachment":[{"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/media?parent=1169"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lastroundai.com\/blog\/wp-json\/wp\/v2\/tags?post=1169"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}