Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 233b4989a8 |
@@ -0,0 +1,31 @@
|
|||||||
|
# macOS test build — 0.1.1-mac.1
|
||||||
|
|
||||||
|
This is an unsigned, unnotarized test prerelease. It has been packaged on Linux; it has NOT been launched or playback-tested on macOS. Do not treat it as an approved customer release.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Based on main commit `2c93670aafd04f05e4e14b803f4bf0eeab6dd991`. Player code is unchanged. Version metadata and three vulnerable build-time transitive dependencies were updated (xmldom, fast-uri, js-yaml).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm ci
|
||||||
|
npm run check
|
||||||
|
npm audit
|
||||||
|
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --mac zip --arm64 --x64 --publish never
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux Electron launch smoke: `npm run smoke:headless`. This checks the shared application, not the macOS executable.
|
||||||
|
|
||||||
|
ZIP packaging works on Linux with this electron-builder version. Signing/notarization and a native macOS launch test remain separate gates. No claim is made that an unsigned app will pass Gatekeeper or Apple Silicon signature enforcement.
|
||||||
|
|
||||||
|
## Test on a Mac
|
||||||
|
|
||||||
|
1. Apple menu > About This Mac: Apple M-series uses arm64; Intel uses x64.
|
||||||
|
2. Download the matching ZIP from this prerelease and extract it.
|
||||||
|
3. Move Alta Video Player.app to Applications and try opening it.
|
||||||
|
4. If macOS blocks it, report the exact message. Do not disable Gatekeeper globally. A scoped Open Anyway option may be available under System Settings > Privacy & Security for this trusted test build; signature failures may instead require a Mac-built/ad-hoc-signed replacement.
|
||||||
|
5. Import a known video, then a representative export ZIP (including an encrypted one if available).
|
||||||
|
6. Check playback, seeking, speed, multiple camera synchronization and zoom.
|
||||||
|
7. Check integrity/certificate verification with a known export.
|
||||||
|
8. Quit and reopen. Report macOS version, processor, and any errors. Keep customer footage and export contents out of GitPeji issues.
|
||||||
|
|
||||||
|
Production remains on its existing stable release. This prerelease does not deliberately change the production download button; publish validated Mac assets to the stable release only after acceptance, retaining Windows downloads.
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Production pageview privacy contract
|
|
||||||
|
|
||||||
`static/privacy-pageview.js` loads the current Umami script only on HTTPS,
|
|
||||||
exact hostname `player.nenasets.com`, default port, and known application document routes.
|
|
||||||
It never loads on loopback, desktop protocol, DEV, previous, rollback, API,
|
|
||||||
admin, download, or unknown routes. Website: `dc1b14a6-d1ba-4802-b895-553b3c81c677`.
|
|
||||||
|
|
||||||
Automatic Umami tracking is disabled, including history and click hooks. One
|
|
||||||
manual pageview is sent per loaded document. The before-send hook reconstructs
|
|
||||||
a strict projection: website, hostname, static title `Alta Export Player`, static path
|
|
||||||
`/`, empty referrer, validated ordinary browser screen/language. It drops
|
|
||||||
identity, unknown/custom events, tags, arbitrary properties, user title, URL,
|
|
||||||
query/fragment, filenames, upload contents, forms and application state.
|
|
||||||
Only fixed download controls emit fixed event names/properties: `#releaseAction` → `download_desktop_app` (`tool: alta-export-player`, `format: desktop`). Native desktop/update modes are excluded.
|
|
||||||
These are click intents, not successful-download reports. A capture listener does
|
|
||||||
not read the link URL or filename, cancel navigation, or await analytics. Unknown events and
|
|
||||||
caller-provided event data are discarded; whitelisted event data is reconstructed.
|
|
||||||
|
|
||||||
The hook is installed before creating/loading the external script, auto tracking
|
|
||||||
is disabled, and an unavailable bootstrap produces no tracker GET/POST. The script
|
|
||||||
uses anonymous CORS and no-referrer; collection fetch credentials are omit.
|
|
||||||
No application data is read. Both script requests and collection requests must
|
|
||||||
have no Cookie, Authorization or Referer headers. Tracker credentials are omit.
|
|
||||||
|
|
||||||
The only CSP additions are the exact script and endpoint paths:
|
|
||||||
`https://umami.nenasets.com/script.js` in script-src and
|
|
||||||
`https://umami.nenasets.com/api/send` in connect-src. Existing restrictions stay.
|
|
||||||
No gateway/auth/deployment/registry configuration is changed by this commit.
|
|
||||||
|
|
||||||
## Repeatable gates
|
|
||||||
|
|
||||||
- `node --test tests/analytics.test.js`: hostname/path/protocol/port allowlist,
|
|
||||||
single pageview, automatic tracking off, reconstruction and identity/event denial.
|
|
||||||
- `node scripts/check.js`: native utility/server checks plus analytics suite.
|
|
||||||
- Root is the only native application document route. No synthetic media fixture
|
|
||||||
is provided; local acceptance exercised shell/layout controls, not footage playback.
|
|
||||||
|
|
||||||
Built-image Chromium desktop/mobile acceptance and request evidence live outside
|
|
||||||
this source checkout in the rollout project worker reports. The audited real
|
|
||||||
tracker was served locally and its POSTs intercepted with a204 sink, not sent to
|
|
||||||
production Umami. This does not prove organic ingestion. ONVIF acceptance uses
|
|
||||||
unchanged documented real bundled catalogue records, independent local signing
|
|
||||||
secrets and a test-only employee verifier; it does not prove a Google login.
|
|
||||||
|
|
||||||
No push or deployment is part of this commit. Inspect CI and deployment bindings
|
|
||||||
before publishing; the coordinator owns exact-image promotion and rollback.
|
|
||||||
Generated
+11
-11
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "webavp-desktop",
|
"name": "webavp-desktop",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1-mac.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "webavp-desktop",
|
"name": "webavp-desktop",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1-mac.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^43.4.1",
|
"electron": "^43.4.1",
|
||||||
"electron-builder": "^26.15.3"
|
"electron-builder": "^26.15.3"
|
||||||
@@ -734,9 +734,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.14",
|
"version": "0.8.15",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.14.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz",
|
||||||
"integrity": "sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==",
|
"integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2026,9 +2026,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-uri": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.1.5",
|
"version": "3.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
|
||||||
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
|
"integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2551,9 +2551,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
|
||||||
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "webavp-desktop",
|
"name": "webavp-desktop",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1-mac.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Standalone desktop shell for Alta Video Player.",
|
"description": "Standalone desktop shell for Alta Video Player.",
|
||||||
"desktopName": "alta-video-player.desktop",
|
"desktopName": "alta-video-player.desktop",
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ for (const file of ['electron/main.js', 'electron/preload.js', 'static/webavp-ut
|
|||||||
run(process.execPath, ['--check', file]);
|
run(process.execPath, ['--check', file]);
|
||||||
}
|
}
|
||||||
run(process.execPath, ['tests/webavp-utils.test.js']);
|
run(process.execPath, ['tests/webavp-utils.test.js']);
|
||||||
run(process.execPath, ['--test', 'tests/analytics.test.js']);
|
|
||||||
|
|
||||||
const html = fs.readFileSync(path.join(ROOT_DIR, 'templates/index.html'), 'utf8');
|
const html = fs.readFileSync(path.join(ROOT_DIR, 'templates/index.html'), 'utf8');
|
||||||
const required = [
|
const required = [
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
/* Static production pageviews/download intent. No application data to Umami. */
|
|
||||||
(() => {
|
|
||||||
'use strict';
|
|
||||||
const hostname = "player.nenasets.com";
|
|
||||||
const website = "dc1b14a6-d1ba-4802-b895-553b3c81c677";
|
|
||||||
const url = "/";
|
|
||||||
const title = "Alta Export Player";
|
|
||||||
const paths = ["/"];
|
|
||||||
const eligible = () => window.location.protocol === 'https:'
|
|
||||||
&& window.location.hostname === hostname && window.location.port === ''
|
|
||||||
&& paths.includes(window.location.pathname);
|
|
||||||
if (!eligible() || window.__altaPageviewLoaded) return;
|
|
||||||
window.__altaPageviewLoaded = true;
|
|
||||||
const controls = {releaseAction: 'download_desktop_app'};
|
|
||||||
const events = {download_desktop_app: {tool: 'alta-export-player', format: 'desktop'}};
|
|
||||||
const own = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
|
|
||||||
// Rebuild, don't spread the input: no identity, arbitrary properties, referrer,
|
|
||||||
// customer title, query, fragment, form, filename or upload data can survive.
|
|
||||||
window.altaSafePageview = (type, payload) => {
|
|
||||||
if (!eligible() || type !== 'event' || !payload) return null;
|
|
||||||
if (payload.name && (typeof payload.name !== 'string' || !own(events, payload.name))) return null;
|
|
||||||
const safe = { website, hostname, url, title, referrer: '',
|
|
||||||
screen: typeof payload.screen === 'string' && /^\d{1,5}x\d{1,5}$/.test(payload.screen) ? payload.screen : undefined,
|
|
||||||
language: typeof payload.language === 'string' && /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(payload.language) ? payload.language : undefined };
|
|
||||||
if (payload.name) { safe.name = payload.name; safe.data = {...events[payload.name]}; }
|
|
||||||
return safe;
|
|
||||||
};
|
|
||||||
const tracker = document.createElement('script');
|
|
||||||
tracker.src = 'https://umami.nenasets.com/script.js';
|
|
||||||
tracker.defer = true;
|
|
||||||
tracker.referrerPolicy = 'no-referrer';
|
|
||||||
tracker.crossOrigin = 'anonymous';
|
|
||||||
for (const [key, value] of Object.entries({
|
|
||||||
'website-id': website, domains: hostname, 'auto-track': 'false',
|
|
||||||
'exclude-search': 'true', 'exclude-hash': 'true',
|
|
||||||
'fetch-credentials': 'omit', 'before-send': 'altaSafePageview'
|
|
||||||
})) tracker.setAttribute('data-' + key, value);
|
|
||||||
let sent = false;
|
|
||||||
tracker.onload = () => {
|
|
||||||
if (!sent && eligible() && window.umami) {
|
|
||||||
sent = true;
|
|
||||||
window.umami.track();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Capture intent once, before application handlers; never cancel/await navigation.
|
|
||||||
// The web download anchor and native desktop/update behavior stay unchanged.
|
|
||||||
document.addEventListener('click', event => {
|
|
||||||
if (!eligible() || window.webavpNative || event.button !== 0) return;
|
|
||||||
const control = event.target.closest?.('a[id]');
|
|
||||||
if (!control || control.disabled || control.getAttribute('aria-disabled') === 'true'
|
|
||||||
|| !own(controls, control.id) || !window.umami) return;
|
|
||||||
const name = controls[control.id];
|
|
||||||
try { window.umami.track(name, {...events[name]}); } catch (_) { /* Analytics is optional. */ }
|
|
||||||
}, true);
|
|
||||||
document.head.appendChild(tracker);
|
|
||||||
})();
|
|
||||||
@@ -3,9 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="referrer" content="no-referrer">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; media-src blob:; img-src 'self' data:;">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://umami.nenasets.com/script.js; style-src 'self' 'unsafe-inline'; connect-src 'self' https://umami.nenasets.com/api/send; media-src blob:; img-src 'self' data:;">
|
|
||||||
<script src="/static/privacy-pageview.js?v=2" defer></script>
|
|
||||||
<title>Alta Video Player</title>
|
<title>Alta Video Player</title>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect rx='20' width='100' height='100' fill='%23121826'/><polygon points='38,25 38,75 78,50' fill='%23006ED7'/></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect rx='20' width='100' height='100' fill='%23121826'/><polygon points='38,25 38,75 78,50' fill='%23006ED7'/></svg>">
|
||||||
<script src="/static/jszip.min.js"></script>
|
<script src="/static/jszip.min.js"></script>
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const test = require('node:test');
|
|
||||||
const assert = require('node:assert/strict');
|
|
||||||
const fs = require('node:fs');
|
|
||||||
const path = require('node:path');
|
|
||||||
const vm = require('node:vm');
|
|
||||||
const root = path.resolve(__dirname, '..');
|
|
||||||
const player = fs.existsSync(path.join(root, 'package.json'));
|
|
||||||
const site = player ? 'dc1b14a6-d1ba-4802-b895-553b3c81c677' : 'e00bb594-4c6e-4471-911d-be3f7fdd9fae';
|
|
||||||
const host = player ? 'player.nenasets.com' : 'tools.nenasets.com';
|
|
||||||
const route = player ? '/' : '/private/onvif/';
|
|
||||||
function boot(url) {
|
|
||||||
const scripts = [], tracked = [], listeners = [];
|
|
||||||
const window = { location: new URL(url), umami: {track: (...args) => tracked.push(args)} };
|
|
||||||
const document = { addEventListener: (type, fn, capture) => listeners.push({type, fn, capture}),
|
|
||||||
createElement: () => ({setAttribute(k,v) { this[k] = v; }}),
|
|
||||||
head: {appendChild: s => { assert.equal(typeof window.altaSafePageview, 'function', 'hook installed before load'); scripts.push(s); }} };
|
|
||||||
const context = vm.createContext({window, document});
|
|
||||||
const source = fs.readFileSync(path.join(root, 'static/privacy-pageview.js'), 'utf8');
|
|
||||||
vm.runInContext(source, context);
|
|
||||||
return {window, scripts, tracked, listeners, context, source};
|
|
||||||
}
|
|
||||||
test('production pageviews use one manually loaded no-referrer tracker', () => {
|
|
||||||
const b = boot('https://' + host + route + '?customer=PRIVATE#PRIVATE');
|
|
||||||
assert.equal(b.scripts.length, 1);
|
|
||||||
const s = b.scripts[0];
|
|
||||||
assert.equal(s.src, 'https://umami.nenasets.com/script.js');
|
|
||||||
assert.equal(s.referrerPolicy, 'no-referrer');
|
|
||||||
assert.equal(s.crossOrigin, 'anonymous');
|
|
||||||
assert.equal(s['data-auto-track'], 'false');
|
|
||||||
assert.equal(s['data-exclude-search'], 'true');
|
|
||||||
assert.equal(s['data-exclude-hash'], 'true');
|
|
||||||
assert.equal(s['data-fetch-credentials'], 'omit');
|
|
||||||
assert.equal(s['data-domains'], host);
|
|
||||||
assert.equal(s['data-website-id'], site);
|
|
||||||
s.onload(); s.onload();
|
|
||||||
vm.runInContext(b.source, b.context);
|
|
||||||
assert.equal(b.scripts.length, 1);
|
|
||||||
assert.equal(b.tracked.length, 1);
|
|
||||||
});
|
|
||||||
test('before-send rebuilds a minimal static pageview; no identity, events or arbitrary data', () => {
|
|
||||||
const b = boot('https://' + host + route);
|
|
||||||
const guard = b.window[b.scripts[0]['data-before-send']];
|
|
||||||
const dirty = {website:'PRIVATE',url:'PRIVATE',title:'PRIVATE',hostname:'PRIVATE',referrer:'PRIVATE',name:'PRIVATE',data:{filename:'PRIVATE'},id:'PRIVATE',tag:'PRIVATE',screen:'1920x1080',language:'en-US'};
|
|
||||||
assert.equal(guard('identify', dirty), null);
|
|
||||||
assert.equal(guard('event', dirty), null);
|
|
||||||
delete dirty.name;
|
|
||||||
const safe = JSON.parse(JSON.stringify(guard('event', dirty)));
|
|
||||||
assert.deepEqual(safe, {website:site,hostname:host,url:route,title:player?'Alta Export Player':'ONVIF Tool',referrer:'',screen:'1920x1080',language:'en-US'});
|
|
||||||
assert.ok(!JSON.stringify(safe).includes('PRIVATE'));
|
|
||||||
b.window.location = new URL('https://' + host + '/private/previous/');
|
|
||||||
assert.equal(guard('event', dirty), null);
|
|
||||||
});
|
|
||||||
test('fail closed on DEV, previous, unknown, API, port, alternate host and HTTP routes', () => {
|
|
||||||
const excluded = ['http://' + host + route, 'https://' + host + ':8443' + route,
|
|
||||||
'https://dev.' + host + route, 'https://127.0.0.1' + route,
|
|
||||||
'https://' + host + '/private/onvif-dev/', 'https://' + host + '/private/onvif-previous/',
|
|
||||||
'https://' + host + '/private/onvif/previous/', 'https://' + host + '/private/onvif/api/lookup',
|
|
||||||
'https://' + host + '/private/onvif/download', 'https://' + host + '/private/onvif/admin',
|
|
||||||
'https://' + host + '/customer-name', 'https://' + host + '/api/verify-cert'];
|
|
||||||
for (const url of excluded) assert.equal(boot(url).scripts.length, 0, url);
|
|
||||||
});
|
|
||||||
|
|
||||||
const downloads = player ? [['releaseAction', 'download_desktop_app', {tool:'alta-export-player',format:'desktop'}]]
|
|
||||||
: [['download-template', 'download_csv_template', {tool:'onvif',format:'csv',kind:'template'}],
|
|
||||||
['download-report', 'download_csv_report', {tool:'onvif',format:'csv',kind:'report'}]];
|
|
||||||
test('static download clicks count once without changing native navigation or reading user data', () => {
|
|
||||||
const b = boot('https://' + host + route);
|
|
||||||
b.scripts[0].onload();
|
|
||||||
vm.runInContext(b.source, b.context);
|
|
||||||
assert.equal(b.listeners.length, 1);
|
|
||||||
assert.equal(b.listeners[0].type, 'click');
|
|
||||||
assert.equal(b.listeners[0].capture, true);
|
|
||||||
for (const [id, name, data] of downloads) {
|
|
||||||
const control = {id,tagName:'A',disabled:false,getAttribute:()=>null,
|
|
||||||
form:{checkValidity:()=>false},closest:()=>control};
|
|
||||||
const event = {target:{closest:()=>control},button:0,preventDefault:()=>assert.fail('navigation intercepted')};
|
|
||||||
const n=b.tracked.length;
|
|
||||||
b.listeners[0].fn(event);
|
|
||||||
assert.equal(b.tracked.length,n+1);
|
|
||||||
assert.equal(b.tracked.at(-1)[0],name);
|
|
||||||
assert.deepEqual(JSON.parse(JSON.stringify(b.tracked.at(-1)[1])),data);
|
|
||||||
const safe=b.window.altaSafePageview('event',{name,data:{url:'PRIVATE',filename:'PRIVATE'},id:'PRIVATE',tag:'PRIVATE'});
|
|
||||||
assert.equal(safe.name,name);
|
|
||||||
assert.deepEqual(JSON.parse(JSON.stringify(safe.data)),data);
|
|
||||||
assert.ok(!JSON.stringify(safe).includes('PRIVATE'));
|
|
||||||
control.disabled=true;b.listeners[0].fn(event);assert.equal(b.tracked.length,n+1);
|
|
||||||
}
|
|
||||||
b.window.location=new URL('https://'+host+'/private/previous/');
|
|
||||||
const n=b.tracked.length;
|
|
||||||
b.listeners[0].fn({target:{closest:()=>({id:downloads[0][0],tagName:'A'})},button:0});
|
|
||||||
assert.equal(b.tracked.length,n);
|
|
||||||
});
|
|
||||||
test('analytics missing or throwing does not prevent download clicks', () => {
|
|
||||||
const b=boot('https://'+host+route);
|
|
||||||
const e={target:{closest:()=>({id:downloads[0][0],tagName:'A',getAttribute:()=>null})},button:0};
|
|
||||||
delete b.window.umami;assert.doesNotThrow(()=>b.listeners[0].fn(e));
|
|
||||||
b.window.umami={track:()=>{throw Error('offline');}};assert.doesNotThrow(()=>b.listeners[0].fn(e));
|
|
||||||
});
|
|
||||||
test('known events use an own-property whitelist, native desktop modes are excluded', () => {
|
|
||||||
const b=boot('https://'+host+route);
|
|
||||||
for(const name of ['__proto__','constructor','toString','PRIVATE', {toString:()=>downloads[0][1]}])
|
|
||||||
assert.equal(b.window.altaSafePageview('event',{name}),null);
|
|
||||||
if(player){
|
|
||||||
b.window.webavpNative={checkForUpdates:()=>{}};
|
|
||||||
const n=b.tracked.length;
|
|
||||||
b.listeners[0].fn({target:{closest:()=>({id:'releaseAction',tagName:'A',getAttribute:()=>null})},button:0});
|
|
||||||
assert.equal(b.tracked.length,n);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('only native application document routes are eligible', () => {
|
|
||||||
for(const suffix of (player ? [''] : ['', 'upload', 'results', 'process', 'clear']))
|
|
||||||
assert.equal(boot('https://' + host + route + suffix).scripts.length, 1, suffix);
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user