Move InstallationSupport code to `/extra` (EE only) folder (#5758)

* move lib/plausible/installation_support/ -> /extra/lib/...

* extract Live.AwaitingPageviews exclusively for CE

* VerificationTest to ee only

* fix the rest of the compile/test errors on CE

* fix warning about not using default for optional argument

* move module attr
This commit is contained in:
RobertJoonas 2025-09-29 15:01:56 +01:00 committed by GitHub
parent 053fe71cc8
commit 9ed79a831e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 817 additions and 709 deletions

View File

@ -4,7 +4,6 @@ defmodule PlausibleWeb.Live.Verification do
Onboarding new sites, renders a standalone component.
Embedded modal variant is available for general site settings.
"""
use Plausible
use PlausibleWeb, :live_view
import PlausibleWeb.Components.Generic
@ -61,66 +60,29 @@ defmodule PlausibleWeb.Live.Verification do
custom_url_input?: custom_url_input?
)
on_ee do
if connected?(socket) and not custom_url_input? do
launch_delayed(socket)
end
if connected?(socket) and not custom_url_input? do
launch_delayed(socket)
end
on_ee do
{:ok, socket}
else
# on CE we skip the verification process and instead,
# we just wait for the first pageview to be recorded
socket =
if has_pageviews? do
redirect_to_stats(socket)
else
schedule_pageviews_check(socket)
end
{:ok, socket}
end
{:ok, socket}
end
on_ee do
def render(assigns) do
~H"""
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Verify installation" />
<.custom_url_form :if={@custom_url_input?} domain={@domain} />
<.live_component
:if={not @custom_url_input?}
module={@component}
installation_type={@installation_type}
domain={@domain}
id="verification-standalone"
attempts={@attempts}
flow={@flow}
awaiting_first_pageview?={not @has_pageviews?}
super_admin?={@super_admin?}
/>
"""
end
else
def render(assigns) do
~H"""
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Verify installation" />
<.awaiting_pageviews />
"""
end
end
on_ce do
defp awaiting_pageviews(assigns) do
~H"""
<.focus_box>
<div class="flex items-center">
<div class="block pulsating-circle"></div>
<p class="ml-8">Awaiting your first pageview </p>
</div>
</.focus_box>
"""
end
def render(assigns) do
~H"""
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Verify installation" />
<.custom_url_form :if={@custom_url_input?} domain={@domain} />
<.live_component
:if={not @custom_url_input?}
module={@component}
installation_type={@installation_type}
domain={@domain}
id="verification-standalone"
attempts={@attempts}
flow={@flow}
awaiting_first_pageview?={not @has_pageviews?}
super_admin?={@super_admin?}
/>
"""
end
def handle_event("launch-verification", _, socket) do
@ -300,48 +262,46 @@ defmodule PlausibleWeb.Live.Verification do
Plausible.Stats.Clickhouse.has_pageviews?(site)
end
on_ee do
defp custom_url_form(assigns) do
~H"""
<.focus_box>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900">
<Heroicons.globe_alt class="h-6 w-6 text-blue-600 dark:text-blue-200" />
</div>
<div class="mt-8">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">
Enter Your Custom URL
</h3>
<p class="text-sm mt-4 text-gray-600 dark:text-gray-400">
Please enter the URL where your website with the Plausible script is located.
</p>
<form phx-submit="verify-custom-url" class="mt-6">
<div class="mb-4">
<label
for="custom_url"
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Website URL
</label>
<input
type="url"
name="custom_url"
id="custom_url"
required
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-800 dark:text-white"
placeholder={"https://#{@domain}"}
value={"https://#{@domain}"}
/>
</div>
<button
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-600"
defp custom_url_form(assigns) do
~H"""
<.focus_box>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900">
<Heroicons.globe_alt class="h-6 w-6 text-blue-600 dark:text-blue-200" />
</div>
<div class="mt-8">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">
Enter Your Custom URL
</h3>
<p class="text-sm mt-4 text-gray-600 dark:text-gray-400">
Please enter the URL where your website with the Plausible script is located.
</p>
<form phx-submit="verify-custom-url" class="mt-6">
<div class="mb-4">
<label
for="custom_url"
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Verify Installation
</button>
</form>
</div>
</.focus_box>
"""
end
Website URL
</label>
<input
type="url"
name="custom_url"
id="custom_url"
required
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-800 dark:text-white"
placeholder={"https://#{@domain}"}
value={"https://#{@domain}"}
/>
</div>
<button
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-600"
>
Verify Installation
</button>
</form>
</div>
</.focus_box>
"""
end
end

View File

@ -21,8 +21,6 @@ defmodule Plausible.Ingestion.Event do
salts: nil,
changeset: nil
@verification_user_agent Plausible.InstallationSupport.user_agent()
@type drop_reason() ::
:bot
| :spam_referrer
@ -193,14 +191,20 @@ defmodule Plausible.Ingestion.Event do
struct!(event, clickhouse_session_attrs: Map.merge(event.clickhouse_session_attrs, attrs))
end
defp drop_verification_agent(%__MODULE__{} = event, _context) do
case event.request.user_agent do
@verification_user_agent ->
drop(event, :verification_agent)
on_ee do
@verification_user_agent Plausible.InstallationSupport.user_agent()
_ ->
event
defp drop_verification_agent(%__MODULE__{} = event, _context) do
case event.request.user_agent do
@verification_user_agent ->
drop(event, :verification_agent)
_ ->
event
end
end
else
defp drop_verification_agent(%__MODULE__{} = event, _context), do: event
end
defp drop_datacenter_ip(%__MODULE__{} = event, _context) do

View File

@ -2,6 +2,7 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
@moduledoc """
Custom PromEx plugin for instrumenting code within Plausible app.
"""
use Plausible
use PromEx.Plugin
alias Plausible.Site
alias Plausible.Ingestion
@ -140,17 +141,26 @@ defmodule Plausible.PromEx.Plugins.PlausibleMetrics do
tags: [:status],
tag_values: &%{status: &1.status}
),
counter(
metric_prefix ++ [:verification, :js_elixir_diff],
event_name:
Plausible.InstallationSupport.Checks.Installation.telemetry_event(_diff = true)
on_ee(
do:
counter(
metric_prefix ++ [:verification, :js_elixir_diff],
event_name:
Plausible.InstallationSupport.Checks.Installation.telemetry_event(_diff = true)
),
else: nil
),
counter(
metric_prefix ++ [:verification, :js_elixir_match],
event_name:
Plausible.InstallationSupport.Checks.Installation.telemetry_event(_diff = false)
on_ee(
do:
counter(
metric_prefix ++ [:verification, :js_elixir_match],
event_name:
Plausible.InstallationSupport.Checks.Installation.telemetry_event(_diff = false)
),
else: nil
)
]
|> Enum.reject(&is_nil/1)
)
end

View File

@ -0,0 +1,99 @@
defmodule PlausibleWeb.Live.AwaitingPageviews do
@moduledoc """
A replacement for installation verification on Community Edition.
"""
use PlausibleWeb, :live_view
import PlausibleWeb.Components.Generic
def mount(
%{"domain" => domain} = params,
_session,
socket
) do
current_user = socket.assigns.current_user
site =
Plausible.Sites.get_for_user!(current_user, domain,
roles: [
:owner,
:admin,
:editor,
:super_admin,
:viewer
]
)
private = Map.get(socket.private.connect_info, :private, %{})
has_pageviews? = has_pageviews?(site)
socket =
assign(socket,
site: site,
domain: domain,
has_pageviews?: has_pageviews?,
delay: private[:delay] || 500,
flow: params["flow"] || "",
polling_pageviews?: false
)
socket =
if has_pageviews? do
redirect_to_stats(socket)
else
schedule_pageviews_check(socket)
end
{:ok, socket}
end
def render(assigns) do
~H"""
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Verify installation" />
<.awaiting_pageviews />
"""
end
defp awaiting_pageviews(assigns) do
~H"""
<.focus_box>
<div class="flex items-center">
<div class="block pulsating-circle"></div>
<p class="ml-8">Awaiting your first pageview </p>
</div>
</.focus_box>
"""
end
def handle_info(:check_pageviews, socket) do
socket =
if has_pageviews?(socket.assigns.site) do
redirect_to_stats(socket)
else
socket
|> assign(polling_pageviews?: false)
|> schedule_pageviews_check()
end
{:noreply, socket}
end
defp schedule_pageviews_check(socket) do
if socket.assigns.polling_pageviews? do
socket
else
Process.send_after(self(), :check_pageviews, socket.assigns.delay * 2)
assign(socket, polling_pageviews?: true)
end
end
defp redirect_to_stats(socket) do
stats_url = Routes.stats_path(PlausibleWeb.Endpoint, :stats, socket.assigns.domain, [])
redirect(socket, to: stats_url)
end
defp has_pageviews?(site) do
Plausible.Stats.Clickhouse.has_pageviews?(site)
end
end

View File

@ -3,7 +3,10 @@ defmodule PlausibleWeb.Live.Installation do
User assistance module around Plausible installation instructions/onboarding
"""
use PlausibleWeb, :live_view
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
on_ee do
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
end
@script_extension_params %{
"outbound_links" => "outbound-links",
@ -68,16 +71,18 @@ defmodule PlausibleWeb.Live.Installation do
{key, Map.get(tracker_script_configuration, string_key)}
end)
if connected?(socket) and is_nil(installation_type) do
LegacyVerification.Checks.run("https://#{domain}", domain,
checks: [
Checks.FetchBody,
Checks.ScanBody
],
report_to: self(),
async?: true,
slowdown: 0
)
on_ee do
if connected?(socket) and is_nil(installation_type) do
LegacyVerification.Checks.run("https://#{domain}", domain,
checks: [
Checks.FetchBody,
Checks.ScanBody
],
report_to: self(),
async?: true,
slowdown: 0
)
end
end
{:ok,
@ -95,23 +100,25 @@ defmodule PlausibleWeb.Live.Installation do
end
end
def handle_info({:all_checks_done, %State{} = state}, socket) do
installation_type =
case state.diagnostics do
%{wordpress_likely?: true} -> "wordpress"
%{gtm_likely?: true} -> "gtm"
_ -> "manual"
end
on_ee do
def handle_info({:all_checks_done, %State{} = state}, socket) do
installation_type =
case state.diagnostics do
%{wordpress_likely?: true} -> "wordpress"
%{gtm_likely?: true} -> "gtm"
_ -> "manual"
end
{:noreply,
assign(socket,
initial_installation_type: installation_type,
installation_type: installation_type
)}
end
{:noreply,
assign(socket,
initial_installation_type: installation_type,
installation_type: installation_type
)}
end
def handle_info(_msg, socket) do
{:noreply, socket}
def handle_info(_msg, socket) do
{:noreply, socket}
end
end
def render(assigns) do

View File

@ -629,7 +629,10 @@ defmodule PlausibleWeb.Router do
scope assigns: %{
dogfood_page_path: "/:website/verification"
} do
live "/:domain/verification", Verification, :verification, as: :site
live "/:domain/verification",
on_ee(do: Verification, else: AwaitingPageviews),
:verification,
as: :site
end
scope assigns: %{

View File

@ -80,22 +80,24 @@ defmodule Plausible.Ingestion.EventTest do
assert_receive :telemetry_handled
end
test "drops installation support user agent" do
site = new_site()
on_ee do
test "drops installation support user agent" do
site = new_site()
payload = %{
name: "pageview",
url: "http://#{site.domain}"
}
payload = %{
name: "pageview",
url: "http://#{site.domain}"
}
conn =
build_conn(:post, "/api/events", payload)
|> Plug.Conn.put_req_header("user-agent", Plausible.InstallationSupport.user_agent())
conn =
build_conn(:post, "/api/events", payload)
|> Plug.Conn.put_req_header("user-agent", Plausible.InstallationSupport.user_agent())
assert {:ok, request} = Request.build(conn)
assert {:ok, request} = Request.build(conn)
assert {:ok, %{buffered: [], dropped: [dropped]}} = Event.build_and_buffer(request)
assert dropped.drop_reason == :verification_agent
assert {:ok, %{buffered: [], dropped: [dropped]}} = Event.build_and_buffer(request)
assert dropped.drop_reason == :verification_agent
end
end
test "drops a request when site does not exists" do

View File

@ -1,40 +1,42 @@
defmodule Plausible.InstallationSupport.Checks.CSPTest do
use Plausible.DataCase, async: true
alias Plausible.InstallationSupport.{State, LegacyVerification}
on_ee do
alias Plausible.InstallationSupport.{State, LegacyVerification}
@check Plausible.InstallationSupport.Checks.CSP
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
@check Plausible.InstallationSupport.Checks.CSP
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
test "skips no headers" do
state = @default_state
assert ^state = @check.perform(state)
end
test "skips no headers" do
state = @default_state
assert ^state = @check.perform(state)
end
test "skips no headers 2" do
state = @default_state |> State.assign(headers: %{})
assert ^state = @check.perform(state)
end
test "skips no headers 2" do
state = @default_state |> State.assign(headers: %{})
assert ^state = @check.perform(state)
end
test "disallowed" do
headers = %{"content-security-policy" => ["default-src 'self' foo.local; example.com"]}
test "disallowed" do
headers = %{"content-security-policy" => ["default-src 'self' foo.local; example.com"]}
state =
@default_state
|> State.assign(headers: headers)
|> @check.perform()
state =
@default_state
|> State.assign(headers: headers)
|> @check.perform()
assert state.diagnostics.disallowed_via_csp?
end
assert state.diagnostics.disallowed_via_csp?
end
test "allowed" do
headers = %{"content-security-policy" => ["default-src 'self' example.com; localhost"]}
test "allowed" do
headers = %{"content-security-policy" => ["default-src 'self' example.com; localhost"]}
state =
@default_state
|> State.assign(headers: headers)
|> @check.perform()
state =
@default_state
|> State.assign(headers: headers)
|> @check.perform()
refute state.diagnostics.disallowed_via_csp?
refute state.diagnostics.disallowed_via_csp?
end
end
end

View File

@ -1,64 +1,66 @@
defmodule Plausible.InstallationSupport.Checks.FetchBodyTest do
use Plausible.DataCase, async: true
import Plug.Conn
on_ee do
import Plug.Conn
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
@check Checks.FetchBody
@check Checks.FetchBody
@normal_body """
<html>
<head>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</head>
<body>Hello</body>
</html>
"""
@normal_body """
<html>
<head>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</head>
<body>Hello</body>
</html>
"""
setup do
{:ok,
state: %State{
url: "https://example.com",
diagnostics: %LegacyVerification.Diagnostics{}
}}
end
setup do
{:ok,
state: %State{
url: "https://example.com",
diagnostics: %LegacyVerification.Diagnostics{}
}}
end
test "extracts document", %{state: state} do
stub()
state = @check.perform(state)
test "extracts document", %{state: state} do
stub()
state = @check.perform(state)
assert state.assigns.raw_body == @normal_body
assert state.assigns.document == Floki.parse_document!(@normal_body)
assert state.assigns.headers["content-type"] == ["text/html; charset=utf-8"]
assert state.assigns.raw_body == @normal_body
assert state.assigns.document == Floki.parse_document!(@normal_body)
assert state.assigns.headers["content-type"] == ["text/html; charset=utf-8"]
assert state.diagnostics.body_fetched?
end
assert state.diagnostics.body_fetched?
end
test "does extract on non-2xx", %{state: state} do
stub(400)
state = @check.perform(state)
assert state.diagnostics.body_fetched?
end
test "does extract on non-2xx", %{state: state} do
stub(400)
state = @check.perform(state)
assert state.diagnostics.body_fetched?
end
test "doesn't extract non-HTML", %{state: state} do
stub(200, @normal_body, "text/plain")
state = @check.perform(state)
test "doesn't extract non-HTML", %{state: state} do
stub(200, @normal_body, "text/plain")
state = @check.perform(state)
assert state.assigns == %{final_domain: "example.com"}
assert state.assigns == %{final_domain: "example.com"}
refute state.diagnostics.body_fetched?
end
refute state.diagnostics.body_fetched?
end
defp stub(f) when is_function(f, 1) do
Req.Test.stub(@check, f)
end
defp stub(f) when is_function(f, 1) do
Req.Test.stub(@check, f)
end
defp stub(status \\ 200, body \\ @normal_body, content_type \\ "text/html") do
stub(fn conn ->
conn
|> put_resp_content_type(content_type)
|> send_resp(status, body)
end)
defp stub(status \\ 200, body \\ @normal_body, content_type \\ "text/html") do
stub(fn conn ->
conn
|> put_resp_content_type(content_type)
|> send_resp(status, body)
end)
end
end
end

View File

@ -1,82 +1,84 @@
defmodule Plausible.InstallationSupport.Checks.ScanBodyTest do
use Plausible.DataCase, async: true
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
on_ee do
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
@check Checks.ScanBody
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
@check Checks.ScanBody
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
test "skips on no raw body" do
assert @default_state = @check.perform(@default_state)
end
test "skips on no raw body" do
assert @default_state = @check.perform(@default_state)
end
test "detects nothing" do
state =
@default_state
|> State.assign(raw_body: "...")
|> @check.perform()
refute state.diagnostics.gtm_likely?
refute state.diagnostics.wordpress_likely?
end
test "detects GTM" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js...")
|> @check.perform()
assert state.diagnostics.gtm_likely?
refute state.diagnostics.wordpress_likely?
refute state.diagnostics.cookie_banner_likely?
end
test "detects GTM and cookie banner" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js...cookiebot...")
|> @check.perform()
assert state.diagnostics.gtm_likely?
assert state.diagnostics.cookie_banner_likely?
refute state.diagnostics.wordpress_likely?
end
for signature <- ["wp-content", "wp-includes", "wp-json"] do
test "detects WordPress: #{signature}" do
test "detects nothing" do
state =
@default_state
|> State.assign(raw_body: "...#{unquote(signature)}...")
|> State.assign(raw_body: "...")
|> @check.perform()
refute state.diagnostics.gtm_likely?
refute state.diagnostics.wordpress_likely?
end
test "detects GTM" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js...")
|> @check.perform()
assert state.diagnostics.gtm_likely?
refute state.diagnostics.wordpress_likely?
refute state.diagnostics.cookie_banner_likely?
end
test "detects GTM and cookie banner" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js...cookiebot...")
|> @check.perform()
assert state.diagnostics.gtm_likely?
assert state.diagnostics.cookie_banner_likely?
refute state.diagnostics.wordpress_likely?
end
for signature <- ["wp-content", "wp-includes", "wp-json"] do
test "detects WordPress: #{signature}" do
state =
@default_state
|> State.assign(raw_body: "...#{unquote(signature)}...")
|> @check.perform()
refute state.diagnostics.gtm_likely?
assert state.diagnostics.wordpress_likely?
refute state.diagnostics.wordpress_plugin?
end
end
test "detects GTM and WordPress" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js....wp-content...")
|> @check.perform()
assert state.diagnostics.gtm_likely?
assert state.diagnostics.wordpress_likely?
refute state.diagnostics.wordpress_plugin?
end
end
test "detects GTM and WordPress" do
state =
@default_state
|> State.assign(raw_body: "...googletagmanager.com/gtm.js....wp-content...")
|> @check.perform()
@d """
<meta name='plausible-analytics-version' content='2.0.9' />
"""
assert state.diagnostics.gtm_likely?
assert state.diagnostics.wordpress_likely?
refute state.diagnostics.wordpress_plugin?
end
test "detects official plugin" do
state =
@default_state
|> State.assign(raw_body: @d, document: Floki.parse_document!(@d))
|> @check.perform()
@d """
<meta name='plausible-analytics-version' content='2.0.9' />
"""
test "detects official plugin" do
state =
@default_state
|> State.assign(raw_body: @d, document: Floki.parse_document!(@d))
|> @check.perform()
assert state.diagnostics.wordpress_likely?
assert state.diagnostics.wordpress_plugin?
assert state.diagnostics.wordpress_likely?
assert state.diagnostics.wordpress_plugin?
end
end
end

View File

@ -1,164 +1,166 @@
defmodule Plausible.InstallationSupport.Checks.SnippetTest do
use Plausible.DataCase, async: true
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
on_ee do
alias Plausible.InstallationSupport.{State, Checks, LegacyVerification}
@check Checks.Snippet
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
@check Checks.Snippet
@default_state %State{diagnostics: %LegacyVerification.Diagnostics{}}
test "skips when there's no document" do
state = @default_state
assert ^state = @check.perform(state)
end
test "skips when there's no document" do
state = @default_state
assert ^state = @check.perform(state)
end
@well_placed """
<head>
<script defer data-domain="example.com" event-author="Me" src="http://localhost:8000/js/script.js"></script>
</head>
"""
@well_placed """
<head>
<script defer data-domain="example.com" event-author="Me" src="http://localhost:8000/js/script.js"></script>
</head>
"""
test "figures out well placed snippet" do
state =
@well_placed
|> new_state()
|> @check.perform()
test "figures out well placed snippet" do
state =
@well_placed
|> new_state()
|> @check.perform()
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
@multi_domain """
<head>
<script defer data-domain="example.org,example.com,example.net" src="http://localhost:8000/js/script.js"></script>
</head>
"""
@multi_domain """
<head>
<script defer data-domain="example.org,example.com,example.net" src="http://localhost:8000/js/script.js"></script>
</head>
"""
test "figures out well placed snippet in a multi-domain setup" do
state =
@multi_domain
|> new_state()
|> @check.perform()
test "figures out well placed snippet in a multi-domain setup" do
state =
@multi_domain
|> new_state()
|> @check.perform()
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
@crazy """
<head>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</head>
<body>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</body>
"""
test "counts snippets" do
state =
@crazy
|> new_state()
|> @check.perform()
@crazy """
<head>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</head>
<body>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
<script defer data-domain="example.com" src="http://localhost:8000/js/script.js"></script>
</body>
"""
test "counts snippets" do
state =
@crazy
|> new_state()
|> @check.perform()
assert state.diagnostics.snippets_found_in_head == 2
assert state.diagnostics.snippets_found_in_body == 3
refute state.diagnostics.manual_script_extension?
end
assert state.diagnostics.snippets_found_in_head == 2
assert state.diagnostics.snippets_found_in_body == 3
refute state.diagnostics.manual_script_extension?
end
test "figures out data-domain mismatch" do
state =
@well_placed
|> new_state(data_domain: "example.typo")
|> @check.perform()
test "figures out data-domain mismatch" do
state =
@well_placed
|> new_state(data_domain: "example.typo")
|> @check.perform()
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
assert state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
assert state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
@proxy_likely """
<head>
<script defer data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
@proxy_likely """
<head>
<script defer data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
test "figures out proxy likely" do
state =
@proxy_likely
|> new_state()
|> @check.perform()
test "figures out proxy likely" do
state =
@proxy_likely
|> new_state()
|> @check.perform()
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
assert state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
assert state.diagnostics.snippets_found_in_head == 1
assert state.diagnostics.snippets_found_in_body == 0
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
assert state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end
@manual_extension """
<head>
<script defer data-domain="example.com" event-author="Me" src="http://localhost:8000/js/script.manual.js"></script>
</head>
"""
@manual_extension """
<head>
<script defer data-domain="example.com" event-author="Me" src="http://localhost:8000/js/script.manual.js"></script>
</head>
"""
test "figures out manual script extension" do
state =
@manual_extension
|> new_state()
|> @check.perform()
test "figures out manual script extension" do
state =
@manual_extension
|> new_state()
|> @check.perform()
assert state.diagnostics.manual_script_extension?
end
assert state.diagnostics.manual_script_extension?
end
@unknown_attributes """
<head>
<script defer data-api="some" data-include="some" data-exclude="some" weird="one" data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
@unknown_attributes """
<head>
<script defer data-api="some" data-include="some" data-exclude="some" weird="one" data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
@valid_attributes """
<head>
<script defer type="text/javascript" data-cfasync='false' data-api="some" data-include="some" data-exclude="some" data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
@valid_attributes """
<head>
<script defer type="text/javascript" data-cfasync='false' data-api="some" data-include="some" data-exclude="some" data-domain="example.com" src="http://my-domain.example.com/js/script.js"></script>
</head>
"""
test "figures out unknown attributes" do
state =
@valid_attributes
|> new_state()
|> @check.perform()
test "figures out unknown attributes" do
state =
@valid_attributes
|> new_state()
|> @check.perform()
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.snippet_unknown_attributes?
state =
@unknown_attributes
|> new_state()
|> @check.perform()
state =
@unknown_attributes
|> new_state()
|> @check.perform()
assert state.diagnostics.snippet_unknown_attributes?
end
assert state.diagnostics.snippet_unknown_attributes?
end
defp new_state(html, opts \\ []) do
doc = Floki.parse_document!(html)
defp new_state(html, opts \\ []) do
doc = Floki.parse_document!(html)
opts =
[data_domain: "example.com"]
|> Keyword.merge(opts)
opts =
[data_domain: "example.com"]
|> Keyword.merge(opts)
@default_state
|> struct!(opts)
|> State.assign(document: doc)
@default_state
|> struct!(opts)
|> State.assign(document: doc)
end
end
end

View File

@ -3,143 +3,151 @@ defmodule Plausible.InstallationSupport.Checks.UrlTest do
Tests for URL check that is used in detection and verification checks pipelines
to fail fast on non-existent domains.
"""
use Plausible.DataCase, async: true
import Mox
alias Plausible.InstallationSupport.{State, Checks, Verification}
on_ee do
import Mox
@check Checks.Url
alias Plausible.InstallationSupport.{State, Checks, Verification}
@check Checks.Url
describe "when domain is set" do
for {site_domain, expected_lookup_domain} <- [
{"plausible.io", ~c"plausible.io"},
{"www.plausible.io", ~c"www.plausible.io"},
{"plausible.io/sites", ~c"plausible.io"}
] do
test "guesses 'https://#{site_domain}' if A-record is found for '#{site_domain}'" do
Plausible.DnsLookup.Mock
|> expect(:lookup, fn unquote(expected_lookup_domain),
_type,
_record,
_opts,
_timeout ->
[{192, 168, 1, 1}]
end)
state =
@check.perform(%State{
data_domain: unquote(site_domain),
url: nil,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "https://#{unquote(site_domain)}"
refute state.diagnostics.service_error
refute state.skip_further_checks?
end
end
test "guesses 'www.{domain}' if A record is not found for 'domain'" do
site_domain = "example.com/any/deeper/path"
describe "when domain is set" do
for {site_domain, expected_lookup_domain} <- [
{"plausible.io", ~c"plausible.io"},
{"www.plausible.io", ~c"www.plausible.io"},
{"plausible.io/sites", ~c"plausible.io"}
] do
test "guesses 'https://#{site_domain}' if A-record is found for '#{site_domain}'" do
Plausible.DnsLookup.Mock
|> expect(:lookup, fn unquote(expected_lookup_domain), _type, _record, _opts, _timeout ->
|> expect(:lookup, fn ~c"example.com", _type, _record, _opts, _timeout ->
[]
end)
|> expect(:lookup, fn ~c"www.example.com", _type, _record, _opts, _timeout ->
[{192, 168, 1, 2}]
end)
state =
@check.perform(%State{
data_domain: site_domain,
url: nil,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "https://www.example.com/any/deeper/path"
refute state.diagnostics.service_error
refute state.skip_further_checks?
end
test "fails if no A-record is found for 'domain' or 'www.{domain}'" do
expected_lookups = 2
Plausible.DnsLookup.Mock
|> expect(:lookup, expected_lookups, fn _domain, _type, _record, _opts, _timeout ->
[]
end)
domain = "any.example.com"
state =
@check.perform(%State{
data_domain: domain,
url: nil,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == nil
assert state.diagnostics.service_error == :domain_not_found
assert state.skip_further_checks?
end
end
describe "when url is set" do
test "for legitimate urls on domains that have an A-record, strips query and fragment" do
site_domain = "example-com-rollup"
url = "https://blog.example.com/recipes?foo=bar#baz"
Plausible.DnsLookup.Mock
|> expect(:lookup, fn ~c"blog.example.com", _type, _record, _opts, _timeout ->
[{192, 168, 1, 1}]
end)
state =
@check.perform(%State{
data_domain: unquote(site_domain),
url: nil,
data_domain: site_domain,
url: url,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "https://#{unquote(site_domain)}"
assert state.url == "https://blog.example.com/recipes"
refute state.diagnostics.service_error
refute state.skip_further_checks?
end
test "rejects file:// scheme, does not check domain" do
state =
@check.perform(%State{
data_domain: "example-com-rollup",
url: "file://example.com/archives/news?p=any#fragment",
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "file://example.com/archives/news?p=any#fragment"
assert state.diagnostics.service_error == :invalid_url
assert state.skip_further_checks?
end
test "rejects invalid urls" do
site_domain = "example-com-rollup"
url = "https://example.com/archives/news?p=any#fragment"
Plausible.DnsLookup.Mock
|> expect(:lookup, fn ~c"example.com", _type, _record, _opts, _timeout ->
[]
end)
state =
@check.perform(%State{
data_domain: site_domain,
url: url,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == url
assert state.diagnostics.service_error == :domain_not_found
assert state.skip_further_checks?
end
end
test "guesses 'www.{domain}' if A record is not found for 'domain'" do
site_domain = "example.com/any/deeper/path"
Plausible.DnsLookup.Mock
|> expect(:lookup, fn ~c"example.com", _type, _record, _opts, _timeout ->
[]
end)
|> expect(:lookup, fn ~c"www.example.com", _type, _record, _opts, _timeout ->
[{192, 168, 1, 2}]
end)
state =
@check.perform(%State{
data_domain: site_domain,
url: nil,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "https://www.example.com/any/deeper/path"
refute state.diagnostics.service_error
refute state.skip_further_checks?
test "reports progress correctly" do
assert @check.report_progress_as() ==
"We're trying to reach your website"
end
test "fails if no A-record is found for 'domain' or 'www.{domain}'" do
expected_lookups = 2
Plausible.DnsLookup.Mock
|> expect(:lookup, expected_lookups, fn _domain, _type, _record, _opts, _timeout ->
[]
end)
domain = "any.example.com"
state =
@check.perform(%State{
data_domain: domain,
url: nil,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == nil
assert state.diagnostics.service_error == :domain_not_found
assert state.skip_further_checks?
end
end
describe "when url is set" do
test "for legitimate urls on domains that have an A-record, strips query and fragment" do
site_domain = "example-com-rollup"
url = "https://blog.example.com/recipes?foo=bar#baz"
Plausible.DnsLookup.Mock
|> expect(:lookup, fn ~c"blog.example.com", _type, _record, _opts, _timeout ->
[{192, 168, 1, 1}]
end)
state =
@check.perform(%State{
data_domain: site_domain,
url: url,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "https://blog.example.com/recipes"
refute state.diagnostics.service_error
refute state.skip_further_checks?
end
test "rejects file:// scheme, does not check domain" do
state =
@check.perform(%State{
data_domain: "example-com-rollup",
url: "file://example.com/archives/news?p=any#fragment",
diagnostics: %Verification.Diagnostics{}
})
assert state.url == "file://example.com/archives/news?p=any#fragment"
assert state.diagnostics.service_error == :invalid_url
assert state.skip_further_checks?
end
test "rejects invalid urls" do
site_domain = "example-com-rollup"
url = "https://example.com/archives/news?p=any#fragment"
Plausible.DnsLookup.Mock
|> expect(:lookup, fn ~c"example.com", _type, _record, _opts, _timeout ->
[]
end)
state =
@check.perform(%State{
data_domain: site_domain,
url: url,
diagnostics: %Verification.Diagnostics{}
})
assert state.url == url
assert state.diagnostics.service_error == :domain_not_found
assert state.skip_further_checks?
end
end
test "reports progress correctly" do
assert @check.report_progress_as() ==
"We're trying to reach your website"
end
end

View File

@ -1,161 +1,164 @@
defmodule PlausibleWeb.Live.Components.VerificationTest do
use PlausibleWeb.ConnCase, async: true
import Phoenix.LiveViewTest, only: [render_component: 2]
import Plausible.Test.Support.HTML
use Plausible
alias Plausible.InstallationSupport.{State, LegacyVerification, Verification}
on_ee do
use PlausibleWeb.ConnCase, async: true
import Phoenix.LiveViewTest, only: [render_component: 2]
import Plausible.Test.Support.HTML
@component PlausibleWeb.Live.Components.Verification
@progress ~s|#verification-ui p#progress|
alias Plausible.InstallationSupport.{State, LegacyVerification, Verification}
@pulsating_circle ~s|div#verification-ui div.pulsating-circle|
@check_circle ~s|div#verification-ui #check-circle|
@error_circle ~s|div#verification-ui #error-circle|
@recommendations ~s|#recommendation|
@super_admin_report ~s|#super-admin-report|
@component PlausibleWeb.Live.Components.Verification
@progress ~s|#verification-ui p#progress|
test "renders initial state" do
html = render_component(@component, domain: "example.com")
assert element_exists?(html, @progress)
@pulsating_circle ~s|div#verification-ui div.pulsating-circle|
@check_circle ~s|div#verification-ui #check-circle|
@error_circle ~s|div#verification-ui #error-circle|
@recommendations ~s|#recommendation|
@super_admin_report ~s|#super-admin-report|
assert text_of_element(html, @progress) ==
"We're visiting your site to ensure that everything is working"
test "renders initial state" do
html = render_component(@component, domain: "example.com")
assert element_exists?(html, @progress)
assert element_exists?(html, @pulsating_circle)
refute class_of_element(html, @pulsating_circle) =~ "hidden"
refute element_exists?(html, @recommendations)
refute element_exists?(html, @check_circle)
refute element_exists?(html, @super_admin_report)
end
assert text_of_element(html, @progress) ==
"We're visiting your site to ensure that everything is working"
test "renders error badge on error" do
html = render_component(@component, domain: "example.com", success?: false, finished?: true)
refute element_exists?(html, @pulsating_circle)
refute element_exists?(html, @check_circle)
refute element_exists?(html, @recommendations)
assert element_exists?(html, @error_circle)
end
assert element_exists?(html, @pulsating_circle)
refute class_of_element(html, @pulsating_circle) =~ "hidden"
refute element_exists?(html, @recommendations)
refute element_exists?(html, @check_circle)
refute element_exists?(html, @super_admin_report)
end
test "renders diagnostic interpretation" do
interpretation =
LegacyVerification.Checks.interpret_diagnostics(%State{
test "renders error badge on error" do
html = render_component(@component, domain: "example.com", success?: false, finished?: true)
refute element_exists?(html, @pulsating_circle)
refute element_exists?(html, @check_circle)
refute element_exists?(html, @recommendations)
assert element_exists?(html, @error_circle)
end
test "renders diagnostic interpretation" do
interpretation =
LegacyVerification.Checks.interpret_diagnostics(%State{
url: "example.com",
diagnostics: %LegacyVerification.Diagnostics{}
})
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
interpretation: interpretation
)
recommendations = html |> find(@recommendations) |> Enum.map(&text/1)
assert recommendations == [
"If your site is running at a different location, please manually check your integration.  Learn more"
]
refute element_exists?(html, @super_admin_report)
end
test "renders super-admin report" do
state = %State{
url: "example.com",
diagnostics: %LegacyVerification.Diagnostics{}
})
}
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
interpretation: interpretation
)
interpretation =
LegacyVerification.Checks.interpret_diagnostics(state)
recommendations = html |> find(@recommendations) |> Enum.map(&text/1)
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
interpretation: interpretation,
verification_state: state,
super_admin?: true
)
assert recommendations == [
"If your site is running at a different location, please manually check your integration.  Learn more"
]
assert element_exists?(html, @super_admin_report)
assert text_of_element(html, @super_admin_report) =~ "Snippets found in body: 0"
end
refute element_exists?(html, @super_admin_report)
end
test "hides pulsating circle when finished, shows check circle" do
html =
render_component(@component,
domain: "example.com",
success?: true,
finished?: true
)
test "renders super-admin report" do
state = %State{
url: "example.com",
diagnostics: %LegacyVerification.Diagnostics{}
}
refute element_exists?(html, @pulsating_circle)
assert element_exists?(html, @check_circle)
end
interpretation =
LegacyVerification.Checks.interpret_diagnostics(state)
test "renders a progress message" do
html = render_component(@component, domain: "example.com", message: "Arbitrary message")
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
interpretation: interpretation,
verification_state: state,
super_admin?: true
)
assert text_of_element(html, @progress) == "Arbitrary message"
end
assert element_exists?(html, @super_admin_report)
assert text_of_element(html, @super_admin_report) =~ "Snippets found in body: 0"
end
test "renders contact link on >3 attempts" do
html = render_component(@component, domain: "example.com", attempts: 2, finished?: true)
refute html =~ "Need further help with your installation?"
refute element_exists?(html, ~s|a[href="https://plausible.io/contact"]|)
test "hides pulsating circle when finished, shows check circle" do
html =
render_component(@component,
domain: "example.com",
success?: true,
finished?: true
)
html = render_component(@component, domain: "example.com", attempts: 3, finished?: true)
assert html =~ "Need further help with your installation?"
assert element_exists?(html, ~s|a[href="https://plausible.io/contact"]|)
end
refute element_exists?(html, @pulsating_circle)
assert element_exists?(html, @check_circle)
end
test "renders link to verify installation at a different URL" do
interpretation =
Verification.Checks.interpret_diagnostics(%State{
url: "example.com",
diagnostics: %Verification.Diagnostics{
plausible_is_on_window: false,
plausible_is_initialized: false,
service_error: :domain_not_found
}
})
test "renders a progress message" do
html = render_component(@component, domain: "example.com", message: "Arbitrary message")
assert interpretation.data.offer_custom_url_input == true
assert text_of_element(html, @progress) == "Arbitrary message"
end
expected_link_href =
PlausibleWeb.Router.Helpers.site_path(PlausibleWeb.Endpoint, :verification, "example.com")
@tag :ee_only
test "renders contact link on >3 attempts" do
html = render_component(@component, domain: "example.com", attempts: 2, finished?: true)
refute html =~ "Need further help with your installation?"
refute element_exists?(html, ~s|a[href="https://plausible.io/contact"]|)
html =
render_component(@component,
domain: "example.com",
finished?: true,
success?: false,
interpretation: interpretation
)
html = render_component(@component, domain: "example.com", attempts: 3, finished?: true)
assert html =~ "Need further help with your installation?"
assert element_exists?(html, ~s|a[href="https://plausible.io/contact"]|)
end
assert text_of_element(html, "#verify-custom-url-link") =~ "different URL?"
assert text_of_attr(html, "#verify-custom-url-link a", "href") =~ expected_link_href
assert text_of_attr(html, "#verify-custom-url-link a", "href") =~ "custom_url=true"
end
test "renders link to verify installation at a different URL" do
interpretation =
Verification.Checks.interpret_diagnostics(%State{
url: "example.com",
diagnostics: %Verification.Diagnostics{
plausible_is_on_window: false,
plausible_is_initialized: false,
service_error: :domain_not_found
}
})
test "offers escape paths: settings and installation instructions on failure" do
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
installation_type: "wordpress",
flow: PlausibleWeb.Flows.review()
)
assert interpretation.data.offer_custom_url_input == true
assert element_exists?(html, ~s|a[href="/example.com/settings/general"]|)
expected_link_href =
PlausibleWeb.Router.Helpers.site_path(PlausibleWeb.Endpoint, :verification, "example.com")
html =
render_component(@component,
domain: "example.com",
finished?: true,
success?: false,
interpretation: interpretation
)
assert text_of_element(html, "#verify-custom-url-link") =~ "different URL?"
assert text_of_attr(html, "#verify-custom-url-link a", "href") =~ expected_link_href
assert text_of_attr(html, "#verify-custom-url-link a", "href") =~ "custom_url=true"
end
test "offers escape paths: settings and installation instructions on failure" do
html =
render_component(@component,
domain: "example.com",
success?: false,
finished?: true,
installation_type: "wordpress",
flow: PlausibleWeb.Flows.review()
)
assert element_exists?(html, ~s|a[href="/example.com/settings/general"]|)
assert element_exists?(
html,
~s|a[href="/example.com/installation?flow=review&installation_type=wordpress"]|
)
assert element_exists?(
html,
~s|a[href="/example.com/installation?flow=review&installation_type=wordpress"]|
)
end
end
end

View File

@ -130,42 +130,44 @@ defmodule PlausibleWeb.Live.InstallationTest do
end
describe "LiveView" do
test "mounts and detects installation type", %{conn: conn, site: site} do
stub_fetch_body(200, "wp-content")
on_ee do
test "mounts and detects installation type", %{conn: conn, site: site} do
stub_fetch_body(200, "wp-content")
{lv, _} = get_lv(conn, site)
{lv, _} = get_lv(conn, site, nil)
assert eventually(fn ->
html = render(lv)
assert eventually(fn ->
html = render(lv)
{
text(html) =~ "Install WordPress",
html
}
end)
{
text(html) =~ "Install WordPress",
html
}
end)
_ = render(lv)
end
_ = render(lv)
end
@tag :slow
test "mounts and does not detect installation type, if it's provided", %{
conn: conn,
site: site
} do
stub_fetch_body(200, "wp-content")
@tag :slow
test "mounts and does not detect installation type, if it's provided", %{
conn: conn,
site: site
} do
stub_fetch_body(200, "wp-content")
{lv, _} = get_lv(conn, site, "?installation_type=gtm")
{lv, _} = get_lv(conn, site, "?installation_type=gtm")
refute eventually(fn ->
html = render(lv)
refute eventually(fn ->
html = render(lv)
{
text(html) =~ "Install WordPress",
html
}
end)
{
text(html) =~ "Install WordPress",
html
}
end)
_ = render(lv)
_ = render(lv)
end
end
test "allows manual snippet customization", %{conn: conn, site: site} do
@ -354,19 +356,21 @@ defmodule PlausibleWeb.Live.InstallationTest do
end
end
defp stub_fetch_body(f) when is_function(f, 1) do
Req.Test.stub(Plausible.InstallationSupport.Checks.FetchBody, f)
on_ee do
defp stub_fetch_body(f) when is_function(f, 1) do
Req.Test.stub(Plausible.InstallationSupport.Checks.FetchBody, f)
end
defp stub_fetch_body(status, body) do
stub_fetch_body(fn conn ->
conn
|> put_resp_content_type("text/html")
|> send_resp(status, body)
end)
end
end
defp stub_fetch_body(status, body) do
stub_fetch_body(fn conn ->
conn
|> put_resp_content_type("text/html")
|> send_resp(status, body)
end)
end
defp get_lv(conn, site, qs \\ nil) do
defp get_lv(conn, site, qs) do
{:ok, lv, html} = live(conn, "/#{site.domain}/installation#{qs}")
{lv, html}