07.04 — Corsicamon scenarios#
Igoristan’s “Corsican Pokédex (Corsicadex)” page. API key input, random draw with 1/5 fail,
transient_errorsforError('lol'), and POM-internal retries against “Corsicamons loaded / network error” states.
Campaign#
# src/tests/campaigns/corsicamon.py
def create_igoristan_corsicamon_campaign(*, drivers_pool) -> TestCampaign:
return TestCampaign(
name="Corsicamon",
suites=[
create_corsicamon_happy_paths_test_suite(drivers_pool=drivers_pool),
create_corsicamon_unhappy_paths_test_suite(drivers_pool=drivers_pool),
],
)
def create_igoristan_corsicamon_smoke_campaign(*, drivers_pool) -> TestCampaign:
return TestCampaign(
name="Corsicamon - Smoke",
suites=[create_corsicamon_smoke_tests_suite(drivers_pool=drivers_pool)],
)Smoke test#
# tests/scenarios/corsicamon/enter_api_key.py
def scenario_enter_api_key_smoke(driver, logger):
page = CorsicamonEnterApiKeyPage(driver=driver)
...
return [
drive_page(
act(page, open_corsicamon_page)...,
act(page, verify_enter_api_key_screen)...,
),
]
test_enter_api_key_smoke = create_selenium_test(
name="Corsicamon - Smoke - enter API key screen visible",
test_scenario=lambda driver, logger: Scenario(
test_chain=scenario_enter_api_key_smoke(driver, logger)
),
)Verifies the API key entry page appears. Fails → smoke fails → main campaigns skipped.
Happy path: API key entry + draw + add#
# tests/scenarios/corsicamon/new_draw.py
def scenario_new_draw_happy_path(driver, logger):
on_enter_key = CorsicamonEnterApiKeyPage(driver=driver)
on_main = CorsicamonMainPage(driver=driver)
env = create_env_getters()
api_key = env.get_value("igor_api_key")
return [
drive_page(
act(on_enter_key, open_corsicamon_page)...,
act(on_enter_key, enter_api_key_with_retries(api_key, retries=10, logger=logger))...,
act(on_enter_key, click_validate_api_key)...,
),
drive_page(
act(on_main, verify_main_page)...,
act(on_main, wait_for_pokemons_or_error_with_retries(retries=10, logger=logger))...,
act(on_main, verify_pokemons_displayed)...,
),
drive_page(
act(on_main, click_redraw_button)...,
act(on_main, wait_for_pokemons_or_error_with_retries(...))...,
act(on_main, verify_pokemons_displayed)...,
),
]- Open → enter API key → validate.
- Wait for Corsicamons (with retries against the 1/5 fail).
- Re-draw with retries.
Handling the 1/5 fail#
corsicamon_main.py:
def wait_for_pokemons_or_error_with_retries(retries: int, logger: ILogger):
def unwrapped(p: CorsicamonMainPage) -> CorsicamonMainPage:
return p.wait_for_pokemons_or_error_with_retries(retries=retries, logger=logger)
return unwrappedpages/corsicamon/main.py:
def wait_for_pokemons_or_error_with_retries(self, *, retries: int, logger: ILogger) -> CorsicamonMainPage:
attempts = 1
while attempts <= retries:
try:
WebDriverWait(self._driver, get_timeout()).until(
lambda d: self._has_pokemons() or self._has_error()
)
break
except TimeoutException:
pass
if self._has_error():
logger.warning(f"Got error on attempt {attempts}/{retries}, retrying...")
self._click_redraw_button()
attempts += 1
continue
if self._has_pokemons():
return self
logger.info(f"Pokemons loaded after {attempts} attempts.")
return selfRetry is internal to the POM. The outer scenario only sees success or failure.
The POM-internal retry pattern is documented in the AI project’s CLAUDE.md:
introduce-pom-retries— POM-internal retries, with split (first-try + with-retries).
Unhappy paths#
tests/scenarios/corsicamon/…
- Entering an invalid API key → verify the error message.
- …
Back to Igoristan#
# tests/scenarios/corsicamon/back_to_igoristan.py
def scenario_back_to_igoristan(driver, logger):
page = CorsicamonEnterApiKeyPage(driver=driver)
return [
drive_page(
act(page, open_corsicamon_page)...,
act(page, click_back_to_igoristan)...,
),
]
test_back_to_igoristan = create_selenium_test(
name="Corsicamon - Back to Igoristan",
test_scenario=lambda driver, logger: Scenario(test_chain=scenario_back_to_igoristan(driver, logger)),
post_test_scenarios_fragments=[verify_homepage],
)