diff --git a/lib/livebook_web/live/settings_live/env_var_component.ex b/lib/livebook_web/live/env_var_component.ex similarity index 67% rename from lib/livebook_web/live/settings_live/env_var_component.ex rename to lib/livebook_web/live/env_var_component.ex index cb33b134f..6356cdfa3 100644 --- a/lib/livebook_web/live/settings_live/env_var_component.ex +++ b/lib/livebook_web/live/env_var_component.ex @@ -1,4 +1,4 @@ -defmodule LivebookWeb.SettingsLive.EnvVarComponent do +defmodule LivebookWeb.EnvVarComponent do use LivebookWeb, :live_component alias Livebook.Settings @@ -21,21 +21,22 @@ defmodule LivebookWeb.SettingsLive.EnvVarComponent do @impl true def render(assigns) do + assigns = assign_new(assigns, :on_save, fn -> "save" end) + ~H"""

<%= if @operation == :new, do: "Add environment variable", else: "Edit environment variable" %>

- Configure your application global environment variables. + <%= @headline %>

<.form id={"#{@id}-form"} let={f} for={@changeset} - phx-target={@myself} - phx-submit="save" - phx-change="validate" + phx-submit={@on_save} + phx-change={JS.push("validate", target: @myself)} autocomplete="off" spellcheck="false" > @@ -51,20 +52,19 @@ defmodule LivebookWeb.SettingsLive.EnvVarComponent do <%= text_input(f, :value, class: "input", autofocus: @operation == :edit) %> + <%= text_input(f, :operation, type: "hidden", value: @operation) %> +
<%= submit("Save", class: "button-base button-blue", disabled: not @changeset.valid?, phx_disabled_with: "Adding..." ) %> - + <%= live_patch("Cancel", + to: @return_to, + type: "button", + class: "button-base button-outlined-gray" + ) %>
@@ -76,22 +76,4 @@ defmodule LivebookWeb.SettingsLive.EnvVarComponent do def handle_event("validate", %{"env_var" => attrs}, socket) do {:noreply, assign(socket, changeset: Settings.change_env_var(socket.assigns.env_var, attrs))} end - - def handle_event("cancel", _, socket) do - {:noreply, push_patch(socket, to: socket.assigns.return_to)} - end - - def handle_event("save", %{"env_var" => attrs}, socket) do - if socket.assigns.changeset.valid? do - case Settings.set_env_var(socket.assigns.env_var, attrs) do - {:ok, _} -> - {:noreply, push_patch(socket, to: socket.assigns.return_to)} - - {:error, changeset} -> - {:noreply, assign(socket, changeset: changeset)} - end - else - {:noreply, socket} - end - end end diff --git a/lib/livebook_web/live/settings_live/env_vars_component.ex b/lib/livebook_web/live/env_vars_component.ex similarity index 79% rename from lib/livebook_web/live/settings_live/env_vars_component.ex rename to lib/livebook_web/live/env_vars_component.ex index ddcbbad2a..034842f46 100644 --- a/lib/livebook_web/live/settings_live/env_vars_component.ex +++ b/lib/livebook_web/live/env_vars_component.ex @@ -1,22 +1,23 @@ -defmodule LivebookWeb.SettingsLive.EnvVarsComponent do +defmodule LivebookWeb.EnvVarsComponent do use LivebookWeb, :live_component - alias Livebook.Settings - @impl true def render(assigns) do + assigns = assign_new(assigns, :target, fn -> nil end) + ~H"""
<%= for env_var <- @env_vars do %>
- <.env_var_info socket={@socket} env_var={env_var} myself={@myself} /> + <.env_var_info socket={@socket} env_var={env_var} target={@target} />
<% end %>
<%= live_patch("Add environment variable", - to: Routes.settings_path(@socket, :add_env_var), + to: @add_env_var_path, + id: "add-env-var", class: "button-base button-blue" ) %>
@@ -45,7 +46,7 @@ defmodule LivebookWeb.SettingsLive.EnvVarsComponent do id={"env-var-#{@env_var.key}-edit"} type="button" phx-click={JS.push("edit_env_var", value: %{env_var: @env_var.key})} - phx-target={@myself} + phx-target={@target} role="menuitem" class="menu-item text-gray-600" > @@ -64,7 +65,7 @@ defmodule LivebookWeb.SettingsLive.EnvVarsComponent do confirm_icon: "delete-bin-6-line" ) } - phx-target={@myself} + phx-target={@target} role="menuitem" class="menu-item text-red-600" > @@ -77,14 +78,4 @@ defmodule LivebookWeb.SettingsLive.EnvVarsComponent do
""" end - - @impl true - def handle_event("edit_env_var", %{"env_var" => key}, socket) do - {:noreply, push_patch(socket, to: Routes.settings_path(socket, :edit_env_var, key))} - end - - def handle_event("delete_env_var", %{"env_var" => key}, socket) do - Settings.delete_env_var(key) - {:noreply, socket} - end end diff --git a/lib/livebook_web/live/hub/edit/fly_component.ex b/lib/livebook_web/live/hub/edit/fly_component.ex index b7966ec91..4d3500899 100644 --- a/lib/livebook_web/live/hub/edit/fly_component.ex +++ b/lib/livebook_web/live/hub/edit/fly_component.ex @@ -8,6 +8,12 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do def update(assigns, socket) do changeset = Fly.change_hub(assigns.hub) {:ok, app} = FlyClient.fetch_app(assigns.hub) + env_vars = env_vars_from_secrets(app["secrets"]) + + env_var = + if key = assigns.env_var_id do + Enum.find(env_vars, &(&1.key == key)) + end {:ok, socket @@ -15,17 +21,21 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do |> assign( app_url: "https://fly.io/apps/#{app["name"]}", changeset: changeset, - env_vars: app["secrets"], - env_var_data: %{}, - operation: :new, - valid_env_var?: false + env_vars: env_vars, + env_var: env_var )} end + defp env_vars_from_secrets(secrets) do + for secret <- secrets do + %Livebook.Settings.EnvVar{key: secret["name"]} + end + end + @impl true def render(assigns) do ~H""" -
"-component"}> +
@@ -89,179 +99,44 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do Environment Variables -
- <%= for env_var <- @env_vars do %> - <.environment_variable_card myself={@myself} env_var={env_var} /> - <% end %> -
- - + <.live_component + module={LivebookWeb.EnvVarsComponent} + id="env-vars" + env_vars={@env_vars} + return_to={Routes.hub_path(@socket, :edit, @hub.id)} + add_env_var_path={Routes.hub_path(@socket, :add_env_var, @hub.id)} + target={@myself} + />
- <.environment_variable_modal - id="environment-variable-modal" - on_save={hide_modal("environment-variable-modal")} - data={@env_var_data} - valid?={@valid_env_var?} - myself={@myself} - /> + <%= if @live_action in [:add_env_var, :edit_env_var] do %> + <.modal + id="env-var-modal" + show + class="w-full max-w-3xl" + target={@myself} + patch={Routes.hub_path(@socket, :edit, @hub.id)} + > + <.live_component + module={LivebookWeb.EnvVarComponent} + id="env-var" + on_save={JS.push("save_env_var", target: @myself)} + env_var={@env_var} + headline="Configute your Fly application system environment variables" + return_to={Routes.hub_path(@socket, :edit, @hub.id)} + /> + + <% end %>
""" end - defp environment_variable_card(assigns) do - ~H""" -
@env_var["id"]} - class="flex items-center justify-between border border-gray-200 rounded-lg p-4" - > -
-
- <.labeled_text label="Name"> - <%= @env_var["name"] %> - -
- -
- <.labeled_text label="Created at"> - <%= @env_var["createdAt"] %> - -
- -
- <.menu id={"env-var-#{@env_var["id"]}-menu"}> - <:toggle> - - - <:content> - - - - -
-
-
- """ - end - - defp environment_variable_modal(assigns) do - ~H""" - <.modal id={@id} class="w-full max-w-lg"> -
-

