analytics/test/plausible_web/controllers/stats_controller_test.exs

969 lines
34 KiB
Elixir
Raw Normal View History

2019-09-02 14:29:19 +03:00
defmodule PlausibleWeb.StatsControllerTest do
use PlausibleWeb.ConnCase, async: false
2019-09-02 14:29:19 +03:00
use Plausible.Repo
import Plausible.Test.Support.HTML
2019-09-02 14:29:19 +03:00
@react_container "div#stats-react-container"
describe "GET /:website - anonymous user" do
2019-09-02 14:29:19 +03:00
test "public site - shows site stats", %{conn: conn} do
Conditionally support switching between v1 and v2 clickhouse schemas (#2780) * Remove ClickhouseSetup module This has been an implicit point of contact to many tests. From now on the goal is for each test to maintain its own, isolated setup so that no accidental clashes and implicit assumptions are relied upon. * Implement v2 schema check An environment variable V2_MIGRATION_DONE acts like a feature flag, switching plausible from using old events/sessions schemas to v2 schemas introduced by NumericIDs migration. * Run both test suites sequentially While the code for v1 and v2 schemas must be kept still, we will from now on run tests against both code paths. Secondary test run will set V2_MIGRATION_DONE=1 variable, thus making all `Plausible.v2?()` checks return `true'. * Remove unused function This is a remnant from the short period when we would check for existing events before allowing creating a new site. * Update test setups/factories with v2 migration check * Make GateKeeper return site id along with :allow * Make Billing module check for v2 schema * Make ingestion aware of v2 schema * Disable site transfers for when v2 is live In a separate changeset we will implement simplified site transfer for when v2 migration is complete. The new transfer will only rename the site domain in postgres and keep track of the original site prior to the transfer so we keep an ingestion grace period until the customers redeploy their scripting. * Make Stats base queries aware of v2 schema switch * Update breakdown with v2 conditionals * Update pageview local start with v2 check * Update current visitoris with v2 check * Update stats controller with v2 checks * Update external controller with v2 checks * Update remaining tests with proper fixtures * Rewrite redundant assignment * Remove unused alias * Mute credo, this is not the right time * Add test_helper prompt * Fetch priv dir so it works with a release * Fetch distinct partitions only * Don't limit inspect output for partitions * Ensure SQL is printed to IO * Remove redundant domain fixture
2023-03-27 14:52:42 +03:00
site = insert(:site, public: true)
populate_stats(site, [build(:pageview)])
2019-09-02 14:29:19 +03:00
Conditionally support switching between v1 and v2 clickhouse schemas (#2780) * Remove ClickhouseSetup module This has been an implicit point of contact to many tests. From now on the goal is for each test to maintain its own, isolated setup so that no accidental clashes and implicit assumptions are relied upon. * Implement v2 schema check An environment variable V2_MIGRATION_DONE acts like a feature flag, switching plausible from using old events/sessions schemas to v2 schemas introduced by NumericIDs migration. * Run both test suites sequentially While the code for v1 and v2 schemas must be kept still, we will from now on run tests against both code paths. Secondary test run will set V2_MIGRATION_DONE=1 variable, thus making all `Plausible.v2?()` checks return `true'. * Remove unused function This is a remnant from the short period when we would check for existing events before allowing creating a new site. * Update test setups/factories with v2 migration check * Make GateKeeper return site id along with :allow * Make Billing module check for v2 schema * Make ingestion aware of v2 schema * Disable site transfers for when v2 is live In a separate changeset we will implement simplified site transfer for when v2 migration is complete. The new transfer will only rename the site domain in postgres and keep track of the original site prior to the transfer so we keep an ingestion grace period until the customers redeploy their scripting. * Make Stats base queries aware of v2 schema switch * Update breakdown with v2 conditionals * Update pageview local start with v2 check * Update current visitoris with v2 check * Update stats controller with v2 checks * Update external controller with v2 checks * Update remaining tests with proper fixtures * Rewrite redundant assignment * Remove unused alias * Mute credo, this is not the right time * Add test_helper prompt * Fetch priv dir so it works with a release * Fetch distinct partitions only * Don't limit inspect output for partitions * Ensure SQL is printed to IO * Remove redundant domain fixture
2023-03-27 14:52:42 +03:00
conn = get(conn, "/#{site.domain}")
resp = html_response(conn, 200)
assert element_exists?(resp, @react_container)
assert text_of_attr(resp, @react_container, "data-domain") == site.domain
assert text_of_attr(resp, @react_container, "data-is-dbip") == "false"
assert text_of_attr(resp, @react_container, "data-has-goals") == "false"
assert text_of_attr(resp, @react_container, "data-conversions-opted-out") == "false"
assert text_of_attr(resp, @react_container, "data-funnels-opted-out") == "false"
assert text_of_attr(resp, @react_container, "data-props-opted-out") == "false"
assert text_of_attr(resp, @react_container, "data-props-available") == "true"
assert text_of_attr(resp, @react_container, "data-funnels-available") == "true"
assert text_of_attr(resp, @react_container, "data-has-props") == "false"
assert text_of_attr(resp, @react_container, "data-logged-in") == "false"
assert text_of_attr(resp, @react_container, "data-embedded") == ""
[{"div", attrs, _}] = find(resp, @react_container)
assert Enum.all?(attrs, fn {k, v} -> is_binary(k) and is_binary(v) end)
assert ["noindex, nofollow"] ==
resp
|> find("meta[name=robots]")
|> Floki.attribute("content")
assert text_of_element(resp, "title") == "Plausible · #{site.domain}"
end
test "plausible.io live demo - shows site stats", %{conn: conn} do
site = insert(:site, domain: "plausible.io", public: true)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/#{site.domain}")
resp = html_response(conn, 200)
assert element_exists?(resp, @react_container)
assert ["index, nofollow"] ==
resp
|> find("meta[name=robots]")
|> Floki.attribute("content")
assert text_of_element(resp, "title") == "Plausible Analytics: Live Demo"
2019-09-02 14:29:19 +03:00
end
test "public site - shows waiting for first pageview", %{conn: conn} do
insert(:site, domain: "some-other-public-site.io", public: true)
conn = get(conn, "/some-other-public-site.io")
assert html_response(conn, 200) =~ "Need to see the snippet again?"
end
2019-09-02 14:29:19 +03:00
test "can not view stats of a private website", %{conn: conn} do
_ = insert(:user)
conn = get(conn, "/test-site.com")
assert html_response(conn, 404) =~ "There's nothing here"
2019-09-02 14:29:19 +03:00
end
end
describe "GET /:website - as a logged in user" do
2019-09-02 14:29:19 +03:00
setup [:create_user, :log_in, :create_site]
test "can view stats of a website I've created", %{conn: conn, site: site} do
Conditionally support switching between v1 and v2 clickhouse schemas (#2780) * Remove ClickhouseSetup module This has been an implicit point of contact to many tests. From now on the goal is for each test to maintain its own, isolated setup so that no accidental clashes and implicit assumptions are relied upon. * Implement v2 schema check An environment variable V2_MIGRATION_DONE acts like a feature flag, switching plausible from using old events/sessions schemas to v2 schemas introduced by NumericIDs migration. * Run both test suites sequentially While the code for v1 and v2 schemas must be kept still, we will from now on run tests against both code paths. Secondary test run will set V2_MIGRATION_DONE=1 variable, thus making all `Plausible.v2?()` checks return `true'. * Remove unused function This is a remnant from the short period when we would check for existing events before allowing creating a new site. * Update test setups/factories with v2 migration check * Make GateKeeper return site id along with :allow * Make Billing module check for v2 schema * Make ingestion aware of v2 schema * Disable site transfers for when v2 is live In a separate changeset we will implement simplified site transfer for when v2 migration is complete. The new transfer will only rename the site domain in postgres and keep track of the original site prior to the transfer so we keep an ingestion grace period until the customers redeploy their scripting. * Make Stats base queries aware of v2 schema switch * Update breakdown with v2 conditionals * Update pageview local start with v2 check * Update current visitoris with v2 check * Update stats controller with v2 checks * Update external controller with v2 checks * Update remaining tests with proper fixtures * Rewrite redundant assignment * Remove unused alias * Mute credo, this is not the right time * Add test_helper prompt * Fetch priv dir so it works with a release * Fetch distinct partitions only * Don't limit inspect output for partitions * Ensure SQL is printed to IO * Remove redundant domain fixture
2023-03-27 14:52:42 +03:00
populate_stats(site, [build(:pageview)])
2019-09-02 14:29:19 +03:00
conn = get(conn, "/" <> site.domain)
resp = html_response(conn, 200)
assert text_of_attr(resp, @react_container, "data-logged-in") == "true"
2019-09-02 14:29:19 +03:00
end
Snippet integration verification (#4106) * Allow running browserless.io locally * Compile tailwind classes based on extra/ too * Add browserless runtime configuration * Ignore verification events on ingestion * Improve extracting HTML text in tests * Update dependencies - Floki will be used on production to parse site contents - Req will be used to handle redundant stuff like retrying etc. * Add shuttle SVG to generic components Later on we'll use it to indicate verification errors * Connect live socket & allow skipping awaiting the first pageview * Connect live socket in general settings * Implement verification checks & diagnostics * Stub remote services with Req for testing * Change snippet screen copy * Update tracker script, so that: 1. headless browsers aren't ignored if `window.__plausible` is defined 2. callback optionally supplies the event response HTTP status This will be later used to check whether the server acknowledged the verification event. * Implement LiveView verification UI * Embed the verification UIs into settings and onboarding * Implement browserless puppeteer verification script It: - tries to visit the site - defines window.__plausible, so the tracker doesn't ignore test events - sends a verification event and instruments the callback - awaits the callback to fire and returns the result * Improve diagnostics for CSP Only report CSP error if the snippet is already found * Put verification behind a feature flag/env setting * Contact Us hint only for Enterprise Edition * For headless code, use JS context instead of EEx interpolation * Update diagnostics test with WordPress scenarios * Shorten exception/throw interception * Rename test * Tidy up * Bust URL always on headless check * Update moduledoc * Detect official Plausible WordPress Plugin and act accordingly on diagnostics interoperation * Stop using 'rating' in favour of 'interpretation' * Only report CSP error if no proxy is likely * Update CHANGELOG * Allow event-* attributes on snippet elements * Improve naive GTM detection, not to confuse it with GA4 * Update lib/plausible/verification.ex Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com> * Update test/plausible/site/verification/checks_test.exs Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com> * s/perform_wrapped/perform_safe * Update lib/plausible/verification/checks/installation.ex Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com> * Remove garbage --------- Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
2024-05-23 16:00:50 +03:00
test "can view stats of a website I've created, enforcing pageviews check skip", %{
conn: conn,
site: site
} do
resp = conn |> get("/" <> site.domain) |> html_response(200)
refute text_of_attr(resp, @react_container, "data-logged-in") == "true"
resp = conn |> get("/" <> site.domain <> "?skip_to_dashboard=true") |> html_response(200)
assert text_of_attr(resp, @react_container, "data-logged-in") == "true"
end
2021-10-26 16:51:45 +03:00
test "shows locked page if page is locked", %{conn: conn, user: user} do
locked_site = insert(:site, locked: true, members: [user])
conn = get(conn, "/" <> locked_site.domain)
assert html_response(conn, 200) =~ "Dashboard locked"
2021-10-26 16:51:45 +03:00
end
2019-09-02 14:29:19 +03:00
test "can not view stats of someone else's website", %{conn: conn} do
site = insert(:site)
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 404) =~ "There's nothing here"
2019-09-02 14:29:19 +03:00
end
end
describe "GET /:website - as a super admin" do
@describetag :ee_only
setup [:create_user, :make_user_super_admin, :log_in]
test "can view a private dashboard with stats", %{conn: conn} do
site = insert(:site)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 200) =~ "stats-react-container"
end
test "can view a private dashboard without stats", %{conn: conn} do
site = insert(:site)
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 200) =~ "Need to see the snippet again?"
end
test "can view a private locked dashboard with stats", %{conn: conn} do
user = insert(:user)
site = insert(:site, locked: true, members: [user])
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
resp = html_response(conn, 200)
assert resp =~ "This dashboard is actually locked"
[{"div", attrs, _}] = find(resp, @react_container)
assert Enum.all?(attrs, fn {k, v} -> is_binary(k) and is_binary(v) end)
end
test "can view a private locked dashboard without stats", %{conn: conn} do
user = insert(:user)
site = insert(:site, locked: true, members: [user])
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 200) =~ "Need to see the snippet again?"
assert html_response(conn, 200) =~ "This dashboard is actually locked"
end
test "can view a locked public dashboard", %{conn: conn} do
site = insert(:site, locked: true, public: true)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
resp = html_response(conn, 200)
[{"div", attrs, _}] = find(resp, @react_container)
assert Enum.all?(attrs, fn {k, v} -> is_binary(k) and is_binary(v) end)
end
end
defp make_user_super_admin(%{user: user}) do
Application.put_env(:plausible, :super_admin_user_ids, [user.id])
end
describe "GET /:website/export" do
setup [:create_user, :create_new_site, :log_in]
test "exports all the necessary CSV files", %{conn: conn, site: site} do
conn = get(conn, "/" <> site.domain <> "/export")
assert {"content-type", "application/zip; charset=utf-8"} =
List.keyfind(conn.resp_headers, "content-type", 0)
{:ok, zip} = :zip.unzip(response(conn, 200), [:memory])
zip = Enum.map(zip, fn {filename, _} -> filename end)
assert ~c"visitors.csv" in zip
assert ~c"browsers.csv" in zip
assert ~c"browser_versions.csv" in zip
assert ~c"cities.csv" in zip
assert ~c"conversions.csv" in zip
assert ~c"countries.csv" in zip
assert ~c"devices.csv" in zip
assert ~c"entry_pages.csv" in zip
assert ~c"exit_pages.csv" in zip
assert ~c"operating_systems.csv" in zip
assert ~c"operating_system_versions.csv" in zip
assert ~c"pages.csv" in zip
assert ~c"regions.csv" in zip
assert ~c"sources.csv" in zip
assert ~c"utm_campaigns.csv" in zip
assert ~c"utm_contents.csv" in zip
assert ~c"utm_mediums.csv" in zip
assert ~c"utm_sources.csv" in zip
assert ~c"utm_terms.csv" in zip
end
test "exports only internally used props in custom_props.csv for a growth plan", %{
conn: conn,
site: site
} do
{:ok, site} = Plausible.Props.allow(site, ["author"])
site = Repo.preload(site, :owner)
insert(:growth_subscription, user: site.owner)
populate_stats(site, [
build(:pageview, "meta.key": ["author"], "meta.value": ["a"]),
build(:event, name: "File Download", "meta.key": ["url"], "meta.value": ["b"])
])
conn = get(conn, "/" <> site.domain <> "/export?period=day")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
{_filename, result} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"custom_props.csv" end)
assert parse_csv(result) == [
["property", "value", "visitors", "events", "percentage"],
["url", "(none)", "1", "1", "50.0"],
["url", "b", "1", "1", "50.0"],
[""]
]
end
test "does not include custom_props.csv for a growth plan if no internal props used", %{
conn: conn,
site: site
} do
{:ok, site} = Plausible.Props.allow(site, ["author"])
site = Repo.preload(site, :owner)
insert(:growth_subscription, user: site.owner)
populate_stats(site, [
build(:pageview, "meta.key": ["author"], "meta.value": ["a"])
])
{:ok, zip} =
conn
|> get("/#{site.domain}/export?period=day")
|> response(200)
|> :zip.unzip([:memory])
files = Map.new(zip)
refute Map.has_key?(files, ~c"custom_props.csv")
end
test "exports data in zipped csvs", %{conn: conn, site: site} do
populate_exported_stats(site)
conn = get(conn, "/" <> site.domain <> "/export?date=2021-10-20")
assert_zip(conn, "30d")
end
test "fails to export with interval=undefined, looking at you, spiders", %{
conn: conn,
site: site
} do
assert conn
|> get("/" <> site.domain <> "/export?date=2021-10-20&interval=undefined")
|> response(400)
end
test "exports allowed event props for a trial account", %{conn: conn, site: site} do
{:ok, site} = Plausible.Props.allow(site, ["author", "logged_in"])
populate_stats(site, [
build(:pageview, "meta.key": ["author"], "meta.value": ["uku"]),
build(:pageview, "meta.key": ["author"], "meta.value": ["uku"]),
build(:event, "meta.key": ["author"], "meta.value": ["marko"], name: "Newsletter Signup"),
build(:pageview, user_id: 999, "meta.key": ["logged_in"], "meta.value": ["true"]),
build(:pageview, user_id: 999, "meta.key": ["logged_in"], "meta.value": ["true"]),
build(:pageview, "meta.key": ["disallowed"], "meta.value": ["whatever"]),
build(:pageview)
])
conn = get(conn, "/" <> site.domain <> "/export?period=day")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
{_filename, result} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"custom_props.csv" end)
assert parse_csv(result) == [
["property", "value", "visitors", "events", "percentage"],
["author", "(none)", "3", "4", "50.0"],
["author", "uku", "2", "2", "33.3"],
["author", "marko", "1", "1", "16.7"],
["logged_in", "(none)", "5", "5", "83.3"],
["logged_in", "true", "1", "2", "16.7"],
[""]
]
end
test "exports data grouped by interval", %{conn: conn, site: site} do
populate_exported_stats(site)
conn = get(conn, "/" <> site.domain <> "/export?date=2021-10-20&period=30d&interval=week")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
{_filename, visitors} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"visitors.csv" end)
assert parse_csv(visitors) == [
[
"date",
"visitors",
"pageviews",
"visits",
"views_per_visit",
"bounce_rate",
"visit_duration"
],
["2021-09-20", "1", "1", "1", "1.0", "100", "0"],
["2021-09-27", "0", "0", "0", "0.0", "", ""],
["2021-10-04", "0", "0", "0", "0.0", "", ""],
["2021-10-11", "0", "0", "0", "0.0", "", ""],
["2021-10-18", "3", "4", "3", "1.33", "33", "40"],
[""]
]
end
test "exports operating system versions", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, operating_system: "Mac", operating_system_version: "14"),
build(:pageview, operating_system: "Mac", operating_system_version: "14"),
build(:pageview, operating_system: "Mac", operating_system_version: "14"),
build(:pageview,
operating_system: "Ubuntu",
operating_system_version: "20.04"
),
build(:pageview,
operating_system: "Ubuntu",
operating_system_version: "20.04"
),
build(:pageview, operating_system: "Mac", operating_system_version: "13")
])
conn = get(conn, "/#{site.domain}/export")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
{_filename, os_versions} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"operating_system_versions.csv" end)
assert parse_csv(os_versions) == [
["name", "version", "visitors"],
["Mac", "14", "3"],
["Ubuntu", "20.04", "2"],
["Mac", "13", "1"],
[""]
]
end
test "exports imported data when requested", %{conn: conn, site: site} do
site_import = insert(:site_import, site: site)
insert(:goal, site: site, event_name: "Outbound Link: Click")
populate_stats(site, site_import.id, [
build(:imported_visitors, visitors: 9),
build(:imported_browsers, browser: "Chrome", pageviews: 1),
build(:imported_devices, device: "Desktop", pageviews: 1),
build(:imported_entry_pages, entry_page: "/test", pageviews: 1),
build(:imported_exit_pages, exit_page: "/test", pageviews: 1),
build(:imported_locations,
country: "PL",
region: "PL-22",
city: 3_099_434,
pageviews: 1
),
build(:imported_operating_systems, operating_system: "Mac", pageviews: 1),
build(:imported_pages, page: "/test", pageviews: 1),
build(:imported_sources,
source: "Google",
utm_medium: "search",
utm_campaign: "ads",
utm_source: "google",
utm_content: "content",
utm_term: "term",
pageviews: 1
),
build(:imported_custom_events,
name: "Outbound Link: Click",
link_url: "https://example.com",
visitors: 5,
events: 10
)
])
conn = get(conn, "/#{site.domain}/export?with_imported=true")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
filenames = zip |> Enum.map(fn {filename, _} -> to_string(filename) end)
# NOTE: currently, custom_props.csv is not populated from imported data
expected_filenames = [
"visitors.csv",
"sources.csv",
"utm_mediums.csv",
"utm_sources.csv",
"utm_campaigns.csv",
"utm_contents.csv",
"utm_terms.csv",
"pages.csv",
"entry_pages.csv",
"exit_pages.csv",
"countries.csv",
"regions.csv",
"cities.csv",
"browsers.csv",
"browser_versions.csv",
"operating_systems.csv",
"operating_system_versions.csv",
"devices.csv",
"conversions.csv",
"referrers.csv"
]
Enum.each(expected_filenames, fn expected ->
assert expected in filenames
end)
Enum.each(zip, fn
{~c"visitors.csv", data} ->
csv = parse_csv(data)
assert List.first(csv) == [
"date",
"visitors",
"pageviews",
"visits",
"views_per_visit",
"bounce_rate",
"visit_duration"
]
assert Enum.at(csv, -2) ==
[Date.to_iso8601(Date.utc_today()), "9", "1", "1", "1.0", "0.0", "10.0"]
{~c"sources.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["Google", "1", "0.0", "10.0"],
[""]
]
{~c"utm_mediums.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["search", "1", "0.0", "10.0"],
[""]
]
{~c"utm_sources.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["google", "1", "0.0", "10.0"],
[""]
]
{~c"utm_campaigns.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["ads", "1", "0.0", "10.0"],
[""]
]
{~c"utm_contents.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["content", "1", "0.0", "10.0"],
[""]
]
{~c"utm_terms.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["term", "1", "0.0", "10.0"],
[""]
]
{~c"pages.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "pageviews", "bounce_rate", "time_on_page"],
["/test", "1", "1", "0.0", "10.0"],
[""]
]
{~c"entry_pages.csv", data} ->
assert parse_csv(data) == [
["name", "unique_entrances", "total_entrances", "visit_duration"],
["/test", "1", "1", "10.0"],
[""]
]
{~c"exit_pages.csv", data} ->
assert parse_csv(data) == [
["name", "unique_exits", "total_exits", "exit_rate"],
["/test", "1", "1", "100.0"],
[""]
]
{~c"countries.csv", data} ->
assert parse_csv(data) == [["name", "visitors"], ["Poland", "1"], [""]]
{~c"regions.csv", data} ->
assert parse_csv(data) == [
["name", "visitors"],
["Pomerania", "1"],
[""]
]
{~c"cities.csv", data} ->
assert parse_csv(data) == [["name", "visitors"], ["Gdańsk", "1"], [""]]
{~c"browsers.csv", data} ->
assert parse_csv(data) == [
["name", "visitors"],
["Chrome", "1"],
[""]
]
{~c"browser_versions.csv", data} ->
assert parse_csv(data) == [
["name", "version", "visitors"],
["Chrome", "(not set)", "1"],
[""]
]
{~c"operating_systems.csv", data} ->
assert parse_csv(data) == [["name", "visitors"], ["Mac", "1"], [""]]
{~c"operating_system_versions.csv", data} ->
assert parse_csv(data) == [
["name", "version", "visitors"],
["Mac", "(not set)", "1"],
[""]
]
{~c"devices.csv", data} ->
assert parse_csv(data) == [["name", "visitors"], ["Desktop", "1"], [""]]
{~c"conversions.csv", data} ->
assert parse_csv(data) == [
["name", "unique_conversions", "total_conversions"],
["Outbound Link: Click", "5", "10"],
[""]
]
{~c"referrers.csv", data} ->
assert parse_csv(data) == [
["name", "visitors", "bounce_rate", "visit_duration"],
["Direct / None", "1", "0.0", "10.0"],
[""]
]
end)
end
end
defp parse_csv(file_content) when is_binary(file_content) do
file_content
|> String.split("\r\n")
|> Enum.map(&String.split(&1, ","))
end
describe "GET /:website/export - via shared link" do
test "exports data in zipped csvs", %{conn: conn} do
site = insert(:site, domain: "new-site.com")
link = insert(:shared_link, site: site)
populate_exported_stats(site)
conn = get(conn, "/" <> site.domain <> "/export?auth=#{link.slug}&date=2021-10-20")
assert_zip(conn, "30d")
end
end
describe "GET /:website/export - for past 6 months" do
setup [:create_user, :create_new_site, :log_in]
test "exports 6 months of data in zipped csvs", %{conn: conn, site: site} do
populate_exported_stats(site)
conn = get(conn, "/" <> site.domain <> "/export?period=6mo&date=2021-10-20")
assert_zip(conn, "6m")
end
end
describe "GET /:website/export - with path filter" do
setup [:create_user, :create_new_site, :log_in]
test "exports filtered data in zipped csvs", %{conn: conn, site: site} do
populate_exported_stats(site)
filters = Jason.encode!(%{page: "/some-other-page"})
conn = get(conn, "/#{site.domain}/export?date=2021-10-20&filters=#{filters}")
assert_zip(conn, "30d-filter-path")
end
end
describe "GET /:website/export - with a custom prop filter" do
setup [:create_user, :create_new_site, :log_in]
test "custom-props.csv only returns the prop and its value in filter", %{
conn: conn,
site: site
} do
{:ok, site} = Plausible.Props.allow(site, ["author", "logged_in"])
populate_stats(site, [
build(:pageview, "meta.key": ["author"], "meta.value": ["uku"]),
build(:pageview, "meta.key": ["author"], "meta.value": ["marko"]),
build(:pageview, "meta.key": ["logged_in"], "meta.value": ["true"])
])
filters = Jason.encode!(%{props: %{author: "marko"}})
conn = get(conn, "/" <> site.domain <> "/export?period=day&filters=#{filters}")
{:ok, zip} = :zip.unzip(response(conn, 200), [:memory])
{_filename, result} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"custom_props.csv" end)
assert parse_csv(result) == [
["property", "value", "visitors", "events", "percentage"],
["author", "marko", "1", "1", "100.0"],
[""]
]
end
end
defp assert_zip(conn, folder) do
assert conn.status == 200
assert {"content-type", "application/zip; charset=utf-8"} =
List.keyfind(conn.resp_headers, "content-type", 0)
{:ok, zip} = :zip.unzip(response(conn, 200), [:memory])
folder = Path.expand(folder, "test/plausible_web/controllers/CSVs")
Enum.map(zip, &assert_csv(&1, folder))
end
defp assert_csv({file, downloaded}, folder) do
file = Path.expand(file, folder)
{:ok, content} = File.read(file)
2021-12-16 16:50:37 +03:00
msg = "CSV file comparison failed (#{file})"
assert downloaded == content, message: msg, left: downloaded, right: content
end
defp populate_exported_stats(site) do
populate_stats(site, [
build(:pageview,
user_id: 123,
pathname: "/",
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], minutes: -1) |> NaiveDateTime.truncate(:second),
country_code: "EE",
subdivision1_code: "EE-37",
city_geoname_id: 588_409,
referrer_source: "Google"
),
build(:pageview,
user_id: 123,
pathname: "/some-other-page",
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], minutes: -2) |> NaiveDateTime.truncate(:second),
country_code: "EE",
subdivision1_code: "EE-37",
city_geoname_id: 588_409,
referrer_source: "Google"
),
build(:pageview,
pathname: "/",
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], days: -1) |> NaiveDateTime.truncate(:second),
utm_medium: "search",
utm_campaign: "ads",
utm_source: "google",
utm_content: "content",
utm_term: "term",
browser: "Firefox",
browser_version: "120",
operating_system: "Mac",
operating_system_version: "14"
),
build(:pageview,
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], months: -1) |> NaiveDateTime.truncate(:second),
country_code: "EE",
browser: "Firefox",
browser_version: "120",
operating_system: "Mac",
operating_system_version: "14"
),
build(:pageview,
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], months: -5) |> NaiveDateTime.truncate(:second),
utm_campaign: "ads",
country_code: "EE",
referrer_source: "Google",
browser: "FirefoxNoVersion",
operating_system: "MacNoVersion"
),
build(:pageview,
user_id: 456,
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], days: -1, minutes: -1)
|> NaiveDateTime.truncate(:second),
pathname: "/signup",
"meta.key": ["variant"],
"meta.value": ["A"]
),
build(:event,
user_id: 456,
timestamp:
Timex.shift(~N[2021-10-20 12:00:00], days: -1) |> NaiveDateTime.truncate(:second),
name: "Signup",
"meta.key": ["variant"],
"meta.value": ["A"]
)
])
insert(:goal, %{site: site, event_name: "Signup"})
end
describe "GET /:website/export - with goal filter" do
setup [:create_user, :create_new_site, :log_in]
test "exports goal-filtered data in zipped csvs", %{conn: conn, site: site} do
populate_exported_stats(site)
filters = Jason.encode!(%{goal: "Signup"})
conn = get(conn, "/#{site.domain}/export?date=2021-10-20&filters=#{filters}")
assert_zip(conn, "30d-filter-goal")
end
test "custom-props.csv only returns the prop names for the goal in filter", %{
conn: conn,
site: site
} do
{:ok, site} = Plausible.Props.allow(site, ["author", "logged_in"])
populate_stats(site, [
build(:event, name: "Newsletter Signup", "meta.key": ["author"], "meta.value": ["uku"]),
build(:event, name: "Newsletter Signup", "meta.key": ["author"], "meta.value": ["marko"]),
build(:event, name: "Newsletter Signup", "meta.key": ["author"], "meta.value": ["marko"]),
build(:pageview, "meta.key": ["logged_in"], "meta.value": ["true"])
])
filters = Jason.encode!(%{goal: "Newsletter Signup"})
conn = get(conn, "/" <> site.domain <> "/export?period=day&filters=#{filters}")
{:ok, zip} = :zip.unzip(response(conn, 200), [:memory])
{_filename, result} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"custom_props.csv" end)
assert parse_csv(result) == [
["property", "value", "visitors", "events", "conversion_rate"],
["author", "marko", "2", "2", "50.0"],
["author", "uku", "1", "1", "25.0"],
[""]
]
end
test "exports conversions and conversion rate for operating system versions", %{
conn: conn,
site: site
} do
populate_stats(site, [
build(:pageview, operating_system: "Mac", operating_system_version: "14"),
build(:event,
name: "Signup",
operating_system: "Mac",
operating_system_version: "14"
),
build(:event,
name: "Signup",
operating_system: "Mac",
operating_system_version: "14"
),
build(:event,
name: "Signup",
operating_system: "Mac",
operating_system_version: "14"
),
build(:event,
name: "Signup",
operating_system: "Ubuntu",
operating_system_version: "20.04"
),
build(:event,
name: "Signup",
operating_system: "Ubuntu",
operating_system_version: "20.04"
),
build(:event,
name: "Signup",
operating_system: "Lubuntu",
operating_system_version: "20.04"
)
])
insert(:goal, site: site, event_name: "Signup")
conn = get(conn, "/#{site.domain}/export?filters=#{Jason.encode!(%{goal: "Signup"})}")
assert response = response(conn, 200)
{:ok, zip} = :zip.unzip(response, [:memory])
{_filename, os_versions} =
Enum.find(zip, fn {filename, _data} -> filename == ~c"operating_system_versions.csv" end)
assert parse_csv(os_versions) == [
["name", "version", "conversions", "conversion_rate"],
["Mac", "14", "3", "75.0"],
["Ubuntu", "20.04", "2", "100.0"],
["Lubuntu", "20.04", "1", "100.0"],
[""]
]
end
end
describe "GET /share/:domain?auth=:auth" do
test "prompts a password for a password-protected link", %{conn: conn} do
site = insert(:site)
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
link =
insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))
conn = get(conn, "/share/#{site.domain}?auth=#{link.slug}")
assert response(conn, 200) =~ "Enter password"
end
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
test "logs anonymous user in straight away if the link is not password-protected", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}")
assert html_response(conn, 200) =~ "stats-react-container"
end
test "returns page with X-Frame-Options disabled so it can be embedded in an iframe", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}")
resp = html_response(conn, 200)
assert text_of_attr(resp, @react_container, "data-embedded") == "false"
assert Plug.Conn.get_resp_header(conn, "x-frame-options") == []
end
2021-12-14 13:10:34 +03:00
test "returns page embedded page", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}&embed=true")
resp = html_response(conn, 200)
assert text_of_attr(resp, @react_container, "data-embedded") == "true"
assert Plug.Conn.get_resp_header(conn, "x-frame-options") == []
[{"div", attrs, _}] = find(resp, @react_container)
assert Enum.all?(attrs, fn {k, v} -> is_binary(k) and is_binary(v) end)
end
2021-12-14 13:10:34 +03:00
test "shows locked page if page is locked", %{conn: conn} do
site = insert(:site, domain: "test-site.com", locked: true)
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}")
assert html_response(conn, 200) =~ "Dashboard locked"
2021-12-14 13:10:34 +03:00
refute String.contains?(html_response(conn, 200), "Back to my sites")
end
test "renders 404 not found when no auth parameter supplied", %{conn: conn} do
conn = get(conn, "/share/example.com")
assert response(conn, 404) =~ "nothing here"
end
test "renders 404 not found when non-existent auth parameter is supplied", %{conn: conn} do
conn = get(conn, "/share/example.com?auth=bad-token")
assert response(conn, 404) =~ "nothing here"
end
test "renders 404 not found when auth parameter for another site is supplied", %{conn: conn} do
site1 = insert(:site, domain: "test-site-1.com")
site2 = insert(:site, domain: "test-site-2.com")
site1_link = insert(:shared_link, site: site1)
conn = get(conn, "/share/#{site2.domain}/?auth=#{site1_link.slug}")
assert response(conn, 404) =~ "nothing here"
end
end
describe "GET /share/:slug - backwards compatibility" do
test "it redirects to new shared link format for historical links", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
site_link = insert(:shared_link, site: site, inserted_at: ~N[2021-12-31 00:00:00])
conn = get(conn, "/share/#{site_link.slug}")
assert redirected_to(conn, 302) == "/share/#{site.domain}?auth=#{site_link.slug}"
end
test "it does nothing for newer links", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
site_link = insert(:shared_link, site: site, inserted_at: ~N[2022-01-01 00:00:00])
conn = get(conn, "/share/#{site_link.slug}")
assert response(conn, 404) =~ "nothing here"
end
end
describe "POST /share/:slug/authenticate" do
test "logs anonymous user in with correct password", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
link =
insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))
conn = post(conn, "/share/#{link.slug}/authenticate", %{password: "password"})
assert redirected_to(conn, 302) == "/share/#{site.domain}?auth=#{link.slug}"
conn = get(conn, "/share/#{site.domain}?auth=#{link.slug}")
assert html_response(conn, 200) =~ "stats-react-container"
end
test "shows form again with wrong password", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
Formatting only changes - No code change (#75) * first commit with test and compile job Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding 'prepare' stage Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci script to include "test" compile phase Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding environment variables for connecting to postgresql Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated ci config for postgres Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-alpine version of elixir Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * re-using the 'compile' artifacts and added explict env variables for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing redundant deps fetching from common code Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting using mix.format -- beware no-code changes! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * added release config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding consistent env variable for Database Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * more cleaning up of environment variables Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding releases config for enabling releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up env configs Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Cleaned up config and prepared config for releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated CI script with new config for test Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added Dockerfile for creating production docker image Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding "docker" build job yay! Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using non-slim version of debian and installing webpack Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding overlays for migrations on releases Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * restricting the docker built to master branch only Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * typo fix Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding "Hosting.md" to explain hosting instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removed the default comments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added documentation related to env variables * updated documentation and fixed typo Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated documentation * Bumping up elixir version as `overlays` are only supported in latest version read release notes: https://github.com/elixir-lang/elixir/releases/tag/v1.10.0 Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding tarball assembly during release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updated HOSTING.md Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for db migration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * minor corrections Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * initializing admin user Admin user has been added in the "migration" phase. A default user is automatically created in the process. One can provide the related env variables, else a new one will be automatically created for you. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Initial base domain update - phase#1 These changes are only meant for correct operating it under self-hosting. There are many other cosmetic changes, that require updates to email, site and other places where the original website and author is used. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Using dedicated config variable `base_domain` instead Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding base_domain to releases config Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * removing the dedicated config "base_domain", relying on endpoint host Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Removed the usage of "Mix" in code! It is bad practice to use "mix" module inside the code as in actual release this module is unavailable. Replacing this with a config environment variable Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Added support for SMTP via Bamboo Smtp Adapter Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Capturing SMTP errors via Sentry Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Minor updates Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * Adding junit formatter -- useful for generating test reports Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding documentation for default user * Resolve "Gitlab Adoption: Add supported services in "Security & Compliance"" * bumping up the debian version to fix issues fixing some vulnerabilities identified by the scanning tools * More updates for self-hosting Changes in most of the places to suit self-hosting. Although, there are some which have been left-off. Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * quick-dirty-fix! * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up the db connect timeout Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * bumping up timeout - skipping MRs :-/ * removing restrictions on watching for changes this stuff isn't working * Update HOSTING.md * renamed the module name * reverting formatting-whitespace changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * reverting the name to release Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * adding docker-compose.yml and related instructions Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * using `plausible_url` instead of assuming `https` this is because, it is much to test in local dev machines and in most cases there's already a layer above which is capable for `https` termination and http -> https upgrade Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * WIP: merging changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * wip: more changes * Pushing in changes from upstream Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * changes to ci for testing Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * cleaning up and finishing clickhouse integration Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * updating readme with hosting details * removing deleted files from upstream * minor config adjustments Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me> * formatting changes Signed-off-by: Chandra Tungathurthi <tckb@tgrthi.me>
2020-06-08 10:35:13 +03:00
link =
insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))
conn = post(conn, "/share/#{link.slug}/authenticate", %{password: "WRONG!"})
assert html_response(conn, 200) =~ "Enter password"
end
test "only gives access to the correct dashboard", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
site2 = insert(:site, domain: "test-site2.com")
link =
insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))
link2 =
insert(:shared_link,
site: site2,
password_hash: Plausible.Auth.Password.hash("password1")
)
conn = post(conn, "/share/#{link.slug}/authenticate", %{password: "password"})
assert redirected_to(conn, 302) == "/share/#{site.domain}?auth=#{link.slug}"
conn = get(conn, "/share/#{site2.domain}?auth=#{link2.slug}")
assert html_response(conn, 200) =~ "Enter password"
end
end
2019-09-02 14:29:19 +03:00
end