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:
parent
053fe71cc8
commit
9ed79a831e
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: %{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Reference in New Issue