- Add environment variable -

-
-

- Enter the environment variable name and its value. -

- <.form - id="env-var-form" - let={f} - for={:env_var} - phx-submit={@on_save |> JS.push("save")} - phx-change="validate" - autocomplete="off" - phx-target={@myself} - > -
-
-
- Key (alphanumeric and underscore) -
- <%= text_input(f, :key, - value: @data["key"], - class: "input", - autofocus: true, - spellcheck: "false" - ) %> -
-
-
Value
- <%= text_input(f, :value, - value: @data["value"], - class: "input", - spellcheck: "false" - ) %> -
-
- <%= submit("Add environment variable", - class: "mt-5 button-base button-blue", - phx_disable_with: "Adding...", - disabled: not @valid? - ) %> - -
-
- - """ - end - @impl true def handle_event("randomize_color", _, socket) do handle_event("validate", %{"fly" => %{"hub_color" => HexColor.random()}}, socket) end - def handle_event("edit", %{"env_var" => %{"name" => name}}, socket) do - {:noreply, assign(socket, operation: :edit, env_var_data: %{"key" => name})} - end - - def handle_event("delete", %{"env_var" => %{"name" => key}}, socket) do - case FlyClient.delete_secrets(socket.assigns.hub, [key]) do - {:ok, _} -> - {:noreply, - socket - |> put_flash(:success, "Environment variable deleted") - |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} - - {:error, _} -> - {:noreply, - socket - |> put_flash(:error, "Failed to delete environment variable") - |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} - end - end - def handle_event("save", %{"fly" => params}, socket) do case Fly.update_hub(socket.assigns.hub, params) do {:ok, hub} -> @@ -275,47 +150,58 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do end end - def handle_event("save", %{"env_var" => params}, socket) do - if socket.assigns.valid_env_var? do - case FlyClient.put_secrets(socket.assigns.hub, [params]) do - {:ok, _} -> - message = - if socket.assigns.operation == :new do - "Environment variable added" - else - "Environment variable updated" - end + def handle_event("validate", %{"fly" => attrs}, socket) do + {:noreply, assign(socket, changeset: Fly.change_hub(socket.assigns.hub, attrs))} + end - {:noreply, - socket - |> put_flash(:success, message) - |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} + # EnvVar component callbacks - {:error, _} -> - message = - if socket.assigns.operation == :new do - "Failed to add environment variable" - else - "Failed to update environment variable" - end + def handle_event("save_env_var", %{"env_var" => attrs}, socket) do + {env_operation, attrs} = Map.pop!(attrs, "operation") - {:noreply, - socket - |> put_flash(:error, message) - |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} - end - else - {:noreply, socket} + case FlyClient.put_secrets(socket.assigns.hub, [attrs]) do + {:ok, _} -> + message = + if env_operation == "new", + do: "Environment variable added", + else: "Environment variable updated" + + {:noreply, + socket + |> put_flash(:success, message) + |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} + + {:error, _} -> + message = + if env_operation == "new", + do: "Failed to add environment variable", + else: "Failed to update environment variable" + + {:noreply, + socket + |> put_flash(:error, message) + |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} end end - def handle_event("validate", %{"fly" => attrs}, socket) do - changeset = Fly.change_hub(socket.assigns.hub, attrs) - {:noreply, assign(socket, changeset: changeset)} + def handle_event("edit_env_var", %{"env_var" => key}, socket) do + {:noreply, + push_patch(socket, to: Routes.hub_path(socket, :edit_env_var, socket.assigns.hub.id, key))} end - def handle_event("validate", %{"env_var" => attrs}, socket) do - valid? = String.match?(attrs["key"], ~r/^\w+$/) and attrs["value"] not in ["", nil] - {:noreply, assign(socket, valid_env_var?: valid?, env_var_data: attrs)} + def handle_event("delete_env_var", %{"env_var" => key}, socket) do + case FlyClient.delete_secrets(socket.assigns.hub, [key]) do + {:ok, _} -> + {:noreply, + socket + |> put_flash(:success, "Environment variable deleted") + |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} + + {:error, _} -> + {:noreply, + socket + |> put_flash(:error, "Failed to delete environment variable") + |> push_redirect(to: Routes.hub_path(socket, :edit, socket.assigns.hub.id))} + end end end diff --git a/lib/livebook_web/live/hub/edit_live.ex b/lib/livebook_web/live/hub/edit_live.ex index 0ce48dd22..54281ff0a 100644 --- a/lib/livebook_web/live/hub/edit_live.ex +++ b/lib/livebook_web/live/hub/edit_live.ex @@ -8,16 +8,23 @@ defmodule LivebookWeb.Hub.EditLive do on_mount LivebookWeb.SidebarHook @impl true - def mount(%{"id" => id}, _session, socket) do - hub = Hubs.fetch_hub!(id) + def mount(_params, _session, socket) do + {:ok, assign(socket, hub: nil, type: nil, page_title: "Livebook - Hub", env_var_id: nil)} + end + + @impl true + def handle_params(params, _url, socket) do + hub = Hubs.fetch_hub!(params["id"]) type = Provider.type(hub) if type == "local" do {:ok, socket |> redirect(to: "/") |> put_flash(:warning, "You can't edit the localhost Hub")} else - {:ok, assign(socket, hub: hub, type: type, page_title: "Livebook - Hub")} + {:ok, assign(socket, hub: hub, type: type, page_title: "Livebook - Hub", params: params)} end + + {:noreply, assign(socket, hub: hub, type: type, env_var_id: params["env_var_id"])} end @impl true @@ -35,7 +42,13 @@ defmodule LivebookWeb.Hub.EditLive do
<%= if @type == "fly" do %> - <.live_component module={LivebookWeb.Hub.Edit.FlyComponent} hub={@hub} id="fly-form" /> + <.live_component + module={LivebookWeb.Hub.Edit.FlyComponent} + hub={@hub} + id="fly-form" + live_action={@live_action} + env_var_id={@env_var_id} + /> <% end %>
diff --git a/lib/livebook_web/live/live_helpers.ex b/lib/livebook_web/live/live_helpers.ex index ba63fd10a..f6ba0288f 100644 --- a/lib/livebook_web/live/live_helpers.ex +++ b/lib/livebook_web/live/live_helpers.ex @@ -19,7 +19,6 @@ defmodule LivebookWeb.LiveHelpers do |> assign_new(:patch, fn -> nil end) |> assign_new(:navigate, fn -> nil end) |> assign_new(:class, fn -> "" end) - |> assign_new(:on_close, fn -> %JS{} end) |> assign(:attrs, assigns_to_attributes(assigns, [:id, :show, :patch, :navigate, :class])) ~H""" @@ -40,8 +39,8 @@ defmodule LivebookWeb.LiveHelpers do aria-modal="true" tabindex="0" autofocus - phx-window-keydown={hide_modal(@on_close, @id)} - phx-click-away={hide_modal(@on_close, @id)} + phx-window-keydown={hide_modal(@id)} + phx-click-away={hide_modal(@id)} phx-key="escape" > <%= if @patch do %> @@ -54,7 +53,7 @@ defmodule LivebookWeb.LiveHelpers do type="button" class="absolute top-6 right-6 text-gray-400 flex space-x-1 items-center" aria_label="close modal" - phx-click={hide_modal(@on_close, @id)} + phx-click={hide_modal(@id)} > (esc) <.remix_icon icon="close-line" class="text-2xl" /> diff --git a/lib/livebook_web/live/settings_live.ex b/lib/livebook_web/live/settings_live.ex index 04bf8c2a7..ebfc8d4e7 100644 --- a/lib/livebook_web/live/settings_live.ex +++ b/lib/livebook_web/live/settings_live.ex @@ -34,7 +34,7 @@ defmodule LivebookWeb.SettingsLive do current_user={@current_user} saved_hubs={@saved_hubs} > -
+
@@ -127,10 +127,12 @@ defmodule LivebookWeb.SettingsLive do application and is accessible only to the current machine.

<.live_component - module={LivebookWeb.SettingsLive.EnvVarsComponent} + module={LivebookWeb.EnvVarsComponent} id="env-vars" env_vars={@env_vars} return_to={Routes.settings_path(@socket, :page)} + add_env_var_path={Routes.settings_path(@socket, :add_env_var)} + target={@socket.view} />
@@ -205,9 +207,10 @@ defmodule LivebookWeb.SettingsLive do patch={Routes.settings_path(@socket, :page)} > <.live_component - module={LivebookWeb.SettingsLive.EnvVarComponent} + module={LivebookWeb.EnvVarComponent} id="env-var" env_var={@env_var} + headline="Configure your application global environment variables." return_to={Routes.settings_path(@socket, :page)} /> @@ -269,7 +272,7 @@ defmodule LivebookWeb.SettingsLive do {:noreply, assign(socket, file_system_id: file_system_id)} end - def handle_params(_params, _url, socket), do: {:noreply, socket} + def handle_params(_params, _url, socket), do: {:noreply, assign(socket, env_var: nil)} @impl true def handle_event("cancel_autosave_path", %{}, socket) do @@ -320,8 +323,25 @@ defmodule LivebookWeb.SettingsLive do {:noreply, assign(socket, :update_check_enabled, enabled)} end - def handle_event("clear_env_var", _, socket) do - {:noreply, assign(socket, env_var: nil)} + def handle_event("save", %{"env_var" => attrs}, socket) do + env_var = %Livebook.Settings.EnvVar{} + + case Livebook.Settings.set_env_var(socket.assigns.env_var || env_var, attrs) do + {:ok, _} -> + {:noreply, push_patch(socket, to: Routes.settings_path(socket, :page))} + + {:error, _changeset} -> + {:noreply, socket} + end + end + + def handle_event("edit_env_var", %{"env_var" => key}, socket) do + {:noreply, push_patch(socket, to: Routes.settings_path(socket, :edit_env_var, key))} + end + + def handle_event("delete_env_var", %{"env_var" => key}, socket) do + Livebook.Settings.delete_env_var(key) + {:noreply, socket} end @impl true @@ -338,7 +358,7 @@ defmodule LivebookWeb.SettingsLive do end def handle_info({:env_vars_changed, env_vars}, socket) do - {:noreply, assign(socket, env_vars: env_vars)} + {:noreply, assign(socket, env_vars: env_vars, env_var: nil)} end def handle_info(_message, socket), do: {:noreply, socket} diff --git a/lib/livebook_web/router.ex b/lib/livebook_web/router.ex index c63148241..44efd6b20 100644 --- a/lib/livebook_web/router.ex +++ b/lib/livebook_web/router.ex @@ -59,6 +59,8 @@ defmodule LivebookWeb.Router do live "/hub", Hub.NewLive, :new, as: :hub live "/hub/:id", Hub.EditLive, :edit, as: :hub + live "/hub/:id/env-var/new", Hub.EditLive, :add_env_var, as: :hub + live "/hub/:id/env-var/edit/:env_var_id", Hub.EditLive, :edit_env_var, as: :hub live "/sessions/:id", SessionLive, :page live "/sessions/:id/shortcuts", SessionLive, :shortcuts diff --git a/test/livebook_web/live/hub/edit_live_test.exs b/test/livebook_web/live/hub/edit_live_test.exs index 54fcb217c..001ef3c16 100644 --- a/test/livebook_web/live/hub/edit_live_test.exs +++ b/test/livebook_web/live/hub/edit_live_test.exs @@ -17,7 +17,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do end describe "fly" do - test "updates fly", %{conn: conn, bypass: bypass} do + test "updates hub", %{conn: conn, bypass: bypass} do {:ok, pid} = Agent.start(fn -> %{fun: &fetch_app_response/2, type: :mount} end) app_id = Livebook.Utils.random_short_id() @@ -70,7 +70,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do refute Hubs.fetch_hub!(hub.id) == hub end - test "add secret", %{conn: conn, bypass: bypass} do + test "add env var", %{conn: conn, bypass: bypass} do {:ok, pid} = Agent.start(fn -> %{fun: &fetch_app_response/2, type: :mount} end) app_id = Livebook.Utils.random_short_id() @@ -87,6 +87,13 @@ defmodule LivebookWeb.Hub.EditLiveTest do assert html =~ "LIVEBOOK_PASSWORD" assert html =~ "LIVEBOOK_SECRET_KEY_BASE" + view + |> element("#add-env-var") + |> render_click(%{}) + + assert_patch(view, Routes.hub_path(conn, :add_env_var, hub.id)) + assert render(view) =~ "Add environment variable" + view |> element("#env-var-form") |> render_change(%{"env_var" => %{"key" => "FOO_ENV_VAR", "value" => "12345"}}) @@ -110,19 +117,9 @@ defmodule LivebookWeb.Hub.EditLiveTest do assert html =~ "LIVEBOOK_SECRET_KEY_BASE" end - test "update secret", %{conn: conn, bypass: bypass} do + test "update env var", %{conn: conn, bypass: bypass} do {:ok, pid} = Agent.start(fn -> %{fun: &fetch_app_response/2, type: :foo} end) - old_env_var = - :foo - |> secrets() - |> Enum.find(&(&1["name"] == "FOO_ENV_VAR")) - - new_env_var = - :updated_foo - |> secrets() - |> Enum.find(&(&1["name"] == "FOO_ENV_VAR")) - app_id = Livebook.Utils.random_short_id() hub = insert_hub(:fly, id: "fly-#{app_id}", application_id: app_id) fly_bypass(bypass, app_id, pid) @@ -134,11 +131,13 @@ defmodule LivebookWeb.Hub.EditLiveTest do assert html =~ "Environment Variables" assert html =~ "FOO_ENV_VAR" - assert html =~ old_env_var["createdAt"] view - |> element("#env-var-#{old_env_var["id"]}-edit") - |> render_click(%{"env_var" => old_env_var}) + |> element("#env-var-FOO_ENV_VAR-edit") + |> render_click(%{"env_var" => "FOO_ENV_VAR"}) + + assert_patch(view, Routes.hub_path(conn, :edit_env_var, hub.id, "FOO_ENV_VAR")) + assert render(view) =~ "Edit environment variable" view |> element("#env-var-form") @@ -159,18 +158,11 @@ defmodule LivebookWeb.Hub.EditLiveTest do assert html =~ "Environment variable updated" assert html =~ "Environment Variables" assert html =~ "FOO_ENV_VAR" - refute html =~ old_env_var["createdAt"] - assert html =~ new_env_var["createdAt"] end - test "delete secret", %{conn: conn, bypass: bypass} do + test "delete env var", %{conn: conn, bypass: bypass} do {:ok, pid} = Agent.start(fn -> %{fun: &fetch_app_response/2, type: :add} end) - env_var = - :add - |> secrets() - |> Enum.find(&(&1["name"] == "FOO_ENV_VAR")) - app_id = Livebook.Utils.random_short_id() hub = insert_hub(:fly, id: "fly-#{app_id}", application_id: app_id) fly_bypass(bypass, app_id, pid) @@ -190,7 +182,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do assert {:ok, _view, html} = view |> with_target("#fly-form-component") - |> render_click("delete", %{"env_var" => env_var}) + |> render_click("delete_env_var", %{"env_var" => "FOO_ENV_VAR"}) |> follow_redirect(conn) assert html =~ "Environment variable deleted" diff --git a/test/livebook_web/live/settings_live_test.exs b/test/livebook_web/live/settings_live_test.exs index 8f90fa247..9c6f9bc19 100644 --- a/test/livebook_web/live/settings_live_test.exs +++ b/test/livebook_web/live/settings_live_test.exs @@ -46,9 +46,7 @@ defmodule LivebookWeb.SettingsLiveTest do assert html =~ env_var.key - view - |> with_target("#env-vars") - |> render_click("edit_env_var", %{"env_var" => env_var.key}) + render_click(view, "edit_env_var", %{"env_var" => env_var.key}) assert_patch(view, Routes.settings_path(conn, :edit_env_var, env_var.key)) assert render(view) =~ "Edit environment variable" @@ -77,9 +75,7 @@ defmodule LivebookWeb.SettingsLiveTest do assert html =~ env_var.key - view - |> with_target("#env-vars") - |> render_click("delete_env_var", %{"env_var" => env_var.key}) + render_click(view, "delete_env_var", %{"env_var" => env_var.key}) refute render(view) =~ env_var.key end