mirror of
https://github.com/plausible/analytics.git
synced 2024-11-26 11:44:03 +03:00
Average Scroll Depth Metric: extracted tracker changes (#4826)
* (cherry-pick) implement scroll depth tracking under pageleave variant * drop unnecessary vars * remove unused require * add scroll depth tests * improve error messages in test util * reevaluate currentDocumentHeight on page load * account for dynamically loaded content when initializing documnent height * remove all semicolons from tracker specs * allow window and document globals in tracker eslint * tweak global tracker dir eslint rules * update comment * reevaluate document height on scroll * add test * remove unneccessary timeout
This commit is contained in:
parent
4d9ea15e9e
commit
2a7d02b6f0
@ -2,12 +2,35 @@
|
|||||||
"parserOptions": { "ecmaVersion": "latest" },
|
"parserOptions": { "ecmaVersion": "latest" },
|
||||||
"env": { "node": true, "es6": true },
|
"env": { "node": true, "es6": true },
|
||||||
"extends": ["eslint:recommended", "plugin:playwright/playwright-test"],
|
"extends": ["eslint:recommended", "plugin:playwright/playwright-test"],
|
||||||
|
"globals": {
|
||||||
|
"window": "readonly",
|
||||||
|
"document": "readonly"
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"max-len": [0, {"code": 120}],
|
"max-len": [0, {"code": 120}],
|
||||||
"max-classes-per-file": [0],
|
"max-classes-per-file": [0],
|
||||||
"no-unused-expressions": [1, { "allowShortCircuit": true }],
|
"no-unused-expressions": [1, { "allowShortCircuit": true }],
|
||||||
"no-unused-vars": [2, { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }],
|
"no-unused-vars": [2, { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" }],
|
||||||
"no-prototype-builtins": [0],
|
"no-prototype-builtins": [0],
|
||||||
"playwright/no-conditional-in-test": [0]
|
"playwright/no-conditional-in-test": [0],
|
||||||
|
"playwright/no-wait-for-timeout": "off",
|
||||||
|
"playwright/expect-expect": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"assertFunctionNames": [
|
||||||
|
"expect",
|
||||||
|
"clickPageElementAndExpectEventRequests",
|
||||||
|
"expectCustomEvent"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.spec.js"],
|
||||||
|
"rules": {
|
||||||
|
"semi": ["warn", "never"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -48,6 +48,49 @@
|
|||||||
// flag prevents sending multiple pageleaves in those cases.
|
// flag prevents sending multiple pageleaves in those cases.
|
||||||
var pageLeaveSending = false
|
var pageLeaveSending = false
|
||||||
|
|
||||||
|
function getDocumentHeight() {
|
||||||
|
return Math.max(
|
||||||
|
document.body.scrollHeight || 0,
|
||||||
|
document.body.offsetHeight || 0,
|
||||||
|
document.body.clientHeight || 0,
|
||||||
|
document.documentElement.scrollHeight || 0,
|
||||||
|
document.documentElement.offsetHeight || 0,
|
||||||
|
document.documentElement.clientHeight || 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentScrollDepthPx() {
|
||||||
|
var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0
|
||||||
|
var scrollTop = window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0
|
||||||
|
|
||||||
|
return currentDocumentHeight <= viewportHeight ? currentDocumentHeight : scrollTop + viewportHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDocumentHeight = getDocumentHeight()
|
||||||
|
var maxScrollDepthPx = getCurrentScrollDepthPx()
|
||||||
|
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
currentDocumentHeight = getDocumentHeight()
|
||||||
|
|
||||||
|
// Update the document height again after every 200ms during the
|
||||||
|
// next 3 seconds. This makes sure dynamically loaded content is
|
||||||
|
// also accounted for.
|
||||||
|
var count = 0
|
||||||
|
var interval = setInterval(function () {
|
||||||
|
currentDocumentHeight = getDocumentHeight()
|
||||||
|
if (++count === 15) {clearInterval(interval)}
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('scroll', function() {
|
||||||
|
currentDocumentHeight = getDocumentHeight()
|
||||||
|
var currentScrollDepthPx = getCurrentScrollDepthPx()
|
||||||
|
|
||||||
|
if (currentScrollDepthPx > maxScrollDepthPx) {
|
||||||
|
maxScrollDepthPx = currentScrollDepthPx
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function triggerPageLeave() {
|
function triggerPageLeave() {
|
||||||
if (pageLeaveSending) {return}
|
if (pageLeaveSending) {return}
|
||||||
pageLeaveSending = true
|
pageLeaveSending = true
|
||||||
@ -55,6 +98,7 @@
|
|||||||
|
|
||||||
var payload = {
|
var payload = {
|
||||||
n: 'pageleave',
|
n: 'pageleave',
|
||||||
|
sd: Math.round((maxScrollDepthPx / currentDocumentHeight) * 100),
|
||||||
d: dataDomain,
|
d: dataDomain,
|
||||||
u: currentPageLeaveURL,
|
u: currentPageLeaveURL,
|
||||||
}
|
}
|
||||||
@ -202,6 +246,8 @@
|
|||||||
if (isSPANavigation && listeningPageLeave) {
|
if (isSPANavigation && listeningPageLeave) {
|
||||||
triggerPageLeave();
|
triggerPageLeave();
|
||||||
currentPageLeaveURL = location.href;
|
currentPageLeaveURL = location.href;
|
||||||
|
currentDocumentHeight = getDocumentHeight()
|
||||||
|
maxScrollDepthPx = getCurrentScrollDepthPx()
|
||||||
}
|
}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const { mockRequest, mockManyRequests, expectCustomEvent } = require('./support/test-utils');
|
const { mockRequest, mockManyRequests, expectCustomEvent } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
const { LOCAL_SERVER_ADDR } = require('./support/server');
|
const { LOCAL_SERVER_ADDR } = require('./support/server')
|
||||||
|
|
||||||
|
|
||||||
test.describe('script.file-downloads.outbound-links.tagged-events.js', () => {
|
test.describe('script.file-downloads.outbound-links.tagged-events.js', () => {
|
||||||
test('sends only outbound link event when clicked link is both download and outbound', async ({ page }) => {
|
test('sends only outbound link event when clicked link is both download and outbound', async ({ page }) => {
|
||||||
@ -14,7 +13,7 @@ test.describe('script.file-downloads.outbound-links.tagged-events.js', () => {
|
|||||||
const requests = await plausibleRequestMockList
|
const requests = await plausibleRequestMockList
|
||||||
expect(requests.length).toBe(1)
|
expect(requests.length).toBe(1)
|
||||||
expectCustomEvent(requests[0], 'Outbound Link: Click', {url: downloadURL})
|
expectCustomEvent(requests[0], 'Outbound Link: Click', {url: downloadURL})
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends file download event when local download link clicked', async ({ page }) => {
|
test('sends file download event when local download link clicked', async ({ page }) => {
|
||||||
await page.goto('/custom-event-edge-case.html')
|
await page.goto('/custom-event-edge-case.html')
|
||||||
@ -24,7 +23,7 @@ test.describe('script.file-downloads.outbound-links.tagged-events.js', () => {
|
|||||||
await page.click('#local-download')
|
await page.click('#local-download')
|
||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'File Download', {url: downloadURL})
|
expectCustomEvent(await plausibleRequestMock, 'File Download', {url: downloadURL})
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends only tagged event when clicked link is tagged + outbound + download', async ({ page }) => {
|
test('sends only tagged event when clicked link is tagged + outbound + download', async ({ page }) => {
|
||||||
await page.goto('/custom-event-edge-case.html')
|
await page.goto('/custom-event-edge-case.html')
|
||||||
@ -35,5 +34,5 @@ test.describe('script.file-downloads.outbound-links.tagged-events.js', () => {
|
|||||||
const requests = await plausibleRequestMockList
|
const requests = await plausibleRequestMockList
|
||||||
expect(requests.length).toBe(1)
|
expect(requests.length).toBe(1)
|
||||||
expectCustomEvent(requests[0], 'Foo', {})
|
expectCustomEvent(requests[0], 'Foo', {})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const { mockRequest, expectCustomEvent, mockManyRequests, metaKey } = require('./support/test-utils');
|
const { mockRequest, expectCustomEvent, mockManyRequests, metaKey } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
const { LOCAL_SERVER_ADDR } = require('./support/server');
|
const { LOCAL_SERVER_ADDR } = require('./support/server')
|
||||||
|
|
||||||
|
|
||||||
test.describe('file-downloads extension', () => {
|
test.describe('file-downloads extension', () => {
|
||||||
test('sends event and does not start download when link opens in new tab', async ({ page }) => {
|
test('sends event and does not start download when link opens in new tab', async ({ page }) => {
|
||||||
@ -14,7 +13,7 @@ test.describe('file-downloads extension', () => {
|
|||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: downloadURL })
|
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: downloadURL })
|
||||||
expect(await downloadRequestMock, "should not make download request").toBeNull()
|
expect(await downloadRequestMock, "should not make download request").toBeNull()
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends event and starts download when link child is clicked', async ({ page }) => {
|
test('sends event and starts download when link child is clicked', async ({ page }) => {
|
||||||
await page.goto('/file-download.html')
|
await page.goto('/file-download.html')
|
||||||
@ -26,7 +25,7 @@ test.describe('file-downloads extension', () => {
|
|||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: downloadURL })
|
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: downloadURL })
|
||||||
expect((await downloadRequestMock).url()).toContain(downloadURL)
|
expect((await downloadRequestMock).url()).toContain(downloadURL)
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends File Download event with query-stripped url property', async ({ page }) => {
|
test('sends File Download event with query-stripped url property', async ({ page }) => {
|
||||||
await page.goto('/file-download.html')
|
await page.goto('/file-download.html')
|
||||||
@ -37,7 +36,7 @@ test.describe('file-downloads extension', () => {
|
|||||||
|
|
||||||
const expectedURL = downloadURL.split("?")[0]
|
const expectedURL = downloadURL.split("?")[0]
|
||||||
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: expectedURL })
|
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: expectedURL })
|
||||||
});
|
})
|
||||||
|
|
||||||
test('starts download only once', async ({ page }) => {
|
test('starts download only once', async ({ page }) => {
|
||||||
await page.goto('/file-download.html')
|
await page.goto('/file-download.html')
|
||||||
@ -47,5 +46,5 @@ test.describe('file-downloads extension', () => {
|
|||||||
await page.click('#local-download')
|
await page.click('#local-download')
|
||||||
|
|
||||||
expect((await downloadRequestMockList).length).toBe(1)
|
expect((await downloadRequestMockList).length).toBe(1)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
BIN
tracker/test/fixtures/img/black3x3000.png
vendored
Normal file
BIN
tracker/test/fixtures/img/black3x3000.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 109 B |
35
tracker/test/fixtures/scroll-depth-content-onscroll.html
vendored
Normal file
35
tracker/test/fixtures/scroll-depth-content-onscroll.html
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<!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 Playwright tests</title>
|
||||||
|
<script defer src="/tracker/js/plausible.local.pageleave.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="main" style="height: 3000px; background-color: lightblue;">
|
||||||
|
<a id="navigate-away" href="/manual.html">Navigate away</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const mainDiv = document.getElementById("main")
|
||||||
|
let loadedMore = false
|
||||||
|
|
||||||
|
window.addEventListener("scroll", () => {
|
||||||
|
if (!loadedMore && window.innerHeight + window.scrollY >= document.body.offsetHeight) {
|
||||||
|
loadedMore = true
|
||||||
|
|
||||||
|
const newDiv = document.createElement("div")
|
||||||
|
newDiv.setAttribute("id", "more-content")
|
||||||
|
newDiv.setAttribute("style", "height: 2000px; background-color: lightcoral;")
|
||||||
|
document.body.appendChild(newDiv);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
tracker/test/fixtures/scroll-depth-dynamic-content-load.html
vendored
Normal file
25
tracker/test/fixtures/scroll-depth-dynamic-content-load.html
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<script defer src="/tracker/js/plausible.local.pageleave.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<br>
|
||||||
|
<div id="content"></div>
|
||||||
|
<script>
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById('content').innerHTML =
|
||||||
|
`
|
||||||
|
<div style="height: 5000px; background-color: gray;">
|
||||||
|
<a id="navigate-away" href="/manual.html">
|
||||||
|
Navigate away
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}, 500)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
37
tracker/test/fixtures/scroll-depth-hash.html
vendored
Normal file
37
tracker/test/fixtures/scroll-depth-hash.html
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!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 Playwright tests</title>
|
||||||
|
<script defer src="/tracker/js/plausible.hash.local.pageleave.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<a id="home-link" href="#home">Home</a>
|
||||||
|
<a id="about-link" href="#about">About</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="content"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const routes = {
|
||||||
|
'#home': '<h1>Home</h1><p>Welcome to the Home page!</p>',
|
||||||
|
'#about': '<h1>About</h1><p style="height: 2000px;">Learn more about us here</p>',
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadContent() {
|
||||||
|
const hash = window.location.hash || '#home';
|
||||||
|
const content = routes[hash]
|
||||||
|
document.getElementById('content').innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('hashchange', loadContent);
|
||||||
|
window.addEventListener('DOMContentLoaded', loadContent);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
16
tracker/test/fixtures/scroll-depth-slow-window-load.html
vendored
Normal file
16
tracker/test/fixtures/scroll-depth-slow-window-load.html
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<script defer src="/tracker/js/plausible.local.pageleave.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a id="navigate-away" href="/manual.html">
|
||||||
|
Navigate away
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<img id="slow-image" src="/img/slow-image" alt="slow image">
|
||||||
|
</body>
|
||||||
|
</html>
|
14
tracker/test/fixtures/scroll-depth.html
vendored
Normal file
14
tracker/test/fixtures/scroll-depth.html
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script defer src="/tracker/js/plausible.local.pageleave.js"></script>
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="height: 5000px; background: repeating-linear-gradient(white, gray 500px);">
|
||||||
|
<a id="navigate-away" href="/manual.html">Navigate away</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,12 +1,12 @@
|
|||||||
const { mockRequest } = require('./support/test-utils')
|
const { mockRequest } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
|
|
||||||
test.describe('combination of hash and exclusions script extensions', () => {
|
test.describe('combination of hash and exclusions script extensions', () => {
|
||||||
test('excludes by hash part of the URL', async ({ page }) => {
|
test('excludes by hash part of the URL', async ({ page }) => {
|
||||||
const plausibleRequestMock = mockRequest(page, '/api/event')
|
const plausibleRequestMock = mockRequest(page, '/api/event')
|
||||||
|
|
||||||
await page.goto('/hash-exclusions.html#this/hash/should/be/ignored');
|
await page.goto('/hash-exclusions.html#this/hash/should/be/ignored')
|
||||||
|
|
||||||
expect(await plausibleRequestMock, "should not have sent event").toBeNull()
|
expect(await plausibleRequestMock, "should not have sent event").toBeNull()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
/* eslint-disable playwright/expect-expect */
|
|
||||||
const { clickPageElementAndExpectEventRequests } = require('./support/test-utils')
|
const { clickPageElementAndExpectEventRequests } = require('./support/test-utils')
|
||||||
const { test } = require('@playwright/test');
|
const { test } = require('@playwright/test')
|
||||||
const { LOCAL_SERVER_ADDR } = require('./support/server');
|
const { LOCAL_SERVER_ADDR } = require('./support/server')
|
||||||
|
|
||||||
test.describe('manual extension', () => {
|
test.describe('manual extension', () => {
|
||||||
test('can trigger custom events with and without a custom URL if pageview was sent with the default URL', async ({ page }) => {
|
test('can trigger custom events with and without a custom URL if pageview was sent with the default URL', async ({ page }) => {
|
||||||
await page.goto('/manual.html');
|
await page.goto('/manual.html')
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger', [
|
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger', [
|
||||||
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/manual.html`}
|
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/manual.html`}
|
||||||
@ -16,10 +15,10 @@ test.describe('manual extension', () => {
|
|||||||
await clickPageElementAndExpectEventRequests(page, '#custom-event-trigger-custom-url', [
|
await clickPageElementAndExpectEventRequests(page, '#custom-event-trigger-custom-url', [
|
||||||
{n: 'CustomEvent', u: `https://example.com/custom/location`}
|
{n: 'CustomEvent', u: `https://example.com/custom/location`}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('can trigger custom events with and without a custom URL if pageview was sent with a custom URL', async ({ page }) => {
|
test('can trigger custom events with and without a custom URL if pageview was sent with a custom URL', async ({ page }) => {
|
||||||
await page.goto('/manual.html');
|
await page.goto('/manual.html')
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger-custom-url', [
|
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger-custom-url', [
|
||||||
{n: 'pageview', u: `https://example.com/custom/location`}
|
{n: 'pageview', u: `https://example.com/custom/location`}
|
||||||
@ -30,5 +29,5 @@ test.describe('manual extension', () => {
|
|||||||
await clickPageElementAndExpectEventRequests(page, '#custom-event-trigger-custom-url', [
|
await clickPageElementAndExpectEventRequests(page, '#custom-event-trigger-custom-url', [
|
||||||
{n: 'CustomEvent', u: `https://example.com/custom/location`}
|
{n: 'CustomEvent', u: `https://example.com/custom/location`}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const { mockRequest, expectCustomEvent, metaKey } = require('./support/test-utils')
|
const { mockRequest, expectCustomEvent, metaKey } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
|
|
||||||
test.describe('outbound-links extension', () => {
|
test.describe('outbound-links extension', () => {
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ test.describe('outbound-links extension', () => {
|
|||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
||||||
expect(await navigationRequestMock, "should not have made navigation request").toBeNull()
|
expect(await navigationRequestMock, "should not have made navigation request").toBeNull()
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends event and navigates to target when link child is clicked', async ({ page }) => {
|
test('sends event and navigates to target when link child is clicked', async ({ page }) => {
|
||||||
await page.goto('/outbound-link.html')
|
await page.goto('/outbound-link.html')
|
||||||
@ -28,7 +28,7 @@ test.describe('outbound-links extension', () => {
|
|||||||
const navigationRequest = await navigationRequestMock
|
const navigationRequest = await navigationRequestMock
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
||||||
expect(navigationRequest.url()).toContain(outboundURL)
|
expect(navigationRequest.url()).toContain(outboundURL)
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends event and does not navigate if default externally prevented', async ({ page }) => {
|
test('sends event and does not navigate if default externally prevented', async ({ page }) => {
|
||||||
await page.goto('/outbound-link.html')
|
await page.goto('/outbound-link.html')
|
||||||
@ -41,5 +41,5 @@ test.describe('outbound-links extension', () => {
|
|||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
|
||||||
expect(await navigationRequestMock, "should not have made navigation request").toBeNull()
|
expect(await navigationRequestMock, "should not have made navigation request").toBeNull()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,46 +1,45 @@
|
|||||||
/* eslint-disable playwright/expect-expect */
|
|
||||||
/* eslint-disable playwright/no-skipped-test */
|
/* eslint-disable playwright/no-skipped-test */
|
||||||
const { clickPageElementAndExpectEventRequests, mockRequest } = require('./support/test-utils')
|
const { clickPageElementAndExpectEventRequests, mockRequest } = require('./support/test-utils')
|
||||||
const { test, expect } = require('@playwright/test');
|
const { test } = require('@playwright/test')
|
||||||
const { LOCAL_SERVER_ADDR } = require('./support/server');
|
const { LOCAL_SERVER_ADDR } = require('./support/server')
|
||||||
|
|
||||||
test.describe('pageleave extension', () => {
|
test.describe('pageleave extension', () => {
|
||||||
test.skip(({browserName}) => browserName === 'webkit', 'Not testable on Webkit');
|
test.skip(({browserName}) => browserName === 'webkit', 'Not testable on Webkit')
|
||||||
|
|
||||||
test('sends a pageleave when navigating to the next page', async ({ page }) => {
|
test('sends a pageleave when navigating to the next page', async ({ page }) => {
|
||||||
const pageviewRequestMock = mockRequest(page, '/api/event')
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/pageleave.html');
|
await page.goto('/pageleave.html')
|
||||||
await pageviewRequestMock;
|
await pageviewRequestMock
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave.html`}
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave.html`}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends pageleave and pageview on hash-based SPA navigation', async ({ page }) => {
|
test('sends pageleave and pageview on hash-based SPA navigation', async ({ page }) => {
|
||||||
const pageviewRequestMock = mockRequest(page, '/api/event')
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/pageleave-hash.html');
|
await page.goto('/pageleave-hash.html')
|
||||||
await pageviewRequestMock;
|
await pageviewRequestMock
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#hash-nav', [
|
await clickPageElementAndExpectEventRequests(page, '#hash-nav', [
|
||||||
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave-hash.html`},
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave-hash.html`},
|
||||||
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/pageleave-hash.html#some-hash`}
|
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/pageleave-hash.html#some-hash`}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends pageleave and pageview on history-based SPA navigation', async ({ page }) => {
|
test('sends pageleave and pageview on history-based SPA navigation', async ({ page }) => {
|
||||||
const pageviewRequestMock = mockRequest(page, '/api/event')
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/pageleave.html');
|
await page.goto('/pageleave.html')
|
||||||
await pageviewRequestMock;
|
await pageviewRequestMock
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#history-nav', [
|
await clickPageElementAndExpectEventRequests(page, '#history-nav', [
|
||||||
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave.html`},
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/pageleave.html`},
|
||||||
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/another-page`}
|
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/another-page`}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends pageleave with the manually overridden URL', async ({ page }) => {
|
test('sends pageleave with the manually overridden URL', async ({ page }) => {
|
||||||
await page.goto('/pageleave-manual.html');
|
await page.goto('/pageleave-manual.html')
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger-custom-url', [
|
await clickPageElementAndExpectEventRequests(page, '#pageview-trigger-custom-url', [
|
||||||
{n: 'pageview', u: 'https://example.com/custom/location'}
|
{n: 'pageview', u: 'https://example.com/custom/location'}
|
||||||
@ -49,22 +48,22 @@ test.describe('pageleave extension', () => {
|
|||||||
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
{n: 'pageleave', u: 'https://example.com/custom/location'}
|
{n: 'pageleave', u: 'https://example.com/custom/location'}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('does not send pageleave when pageview was not sent in manual mode', async ({ page }) => {
|
test('does not send pageleave when pageview was not sent in manual mode', async ({ page }) => {
|
||||||
await page.goto('/pageleave-manual.html');
|
await page.goto('/pageleave-manual.html')
|
||||||
|
|
||||||
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [], [
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [], [
|
||||||
{n: 'pageleave'}
|
{n: 'pageleave'}
|
||||||
])
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
test('script.exclusions.hash.pageleave.js sends pageleave only from URLs where a pageview was sent', async ({ page }) => {
|
test('script.exclusions.hash.pageleave.js sends pageleave only from URLs where a pageview was sent', async ({ page }) => {
|
||||||
const pageBaseURL = `${LOCAL_SERVER_ADDR}/pageleave-hash-exclusions.html`
|
const pageBaseURL = `${LOCAL_SERVER_ADDR}/pageleave-hash-exclusions.html`
|
||||||
|
|
||||||
const pageviewRequestMock = mockRequest(page, '/api/event')
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/pageleave-hash-exclusions.html');
|
await page.goto('/pageleave-hash-exclusions.html')
|
||||||
await pageviewRequestMock;
|
await pageviewRequestMock
|
||||||
|
|
||||||
// After the initial pageview is sent, navigate to ignored page ->
|
// After the initial pageview is sent, navigate to ignored page ->
|
||||||
// pageleave event is sent from the initial page URL
|
// pageleave event is sent from the initial page URL
|
||||||
@ -91,5 +90,5 @@ test.describe('pageleave extension', () => {
|
|||||||
{n: 'pageview', u: `${pageBaseURL}#hash2`, h: 1}
|
{n: 'pageview', u: `${pageBaseURL}#hash2`, h: 1}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
});
|
})
|
||||||
});
|
})
|
@ -1,13 +1,13 @@
|
|||||||
const { mockRequest } = require('./support/test-utils')
|
const { mockRequest } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
|
|
||||||
test.describe('Basic installation', () => {
|
test.describe('Basic installation', () => {
|
||||||
test('Sends pageview automatically', async ({ page }) => {
|
test('Sends pageview automatically', async ({ page }) => {
|
||||||
const plausibleRequestMock = mockRequest(page, '/api/event')
|
const plausibleRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/simple.html');
|
await page.goto('/simple.html')
|
||||||
|
|
||||||
const plausibleRequest = await plausibleRequestMock;
|
const plausibleRequest = await plausibleRequestMock
|
||||||
expect(plausibleRequest.url()).toContain('/api/event')
|
expect(plausibleRequest.url()).toContain('/api/event')
|
||||||
expect(plausibleRequest.postDataJSON().n).toEqual('pageview')
|
expect(plausibleRequest.postDataJSON().n).toEqual('pageview')
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
const { mockRequest, expectCustomEvent } = require('./support/test-utils');
|
const { mockRequest, expectCustomEvent } = require('./support/test-utils')
|
||||||
const { expect, test } = require('@playwright/test');
|
const { expect, test } = require('@playwright/test')
|
||||||
|
|
||||||
test.describe('with revenue script extension', () => {
|
test.describe('with revenue script extension', () => {
|
||||||
test('sends revenue currency and amount in manual mode', async ({ page }) => {
|
test('sends revenue currency and amount in manual mode', async ({ page }) => {
|
||||||
const plausibleRequestMock = mockRequest(page, '/api/event')
|
const plausibleRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/revenue.html');
|
await page.goto('/revenue.html')
|
||||||
await page.click('#manual-purchase')
|
await page.click('#manual-purchase')
|
||||||
|
|
||||||
const plausibleRequest = await plausibleRequestMock
|
const plausibleRequest = await plausibleRequestMock
|
||||||
expect(plausibleRequest.postDataJSON()["$"]).toEqual({amount: 15.99, currency: "USD"})
|
expect(plausibleRequest.postDataJSON()["$"]).toEqual({amount: 15.99, currency: "USD"})
|
||||||
});
|
})
|
||||||
|
|
||||||
test('sends revenue currency and amount with tagged class name', async ({ page }) => {
|
test('sends revenue currency and amount with tagged class name', async ({ page }) => {
|
||||||
const plausibleRequestMock = mockRequest(page, '/api/event')
|
const plausibleRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.goto('/revenue.html');
|
await page.goto('/revenue.html')
|
||||||
await page.click('#tagged-purchase')
|
await page.click('#tagged-purchase')
|
||||||
|
|
||||||
const plausibleRequest = await plausibleRequestMock
|
const plausibleRequest = await plausibleRequestMock
|
||||||
expect(plausibleRequest.postDataJSON()["$"]).toEqual({amount: "13.32", currency: "EUR"})
|
expect(plausibleRequest.postDataJSON()["$"]).toEqual({amount: "13.32", currency: "EUR"})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
89
tracker/test/scroll-depth.spec.js
Normal file
89
tracker/test/scroll-depth.spec.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* eslint-disable playwright/no-skipped-test */
|
||||||
|
const { clickPageElementAndExpectEventRequests, mockRequest } = require('./support/test-utils')
|
||||||
|
const { test } = require('@playwright/test')
|
||||||
|
const { LOCAL_SERVER_ADDR } = require('./support/server')
|
||||||
|
|
||||||
|
test.describe('scroll depth', () => {
|
||||||
|
test.skip(({browserName}) => browserName === 'webkit', 'Not testable on Webkit')
|
||||||
|
|
||||||
|
test('sends scroll_depth in the pageleave payload when navigating to the next page', async ({ page }) => {
|
||||||
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
|
await page.goto('/scroll-depth.html')
|
||||||
|
await pageviewRequestMock
|
||||||
|
|
||||||
|
await page.evaluate(() => window.scrollBy(0, 300))
|
||||||
|
await page.evaluate(() => window.scrollBy(0, 0))
|
||||||
|
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth.html`, sd: 20}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('sends scroll depth on hash navigation', async ({ page }) => {
|
||||||
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
|
await page.goto('/scroll-depth-hash.html')
|
||||||
|
await pageviewRequestMock
|
||||||
|
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#about-link', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth-hash.html`, sd: 100},
|
||||||
|
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/scroll-depth-hash.html#about`}
|
||||||
|
])
|
||||||
|
|
||||||
|
// Wait 600ms before navigating again because pageleave events are throttled to 500ms.
|
||||||
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#home-link', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth-hash.html#about`, sd: 34},
|
||||||
|
{n: 'pageview', u: `${LOCAL_SERVER_ADDR}/scroll-depth-hash.html#home`}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('document height gets reevaluated after window load', async ({ page }) => {
|
||||||
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
|
await page.goto('/scroll-depth-slow-window-load.html')
|
||||||
|
await pageviewRequestMock
|
||||||
|
|
||||||
|
// Wait for the image to be loaded
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
return document.getElementById('slow-image').complete
|
||||||
|
})
|
||||||
|
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth-slow-window-load.html`, sd: 24}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dynamically loaded content affects documentHeight', async ({ page }) => {
|
||||||
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
|
await page.goto('/scroll-depth-dynamic-content-load.html')
|
||||||
|
await pageviewRequestMock
|
||||||
|
|
||||||
|
// The link appears dynamically after 500ms.
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth-dynamic-content-load.html`, sd: 14}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('document height gets reevaluated on scroll', async ({ page }) => {
|
||||||
|
const pageviewRequestMock = mockRequest(page, '/api/event')
|
||||||
|
await page.goto('/scroll-depth-content-onscroll.html')
|
||||||
|
await pageviewRequestMock
|
||||||
|
|
||||||
|
// During the first 3 seconds, the script periodically updates document height
|
||||||
|
// to account for dynamically loaded content. Since we want to test document
|
||||||
|
// height also getting updated on scroll, we need to just wait for 3 seconds.
|
||||||
|
await page.waitForTimeout(3100)
|
||||||
|
|
||||||
|
// scroll to the bottom of the page
|
||||||
|
await page.evaluate(() => window.scrollBy(0, document.body.scrollHeight))
|
||||||
|
|
||||||
|
// Wait until documentHeight gets increased by the fixture JS
|
||||||
|
await page.waitForSelector('#more-content')
|
||||||
|
|
||||||
|
await page.evaluate(() => window.scrollBy(0, 1000))
|
||||||
|
|
||||||
|
await clickPageElementAndExpectEventRequests(page, '#navigate-away', [
|
||||||
|
{n: 'pageleave', u: `${LOCAL_SERVER_ADDR}/scroll-depth-content-onscroll.html`, sd: 80}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
@ -11,6 +11,13 @@ exports.runLocalFileServer = function () {
|
|||||||
app.use(express.static(FIXTURES_PATH));
|
app.use(express.static(FIXTURES_PATH));
|
||||||
app.use('/tracker', express.static(TRACKERS_PATH));
|
app.use('/tracker', express.static(TRACKERS_PATH));
|
||||||
|
|
||||||
|
// A test utility - serve an image with an artificial delay
|
||||||
|
app.get('/img/slow-image', (_req, res) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
res.sendFile(path.join(FIXTURES_PATH, '/img/black3x3000.png'));
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(LOCAL_SERVER_PORT, function () {
|
app.listen(LOCAL_SERVER_PORT, function () {
|
||||||
console.log(`Local server listening on ${LOCAL_SERVER_ADDR}`)
|
console.log(`Local server listening on ${LOCAL_SERVER_ADDR}`)
|
||||||
});
|
});
|
||||||
|
@ -54,33 +54,56 @@ exports.expectCustomEvent = function (request, eventName, eventProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A powerful utility function that makes it easy to assert on the event
|
||||||
|
* requests that should or should not have been made after clicking a page
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* This function accepts subsets of request bodies (the JSON payloads) as
|
||||||
|
* arguments, and compares them with the bodies of the requests that were
|
||||||
|
* actually made. For a body subset to match a request, all the key-value
|
||||||
|
* pairs present in the subset should also appear in the request body.
|
||||||
|
*/
|
||||||
exports.clickPageElementAndExpectEventRequests = async function (page, locatorToClick, expectedBodySubsets, refutedBodySubsets = []) {
|
exports.clickPageElementAndExpectEventRequests = async function (page, locatorToClick, expectedBodySubsets, refutedBodySubsets = []) {
|
||||||
const requestsToExpect = expectedBodySubsets.length
|
const requestsToExpect = expectedBodySubsets.length
|
||||||
const requestsToAwait = requestsToExpect + refutedBodySubsets.length
|
const requestsToAwait = requestsToExpect + refutedBodySubsets.length
|
||||||
|
|
||||||
const plausibleRequestMockList = mockManyRequests(page, '/api/event', requestsToAwait)
|
const plausibleRequestMockList = mockManyRequests(page, '/api/event', requestsToAwait)
|
||||||
await page.click(locatorToClick)
|
await page.click(locatorToClick)
|
||||||
const requests = await plausibleRequestMockList
|
const requestBodies = (await plausibleRequestMockList).map(r => r.postDataJSON())
|
||||||
|
|
||||||
expect(requests.length).toBe(requestsToExpect)
|
const expectedButNotFoundBodySubsets = []
|
||||||
|
|
||||||
expectedBodySubsets.forEach((bodySubset) => {
|
expectedBodySubsets.forEach((bodySubset) => {
|
||||||
expect(requests.some((request) => {
|
const wasFound = requestBodies.some((requestBody) => {
|
||||||
return hasExpectedBodyParams(request, bodySubset)
|
return includesSubset(requestBody, bodySubset)
|
||||||
})).toBe(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!wasFound) {expectedButNotFoundBodySubsets.push(bodySubset)}
|
||||||
|
})
|
||||||
|
|
||||||
|
const refutedButFoundRequestBodies = []
|
||||||
|
|
||||||
refutedBodySubsets.forEach((bodySubset) => {
|
refutedBodySubsets.forEach((bodySubset) => {
|
||||||
expect(requests.every((request) => {
|
const found = requestBodies.find((requestBody) => {
|
||||||
return !hasExpectedBodyParams(request, bodySubset)
|
return includesSubset(requestBody, bodySubset)
|
||||||
})).toBe(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (found) {refutedButFoundRequestBodies.push(found)}
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectedBodySubsetsErrorMessage = `The following body subsets were not found from the requests that were made:\n\n${JSON.stringify(expectedButNotFoundBodySubsets, null, 4)}\n\nReceived requests with the following bodies:\n\n${JSON.stringify(requestBodies, null, 4)}`
|
||||||
|
expect(expectedButNotFoundBodySubsets, expectedBodySubsetsErrorMessage).toHaveLength(0)
|
||||||
|
|
||||||
|
const refutedBodySubsetsErrorMessage = `The following requests were made, but were not expected:\n\n${JSON.stringify(refutedButFoundRequestBodies, null, 4)}`
|
||||||
|
expect(refutedButFoundRequestBodies, refutedBodySubsetsErrorMessage).toHaveLength(0)
|
||||||
|
|
||||||
|
expect(requestBodies.length).toBe(requestsToExpect)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasExpectedBodyParams(request, expectedBodyParams) {
|
function includesSubset(body, subset) {
|
||||||
const body = request.postDataJSON()
|
return Object.keys(subset).every((key) => {
|
||||||
|
return body[key] === subset[key]
|
||||||
return Object.keys(expectedBodyParams).every((key) => {
|
|
||||||
return body[key] === expectedBodyParams[key]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
const plausibleRequestMock = mockRequest(page, '/api/event')
|
const plausibleRequestMock = mockRequest(page, '/api/event')
|
||||||
await page.click('#link')
|
await page.click('#link')
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Payment Complete', { amount: '100', method: "Credit Card", url: linkURL })
|
expectCustomEvent(await plausibleRequestMock, 'Payment Complete', { amount: '100', method: "Credit Card", url: linkURL })
|
||||||
});
|
})
|
||||||
|
|
||||||
test('tracks a tagged form submit with custom props when submitting by pressing enter', async ({ page }) => {
|
test('tracks a tagged form submit with custom props when submitting by pressing enter', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -21,7 +21,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
await inputLocator.press('Enter')
|
await inputLocator.press('Enter')
|
||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Signup', { type: "Newsletter" })
|
expectCustomEvent(await plausibleRequestMock, 'Signup', { type: "Newsletter" })
|
||||||
});
|
})
|
||||||
|
|
||||||
test('tracks submit on a form with a tagged parent when submit button is clicked', async ({ page }) => {
|
test('tracks submit on a form with a tagged parent when submit button is clicked', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -34,7 +34,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
|
|
||||||
expect(requests.length).toBe(1)
|
expect(requests.length).toBe(1)
|
||||||
expectCustomEvent(requests[0], "Form Submit", {})
|
expectCustomEvent(requests[0], "Form Submit", {})
|
||||||
});
|
})
|
||||||
|
|
||||||
test('tracks click and auxclick on any tagged HTML element', async ({ page }) => {
|
test('tracks click and auxclick on any tagged HTML element', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -48,7 +48,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
const requests = await plausibleRequestMockList
|
const requests = await plausibleRequestMockList
|
||||||
expect(requests.length).toBe(3)
|
expect(requests.length).toBe(3)
|
||||||
requests.forEach(request => expectCustomEvent(request, 'Custom Event', { foo: "bar" }))
|
requests.forEach(request => expectCustomEvent(request, 'Custom Event', { foo: "bar" }))
|
||||||
});
|
})
|
||||||
|
|
||||||
test('does not track elements without plausible-event-name class + link elements navigate', async ({ page }) => {
|
test('does not track elements without plausible-event-name class + link elements navigate', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -65,7 +65,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
|
|
||||||
expect(await plausibleRequestMock, "should not have made Plausible request").toBeNull()
|
expect(await plausibleRequestMock, "should not have made Plausible request").toBeNull()
|
||||||
expect((await navigationRequestMock).url()).toContain(linkURL)
|
expect((await navigationRequestMock).url()).toContain(linkURL)
|
||||||
});
|
})
|
||||||
|
|
||||||
test('tracks tagged HTML elements when their child element is clicked', async ({ page }) => {
|
test('tracks tagged HTML elements when their child element is clicked', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -78,7 +78,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
const requests = await plausibleRequestMockList
|
const requests = await plausibleRequestMockList
|
||||||
expect(requests.length).toBe(2)
|
expect(requests.length).toBe(2)
|
||||||
requests.forEach(request => expectCustomEvent(request, 'Custom Event', { foo: "bar" }))
|
requests.forEach(request => expectCustomEvent(request, 'Custom Event', { foo: "bar" }))
|
||||||
});
|
})
|
||||||
|
|
||||||
test('tracks tagged element that is dynamically added to the DOM', async ({ page }) => {
|
test('tracks tagged element that is dynamically added to the DOM', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -92,7 +92,7 @@ test.describe('tagged-events extension', () => {
|
|||||||
await buttonLocator.click()
|
await buttonLocator.click()
|
||||||
|
|
||||||
expectCustomEvent(await plausibleRequestMock, 'Custom Event', {})
|
expectCustomEvent(await plausibleRequestMock, 'Custom Event', {})
|
||||||
});
|
})
|
||||||
|
|
||||||
test('does not track clicks inside a tagged form, except submit click', async ({ page }) => {
|
test('does not track clicks inside a tagged form, except submit click', async ({ page }) => {
|
||||||
await page.goto('/tagged-event.html')
|
await page.goto('/tagged-event.html')
|
||||||
@ -104,5 +104,5 @@ test.describe('tagged-events extension', () => {
|
|||||||
await page.click('#form-div')
|
await page.click('#form-div')
|
||||||
|
|
||||||
expect(await plausibleRequestMock, "should not have made Plausible request").toBeNull()
|
expect(await plausibleRequestMock, "should not have made Plausible request").toBeNull()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user