Browse Source

♻️ Consolidate Playwright screenshot scripts into a single file

- Merge 12 separate screenshot scripts into generate_screenshots.py
- Fix inverted retry logic in server readiness check (break on success instead of on ConnectError)
- Fix duplicate browser launch and viewport loss in cookie_param_models and query_param_models
- Add wait_for_server() for scripts that were missing it (separate_openapi_schemas)
- Support running all or individual screenshots via CLI arguments
pull/15399/head
yangxiangwen 2 months ago
parent
commit
74a4c31c39
Failed to extract signature
  1. 38
      scripts/playwright/cookie_param_models/image01.py
  2. 233
      scripts/playwright/generate_screenshots.py
  3. 39
      scripts/playwright/header_param_models/image01.py
  4. 38
      scripts/playwright/json_base64_bytes/image01.py
  5. 40
      scripts/playwright/query_param_models/image01.py
  6. 39
      scripts/playwright/request_form_models/image01.py
  7. 32
      scripts/playwright/separate_openapi_schemas/image01.py
  8. 33
      scripts/playwright/separate_openapi_schemas/image02.py
  9. 33
      scripts/playwright/separate_openapi_schemas/image03.py
  10. 32
      scripts/playwright/separate_openapi_schemas/image04.py
  11. 32
      scripts/playwright/separate_openapi_schemas/image05.py
  12. 38
      scripts/playwright/sql_databases/image01.py
  13. 38
      scripts/playwright/sql_databases/image02.py

38
scripts/playwright/cookie_param_models/image01.py

@ -1,38 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("link", name="/items/").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/cookie-param-models/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/cookie_param_models/tutorial001.py"]
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

233
scripts/playwright/generate_screenshots.py

