From 08e39160ea668a7b8592ec8f6381a53a0062e27c Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Nov 2026 00:32:54 +0530 Subject: [PATCH 1/2] feat(pages): read the org card logos on the AI Maker landing page --- locators/ai_maker_locators.py | 3 +++ pages/ai_maker_page.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/locators/ai_maker_locators.py b/locators/ai_maker_locators.py index 2364f6a..8dac72d 100644 --- a/locators/ai_maker_locators.py +++ b/locators/ai_maker_locators.py @@ -40,6 +40,9 @@ class AIMakerLocators: STAT_CARD = "[class*='stat'], [class*='card'], [class*='Card']" STAT_CARD_VALUE = ".metric-card-value" + # ── Org selection page: org card logos (alt is " logo") ───────── + ORG_CARD_LOGO = "img[alt$=' logo']" + # ── Org selection page: Add Organisation button (Jun 2026) ─────────────── ADD_ORGANISATION_BUTTON = ( "button:has-text('Add Organisation'), a:has-text('Add Organisation')" diff --git a/pages/ai_maker_page.py b/pages/ai_maker_page.py index 0751f9b..eedafaf 100644 --- a/pages/ai_maker_page.py +++ b/pages/ai_maker_page.py @@ -56,6 +56,18 @@ def go_to_landing(self) -> "AIMakerPage": self.wait_for_app_ready() return self + def org_card_logos(self) -> list[dict]: + """src and load state of each org card logo on the landing page, once they finish loading.""" + logos = self.page.locator(AIMakerLocators.ORG_CARD_LOGO) + logos.first.wait_for(state="attached") + self.page.wait_for_function( + "sel => [...document.querySelectorAll(sel)].every(i => i.complete)", + arg=AIMakerLocators.ORG_CARD_LOGO, + ) + return logos.evaluate_all( + "imgs => imgs.map(i => ({alt: i.alt, src: i.getAttribute('src'), width: i.naturalWidth}))" + ) + def go_to_models(self) -> None: self.click(self.SIDEBAR_MODELS) self.wait_for_app_ready() From 8ce6e300aaef9ae62b7773b6889dce6d204a3350 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Nov 2026 00:32:54 +0530 Subject: [PATCH 2/2] test(e2e): org logos use their stored S3/CloudFront URL as-is (ParakhAI-frontend#470) --- tests/e2e/test_ai_maker_dashboard.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/e2e/test_ai_maker_dashboard.py b/tests/e2e/test_ai_maker_dashboard.py index b9f9cee..4bb09d3 100644 --- a/tests/e2e/test_ai_maker_dashboard.py +++ b/tests/e2e/test_ai_maker_dashboard.py @@ -216,6 +216,29 @@ def test_text_generation_badge_is_present(self, page: Page): # ── Add Organisation / Add A New Model buttons (Jun 2026) ───────────────────── +class TestOrgCardLogos: + """Org logos are stored as absolute S3/CloudFront URLs and must be used as-is + (ParakhAI-frontend#470). The old code prefixed the DataSpace API host, which + turned an absolute URL into a broken one.""" + + def test_org_card_logos_load_from_their_own_url(self, page: Page, authenticated_graphql_client): + orgs = authenticated_graphql_client( + "query { myOrganizations { name logoUrl } }" + )["data"]["myOrganizations"] + uploaded = {f"{o['name']} logo": o["logoUrl"] for o in orgs if (o.get("logoUrl") or "").startswith("http")} + if not uploaded: + pytest.skip("No organisation of this account has an absolute logo URL") + + shown = {} # alt -> srcs; the page header also has a "CivicDataLab logo" + for logo in AIMakerPage(page).go_to_landing().org_card_logos(): + shown.setdefault(logo["alt"], []).append(logo["src"]) + wrong = {alt: shown.get(alt) for alt, url in uploaded.items() if url not in shown.get(alt, [])} + assert not wrong, ( + f"{len(wrong)} of {len(uploaded)} org logos do not use their stored URL as-is " + f"(a glued-on host, or the fallback after a failed load): {dict(list(wrong.items())[:3])}" + ) + + class TestAIMakerButtons: """New action buttons trigger AlertDialogs for external CivicDataSpace redirects."""