test/openagents_web/home_controller_test.exs

58e6347eeb72 · 3 KB

defmodule OpenAgentsWeb.HomeControllerTest do
  use OpenAgentsWeb.ConnCase, async: false

  alias OpenAgents.{Repo, Repositories}

  test "the public homepage is the OpenAgents hero", %{conn: conn} do
    response = get(conn, ~p"/")
    html = html_response(response, 200)

    # OpenAgents deliberately ships its own landing page, not Sarah's. The
    # band above the fold is the Coder pitch; these assertions match the
    # current product identity.
    assert html =~ "Introducing"
    assert html =~ "Your all-in-one coding agent."
    assert html =~ ~s(action="/auth/github")
    assert html =~ "Log in with GitHub"

    # The scope disclosure was removed from the hero at the owner's direction.
    # GitHub's own consent screen still states the scope before the grant is
    # made, so this asserts the paragraph is gone rather than that it is there.
    refute html =~ ~s(id="github-tools-disclosure")
    refute html =~ "retain an encrypted GitHub grant"

    refute html =~ "One continuing conversation"
    refute html =~ ~s(href="/chat")
    refute html =~ "Account menu"
    refute html =~ ~s(href="/changelog")
  end

  test "an authenticated account sees the dashboard, not the pitch", %{conn: conn} do
    conn = log_in_github_user(conn, "authenticated-home-user")
    html = html_response(get(conn, ~p"/"), 200)

    # Someone who has signed in has already been sold. The same route shows the
    # state of the work instead: open issues and projects.
    refute html =~ "Your all-in-one coding agent."
    assert html =~ "Open issues"
    refute html =~ "Latest from the changelog"
    refute html =~ ~s(href="/changelog")
    assert html =~ ~s(class="dashboard")
  end

  test "an authenticated account sees the dashboard before the first import", %{conn: conn} do
    conn = log_in_github_user(conn, "authenticated-empty-home-user")
    Repositories.get_by_path!("OpenAgentsInc", "openagents.com") |> Repo.delete!()

    html = html_response(get(conn, ~p"/"), 200)

    assert html =~ ~s(class="dashboard")
    assert html =~ "No repositories yet."
  end

  test "the browser policy permits only the narrow GitHub avatar origin", %{conn: conn} do
    response = get(conn, ~p"/")

    [policy] = get_resp_header(response, "content-security-policy")
    assert policy =~ "img-src 'self' data: https://avatars.githubusercontent.com"
    refute policy =~ ~r/img-src[^;]*\shttps:(?:\s|;)/
  end

  test "the browser policy admits only the response-scoped theme bootstrap", %{conn: conn} do
    first = get(conn, ~p"/")
    second = conn |> recycle() |> get(~p"/")

    [first_policy] = get_resp_header(first, "content-security-policy")
    [second_policy] = get_resp_header(second, "content-security-policy")

    [first_nonce] =
      Regex.run(~r/script-src 'self' 'nonce-([A-Za-z0-9_-]{24})'/, first_policy,
        capture: :all_but_first
      )

    [second_nonce] =
      Regex.run(~r/script-src 'self' 'nonce-([A-Za-z0-9_-]{24})'/, second_policy,
        capture: :all_but_first
      )

    refute first_nonce == second_nonce
    refute first_policy =~ ~r/script-src[^;]*'unsafe-inline'/

    assert first_policy =~
             "script-src 'self' 'nonce-#{first_nonce}' https://us-assets.i.posthog.com"

    document = first |> html_response(200) |> LazyHTML.from_document()

    assert document
           |> LazyHTML.query(~s{script[nonce="#{first_nonce}"]})
           |> LazyHTML.to_tree()
           |> length() == 1
  end

  test "the landing styles do not use Sarah's cycling verb" do
    css = File.read!("assets/css/app.css")

    # OpenAgents does not ship Sarah's animated landing verb. This assertion
    # only guards against that leftover coming back.
    refute css =~ "landing-verb"
  end
end