89 lines
3.7 KiB
JavaScript
89 lines
3.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
injectMindSpaceAnalytics,
|
|
pseudonymizeAnalyticsId,
|
|
resolveAnalyticsOwnerSegment,
|
|
resolveAnalyticsOwnerLabel,
|
|
resolveMindSpaceAnalyticsConfig,
|
|
sendMindSpaceAnalyticsEvent,
|
|
} from './mindspace-analytics.mjs';
|
|
|
|
test('analytics config is disabled unless explicitly enabled and configured', () => {
|
|
assert.equal(resolveMindSpaceAnalyticsConfig({ MEMIND_ANALYTICS_ENABLED: 'true' }).enabled, false);
|
|
assert.equal(resolveMindSpaceAnalyticsConfig({
|
|
MEMIND_ANALYTICS_ENABLED: 'true',
|
|
MEMIND_ANALYTICS_WEBSITE_ID: 'local-website',
|
|
}).enabled, false);
|
|
const config = resolveMindSpaceAnalyticsConfig({
|
|
MEMIND_ANALYTICS_ENABLED: 'true',
|
|
MEMIND_ANALYTICS_URL: 'http://127.0.0.1:3200',
|
|
MEMIND_ANALYTICS_WEBSITE_ID: 'local-website',
|
|
MEMIND_ANALYTICS_ID_SECRET: 'local-secret',
|
|
});
|
|
assert.equal(config.enabled, true);
|
|
assert.equal(config.analyticsUrl, 'http://127.0.0.1:3200');
|
|
assert.equal(config.hostPath, '/analytics');
|
|
});
|
|
|
|
test('owner ids are stable pseudonyms and never expose the source id', () => {
|
|
const first = pseudonymizeAnalyticsId('user-123', 'secret');
|
|
assert.equal(first, pseudonymizeAnalyticsId('user-123', 'secret'));
|
|
assert.notEqual(first, 'user-123');
|
|
assert.notEqual(first, pseudonymizeAnalyticsId('user-456', 'secret'));
|
|
});
|
|
|
|
test('owner segments come from server-side Memind user profile data', () => {
|
|
assert.equal(resolveAnalyticsOwnerSegment({ role: 'admin' }), 'admin');
|
|
assert.equal(resolveAnalyticsOwnerSegment({ role: 'user', planType: 'pro' }), 'plan:pro');
|
|
assert.equal(resolveAnalyticsOwnerSegment({ role: 'user' }), 'plan:free');
|
|
});
|
|
|
|
test('owner labels are readable but bounded and stripped of control characters', () => {
|
|
assert.equal(resolveAnalyticsOwnerLabel({ displayName: '张三\n管理员' }), '张三 管理员');
|
|
assert.equal(resolveAnalyticsOwnerLabel({}), '未命名用户');
|
|
});
|
|
|
|
test('injects one local same-origin tracker with page dimensions', () => {
|
|
const html = '<!doctype html><html><head><title>Demo</title></head><body><h1>Demo</h1></body></html>';
|
|
const out = injectMindSpaceAnalytics(html, {
|
|
ownerId: 'user-123',
|
|
ownerLabel: '张三',
|
|
pageId: 'page-1',
|
|
publicationId: 'pub-1',
|
|
config: {
|
|
enabled: true,
|
|
websiteId: 'local-website',
|
|
idSecret: 'secret',
|
|
scriptPath: '/analytics/script.js',
|
|
hostPath: '/analytics',
|
|
domains: '127.0.0.1,localhost',
|
|
},
|
|
});
|
|
assert.match(out, /src="\/analytics\/script\.js"/);
|
|
assert.match(out, /data-host-url="\/analytics"/);
|
|
assert.match(out, /data-auto-track="false"/);
|
|
assert.match(out, /page_id/);
|
|
assert.match(out, /owner_segment/);
|
|
assert.doesNotMatch(out, /owner_label/);
|
|
assert.doesNotMatch(out, /张三/);
|
|
assert.match(out, /page_click/);
|
|
assert.match(out, /page_form_submit/);
|
|
assert.match(out, /page_scroll_/);
|
|
assert.match(out, /page_engaged_10s/);
|
|
assert.doesNotMatch(out, /user-123/);
|
|
assert.equal(injectMindSpaceAnalytics(out, { ownerId: 'user-123', config: { enabled: true, websiteId: 'local-website', idSecret: 'secret' } }), out);
|
|
});
|
|
|
|
test('does not alter non-full-html or disabled pages', () => {
|
|
const fragment = '<div>hello</div>';
|
|
assert.equal(injectMindSpaceAnalytics(fragment, { ownerId: 'u', config: { enabled: true, websiteId: 'w', idSecret: 's' } }), fragment);
|
|
const html = '<!doctype html><html><head></head><body></body></html>';
|
|
assert.equal(injectMindSpaceAnalytics(html, { ownerId: 'u', config: { enabled: false, websiteId: 'w', idSecret: 's' } }), html);
|
|
});
|
|
|
|
test('analytics event sender is fail-open when analytics is disabled', async () => {
|
|
assert.equal(await sendMindSpaceAnalyticsEvent({ eventName: 'page_generated', ownerId: 'u', config: { enabled: false } }), false);
|
|
});
|