Use user-agent instead of screen_width to get device type (#2711)

* Use user-agent instead of screen_width to get device type

Co-authored-by: eriknakata <erik.nakata5@gmail.com>

* Fix credo

* Log on unhandled UAInspector device type

* Make 'browser' the default tab in devices report

* Remove device tooltip

* Remove screen_width from ingestion completely

* Remove browserstack harness, run playwright directly

* Select meta key based on OS platform

* Run CI tests in parallel

* Improve device match readability

* Add changelog

---------

Co-authored-by: eriknakata <erik.nakata5@gmail.com>
This commit is contained in:
Uku Taht 2023-03-02 12:04:01 +02:00 committed by GitHub
parent f26ca7da9f
commit 43bf7dd09f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 546 additions and 2007 deletions

View File

@ -7,27 +7,18 @@ on:
- 'tracker/**'
jobs:
build:
name: Build and test
test:
timeout-minutes: 15
runs-on: buildjet-4vcpu-ubuntu-2004
steps:
- uses: actions/checkout@v2
- name: Read .tool-versions
uses: marocchino/tool-versions-action@v1
id: versions
- name: Set up Node
uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{steps.versions.outputs.nodejs}}
- name: 'BrowserStack Env Setup'
uses: 'browserstack/github-actions/setup-env@master'
with:
username: ${{ secrets.BROWSERSTACK_USERNAME }}
access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
build-name: 'BUILD_INFO'
- run: npm install --prefix ./tracker
- run: npm run deploy --prefix ./tracker
- name: "Install Playwright dependencies"
node-version: 16
- name: Install dependencies
run: npm --prefix ./tracker ci
- name: Install Playwright Browsers
working-directory: ./tracker
run: npx playwright install-deps
- run: npm --prefix ./tracker test
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npm --prefix ./tracker test

View File

@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file.
- Reject events with long URIs and data URIs plausible/analytics#2536
- Always show direct traffic in sources reports plausible/analytics#2531
- Stop recording XX and T1 country codes plausible/analytics#2556
- Device type is now determined from the User-Agent instead of window.innerWidth plausible/analytics#2711
### Removed
- Remove the ability to collapse the main graph plausible/analytics#2627

View File

