ScriptV2: Configuration renames, iteration (#5427)
* plausible-main -> plausible-web * Change elixir workflow * Update tracker option names in tracker.ex * config.hash -> config.hashBasedRouting * Enable revenue by default for plausible-web * Enable taggedEvents by default * config.local -> config.captureOnLocalhost * manual -> autoCapturePageviews * Update playwright tests * Support adding/editing file types for download in plausible-web Original docs: https://plausible.io/docs/file-downloads-tracking#what-if-i-want-to-track-a-different-file-type * rebase: initialize-page-dynamically update * chore: Bump tracker_script_version to 12 * Ignore pageviews in file-downloads.spec * Phrasing in tests * Remove unneeded conditional
This commit is contained in:
parent
1de10b7867
commit
98cdeb23dd
|
|
@ -83,9 +83,9 @@ jobs:
|
|||
filters: |
|
||||
tracker:
|
||||
- 'tracker/**'
|
||||
- name: Check if priv/tracker/js/plausible.js exists
|
||||
- name: Check if priv/tracker/js/plausible-web.js exists
|
||||
run: |
|
||||
if [ -f priv/tracker/js/plausible.js ]; then
|
||||
if [ -f priv/tracker/js/plausible-web.js ]; then
|
||||
echo "HAS_BUILT_TRACKER=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "HAS_BUILT_TRACKER=false" >> $GITHUB_ENV
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ defmodule PlausibleWeb.Tracker do
|
|||
use Plausible.Repo
|
||||
alias Plausible.Site.TrackerScriptConfiguration
|
||||
|
||||
path = Application.app_dir(:plausible, "priv/tracker/js/plausible-main.js")
|
||||
path = Application.app_dir(:plausible, "priv/tracker/js/plausible-web.js")
|
||||
# On CI, the file might not be present for static checks so we create an empty one
|
||||
File.touch!(path)
|
||||
|
||||
@plausible_main_script File.read!(path)
|
||||
@external_resource "priv/tracker/js/plausible-main.js"
|
||||
@external_resource "priv/tracker/js/plausible-web.js"
|
||||
|
||||
def plausible_main_script_tag(tracker_script_configuration) do
|
||||
config_js_content =
|
||||
|
|
@ -35,14 +35,10 @@ defmodule PlausibleWeb.Tracker do
|
|||
%{
|
||||
domain: tracker_script_configuration.site.domain,
|
||||
endpoint: "#{PlausibleWeb.Endpoint.url()}/api/event",
|
||||
hash: tracker_script_configuration.hash_based_routing,
|
||||
hashBasedRouting: tracker_script_configuration.hash_based_routing,
|
||||
outboundLinks: tracker_script_configuration.outbound_links,
|
||||
fileDownloads: tracker_script_configuration.file_downloads,
|
||||
taggedEvents: tracker_script_configuration.tagged_events,
|
||||
revenue: tracker_script_configuration.revenue_tracking,
|
||||
# Options not directly exposed via onboarding
|
||||
local: false,
|
||||
manual: false
|
||||
formSubmissions: tracker_script_configuration.form_submissions
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule PlausibleWeb.TrackerPlugTest do
|
|||
|
||||
alias PlausibleWeb.Tracker
|
||||
|
||||
describe "plausible-main.js" do
|
||||
describe "plausible-web.js" do
|
||||
@example_config %{
|
||||
installation_type: :manual,
|
||||
track_404_pages: true,
|
||||
|
|
@ -28,7 +28,8 @@ defmodule PlausibleWeb.TrackerPlugTest do
|
|||
outbound_links: false,
|
||||
pageview_props: false,
|
||||
tagged_events: true,
|
||||
revenue_tracking: false
|
||||
revenue_tracking: false,
|
||||
form_submissions: true
|
||||
}
|
||||
|
||||
test "returns the script for an existing site", %{conn: conn} do
|
||||
|
|
@ -45,9 +46,10 @@ defmodule PlausibleWeb.TrackerPlugTest do
|
|||
|
||||
assert String.contains?(response, "!function(){var")
|
||||
assert String.contains?(response, "domain:\"#{site.domain}\"")
|
||||
assert String.contains?(response, "hash:!0")
|
||||
assert String.contains?(response, "taggedEvents:!0")
|
||||
assert String.contains?(response, "hashBasedRouting:!0")
|
||||
assert String.contains?(response, "formSubmissions:!0")
|
||||
refute String.contains?(response, "outboundLinks:!0")
|
||||
refute String.contains?(response, "fileDownloads:!0")
|
||||
end
|
||||
|
||||
# window.plausible is a substring checked for by the wordpress plugin to avoid 'optimization' by other wordpress plugins
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ defmodule PlausibleWeb.TrackerTest do
|
|||
alias Plausible.Site.TrackerScriptConfiguration
|
||||
alias PlausibleWeb.Tracker
|
||||
|
||||
describe "plausible-main.js" do
|
||||
describe "plausible-web.js" do
|
||||
@example_config %{
|
||||
installation_type: :manual,
|
||||
track_404_pages: true,
|
||||
|
|
@ -14,7 +14,8 @@ defmodule PlausibleWeb.TrackerTest do
|
|||
outbound_links: false,
|
||||
pageview_props: false,
|
||||
tagged_events: true,
|
||||
revenue_tracking: false
|
||||
revenue_tracking: false,
|
||||
form_submissions: true
|
||||
}
|
||||
|
||||
test "can calculate config" do
|
||||
|
|
@ -24,13 +25,10 @@ defmodule PlausibleWeb.TrackerTest do
|
|||
assert PlausibleWeb.Tracker.plausible_main_config(tracker_script_configuration) == %{
|
||||
domain: site.domain,
|
||||
endpoint: "#{PlausibleWeb.Endpoint.url()}/api/event",
|
||||
hash: true,
|
||||
hashBasedRouting: true,
|
||||
outboundLinks: false,
|
||||
fileDownloads: false,
|
||||
taggedEvents: true,
|
||||
revenue: false,
|
||||
local: false,
|
||||
manual: false
|
||||
formSubmissions: true
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -41,7 +39,7 @@ defmodule PlausibleWeb.TrackerTest do
|
|||
script_tag = PlausibleWeb.Tracker.plausible_main_script_tag(tracker_script_configuration)
|
||||
|
||||
assert script_tag =~
|
||||
~s(={endpoint:"#{PlausibleWeb.Endpoint.url()}/api/event",domain:"#{site.domain}",taggedEvents:!0,hash:!0})
|
||||
~s(={endpoint:"#{PlausibleWeb.Endpoint.url()}/api/event",domain:"#{site.domain}",hashBasedRouting:!0,formSubmissions:!0})
|
||||
end
|
||||
|
||||
test "script tag escapes problematic characters as expected" do
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"manualVariants": [
|
||||
{
|
||||
"name": "plausible-main.js",
|
||||
"name": "plausible-web.js",
|
||||
"features": [
|
||||
"plausible-main"
|
||||
"plausible-web"
|
||||
],
|
||||
"globals": {
|
||||
"COMPILE_CONFIG": true,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"tracker_script_version": 11,
|
||||
"tracker_script_version": 12,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"deploy": "node compile.js",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ if (COMPILE_CONFIG) {
|
|||
|
||||
var endpoint
|
||||
var dataDomain
|
||||
var autoCapturePageviews = !COMPILE_MANUAL
|
||||
|
||||
// Exported public function
|
||||
function trigger(eventName, options) {
|
||||
|
|
@ -29,7 +30,7 @@ function trigger(eventName, options) {
|
|||
maxScrollDepthPx = getCurrentScrollDepthPx()
|
||||
}
|
||||
|
||||
if (!(COMPILE_LOCAL && (!COMPILE_CONFIG || config.local))) {
|
||||
if (!(COMPILE_LOCAL && (!COMPILE_CONFIG || config.captureOnLocalhost))) {
|
||||
if (/^localhost$|^127(\.[0-9]+){0,2}\.[0-9]+$|^\[::1?\]$/.test(location.hostname) || location.protocol === 'file:') {
|
||||
return onIgnoredEvent(eventName, options, 'localhost')
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ function trigger(eventName, options) {
|
|||
function pathMatches(wildcardPath) {
|
||||
var actualPath = location.pathname
|
||||
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hash)) {
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hashBasedRouting)) {
|
||||
actualPath += location.hash
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ function trigger(eventName, options) {
|
|||
payload.n = eventName
|
||||
payload.v = COMPILE_TRACKER_SCRIPT_VERSION
|
||||
|
||||
if (COMPILE_MANUAL && (!COMPILE_CONFIG || config.manual)) {
|
||||
if (COMPILE_MANUAL || !autoCapturePageviews) {
|
||||
var customURL = options && options.u
|
||||
|
||||
payload.u = customURL ? customURL : location.href
|
||||
|
|
@ -90,7 +91,7 @@ function trigger(eventName, options) {
|
|||
if (options && options.interactive === false) {
|
||||
payload.i = false
|
||||
}
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
if (options && options.revenue) {
|
||||
payload.$ = options.revenue
|
||||
}
|
||||
|
|
@ -124,7 +125,7 @@ function trigger(eventName, options) {
|
|||
}
|
||||
}
|
||||
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hash)) {
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hashBasedRouting)) {
|
||||
payload.h = 1
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +219,7 @@ function triggerEngagement() {
|
|||
runningEngagementStart = 0
|
||||
currentEngagementTime = 0
|
||||
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hash)) {
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hashBasedRouting)) {
|
||||
payload.h = 1
|
||||
}
|
||||
|
||||
|
|
@ -308,11 +309,12 @@ function init(overrides) {
|
|||
return
|
||||
}
|
||||
|
||||
// Explicitly set dataDomain before any overrides are applied as `plausible-main` does not support overriding it
|
||||
// Explicitly set dataDomain before any overrides are applied as `plausible-web` does not support overriding it
|
||||
dataDomain = COMPILE_CONFIG ? config.domain : scriptEl.getAttribute('data-domain')
|
||||
|
||||
if (COMPILE_CONFIG) {
|
||||
Object.assign(config, overrides)
|
||||
autoCapturePageviews = config.autoCapturePageviews !== false
|
||||
}
|
||||
|
||||
endpoint = COMPILE_CONFIG ? config.endpoint : (scriptEl.getAttribute('data-api') || defaultEndpoint())
|
||||
|
|
@ -342,11 +344,11 @@ function init(overrides) {
|
|||
}
|
||||
})
|
||||
|
||||
if (!(COMPILE_MANUAL && (!COMPILE_CONFIG || config.manual))) {
|
||||
if (!COMPILE_MANUAL || autoCapturePageviews) {
|
||||
var lastPage;
|
||||
|
||||
function page(isSPANavigation) {
|
||||
if (!(COMPILE_HASH && (!COMPILE_CONFIG || config.hash))) {
|
||||
if (!(COMPILE_HASH && (!COMPILE_CONFIG || config.hashBasedRouting))) {
|
||||
if (isSPANavigation && lastPage === location.pathname) return;
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +358,7 @@ function init(overrides) {
|
|||
|
||||
var onSPANavigation = function () { page(true) }
|
||||
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hash)) {
|
||||
if (COMPILE_HASH && (!COMPILE_CONFIG || config.hashBasedRouting)) {
|
||||
window.addEventListener('hashchange', onSPANavigation)
|
||||
} else {
|
||||
var his = window.history
|
||||
|
|
@ -419,7 +421,7 @@ function init(overrides) {
|
|||
var link = getLinkEl(event.target)
|
||||
var hrefWithoutQuery = link && link.href && link.href.split('?')[0]
|
||||
|
||||
if (COMPILE_TAGGED_EVENTS && (!COMPILE_CONFIG || config.taggedEvents)) {
|
||||
if (COMPILE_TAGGED_EVENTS) {
|
||||
if (isElementOrParentTagged(link, 0)) {
|
||||
// Return to prevent sending multiple events with the same action.
|
||||
// Clicks on tagged links are handled by another function.
|
||||
|
|
@ -452,7 +454,7 @@ function init(overrides) {
|
|||
|
||||
if (shouldFollowLink(event, link)) {
|
||||
var attrs = { props: eventAttrs.props, callback: followLink }
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
attrs.revenue = eventAttrs.revenue
|
||||
}
|
||||
plausible(eventAttrs.name, attrs)
|
||||
|
|
@ -460,7 +462,7 @@ function init(overrides) {
|
|||
event.preventDefault()
|
||||
} else {
|
||||
var attrs = { props: eventAttrs.props }
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
attrs.revenue = eventAttrs.revenue
|
||||
}
|
||||
plausible(eventAttrs.name, attrs)
|
||||
|
|
@ -478,9 +480,23 @@ function init(overrides) {
|
|||
|
||||
if (COMPILE_FILE_DOWNLOADS && (!COMPILE_CONFIG || config.fileDownloads)) {
|
||||
var defaultFileTypes = ['pdf', 'xlsx', 'docx', 'txt', 'rtf', 'csv', 'exe', 'key', 'pps', 'ppt', 'pptx', '7z', 'pkg', 'rar', 'gz', 'zip', 'avi', 'mov', 'mp4', 'mpeg', 'wmv', 'midi', 'mp3', 'wav', 'wma', 'dmg']
|
||||
var fileTypesAttr = scriptEl.getAttribute('file-types')
|
||||
var addFileTypesAttr = scriptEl.getAttribute('add-file-types')
|
||||
var fileTypesToTrack = (fileTypesAttr && fileTypesAttr.split(",")) || (addFileTypesAttr && addFileTypesAttr.split(",").concat(defaultFileTypes)) || defaultFileTypes;
|
||||
var fileTypesToTrack = defaultFileTypes
|
||||
|
||||
if (COMPILE_CONFIG) {
|
||||
if (Array.isArray(config.fileDownloads)) {
|
||||
fileTypesToTrack = config.fileDownloads
|
||||
}
|
||||
} else {
|
||||
var fileTypesAttr = scriptEl.getAttribute('file-types')
|
||||
var addFileTypesAttr = scriptEl.getAttribute('add-file-types')
|
||||
|
||||
if (fileTypesAttr) {
|
||||
fileTypesToTrack = fileTypesAttr.split(",")
|
||||
}
|
||||
if (addFileTypesAttr) {
|
||||
fileTypesToTrack = addFileTypesAttr.split(",").concat(defaultFileTypes)
|
||||
}
|
||||
}
|
||||
|
||||
function isDownloadToTrack(url) {
|
||||
if (!url) { return false }
|
||||
|
|
@ -491,7 +507,7 @@ function init(overrides) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (COMPILE_CONFIG && config.formSubmissions) {
|
||||
function trackFormSubmission(e) {
|
||||
if (e.target.hasAttribute('novalidate') || e.target.checkValidity()) {
|
||||
|
|
@ -502,13 +518,13 @@ function init(overrides) {
|
|||
document.addEventListener('submit', trackFormSubmission, true);
|
||||
}
|
||||
|
||||
if (COMPILE_TAGGED_EVENTS && (!COMPILE_CONFIG || config.taggedEvents)) {
|
||||
if (COMPILE_TAGGED_EVENTS) {
|
||||
// Finds event attributes by iterating over the given element's (or its
|
||||
// parent's) classList. Returns an object with `name` and `props` keys.
|
||||
function getTaggedEventAttributes(htmlElement) {
|
||||
var taggedElement = isTagged(htmlElement) ? htmlElement : htmlElement && htmlElement.parentNode
|
||||
var eventAttrs = { name: null, props: {} }
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
eventAttrs.revenue = {}
|
||||
}
|
||||
|
||||
|
|
@ -530,7 +546,7 @@ function init(overrides) {
|
|||
}
|
||||
}
|
||||
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
var revenueMatchList = className.match(/plausible-revenue-(.+)(=|--)(.+)/)
|
||||
if (revenueMatchList) {
|
||||
var key = revenueMatchList[1]
|
||||
|
|
@ -561,7 +577,7 @@ function init(overrides) {
|
|||
setTimeout(submitForm, 5000)
|
||||
|
||||
var attrs = { props: eventAttrs.props, callback: submitForm }
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
attrs.revenue = eventAttrs.revenue
|
||||
}
|
||||
plausible(eventAttrs.name, attrs)
|
||||
|
|
@ -603,7 +619,7 @@ function init(overrides) {
|
|||
} else {
|
||||
var attrs = {}
|
||||
attrs.props = eventAttrs.props
|
||||
if (COMPILE_REVENUE && (!COMPILE_CONFIG || config.revenue)) {
|
||||
if (COMPILE_REVENUE) {
|
||||
attrs.revenue = eventAttrs.revenue
|
||||
}
|
||||
plausible(eventAttrs.name, attrs)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { mockRequest, mockManyRequests, metaKey, expectPlausibleInAction } from
|
|||
import { expect, test } from '@playwright/test'
|
||||
import { LOCAL_SERVER_ADDR } from './support/server'
|
||||
|
||||
test.describe('file-downloads extension', () => {
|
||||
test.describe('legacy file-downloads extension', () => {
|
||||
test('sends event and does not start download when link opens in new tab', async ({ page }) => {
|
||||
await page.goto('/file-download.html')
|
||||
const downloadURL = await page.locator('#link').getAttribute('href')
|
||||
|
|
@ -51,3 +51,71 @@ test.describe('file-downloads extension', () => {
|
|||
expect((await downloadRequestMockList).length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
test.describe('file downloads', () => {
|
||||
test.beforeEach(({ page }) => {
|
||||
// Mock file download requests
|
||||
mockRequest(page, 'https://awesome.website.com/file.iso')
|
||||
})
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
domain: 'example.com',
|
||||
endpoint: `${LOCAL_SERVER_ADDR}/api/event`,
|
||||
captureOnLocalhost: true,
|
||||
autoCapturePageviews: false
|
||||
}
|
||||
|
||||
async function openPage(page, config) {
|
||||
await page.goto(`/file-download-plausible-web.html`)
|
||||
await page.waitForFunction('window.plausible !== undefined')
|
||||
|
||||
await page.evaluate((config) => {
|
||||
window.plausible.init(config)
|
||||
}, { ...DEFAULT_CONFIG, ...config })
|
||||
}
|
||||
|
||||
test('does not track iso files by default', async ({ page }) => {
|
||||
await openPage(page, { fileDownloads: true })
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => page.click('#file-download-iso', { modifiers: [metaKey()] }),
|
||||
expectedRequests: [],
|
||||
rejectRequests: [{ n: 'File Download' }],
|
||||
})
|
||||
})
|
||||
|
||||
test('tracks iso but not pdf files when config.fileDownloads includes "iso"', async ({ page }) => {
|
||||
await openPage(page, { fileDownloads: ['iso'] })
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: async () => {
|
||||
await page.click('#file-download-iso', { modifiers: [metaKey()] })
|
||||
await page.click('#file-download', { modifiers: [metaKey()] })
|
||||
},
|
||||
expectedRequests: [
|
||||
{ n: 'File Download', p: { url: 'https://awesome.website.com/file.iso' } },
|
||||
],
|
||||
rejectRequests: [
|
||||
{ n: 'File Download', p: { url: 'https://awesome.website.com/file.pdf' } },
|
||||
]
|
||||
})
|
||||
})
|
||||
|
||||
test('ignores malformed value but enables the feature', async ({ page }) => {
|
||||
await openPage(page, { fileDownloads: 'iso' })
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: async () => {
|
||||
await page.click('#file-download-iso', { modifiers: [metaKey()] })
|
||||
await page.click('#file-download', { modifiers: [metaKey()] })
|
||||
},
|
||||
expectedRequests: [
|
||||
{ n: 'File Download', p: { url: 'https://awesome.website.com/file.pdf' } },
|
||||
],
|
||||
rejectRequests: [
|
||||
{ n: 'File Download', p: { url: 'https://awesome.website.com/file.iso' } },
|
||||
]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>plausible-web.js tests</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a id="file-download" href="https://awesome.website.com/file.pdf">Download</a>
|
||||
<a id="file-download-iso" href="https://awesome.website.com/file.iso">Download ISO</a>
|
||||
|
||||
<script>
|
||||
window.plausible=window.plausible||function(){(window.plausible.q = window.plausible.q || []).push(arguments)}
|
||||
window.plausible.init = function(overrides) { window.plausible.o = overrides || {} }
|
||||
|
||||
var config = { domain: 'example.com' }
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = `/tracker/js/plausible-web.js?script_config=${encodeURIComponent(JSON.stringify(config))}`
|
||||
var r = document.getElementsByTagName("script")[0]
|
||||
r.parentNode.insertBefore(script, r)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -5,15 +5,16 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>plausible-main.js tests</title>
|
||||
<title>plausible-web.js tests</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a id="file-download" href="https://awesome.website.com/file.pdf">Download</a>
|
||||
<a id="file-download-iso" href="https://awesome.website.com/file.iso">Download ISO</a>
|
||||
|
||||
<a id="outbound-link" href="https://example.com">Outbound link</a>
|
||||
|
||||
<a id="manual-pageview" onclick="plausible('pageview', { u: '/:test-plausible-main' })">Manual pageview</a>
|
||||
<a id="manual-pageview" onclick="plausible('pageview', { u: '/:test-plausible-web' })">Manual pageview</a>
|
||||
|
||||
<a id="custom-event" onclick="plausible('Custom event', { props: { author: 'Karl' } })">Custom event</a>
|
||||
|
||||
|
|
@ -44,7 +45,7 @@
|
|||
// Load the script with the passed config
|
||||
const config = params.get('script_config')
|
||||
const script = document.createElement('script')
|
||||
script.src = `/tracker/js/plausible-main.js?script_config=${encodeURIComponent(config)}`
|
||||
script.src = `/tracker/js/plausible-web.js?script_config=${encodeURIComponent(config)}`
|
||||
|
||||
var r = document.getElementsByTagName("script")[0]
|
||||
r.parentNode.insertBefore(script, r);
|
||||
|
|
@ -10,7 +10,7 @@ import { ScriptConfig } from './support/types'
|
|||
const DEFAULT_CONFIG: ScriptConfig = {
|
||||
domain: 'example.com',
|
||||
endpoint: `${LOCAL_SERVER_ADDR}/api/event`,
|
||||
local: true
|
||||
captureOnLocalhost: true
|
||||
}
|
||||
|
||||
test('does not track form submissions when the feature is disabled', async ({
|
||||
|
|
@ -70,7 +70,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await page.fill('input[type="text"]', 'Any Name')
|
||||
await page.click('input[type="submit"]')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -101,7 +101,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await ensurePlausibleInitialized(page)
|
||||
await page.click('input[type="submit"]')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -140,7 +140,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await page.click('button#dynamically-insert-form')
|
||||
await page.click('input[type="submit"]')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -174,7 +174,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await page.fill('input[type="email"]', 'invalid email')
|
||||
await page.click('input[type="submit"]')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -276,7 +276,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await ensurePlausibleInitialized(page)
|
||||
await page.click('input[type="submit"]')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -290,7 +290,7 @@ test.describe('form submissions feature is enabled', () => {
|
|||
await page.fill('input[type="email"]', 'customer@example.com')
|
||||
await page.keyboard.press('Enter')
|
||||
},
|
||||
shouldIgnoreRequest: isViewOrEngagementEvent,
|
||||
shouldIgnoreRequest: pageviewOrEngagementEvent,
|
||||
expectedRequests: [
|
||||
{
|
||||
n: 'Form Submission',
|
||||
|
|
@ -301,16 +301,15 @@ test.describe('form submissions feature is enabled', () => {
|
|||
})
|
||||
})
|
||||
/**
|
||||
* This function mitigates test flakiness due to the test runner triggering the submit action
|
||||
* before the tracker script has attached the event listener.
|
||||
* This flakiness will happen in the real world as well:
|
||||
* forms submitted before the tracker script attaches the event listener will not be tracked.
|
||||
* This function ensures that the tracker script has attached the event listener before test is run.
|
||||
* Note that this race condition happens in the real world as well:
|
||||
* forms submitted before the tracker script is initialized will not be tracked.
|
||||
*/
|
||||
function ensurePlausibleInitialized(page: Page) {
|
||||
return page.waitForFunction(() => (window as any).plausible?.l === true)
|
||||
}
|
||||
|
||||
const isViewOrEngagementEvent = ({ n }) =>
|
||||
const pageviewOrEngagementEvent = ({ n }) =>
|
||||
['pageview', 'engagement'].includes(n)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Tests for plausible-main.js script variant
|
||||
Tests for plausible-web.js script variant
|
||||
|
||||
Unlike in production, we're manually interpolating the script config in this file to
|
||||
better test the script in isolation of the plausible codebase.
|
||||
|
|
@ -18,12 +18,12 @@ import { LOCAL_SERVER_ADDR } from './support/server'
|
|||
const DEFAULT_CONFIG = {
|
||||
domain: 'example.com',
|
||||
endpoint: `${LOCAL_SERVER_ADDR}/api/event`,
|
||||
local: true
|
||||
captureOnLocalhost: true
|
||||
}
|
||||
|
||||
async function openPage(page, config, options = {}) {
|
||||
const configJson = JSON.stringify({ ...DEFAULT_CONFIG, ...config })
|
||||
let path = `/plausible-main.html?script_config=${configJson}`
|
||||
let path = `/plausible-web.html?script_config=${configJson}`
|
||||
if (options.beforeScriptLoaded) {
|
||||
path += `&beforeScriptLoaded=${options.beforeScriptLoaded}`
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ async function openPage(page, config, options = {}) {
|
|||
await page.waitForFunction('window.plausible !== undefined')
|
||||
}
|
||||
|
||||
test.describe('plausible-main.js', () => {
|
||||
test.describe('plausible-web.js', () => {
|
||||
test.beforeEach(({ page }) => {
|
||||
// Mock file download requests
|
||||
mockRequest(page, 'https://awesome.website.com/file.pdf')
|
||||
|
|
@ -43,33 +43,32 @@ test.describe('plausible-main.js', () => {
|
|||
test('triggers pageview and engagement automatically', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => openPage(page, {}),
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-main.html')}]
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-web.html')}]
|
||||
})
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => hideAndShowCurrentTab(page, {delay: 2000}),
|
||||
expectedRequests: [{n: 'engagement', d: 'example.com', u: expecting.stringContaining('plausible-main.html')}],
|
||||
expectedRequests: [{n: 'engagement', d: 'example.com', u: expecting.stringContaining('plausible-web.html')}],
|
||||
})
|
||||
})
|
||||
|
||||
test('does not trigger any events when `local` config is disabled', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => openPage(page, { local: false }),
|
||||
action: () => openPage(page, { captureOnLocalhost: false }),
|
||||
expectedRequests: [],
|
||||
refutedRequests: [{ n: 'pageview' }]
|
||||
})
|
||||
})
|
||||
|
||||
test('does not track pageview props, outbound links, file downloads or tagged events without features being enabled', async ({ page }) => {
|
||||
test('does not track pageview props, outbound links or file downloads without features being enabled', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: async () => {
|
||||
await openPage(page, {})
|
||||
await page.click('#file-download', { modifiers: [metaKey()] })
|
||||
await page.click('#tagged-event')
|
||||
await page.click('#outbound-link')
|
||||
},
|
||||
expectedRequests: [{ n: 'pageview', p: expecting.toBeUndefined() }],
|
||||
refutedRequests: [{ n: 'File Download' }, { n: 'Purchase' }, { n: 'Outbound Link: Click' }],
|
||||
refutedRequests: [{ n: 'File Download' }, { n: 'Outbound Link: Click' }],
|
||||
// Webkit captures engagement events differently, so we ignore them in this test
|
||||
shouldIgnoreRequest: (payload) => payload.n === 'engagement'
|
||||
})
|
||||
|
|
@ -82,8 +81,8 @@ test.describe('plausible-main.js', () => {
|
|||
await page.click('#outbound-link')
|
||||
},
|
||||
expectedRequests: [
|
||||
{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-main.html') },
|
||||
{ n: 'Outbound Link: Click', d: 'example.com', u: expecting.stringContaining('plausible-main.html'), p: { url: 'https://example.com/' } },
|
||||
{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-web.html') },
|
||||
{ n: 'Outbound Link: Click', d: 'example.com', u: expecting.stringContaining('plausible-web.html'), p: { url: 'https://example.com/' } },
|
||||
]
|
||||
})
|
||||
})
|
||||
|
|
@ -117,7 +116,7 @@ test.describe('plausible-main.js', () => {
|
|||
plausible.init({ customProperties: () => ({ "title": document.title }) })
|
||||
})
|
||||
},
|
||||
expectedRequests: [{ n: 'pageview', p: { "title": "plausible-main.js tests" } }]
|
||||
expectedRequests: [{ n: 'pageview', p: { "title": "plausible-web.js tests" } }]
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -167,10 +166,10 @@ test.describe('plausible-main.js', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('manual mode does not track pageviews', async ({ page }) => {
|
||||
test('autoCapturePageviews=false mode does not track pageviews', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: async () => {
|
||||
await openPage(page, { manual: true })
|
||||
await openPage(page, { autoCapturePageviews: false })
|
||||
await hideAndShowCurrentTab(page, { delay: 200 })
|
||||
},
|
||||
expectedRequests: [],
|
||||
|
|
@ -178,21 +177,21 @@ test.describe('plausible-main.js', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('manual mode after manual pageview continues tracking', async ({ page }) => {
|
||||
test('autoCapturePageviews=false mode after manual pageview continues tracking', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: async () => {
|
||||
await openPage(page, { manual: true })
|
||||
await openPage(page, { autoCapturePageviews: false })
|
||||
await page.click('#manual-pageview')
|
||||
await hideAndShowCurrentTab(page, { delay: 200 })
|
||||
},
|
||||
expectedRequests: [
|
||||
{ n: 'pageview', u: '/:test-plausible-main', d: 'example.com' },
|
||||
{ n: 'engagement', u: '/:test-plausible-main', d: 'example.com' },
|
||||
{ n: 'pageview', u: '/:test-plausible-web', d: 'example.com' },
|
||||
{ n: 'engagement', u: '/:test-plausible-web', d: 'example.com' },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test('does not send `h` parameter when `hash` config is disabled', async ({ page }) => {
|
||||
test('does not send `h` parameter when `hashBasedRouting` config is disabled', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => openPage(page, {}),
|
||||
expectedRequests: [{ n: 'pageview', h: expecting.toBeUndefined() }]
|
||||
|
|
@ -201,22 +200,13 @@ test.describe('plausible-main.js', () => {
|
|||
|
||||
test('sends `h` parameter when `hash` config is enabled', async ({ page }) => {
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => openPage(page, { hash: true }),
|
||||
action: () => openPage(page, { hashBasedRouting: true }),
|
||||
expectedRequests: [{ n: 'pageview', h: 1 }]
|
||||
})
|
||||
})
|
||||
|
||||
test('tracking tagged events (when feature enabled)', async ({ page }) => {
|
||||
await openPage(page, { taggedEvents: true })
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => page.click('#tagged-event'),
|
||||
expectedRequests: [{ n: 'Purchase', p: { foo: 'bar' }, $: expecting.toBeUndefined() }]
|
||||
})
|
||||
})
|
||||
|
||||
test('tracking tagged events with revenue (when enabled)', async ({ page }) => {
|
||||
await openPage(page, { taggedEvents: true, revenue: true })
|
||||
test('tracking tagged events with revenue', async ({ page }) => {
|
||||
await openPage(page, {})
|
||||
|
||||
await expectPlausibleInAction(page, {
|
||||
action: () => page.click('#tagged-event'),
|
||||
|
|
@ -279,7 +269,7 @@ test.describe('plausible-main.js', () => {
|
|||
})
|
||||
})
|
||||
},
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-main.html') }]
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-web.html') }]
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -294,7 +284,7 @@ test.describe('plausible-main.js', () => {
|
|||
})
|
||||
})
|
||||
},
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-main.html')}]
|
||||
expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining('plausible-web.html')}]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -31,7 +31,7 @@ export async function initializePageDynamically(
|
|||
await route.fulfill({
|
||||
body: TEMPLATE.replace(
|
||||
"<%= plausible_script_url %>",
|
||||
`/tracker/js/plausible-main.js?script_config=${encodeURIComponent(
|
||||
`/tracker/js/plausible-web.js?script_config=${encodeURIComponent(
|
||||
JSON.stringify(scriptConfig)
|
||||
)}`
|
||||
).replace("<body></body>", `<body>${bodyContent}</body>`),
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export function runLocalFileServer() {
|
|||
|
||||
let code = compileFile(variant, { returnCode: true })
|
||||
|
||||
if (name === 'plausible-main.js') {
|
||||
if (name === 'plausible-web.js') {
|
||||
code = code.replace('"<%= @config_js %>"', req.query.script_config)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
export type Options = {
|
||||
hash: boolean;
|
||||
local: boolean;
|
||||
exclusions: boolean;
|
||||
manual: boolean;
|
||||
revenue: boolean;
|
||||
pageviewProps: boolean;
|
||||
hashBasedRouting: boolean;
|
||||
outboundLinks: boolean;
|
||||
fileDownloads: boolean;
|
||||
taggedEvents: boolean;
|
||||
formSubmissions: boolean;
|
||||
captureOnLocalhost: boolean;
|
||||
autoCapturePageviews: boolean;
|
||||
};
|
||||
|
||||
export type ScriptConfig = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue