Chaos Testing Playground
Real interview simulator with random IDs, dynamic loading, and Shadow DOM
⥠Quick Start (3 minutes)
ðŊ Challenge 1: Random IDs
- 1. Note button IDs below (e.g., btn-x9z2)
- 2. Click "Full Page Refresh"
- 3. Watch IDs change (btn-a4f7)
- 4. Try clicking by text instead
âąïļ Challenge 2: Delays
- 1. Refresh page, see spinner
- 2. Note delay time (e.g., 3.2s)
- 3. Wait for green button
- 4. Test with 10s explicit wait
ð Challenge 3: Shadow DOM
- 1. Type in gray box input
- 2. F12 â Find #shadow-root
- 3. See input hidden inside
- 4. Use shadowRoot.querySelector()
â ïļ Challenge Overview
This page simulates the most difficult automation scenarios you'll face in real production environments:
- Random IDs: Button IDs regenerate on every page refresh (e.g., btn-x9z2 â btn-a1b4)
- Dynamic Loading: Elements appear after random delays (2-5 seconds) requiring explicit waits
- Shadow DOM: Input fields hidden inside Shadow DOM trees requiring special selectors
ðĄ Pro Tip: Use text content, ARIA labels, and semantic HTML instead of relying on IDs or classes
Challenge 1: Random IDs on Every Refresh
These buttons have completely random IDs that change on every page load. Your automation must locate them without using ID selectors.
Current IDs:
Button 1:
Button 2:
Button 3:
Challenge 2: Dynamic Loading with Random Delay
This button appears after a random delay (0s on current load). Your test must implement explicit waits.
Delay on this load: 0s
Challenge 3: Shadow DOM Input FieldRequires Selenium 4+
This input field is encapsulated inside a Shadow DOM. Standard selectors won't work - you need shadow piercing techniques.
ð Shadow DOM Piercing Required
â ïļ Selenium 4+ has native shadow DOM support. Selenium 3 requires JavaScript execution.
# Selenium 4+ (Native) shadow_host = driver.find_element(By.CSS_SELECTOR, "[data-testid='shadow-host']") shadow_root = shadow_host.shadow_root input_field = shadow_root.find_element(By.CSS_SELECTOR, ".shadow-input")
# Selenium 3 (JavaScript fallback)
shadow_root = driver.execute_script(
"return arguments[0].shadowRoot", shadow_host
)
input_field = shadow_root.find_element(By.CSS_SELECTOR, ".shadow-input")Challenge 4: The Stale Element TrapINTERVIEW KILLER
This button is removed from the DOM and re-added every 2 seconds. If you store a reference to it, you'll get StaleElementReferenceException.
ðĨ Why This Breaks Tests
Beginner code:
button = driver.find_element(By.TEST_ID, "stale-element-button") time.sleep(3) # Element gets removed during this! button.click() # â StaleElementReferenceException
â Solution: Re-query or use retry logic with ExpectedConditions.refreshed()
# Python with retry
def click_with_retry(locator, retries=3):
for i in range(retries):
try:
driver.find_element(*locator).click()
break
except StaleElementReferenceException:
if i == retries - 1: raise
time.sleep(0.5)Challenge 5: The Honeypot SelectorTHE TRAP
There are TWO buttons below. The visible one has NO ID. The hidden one has id="submit-button". Trust IDs blindly? Your test passes but nothing happens.
â ïļ This hidden button has id="submit-button" but is display:none
ðŠĪ The Trap
# This finds the HIDDEN button driver.find_element(By.ID, "submit-button").click() # â ElementNotInteractableException (or test passes but does nothing!)
â Always verify visibility:
button = driver.find_element(By.ID, "submit-button")
if not button.is_displayed():
raise Exception("Button is hidden!")
button.click()Challenge 6: IFrame InceptionCONTEXT HELL
Button inside IFrame, inside another IFrame. You must switch context twice. Banks still use these!
ðŊ Context Switching Required
# Python Selenium driver.switch_to.frame(0) # Enter outer iframe driver.switch_to.frame(0) # Enter inner iframe driver.find_element(By.ID, "nested-button").click() driver.switch_to.default_content() # Exit back to main page
// Playwright
const outerFrame = page.frameLocator('iframe').first();
const innerFrame = outerFrame.frameLocator('iframe').first();
await innerFrame.locator('#nested-button').click();Challenge 7: The Moving TargetVISUAL CHAOS
This button moves to a new position every 500ms. Coordinate-based clicks will miss. Forces object-based location.
ðŪ Why Coordinate Clicks Fail
# â This will miss the button from selenium.webdriver.common.action_chains import ActionChains ActionChains(driver).move_to_location(400, 200).click().perform() # Button has already moved by the time you click!
â Use element reference instead:
# Selenium tracks the element's position dynamically button = driver.find_element(By.TEST_ID, "moving-target") button.click() # Works even if button moves!
Reset Challenge
Refresh the page or click below to regenerate all random IDs and delays. Perfect for repeated test runs.
â How to Verify Chaos Features (Step-by-Step)
1Verify Random IDs
- 1. Look at the "Current IDs" section under the three colored buttons
- 2. Note down the IDs (e.g., Button 1:
btn-x9z2k1) - 3. Click the "Full Page Refresh" button at the bottom
- 4. Watch the IDs change completely (e.g., now
btn-a4f7g2) - 5. Open DevTools (F12) â Elements tab
- 6. Inspect any colored button - the
idattribute changes with each refresh - 7. Try clicking buttons using their text instead of ID
2Verify Dynamic Loading Delay
- 1. Refresh the page (F5) or click "Full Page Refresh"
- 2. Watch the Challenge 2 section - you'll see a loading spinner
- 3. Note the delay time shown (e.g., "Delay on this load: 3.5s")
- 4. Wait the specified time - button will appear with green background
- 5. The delay is random between 2-5 seconds on each load
- 6. Test automation waits: Your test must wait up to 10 seconds for this button
- 7. Click "Regenerate Random IDs" to trigger a new delay without full refresh
3Verify Shadow DOM Input
- 1. Scroll to Challenge 3: Shadow DOM Input Field
- 2. Try clicking in the gray box - you'll see an input field appear
- 3. Type some text in the input - this is inside Shadow DOM
- 4. Open DevTools (F12) â Elements tab
- 5. Find
<div data-testid="shadow-host"> - 6. Click the #shadow-root to expand it
- 7. You'll see the input is hidden inside, not directly accessible
- 8. Standard selectors won't work - you need shadow piercing techniques
- 9. Try the automation code examples shown in the orange warning box
4Inspect in Browser DevTools
- 1. Open DevTools (F12)
- 2. Go to Console tab
- 3. Type and run this command:
// Check current random IDs
const btn1 = document.querySelector('button:has-text("Random Button 1")');
console.log("Button 1 ID:", btn1?.id);
// Access Shadow DOM
const host = document.querySelector('[data-testid="shadow-host"]');
const input = host.shadowRoot.querySelector('input');
console.log("Shadow input found:", input);
input.value = "Testing Shadow DOM!";5Write Automation Tests
Test Random IDs (use text instead):
# Selenium Python - Find by text content
button = driver.find_element(By.XPATH, "//button[text()='Random Button 1']")
button.click()
# Playwright JavaScript - Use text selector
await page.locator('button:has-text("Random Button 1")').click();Test Dynamic Loading (explicit waits):
# Selenium with WebDriverWait
delayed_btn = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "[data-testid='delayed-button']"))
)
delayed_btn.click()
# Playwright auto-waits for you
await page.locator('[data-testid="delayed-button"]').click();Test Shadow DOM (pierce shadow root):
# Selenium 4+ - Native shadow DOM support
shadow_host = driver.find_element(By.CSS_SELECTOR, "[data-testid='shadow-host']")
shadow_root = shadow_host.shadow_root
input_field = shadow_root.find_element(By.CSS_SELECTOR, ".shadow-input")
input_field.send_keys("Test text")
# Playwright - Use >>> combinator
await page.locator('[data-testid="shadow-host"] >>> input').fill('Test text');Test Stale Elements (retry pattern):
# Selenium - Retry with re-query
def click_with_retry(driver, locator, retries=3):
for i in range(retries):
try:
element = driver.find_element(*locator)
element.click()
return
except StaleElementReferenceException:
if i == retries - 1: raise
time.sleep(0.5)
click_with_retry(driver, (By.TEST_ID, "stale-element-button"))
# Playwright - Auto-retries for you
await page.locator('[data-testid="stale-element-button"]').click();Test Honeypot Selectors (verify visibility):
# Selenium - Check visibility before clicking
button = driver.find_element(By.ID, "submit-button")
if not button.is_displayed():
raise Exception("Button is hidden - this is a trap!")
button.click()
# Playwright - Use :visible pseudo-selector
await page.locator('#submit-button:visible').click();Test IFrame Inception (double switchTo):
# Selenium - Switch to nested iframes
driver.switch_to.frame(0) # Enter outer iframe
driver.switch_to.frame(0) # Enter inner iframe
driver.find_element(By.ID, "nested-button").click()
driver.switch_to.default_content() # Exit all frames
# Playwright - Chain frameLocator
const outerFrame = page.frameLocator('iframe').first();
const innerFrame = outerFrame.frameLocator('iframe').first();
await innerFrame.locator('#nested-button').click();Test Moving Target (object-based locator):
# Selenium - Let WebDriver handle positioning
button = driver.find_element(By.TEST_ID, "moving-target")
button.click() # â
Works even while moving!
# DON'T use coordinate-based clicks
# ActionChains(driver).move_to_location(x, y).click() # â Will miss
# Playwright - Element-based click (auto-tracks position)
await page.locator('[data-testid="moving-target"]').click();ð Expected Behavior Reference
Random IDs
â Expected:
- âĒ IDs change on every refresh
- âĒ Format: btn-[6 random chars]
- âĒ Text content stays same
â Common Issue:
Using driver.find_element_by_id() - This will break!
Dynamic Loading
â Expected:
- âĒ Random delay: 2-5 seconds
- âĒ Loading spinner shows
- âĒ Button appears after delay
â Common Issue:
Using sleep(3) - Button might take 5s!
Shadow DOM
â Expected:
- âĒ Input in #shadow-root
- âĒ Standard selectors fail
- âĒ Need shadowRoot access
â Common Issue:
driver.find_element(By.CSS, 'input') - Won't find it!
Stale Elements
â Expected:
- âĒ Element refreshes every 2s
- âĒ Old references go stale
- âĒ Need retry/re-query logic
â Common Issue:
Storing element reference - StaleElementReferenceException!
Honeypot Selectors
â Expected:
- âĒ Visible button has no ID
- âĒ Hidden button has ID
- âĒ Must verify visibility
â Common Issue:
find_element(By.ID) clicks hidden element!
IFrame & Moving
â Expected:
- âĒ IFrame: double switchTo()
- âĒ Moving: element-based clicks
- âĒ Context switching required
â Common Issue:
Coordinate clicks fail on moving targets!
â ïļ Common Mistakes in Chaos Testing
â Mistake: Relying on IDs
# This will fail! button = driver.find_element(By.ID, 'btn-x9z2') button.click()
â Solution: Use text content
button = driver.find_element(
By.XPATH, "//button[text()='Random Button 1']"
)â Mistake: Fixed sleep() waits
# Might be too short! import time time.sleep(3) button.click()
â Solution: Explicit waits
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.TEST_ID, "delayed-button"))
)â Mistake: Storing element references
# Stale after 2 seconds! btn = driver.find_element(By.TEST_ID, "stale-element-button") time.sleep(3) btn.click() # â StaleElementReferenceException
â Solution: Re-query or retry
# Re-query every time driver.find_element(By.TEST_ID, "stale-element-button").click()
â Mistake: Trusting IDs blindly
# Clicks hidden honeypot! driver.find_element(By.ID, "submit-button").click() # Test passes but nothing happens
â Solution: Verify visibility
btn = driver.find_element(By.ID, "submit-button") assert btn.is_displayed(), "Button is hidden!" btn.click()
â Mistake: Forgetting iframe context
# Can't find button in main context! driver.find_element(By.ID, "nested-button").click() # â NoSuchElementException
â Solution: Switch to iframe
driver.switch_to.frame(0).switch_to.frame(0) driver.find_element(By.ID, "nested-button").click()
â Mistake: Coordinate-based clicks
# Will miss moving button! ActionChains(driver).move_to_location(400, 200).click() # Button has moved by click time
â Solution: Element-based clicks
# WebDriver tracks position dynamically driver.find_element(By.TEST_ID, "moving-target").click()
ðž Interview Questions You Can Answer Now
Q: How do you handle elements with dynamic IDs that change on every page load?
A: I've practiced with PassTheNote's Chaos Mode where IDs regenerate on every refresh (btn-x9z2 â btn-a4f7). I use text-based selectors with XPath "//button[text()='Submit']" or Playwright'spage.locator('button:has-text("Submit")'). I also leverage ARIA labels and semantic HTML.
Q: How do you test elements inside Shadow DOM?
A: I've worked with Shadow DOM in PassTheNote's Chaos page. In Selenium, I use JavaScript execution:driver.execute_script("return document.querySelector('host').shadowRoot.querySelector('input')"). In Playwright, I use the >>> combinator for shadow piercing.
Q: What's the difference between implicit and explicit waits?
A: I practiced with 2-5 second random delays in Chaos Mode. Implicit waits apply globally but can't handle specific conditions. Explicit waits (WebDriverWait) are better because they wait for specific conditions like element_to_be_clickable, visibility, or presence. I always use explicit waits with 10-15 second timeouts for production-like delays.
ð Explore More Testing Challenges
Combine Chaos Mode with other advanced testing techniques to build a complete automation skillset.
Bug Hunting
Practice debugging with intentional bugs like API timeouts, disabled buttons, and corrupted data.
A11y Audit
Toggle WCAG violations and practice accessibility testing with screen readers and automated tools.
Practice Challenges
Curated test scenarios for e-commerce, API testing, and dynamic elements with acceptance criteria.
ðŊ Automation Strategies
For Random IDs:
- Use text content:
driver.find_element(By.XPATH, "//button[text()='Random Button 1']") - Use CSS with text:
page.locator('button:has-text("Random Button 1")')
For Dynamic Loading:
- Explicit waits:
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TEST_ID, "delayed-button"))) - Playwright auto-wait:
await page.locator('[data-testid="delayed-button"]').click()
For Shadow DOM:
- JavaScript execution:
driver.execute_script("return document.querySelector('[data-testid=shadow-host]').shadowRoot.querySelector('input')") - Playwright piercing:
page.locator('[data-testid=shadow-host] >>> input')