@ -84,10 +84,6 @@ function ScreenSizes({ query, site }) {
return iconFor(screenSize.name)
}
function renderTooltipText(screenSize) {
return EXPLANATION[screenSize.name]
}
return (
<ListReport
fetchData={fetchData}
@ -95,18 +91,10 @@ function ScreenSizes({ query, site }) {
keyLabel="Screen size"
query={query}
renderIcon={renderIcon}
tooltipText={renderTooltipText}
/>
)
}
const EXPLANATION = {
'Mobile': 'up to 576px',
'Tablet': '576px to 992px',
'Laptop': '992px to 1440px',
'Desktop': 'above 1440px',
}
function iconFor(screenSize) {
if (screenSize === 'Mobile') {
return (
@ -135,7 +123,7 @@ export default class Devices extends React.Component {
this.tabKey = `deviceTab__${props.site.domain}`
const storedTab = storage.getItem(this.tabKey)
this.state = {
mode: storedTab || 'size'
mode: storedTab || 'browser'
}
}
@ -200,9 +188,9 @@ export default class Devices extends React.Component {
<div className="flex justify-between w-full">
<h3 className="font-bold dark:text-gray-100">Devices</h3>
<div className="flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2">
{this.renderPill('Size', 'size')}
{this.renderPill('Browser', 'browser')}
{this.renderPill('OS', 'os')}
{this.renderPill('Size', 'size')}
</div>
</div>
{this.renderContent()}

View File

@ -89,7 +89,7 @@ export default function ListReport(props) {
maxWidthDeduction={maxWidthDeduction}
plot={valueKey}
>
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-all" tooltip={props.tooltipText && props.tooltipText(listItem)}>
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-all">
<Link onClick={props.onClick || noop} className="md:truncate block hover:underline" to={{search: query.toString()}}>
{props.renderIcon && props.renderIcon(listItem)}
{props.renderIcon && ' '}

View File

@ -90,7 +90,6 @@ defmodule Mix.Tasks.SendPageview do
url: "http://#{domain}#{page}",
domain: domain,
referrer: referrer,
width: 1666,
props: props
}
end

View File

@ -95,7 +95,6 @@ defmodule Plausible.Ingestion.Event do
&put_referrer/1,
&put_utm_tags/1,
&put_geolocation/1,
&put_screen_size/1,
&put_props/1,
&put_salts/1,
&put_user_id/1,
@ -145,7 +144,8 @@ defmodule Plausible.Ingestion.Event do
operating_system: os_name(user_agent),
operating_system_version: os_version(user_agent),
browser: browser_name(user_agent),
browser_version: browser_version(user_agent)
browser_version: browser_version(user_agent),
screen_size: screen_size(user_agent)
})
_any ->
@ -190,19 +190,6 @@ defmodule Plausible.Ingestion.Event do
update_attrs(event, result)
end
defp put_screen_size(%__MODULE__{} = event) do
screen_size =
case event.request.screen_width do
nil -> nil
width when width < 576 -> "Mobile"
width when width < 992 -> "Tablet"
width when width < 1440 -> "Laptop"
width when width >= 1440 -> "Desktop"
end
update_attrs(event, %{screen_size: screen_size})
end
defp put_props(%__MODULE__{request: %{props: %{} = props}} = event) do
update_attrs(event, %{
"meta.key": Map.keys(props),
@ -324,6 +311,41 @@ defmodule Plausible.Ingestion.Event do
end
end
@mobile_types [
"smartphone",
"feature phone",
"portable media player",
"phablet",
"wearable",
"camera"
]
@tablet_types ["car browser", "tablet"]
@desktop_types ["tv", "console", "desktop"]
alias UAInspector.Result.Device
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp screen_size(ua) do
case ua.device do
%Device{type: t} when t in @mobile_types ->
"Mobile"
%Device{type: t} when t in @tablet_types ->
"Tablet"
%Device{type: t} when t in @desktop_types ->
"Desktop"
%Device{type: type} ->
Sentry.capture_message("Could not determine device type from UAInspector",
extra: %{type: type}
)
nil
_ ->
nil
end
end
defp browser_version(ua) do
case ua.client do
:unknown -> ""

View File

@ -16,7 +16,6 @@ defmodule Plausible.Ingestion.Request do
field :hostname, :string
field :referrer, :string
field :domains, {:array, :string}
field :screen_width, :string
field :hash_mode, :string
field :pathname, :string
field :props, :map
@ -89,7 +88,6 @@ defmodule Plausible.Ingestion.Request do
changeset,
event_name: request_body["n"] || request_body["name"],
referrer: request_body["r"] || request_body["referrer"],
screen_width: request_body["w"] || request_body["screen_width"],
hash_mode: request_body["h"] || request_body["hashMode"],
props: parse_props(request_body)
)

View File

@ -142,7 +142,6 @@ defmodule Plausible.Ingestion.RequestTest do
domain: "dummy.site",
url: "http://dummy.site/index.html",
referrer: "https://example.com",
screen_width: 1024,
hashMode: 1,
props: %{
"custom1" => "property1",
@ -154,7 +153,6 @@ defmodule Plausible.Ingestion.RequestTest do
assert {:ok, request} = Request.build(conn)
assert request.referrer == "https://example.com"
assert request.screen_width == 1024
assert request.hash_mode == 1
assert request.props["custom1"] == "property1"
assert request.props["custom2"] == "property2"

View File

@ -3,6 +3,8 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
use Plausible.ClickhouseRepo
@user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
@user_agent_mobile "Mozilla/5.0 (Linux; Android 6.0; U007 Pro Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36"
@user_agent_tablet "Mozilla/5.0 (Linux; U; Android 4.2.2; it-it; Surfing TAB B 9.7 3G Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
describe "POST /api/event" do
setup do
@ -15,8 +17,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
domain: domain,
name: "pageview",
url: "http://gigride.live/",
referrer: "http://m.facebook.com/",
screen_width: 1440
referrer: "http://m.facebook.com/"
}
conn =
@ -36,8 +37,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
params = %{
domain: domain,
name: "pageview",
url: "http://gigride.live/",
screen_width: 1440
url: "http://gigride.live/"
}
conn =
@ -72,8 +72,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
name: "pageview",
url: "http://gigride.live/",
referrer: "http://m.facebook.com/",
domain: "#{domain1},#{domain2}",
screen_width: 1440
domain: "#{domain1},#{domain2}"
}
conn =
@ -93,8 +92,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
domain: domain,
name: "pageview",
url: "http://gigride.live/",
referrer: "http://m.facebook.com/",
screen_width: 1440
referrer: "http://m.facebook.com/"
}
t1 = System.convert_time_unit(System.monotonic_time(), :native, :millisecond)
@ -273,8 +271,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
domain: domain,
name: "pageview",
url: "http://gigride.live/",
referrer: "https://www.1-best-seo.com",
screen_width: 1440
referrer: "https://www.1-best-seo.com"
}
conn =
@ -451,17 +448,16 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
assert pageview.referrer_source == ""
end
test "screen size is calculated from screen_width", %{conn: conn, domain: domain} do
test "screen size is calculated from user agent", %{conn: conn, domain: domain} do
params = %{
name: "pageview",
url: "http://gigride.live/",
screen_width: 480,
domain: domain
}
conn =
conn
|> put_req_header("user-agent", @user_agent)
|> put_req_header("user-agent", @user_agent_mobile)
|> post("/api/event", params)
pageview = get_event(domain)
@ -470,7 +466,46 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
assert pageview.screen_size == "Mobile"
end
test "screen size is nil if screen_width is missing", %{conn: conn, domain: domain} do
test "screen size is nil if user agent is unknown", %{conn: conn, domain: domain} do
params = %{
name: "pageview",
url: "http://gigride.live/",
domain: domain
}
conn =
conn
|> put_req_header("user-agent", "unknown UA")
|> post("/api/event", params)
pageview = get_event(domain)
assert response(conn, 202) == "ok"
assert pageview.screen_size == ""
end
test "screen size is calculated from user_agent when is tablet", %{conn: conn, domain: domain} do
params = %{
name: "pageview",
url: "http://gigride.live/",
domain: domain
}
conn =
conn
|> put_req_header("user-agent", @user_agent_tablet)
|> post("/api/event", params)
pageview = get_event(domain)
assert response(conn, 202) == "ok"
assert pageview.screen_size == "Tablet"
end
test "screen size is calculated from user_agent when is desktop", %{
conn: conn,
domain: domain
} do
params = %{
name: "pageview",
url: "http://gigride.live/",
@ -485,7 +520,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
pageview = get_event(domain)
assert response(conn, 202) == "ok"
assert pageview.screen_size == ""
assert pageview.screen_size == "Desktop"
end
test "can trigger a custom event", %{conn: conn, domain: domain} do
@ -875,8 +910,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
n: "pageview",
u: "http://www.example.com/opportunity",
d: domain,
r: "https://facebook.com/page",
w: 300
r: "https://facebook.com/page"
}
conn
@ -887,7 +921,6 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
assert pageview.pathname == "/opportunity"
assert pageview.referrer_source == "Facebook"
assert pageview.referrer == "facebook.com/page"
assert pageview.screen_size == "Mobile"
end
test "records hash when in hash mode", %{conn: conn, domain: domain} do