@ -0,0 +1,233 @@
"""Generate screenshots for FastAPI documentation.
Usage:
python generate_screenshots.py # Generate all screenshots
python generate_screenshots.py <name> # Generate a specific screenshot
python generate_screenshots.py --list # List available screenshot names
"""
import subprocess
import time
import httpx
from playwright.sync_api import Page, sync_playwright
def wait_for_server(
url: str = "http://localhost:8000/docs", *, retries: int = 10
) -> None:
"""Wait for the server to be ready by polling the given URL."""
for _ in range(retries):
try:
response = httpx.get(url)
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
# ---- Interaction steps for each screenshot ----
def separate_openapi_schemas_image01(page: Page) -> None:
page.get_by_text("POST/items/Create Item").click()
page.get_by_role("tab", name="Schema").first.click()
def separate_openapi_schemas_image02(page: Page) -> None:
page.get_by_text("GET/items/Read Items").click()
page.get_by_role("button", name="Try it out").click()
page.get_by_role("button", name="Execute").click()
def separate_openapi_schemas_image03(page: Page) -> None:
page.get_by_text("GET/items/Read Items").click()
page.get_by_role("tab", name="Schema").click()
page.get_by_label("Schema").get_by_role("button", name="Expand all").click()
def separate_openapi_schemas_image04(page: Page) -> None:
page.get_by_role("button", name="Item", exact=True).click()
page.set_viewport_size({"width": 960, "height": 820})
def separate_openapi_schemas_image05(page: Page) -> None:
page.get_by_role("button", name="Item", exact=True).click()
page.set_viewport_size({"width": 960, "height": 700})
def request_form_models_image01(page: Page) -> None:
page.get_by_role("button", name="POST /login/ Login").click()
page.get_by_role("button", name="Try it out").click()
def header_param_models_image01(page: Page) -> None:
page.get_by_role("button", name="GET /items/ Read Items").click()
page.get_by_role("button", name="Try it out").click()
def json_base64_bytes_image01(page: Page) -> None:
page.get_by_role("button", name="POST /data Post Data").click()
def cookie_param_models_image01(page: Page) -> None:
page.get_by_role("link", name="/items/").click()
def query_param_models_image01(page: Page) -> None:
page.get_by_role("button", name="GET /items/ Read Items").click()
page.get_by_role("button", name="Try it out").click()
page.get_by_role("heading", name="Servers").click()
def sql_databases_image01(page: Page) -> None:
page.get_by_label("post /heroes/").click()
def sql_databases_image02(page: Page) -> None:
page.get_by_label("post /heroes/").click()
# ---- Screenshot configurations ----
SCREENSHOTS: list[dict] = [
# separate_openapi_schemas
{
"name": "separate_openapi_schemas_image01",
"cmd": ["fastapi", "run", "docs_src/separate_openapi_schemas/tutorial001_py310.py"],
"wait_for_server": True,
"interact": separate_openapi_schemas_image01,
"screenshot_path": "docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png",
},
{
"name": "separate_openapi_schemas_image02",
"cmd": ["fastapi", "run", "docs_src/separate_openapi_schemas/tutorial001_py310.py"],
"wait_for_server": True,
"interact": separate_openapi_schemas_image02,
"screenshot_path": "docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png",
},
{
"name": "separate_openapi_schemas_image03",
"cmd": ["fastapi", "run", "docs_src/separate_openapi_schemas/tutorial001_py310.py"],
"wait_for_server": True,
"interact": separate_openapi_schemas_image03,
"screenshot_path": "docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png",
},
{
"name": "separate_openapi_schemas_image04",
"cmd": ["fastapi", "run", "docs_src/separate_openapi_schemas/tutorial001_py310.py"],
"wait_for_server": True,
"interact": separate_openapi_schemas_image04,
"screenshot_path": "docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png",
},
{
"name": "separate_openapi_schemas_image05",
"cmd": ["fastapi", "run", "docs_src/separate_openapi_schemas/tutorial002_py310.py"],
"wait_for_server": True,
"interact": separate_openapi_schemas_image05,
"screenshot_path": "docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png",
},
# request_form_models
{
"name": "request_form_models_image01",
"cmd": ["fastapi", "run", "docs_src/request_form_models/tutorial001_py310.py"],
"wait_for_server": True,
"interact": request_form_models_image01,
"screenshot_path": "docs/en/docs/img/tutorial/request-form-models/image01.png",
},
# header_param_models
{
"name": "header_param_models_image01",
"cmd": ["fastapi", "run", "docs_src/header_param_models/tutorial001_py310.py"],
"wait_for_server": True,
"interact": header_param_models_image01,
"screenshot_path": "docs/en/docs/img/tutorial/header-param-models/image01.png",
},
# json_base64_bytes
{
"name": "json_base64_bytes_image01",
"cmd": ["fastapi", "run", "docs_src/json_base64_bytes/tutorial001_py310.py"],
"wait_for_server": True,
"interact": json_base64_bytes_image01,
"screenshot_path": "docs/en/docs/img/tutorial/json-base64-bytes/image01.png",
},
# cookie_param_models
{
"name": "cookie_param_models_image01",
"cmd": ["fastapi", "run", "docs_src/cookie_param_models/tutorial001_py310.py"],
"wait_for_server": True,
"interact": cookie_param_models_image01,
"screenshot_path": "docs/en/docs/img/tutorial/cookie-param-models/image01.png",
},
# query_param_models
{
"name": "query_param_models_image01",
"cmd": ["fastapi", "run", "docs_src/query_param_models/tutorial001_py310.py"],
"wait_for_server": True,
"interact": query_param_models_image01,
"screenshot_path": "docs/en/docs/img/tutorial/query-param-models/image01.png",
},
# sql_databases
{
"name": "sql_databases_image01",
"cmd": ["fastapi", "run", "docs_src/sql_databases/tutorial001_py310.py"],
"wait_for_server": True,
"interact": sql_databases_image01,
"screenshot_path": "docs/en/docs/img/tutorial/sql-databases/image01.png",
},
{
"name": "sql_databases_image02",
"cmd": ["fastapi", "run", "docs_src/sql_databases/tutorial002_py310.py"],
"wait_for_server": True,
"interact": sql_databases_image02,
"screenshot_path": "docs/en/docs/img/tutorial/sql-databases/image02.png",
},
]
def generate_screenshot(config: dict) -> None:
"""Generate a single screenshot based on the given configuration."""
process = subprocess.Popen(config["cmd"])
try:
if config.get("wait_for_server"):
wait_for_server()
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
# Run custom interaction steps
config["interact"](page)
# Manually add the screenshot
page.screenshot(path=config["screenshot_path"])
# ---------------------
context.close()
browser.close()
finally:
process.terminate()
if __name__ == "__main__":
import sys
if "--list" in sys.argv:
for config in SCREENSHOTS:
print(config["name"])
sys.exit(0)
name = sys.argv[1] if len(sys.argv) > 1 else None
failed: list[str] = []
for config in SCREENSHOTS:
if name is None or config["name"] == name:
try:
generate_screenshot(config)
except Exception as e:
print(f"ERROR: Failed to generate '{config['name']}': {e}")
failed.append(config["name"])
if failed:
print(f"\nFailed screenshots: {', '.join(failed)}")
sys.exit(1)

