4.4 KiB · text History dc2edab
defmodule DiogramosWeb.CanvasDeleteTest do
  use DiogramosWeb.ConnCase, async: true

  import Phoenix.LiveViewTest
  import Diogramos.AccountsFixtures
  import Diogramos.DiagramsFixtures

  alias Diogramos.Accounts.Scope
  alias Diogramos.Diagrams

  setup %{conn: conn} do
    owner = user_fixture()
    scope = Scope.for_user(owner)
    canvas = canvas_fixture(scope, %{title: "Doomed"})

    %{conn: log_in_user(conn, owner), owner: owner, scope: scope, canvas: canvas}
  end

  defp open_manage(lv, canvas) do
    lv |> element("button[phx-value-id='#{canvas.id}']") |> render_click()
  end

  describe "the danger zone" do
    test "asks for the slug before it will delete", %{conn: conn, canvas: canvas} do
      {:ok, lv, _html} = live(conn, ~p"/canvases")

      html = open_manage(lv, canvas)

      assert html =~ "Danger zone"
      assert html =~ "Type"
      assert html =~ canvas.slug
      # The button stays disabled until the slug is typed, so a stray
      # click can't delete anything.
      assert html =~ "disabled"
    end

    test "deletes when the slug matches", %{conn: conn, scope: scope, canvas: canvas} do
      {:ok, lv, _html} = live(conn, ~p"/canvases")
      open_manage(lv, canvas)

      html =
        lv
        |> form("#manage-delete-form", %{"confirm" => canvas.slug})
        |> render_submit()

      assert html =~ "Deleted Doomed."
      refute html =~ canvas.slug
      refute Diagrams.get_canvas_by_slug(scope, canvas.slug)
    end

    test "refuses anything that isn't the slug, and keeps the canvas", %{
      conn: conn,
      scope: scope,
      canvas: canvas
    } do
      {:ok, lv, _html} = live(conn, ~p"/canvases")
      open_manage(lv, canvas)

      for typed <- ["", "  ", "Doomed", canvas.slug <> "x", String.upcase(canvas.slug)] do
        html =
          lv
          |> form("#manage-delete-form", %{"confirm" => typed})
          |> render_submit()

        assert html =~ "Type #{canvas.slug} exactly to confirm"
        assert Diagrams.get_canvas_by_slug(scope, canvas.slug)
      end
    end

    test "surrounding whitespace is forgiven", %{conn: conn, scope: scope, canvas: canvas} do
      {:ok, lv, _html} = live(conn, ~p"/canvases")
      open_manage(lv, canvas)

      # Pasting a slug tends to bring a space with it, and the typed
      # confirmation is there to prove intent, not typing accuracy.
      lv
      |> form("#manage-delete-form", %{"confirm" => "  #{canvas.slug} "})
      |> render_submit()

      refute Diagrams.get_canvas_by_slug(scope, canvas.slug)
    end

    test "a delete event with no canvas open does nothing", %{
      conn: conn,
      scope: scope,
      canvas: canvas
    } do
      {:ok, lv, _html} = live(conn, ~p"/canvases")

      # The modal was never opened, so there is nothing to confirm
      # against -- and the slug alone must not be enough.
      render_click(lv, "delete_canvas", %{"confirm" => canvas.slug})

      assert Diagrams.get_canvas_by_slug(scope, canvas.slug)
    end
  end

  describe "access" do
    test "an editor sees the canvas but is offered no way to manage it", %{
      conn: conn,
      canvas: canvas
    } do
      editor = user_fixture()
      {:ok, _} = Diagrams.grant_permission("canvas", canvas.id, "user", editor.id, "editor")

      {:ok, lv, html} = live(log_in_user(conn, editor), ~p"/canvases")

      assert html =~ canvas.title
      refute has_element?(lv, "button[phx-value-id='#{canvas.id}']")
    end

    test "an editor sending the event by hand is refused", %{conn: conn, canvas: canvas} do
      editor = user_fixture()
      {:ok, _} = Diagrams.grant_permission("canvas", canvas.id, "user", editor.id, "editor")

      {:ok, lv, _html} = live(log_in_user(conn, editor), ~p"/canvases")

      # The button isn't rendered for them, so this is the interesting
      # path: the handler has to refuse it on its own.
      html = render_click(lv, "open_manage_modal", %{"id" => to_string(canvas.id)})

      assert html =~ "Only the owner can manage collaborators."
      refute html =~ "Danger zone"
    end

    test "an editor's delete event is refused by the context too", %{canvas: canvas} do
      editor = user_fixture()
      {:ok, _} = Diagrams.grant_permission("canvas", canvas.id, "user", editor.id, "editor")

      # Defence in depth: the modal is owner-only, and so is the delete.
      assert {:error, :forbidden} =
               Diagrams.delete_canvas(Scope.for_user(editor), canvas)
    end
  end
end