agnes-the-ai-analyst/tests/test_web_marketplace_guide.py
Vojtech cb13f80241
feat(marketplace): rename CTA + expand submit-flow guides (#308)
Three coordinated tweaks to the publication discovery surface:

1. Action-row CTA on /marketplace?tab=curated reads 'Submit a skill
   or plugin' instead of 'Submit a plugin'. Skills are first-class
   citizens of the curated shelf; the old wording made them feel
   like an afterthought. Same rename in the empty-state JS innerHTML
   so the two paths can't drift.

2. Curated guide page (/marketplace/guide/curated) expanded from a
   4-line stub into a 3-step ordered list documenting the Named
   Curator handoff (find curator → handoff → publish + lifecycle).
   New '.guide-fastpath' callout block points users at the Flea
   Market when they want lighter review-bar / faster path. Primary
   CTA at the bottom of the curated guide now links to the flea
   guide too, so users who skim past the fast-path callout still
   see the escape hatch.

3. Flea guide page (/marketplace/guide/flea) expanded from a
   3-line stub into a 4-step ordered list (package → upload via
   form → automated review → published). Documents the actual
   /store/new flow + the automated guardrails (manifest, content
   quality, prompt-injection scan) so users know what 'self-service'
   actually means before they upload.

Route titles updated to match: 'Submit a skill or plugin to
Curated Marketplace'.

New file: tests/test_web_marketplace_guide.py — three tests covering
the CTA rename, the curated guide's structural elements (Named
Curators lede, 3 steps, fastpath callout, primary-CTA href), and
the flea guide's structural elements (4 steps, no fastpath
asymmetry, /store/new primary CTA).
2026-05-14 19:44:33 +02:00

140 lines
5.2 KiB
Python

"""GET /marketplace/guide/{curated,flea} — submission flow guides.
Both routes are authed (`get_current_user` dependency). The curated guide
documents the Named Curator handoff and has a fast-path callout pointing
at the flea self-service guide; the flea guide documents the /store/new
upload flow. Together with the action-row CTA on /marketplace?tab=curated,
this trio is the discovery surface for "how do I get my plugin published".
"""
from __future__ import annotations
import tempfile
import uuid
import pytest
@pytest.fixture
def fresh_db(monkeypatch):
with tempfile.TemporaryDirectory() as tmp:
monkeypatch.setenv("DATA_DIR", tmp)
monkeypatch.setenv("TESTING", "1")
monkeypatch.setenv("JWT_SECRET_KEY", "test-jwt-secret-key-minimum-32-chars!!")
yield tmp
def _make_user_and_session(conn, email="u@example.com"):
from src.repositories.users import UserRepository
from app.auth.jwt import create_access_token
uid = str(uuid.uuid4())
UserRepository(conn).create(id=uid, email=email, name=email.split("@")[0])
return uid, create_access_token(user_id=uid, email=email)
def _client():
from fastapi.testclient import TestClient
from app.main import app
return TestClient(app)
def test_marketplace_curated_tab_cta_text(fresh_db):
"""The action-row CTA on /marketplace?tab=curated reads
'Submit a skill or plugin' (renamed from 'Submit a plugin' so skills
aren't an afterthought) and links to the curated guide. Empty-state
fallback in JS uses the same string so both surfaces stay in sync."""
from src.db import get_system_db, close_system_db
conn = get_system_db()
try:
_, sess = _make_user_and_session(conn)
finally:
conn.close()
close_system_db()
body = _client().get(
"/marketplace?tab=curated", cookies={"access_token": sess}
).text
# Action-row anchor — primary discovery path.
assert (
'<a class="btn btn-secondary" data-actions-for="curated" '
'href="/marketplace/guide/curated">Submit a skill or plugin</a>'
) in body
# Empty-state JS innerHTML — same string, no drift.
assert "Submit a skill or plugin →" in body
# Old wording must be gone — guards against partial rename.
assert ">Submit a plugin<" not in body
def test_marketplace_guide_curated_page(fresh_db):
"""Curated guide page documents the Named Curator handoff. Three-step
flow (find → handoff → publish) lives inside `.guide-steps`. The
fast-path callout points users at the flea guide as the lighter
review-bar alternative; the primary CTA at the bottom does the same
so users who skim past the callout still see the escape hatch."""
from src.db import get_system_db, close_system_db
conn = get_system_db()
try:
_, sess = _make_user_and_session(conn)
finally:
conn.close()
close_system_db()
resp = _client().get(
"/marketplace/guide/curated", cookies={"access_token": sess}
)
assert resp.status_code == 200
body = resp.text
# Title carries the new 'skill or plugin' wording.
assert "Submit a skill or plugin to Curated Marketplace" in body
# Lede surfaces the gatekeeping concept.
assert "Named Curators" in body
# Three-step ordered list under `.guide-steps`.
assert '<ol class="guide-steps">' in body
assert "Find a Curator" in body
assert "Hand off your skill or plugin" in body
assert "Curator publishes" in body
# Fast-path callout exists and the CTA inside it points at the flea
# guide (NOT /store/new directly — we want users to read the flea
# context before they upload).
assert '<div class="guide-fastpath">' in body
assert 'href="/marketplace/guide/flea"' in body
# Primary CTA at the bottom also surfaces the flea path.
assert 'class="primary" href="/marketplace/guide/flea"' in body
def test_marketplace_guide_flea_page(fresh_db):
"""Flea guide documents the /store/new self-service flow. Four-step
body (package → upload → automated review → published) replaces the
earlier stub. Primary CTA goes directly to /store/new since users
landing on the flea guide have already chosen the self-service path."""
from src.db import get_system_db, close_system_db
conn = get_system_db()
try:
_, sess = _make_user_and_session(conn)
finally:
conn.close()
close_system_db()
resp = _client().get(
"/marketplace/guide/flea", cookies={"access_token": sess}
)
assert resp.status_code == 200
body = resp.text
assert "Upload to Flea Market" in body
# Four-step ordered list (no fast-path callout on flea — it IS the
# fast path, the curated guide is what links here).
assert '<ol class="guide-steps">' in body
assert "Package what you" in body
assert "Upload via the form" in body
assert "Automated review" in body
assert "Published" in body
# Primary CTA goes straight to /store/new (flea is one click away
# from being live, no intermediate handoff).
assert 'class="primary" href="/store/new"' in body
# No fast-path callout here — sanity check the asymmetry sticks.
assert '<div class="guide-fastpath">' not in body