|
20 | 20 | sys.path.insert(0, str(REPO_ROOT)) |
21 | 21 |
|
22 | 22 | from playwright.sync_api import Browser, Request, Response, sync_playwright # noqa: E402 |
| 23 | +from playwright.sync_api import TimeoutError as PlaywrightTimeoutError # noqa: E402 |
23 | 24 | from scripts.api_reference_release import load_release_identity # noqa: E402 |
24 | 25 | from scripts.check_api_reference_install import verify_public_deployment # noqa: E402 |
25 | 26 |
|
|
29 | 30 | PROMOTION_EVENT_URL = "https://cloud.durable-workflow.com/early-access/promotion-events" |
30 | 31 | PROMOTION_SOURCE = "sdk-python-reference" |
31 | 32 | QUALIFICATION_EVENT = "qualification" |
| 33 | +VIEWPORT_ATTEMPTS = 3 |
32 | 34 | VIEWPORTS = ( |
33 | 35 | ("desktop", 1440, 900), |
34 | 36 | ("intermediate", 768, 1024), |
@@ -104,7 +106,7 @@ def assert_event_response(response: Response, event: str) -> None: |
104 | 106 | assert "no-store" in response.headers.get("cache-control", ""), f"{event} promotion response was cacheable" |
105 | 107 |
|
106 | 108 |
|
107 | | -def qualify_viewport(browser: Browser, name: str, width: int, height: int) -> None: |
| 109 | +def qualify_viewport_once(browser: Browser, name: str, width: int, height: int) -> None: |
108 | 110 | context = browser.new_context(viewport={"width": width, "height": height}, reduced_motion="reduce") |
109 | 111 | page = context.new_page() |
110 | 112 | errors: list[str] = [] |
@@ -179,6 +181,21 @@ def record_initiated_event(payload: object) -> None: |
179 | 181 | context.close() |
180 | 182 |
|
181 | 183 |
|
| 184 | +def qualify_viewport(browser: Browser, name: str, width: int, height: int) -> None: |
| 185 | + """Qualify one viewport, retrying only transient browser transport timeouts.""" |
| 186 | + timeouts: list[str] = [] |
| 187 | + for attempt in range(1, VIEWPORT_ATTEMPTS + 1): |
| 188 | + try: |
| 189 | + qualify_viewport_once(browser, name, width, height) |
| 190 | + return |
| 191 | + except PlaywrightTimeoutError as error: |
| 192 | + timeouts.append(f"attempt {attempt}: {error}") |
| 193 | + |
| 194 | + raise AssertionError( |
| 195 | + f"promotion qualification timed out at {name} after {VIEWPORT_ATTEMPTS} attempts: " + "; ".join(timeouts) |
| 196 | + ) |
| 197 | + |
| 198 | + |
182 | 199 | def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: |
183 | 200 | parser = argparse.ArgumentParser(description=__doc__) |
184 | 201 | parser.add_argument( |
@@ -228,9 +245,10 @@ def main(argv: Sequence[str] | None = None) -> int: |
228 | 245 | browser.close() |
229 | 246 |
|
230 | 247 | print( |
231 | | - f"Confirmed deployed revision {args.source_revision}, two non-aggregating qualification requests, " |
232 | | - "the source-attributed impression/click initiation and destination behavior, and no browser errors at " |
233 | | - "desktop, intermediate, mobile, and short-height viewports." |
| 248 | + f"Confirmed deployed revision {args.source_revision}, two successful non-aggregating qualification " |
| 249 | + "requests per viewport with bounded transport retries, the source-attributed impression/click initiation " |
| 250 | + "and destination behavior, and no browser errors at desktop, intermediate, mobile, and short-height " |
| 251 | + "viewports." |
234 | 252 | ) |
235 | 253 | return 0 |
236 | 254 |
|
|
0 commit comments