2039
tracker/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,15 @@
{
"scripts": {
"deploy": "node compile.js",
"test": "npm run deploy && BROWSERSTACK_LOCAL=true npx playwright test --config=./test/support/playwright.config.js"
"test": "npm run deploy && npx playwright test --config=./test/support/playwright.config.js",
"start": "node test/support/server.js"
},
"license": "MIT",
"dependencies": {
"@playwright/test": "^1.22.2",
"browserstack-local": "^1.4.8",
"@playwright/test": "^1.31.0",
"express": "^4.18.1",
"generatorics": "^1.1.0",
"handlebars": "^4.7.7",
"playwright": "^1.22.2",
"uglify-js": "^3.9.4"
},
"devDependencies": {

View File

@ -70,7 +70,6 @@
{{/if}}
payload.d = scriptEl.getAttribute('data-domain')
payload.r = document.referrer || null
payload.w = window.innerWidth
if (options && options.meta) {
payload.m = JSON.stringify(options.meta)
}

View File

@ -1,6 +1,5 @@
const { test } = require('./support/harness');
const { mockRequest, mockManyRequests, expectCustomEvent } = require('./support/test-utils');
const { expect } = require('@playwright/test');
const { expect, test } = require('@playwright/test');
const { LOCAL_SERVER_ADDR } = require('./support/server');

View File

@ -1,17 +1,16 @@
const { test } = require('./support/harness');
const { mockRequest, expectCustomEvent, isMac, mockManyRequests } = require('./support/test-utils');
const { expect } = require('@playwright/test');
const { mockRequest, expectCustomEvent, mockManyRequests, metaKey } = require('./support/test-utils');
const { expect, test } = require('@playwright/test');
const { LOCAL_SERVER_ADDR } = require('./support/server');
test.describe('file-downloads extension', () => {
test('sends event and does not start download when link opens in new tab', async ({ page }, workerInfo) => {
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')
const plausibleRequestMock = mockRequest(page, '/api/event')
const downloadRequestMock = mockRequest(page, downloadURL)
await page.click('#link', { modifiers: [isMac(workerInfo) ? 'Meta' : 'Control'] })
await page.click('#link', { modifiers: [metaKey()] })
expectCustomEvent(await plausibleRequestMock, 'File Download', { url: downloadURL })
expect(await downloadRequestMock, "should not make download request").toBeNull()

View File

@ -1,6 +1,5 @@
const { test } = require('./support/harness');
const { mockRequest } = require('./support/test-utils')
const { expect } = require('@playwright/test');
const { expect, test } = require('@playwright/test');
test.describe('combination of hash and exclusions script extensions', () => {
test('excludes by hash part of the URL', async ({ page }) => {

View File

@ -1,17 +1,16 @@
const { test } = require('./support/harness')
const { mockRequest, isMac, expectCustomEvent } = require('./support/test-utils')
const { expect } = require('@playwright/test');
const { mockRequest, expectCustomEvent, metaKey } = require('./support/test-utils')
const { expect, test } = require('@playwright/test');
test.describe('outbound-links extension', () => {
test('sends event and does not navigate when link opens in new tab', async ({ page }, workerInfo) => {
test('sends event and does not navigate when link opens in new tab', async ({ page }) => {
await page.goto('/outbound-link.html')
const outboundURL = await page.locator('#link').getAttribute('href')
const plausibleRequestMock = mockRequest(page, '/api/event')
const navigationRequestMock = mockRequest(page, outboundURL)
await page.click('#link', { modifiers: [isMac(workerInfo) ? 'Meta' : 'Control'] })
await page.click('#link', { modifiers: [metaKey()] })
expectCustomEvent(await plausibleRequestMock, 'Outbound Link: Click', { url: outboundURL })
expect(await navigationRequestMock, "should not have made navigation request").toBeNull()

View File

@ -1,6 +1,5 @@
const { test } = require('./support/harness');
const { mockRequest } = require('./support/test-utils')
const { expect } = require('@playwright/test');
const { expect, test } = require('@playwright/test');
test.describe('Basic installation', () => {
test('Sends pageview automatically', async ({ page }) => {

View File

@ -1,16 +0,0 @@
const BrowserStackLocal = require('browserstack-local');
exports.bsLocal = new BrowserStackLocal.Local();
exports.BS_LOCAL_ARGS = {
key: process.env.BROWSERSTACK_ACCESS_KEY
};
exports.ensureCredentials = function() {
if (!process.env.BROWSERSTACK_ACCESS_KEY) {
throw 'Please configure process.env.BROWSERSTACK_ACCESS_KEY'
}
if (!process.env.BROWSERSTACK_USERNAME) {
throw 'Please configure process.env.BROWSERSTACK_USERNAME'
}
}

View File

@ -1,30 +0,0 @@
// global-setup.js
const { bsLocal, ensureCredentials, BS_LOCAL_ARGS } = require('./browserstack');
const { promisify } = require('util');
const { runLocalFileServer } = require('./server')
const sleep = promisify(setTimeout);
const redColour = '\x1b[31m';
const whiteColour = '\x1b[0m';
ensureCredentials()
module.exports = async () => {
console.log('Starting BrowserStackLocal ...');
runLocalFileServer()
// Starts the Local instance with the required arguments
let localResponseReceived = false;
bsLocal.start(BS_LOCAL_ARGS, (err) => {
if (err) {
console.error(
`${redColour}Error starting BrowserStackLocal${whiteColour}`
);
} else {
console.log('BrowserStackLocal Started');
}
localResponseReceived = true;
});
while (!localResponseReceived) {
await sleep(1000);
}
};

View File

@ -1,19 +0,0 @@
// global-teardown.js
const { bsLocal } = require('./browserstack');
const { promisify } = require('util');
const sleep = promisify(setTimeout);
module.exports = async () => {
// Stop the Local instance after your test run is completed, i.e after driver.quit
let localStopped = false;
if (bsLocal && bsLocal.isRunning()) {
bsLocal.stop(() => {
localStopped = true;
console.log('Stopped BrowserStackLocal');
});
while (!localStopped) {
await sleep(1000);
}
}
};

View File

@ -1,92 +0,0 @@
const base = require('@playwright/test');
const cp = require('child_process');
const clientPlaywrightVersion = cp
.execSync('npx playwright --version')
.toString()
.trim()
.split(' ')[1];
// BrowserStack Specific Capabilities.
const caps = {
browser: 'chrome',
os: 'osx',
os_version: 'catalina',
name: 'My first playwright test',
build: process.env.BROWSERSTACK_BUILD_NAME || `local-build-${new Date().getTime()}`,
'browserstack.username': process.env.BROWSERSTACK_USERNAME || 'YOUR_USERNAME',
'browserstack.accessKey':
process.env.BROWSERSTACK_ACCESS_KEY || 'YOUR_ACCESS_KEY',
'browserstack.local': process.env.BROWSERSTACK_LOCAL || false,
'client.playwrightVersion': clientPlaywrightVersion,
'browserstack.debug': true,
'browserstack.networkLogs': true,
'browserstack.networkLogsOptions': {
"captureContent": "true"
}
};
// Patching the capabilities dynamically according to the project name.
const patchCaps = (name, title) => {
let combination = name.split(/@browserstack/)[0];
let [browerCaps, osCaps] = combination.split(/:/);
let [browser, browser_version] = browerCaps.split(/@/);
let osCapsSplit = osCaps.split(/ /);
let os = osCapsSplit.shift();
let os_version = osCapsSplit.join(' ');
caps.browser = browser;
if (browser.includes('playwright-')) {
caps['browserstack.playwrightVersion'] = browser_version;
} else {
caps.browser_version = browser_version;
}
caps.os = os;
caps.os_version = os_version;
caps.name = title;
};
const isHash = (entity) => Boolean(entity && typeof(entity) === "object" && !Array.isArray(entity));
const nestedKeyValue = (hash, keys) => keys.reduce((hash, key) => (isHash(hash) ? hash[key] : undefined), hash);
const isUndefined = val => (val === undefined || val === null || val === '');
const evaluateSessionStatus = (status) => {
if (!isUndefined(status)) {
status = status.toLowerCase();
}
if (status === "passed") {
return "passed";
} else if (status === "failed" || status === "timedout") {
return "failed";
} else {
return "";
}
}
exports.test = base.test.extend({
page: async ({ page, playwright }, use, testInfo) => {
// Use BrowserStack Launched Browser according to capabilities for cross-browser testing.
if (testInfo.project.name.match(/browserstack/)) {
patchCaps(testInfo.project.name, `${testInfo.file} - ${testInfo.title}`);
const vBrowser = await playwright.chromium.connect({
wsEndpoint:
`wss://cdp.browserstack.com/playwright?caps=` +
`${encodeURIComponent(JSON.stringify(caps))}`,
});
const vContext = await vBrowser.newContext(testInfo.project.use);
const vPage = await vContext.newPage();
await use(vPage);
const testResult = {
action: 'setSessionStatus',
arguments: {
status: evaluateSessionStatus(testInfo.status),
reason: nestedKeyValue(testInfo, ['error', 'message'])
},
};
await vPage.evaluate(() => {},
`browserstack_executor: ${JSON.stringify(testResult)}`);
await vPage.close();
await vBrowser.close();
} else {
use(page);
}
},
});

View File

@ -1,102 +1,37 @@
// playwright.config.js
// @ts-check
const { LOCAL_SERVER_ADDR } = require('./server')
const { devices } = require('@playwright/test');
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
/**
* @see https://playwright.dev/docs/test-configuration
*/
module.exports = {
testDir: '../',
testMatch: '**/*.spec.js',
// Use globalSetup & globalTearedown only if browserstack.local = true
globalSetup: require.resolve('./global-setup'),
globalTeardown: require.resolve('./global-teardown'),
timeout: 60000,
retries: 2,
use: {
viewport: null,
baseURL: LOCAL_SERVER_ADDR
},
timeout: 60 * 1000,
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 1 : 0,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'list',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
projects: [
// -- BrowserStack Projects --
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// name should be of the format 'browser@browser_version:os os_version@browserstack'
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
// use playwright version (or '1.latest') instead of browser version for `playwright-browser`.
// see which playwright versions use which browser versions:
// https://www.browserstack.com/docs/automate/playwright/playwright-browser-compatibility
//
// supported os and browser options:
// https://www.browserstack.com/docs/automate/playwright/browsers-and-os
// Chrome on Mac
{
name: 'chrome@latest:OSX Monterey@browserstack',
use: {
browserName: 'chromium',
channel: 'chrome'
},
},
{
name: 'chrome@86:OSX Mojave@browserstack',
use: {
browserName: 'chromium',
channel: 'chrome'
},
},
// Chrome on Windows
{
name: 'chrome@latest:Windows 11@browserstack',
use: {
browserName: 'chromium',
channel: 'chrome'
},
},
{
name: 'chrome@86:Windows 10@browserstack',
use: {
browserName: 'chromium',
channel: 'chrome'
},
},
// Firefox on Mac
{
name: 'playwright-firefox@1.latest:OSX Big Sur@browserstack',
use: {
browserName: 'firefox',
},
},
{
name: 'playwright-firefox@1.18.1:OSX Catalina@browserstack',
use: {
browserName: 'firefox',
},
},
// Firefox on Windows
{
name: 'playwright-firefox@1.latest:Windows 11@browserstack',
use: {
browserName: 'firefox',
},
},
{
name: 'playwright-firefox@1.18.1:Windows 10@browserstack',
use: {
browserName: 'firefox',
},
},
// Safari on Mac
{
name: 'playwright-webkit@1.latest:OSX Monterey@browserstack',
use: {
browserName: 'webkit',
},
},
{
name: 'playwright-webkit@1.19.1:OSX Big Sur@browserstack',
use: {
browserName: 'webkit',
},
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
};
module.exports = config;
webServer: {
command: 'npm run start',
port: 3000,
},
}

View File

@ -14,6 +14,14 @@ exports.mockRequest = function (page, path) {
})
}
exports.metaKey = function() {
if (process.platform === 'darwin') {
return 'Meta'
} else {
return 'Control'
}
}
// Mocks a specified number of HTTP requests with given path. Returns a promise that resolves to a
// list of requests as soon as the specified number of requests is made, or 10 seconds has passed.
exports.mockManyRequests = function(page, path, numberOfRequests) {
@ -32,10 +40,6 @@ exports.mockManyRequests = function(page, path, numberOfRequests) {
})
}
exports.isMac = function (workerInfo) {
return workerInfo.project.name.includes('OSX')
}
exports.expectCustomEvent = function (request, eventName, eventProps) {
const payload = request.postDataJSON()

View File

@ -1,6 +1,5 @@
const { test } = require('./support/harness')
const { mockRequest, mockManyRequests, isMac, expectCustomEvent } = require('./support/test-utils')
const { expect } = require('@playwright/test')
const { mockRequest, mockManyRequests, expectCustomEvent, metaKey } = require('./support/test-utils')
const { expect, test } = require('@playwright/test')
test.describe('tagged-events extension', () => {
test('tracks a tagged link click with custom props + url prop', async ({ page }) => {
@ -37,14 +36,14 @@ test.describe('tagged-events extension', () => {
expectCustomEvent(requests[0], "Form Submit", {})
});
test('tracks click and auxclick on any tagged HTML element', async ({ page }, workerInfo) => {
test('tracks click and auxclick on any tagged HTML element', async ({ page }) => {
await page.goto('/tagged-event.html')
const plausibleRequestMockList = mockManyRequests(page, '/api/event', 3)
await page.click('#button')
await page.click('#span')
await page.click('#div', { modifiers: [isMac(workerInfo) ? 'Meta' : 'Control'] })
await page.click('#div', { modifiers: [metaKey()] })
const requests = await plausibleRequestMockList
expect(requests.length).toBe(3)
@ -68,12 +67,12 @@ test.describe('tagged-events extension', () => {
expect((await navigationRequestMock).url()).toContain(linkURL)
});
test('tracks tagged HTML elements when their child element is clicked', async ({ page }, workerInfo) => {
test('tracks tagged HTML elements when their child element is clicked', async ({ page }) => {
await page.goto('/tagged-event.html')
const plausibleRequestMockList = mockManyRequests(page, '/api/event', 2)
await page.click('#h2-with-link-parent', { modifiers: [isMac(workerInfo) ? 'Meta' : 'Control'] })
await page.click('#h2-with-link-parent', { modifiers: [metaKey()] })
await page.click('#link-with-div-parent')
const requests = await plausibleRequestMockList