39
scripts/playwright/header_param_models/image01.py

@ -1,39 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="GET /items/ Read Items").click()
page.get_by_role("button", name="Try it out").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/header-param-models/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/header_param_models/tutorial001.py"]
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

38
scripts/playwright/json_base64_bytes/image01.py

@ -1,38 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="POST /data Post Data").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/json-base64-bytes/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/json_base64_bytes/tutorial001_py310.py"]
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

40
scripts/playwright/query_param_models/image01.py

@ -1,40 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="GET /items/ Read Items").click()
page.get_by_role("button", name="Try it out").click()
page.get_by_role("heading", name="Servers").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/query-param-models/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/query_param_models/tutorial001.py"]
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

39
scripts/playwright/request_form_models/image01.py

@ -1,39 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="POST /login/ Login").click()
page.get_by_role("button", name="Try it out").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/request-form-models/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/request_form_models/tutorial001.py"]
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

32
scripts/playwright/separate_openapi_schemas/image01.py

@ -1,32 +0,0 @@
import subprocess
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_text("POST/items/Create Item").click()
page.get_by_role("tab", name="Schema").first.click()
# Manually add the screenshot
page.screenshot(
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png"
)
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
)
try:
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

33
scripts/playwright/separate_openapi_schemas/image02.py

@ -1,33 +0,0 @@
import subprocess
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_text("GET/items/Read Items").click()
page.get_by_role("button", name="Try it out").click()
page.get_by_role("button", name="Execute").click()
# Manually add the screenshot
page.screenshot(
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png"
)
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
)
try:
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

33
scripts/playwright/separate_openapi_schemas/image03.py

@ -1,33 +0,0 @@
import subprocess
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_text("GET/items/Read Items").click()
page.get_by_role("tab", name="Schema").click()
page.get_by_label("Schema").get_by_role("button", name="Expand all").click()
# Manually add the screenshot
page.screenshot(
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png"
)
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
)
try:
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

32
scripts/playwright/separate_openapi_schemas/image04.py

@ -1,32 +0,0 @@
import subprocess
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="Item-Input").click()
page.get_by_role("button", name="Item-Output").click()
page.set_viewport_size({"width": 960, "height": 820})
# Manually add the screenshot
page.screenshot(
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png"
)
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
)
try:
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

32
scripts/playwright/separate_openapi_schemas/image05.py

@ -1,32 +0,0 @@
import subprocess
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_role("button", name="Item", exact=True).click()
page.set_viewport_size({"width": 960, "height": 700})
# Manually add the screenshot
page.screenshot(
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png"
)
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["uvicorn", "docs_src.separate_openapi_schemas.tutorial002:app"]
)
try:
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

38
scripts/playwright/sql_databases/image01.py

@ -1,38 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_label("post /heroes/").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/sql-databases/image01.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/sql_databases/tutorial001.py"],
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()

38
scripts/playwright/sql_databases/image02.py

@ -1,38 +0,0 @@
import subprocess
import time
import httpx
from playwright.sync_api import Playwright, sync_playwright
# Run playwright codegen to generate the code below, copy paste the sections in run()
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
# Update the viewport manually
context = browser.new_context(viewport={"width": 960, "height": 1080})
page = context.new_page()
page.goto("http://localhost:8000/docs")
page.get_by_label("post /heroes/").click()
# Manually add the screenshot
page.screenshot(path="docs/en/docs/img/tutorial/sql-databases/image02.png")
# ---------------------
context.close()
browser.close()
process = subprocess.Popen(
["fastapi", "run", "docs_src/sql_databases/tutorial002.py"],
)
try:
for _ in range(10):
try:
response = httpx.get("http://localhost:8000/docs")
if response.status_code == 200:
break
except httpx.ConnectError:
time.sleep(1)
with sync_playwright() as playwright:
run(playwright)
finally:
process.terminate()
Loading…
Cancel
Save