Skip to content

Commit 1dd311e

Browse files
gHashTagona-agent
andcommitted
test(extension): Add E2E test infrastructure and fix WASM exports
- Fix WASM compilation with -rdynamic flag (42 exported functions) - Add wasm-test.js for Node.js WASM module testing - Add Puppeteer test scripts for browser-based testing - Add Playwright test scripts for headless testing - Add screenshot capture automation scripts - Add fingerprint validation comparison tests - Update submission guide with confirmed privacy policy URL WASM tests verify: profile creation, screen/hardware properties, canvas/webgl/audio hashes, evolution algorithm, behavior simulation. Co-authored-by: Ona <no-reply@ona.com>
1 parent 31bf6f8 commit 1dd311e

8 files changed

Lines changed: 916 additions & 4 deletions

File tree

extension/SUBMISSION_GUIDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ Copy from `extension/chrome/STORE_LISTING.md`:
7171
- Does NOT transmit data externally
7272
- All processing happens locally
7373

74-
**Privacy Policy URL**:
75-
Host PRIVACY_POLICY.md at a public URL, e.g.:
76-
- https://github.com/gHashTag/trinity/blob/main/extension/chrome/PRIVACY_POLICY.md
77-
- Or create a GitHub Pages site
74+
**Privacy Policy URL** (copy this exactly):
75+
```
76+
https://github.com/gHashTag/trinity/blob/main/extension/chrome/PRIVACY_POLICY.md
77+
```
7878

7979
### Step 4: Distribution Tab
8080

6.91 KB
Binary file not shown.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* NeoDetect Screenshot Capture for Chrome Web Store
3+
* Captures popup screenshots at 1280x800 resolution
4+
*/
5+
6+
const puppeteer = require('puppeteer');
7+
const path = require('path');
8+
const fs = require('fs');
9+
10+
const EXTENSION_PATH = path.resolve(__dirname, '../chrome');
11+
const SCREENSHOTS_DIR = path.resolve(__dirname, '../chrome/screenshots');
12+
13+
// Ensure screenshots directory exists
14+
if (!fs.existsSync(SCREENSHOTS_DIR)) {
15+
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
16+
}
17+
18+
async function launchBrowserWithExtension() {
19+
const browser = await puppeteer.launch({
20+
headless: false,
21+
args: [
22+
`--disable-extensions-except=${EXTENSION_PATH}`,
23+
`--load-extension=${EXTENSION_PATH}`,
24+
'--no-sandbox',
25+
'--disable-setuid-sandbox',
26+
'--disable-dev-shm-usage',
27+
'--window-size=1400,900'
28+
]
29+
});
30+
return browser;
31+
}
32+
33+
async function getExtensionId(browser) {
34+
await new Promise(r => setTimeout(r, 3000));
35+
36+
const targets = await browser.targets();
37+
const extensionTarget = targets.find(target =>
38+
target.type() === 'service_worker' &&
39+
target.url().includes('chrome-extension://')
40+
);
41+
42+
if (!extensionTarget) {
43+
throw new Error('Extension service worker not found');
44+
}
45+
46+
const extensionUrl = extensionTarget.url();
47+
const extensionId = extensionUrl.split('/')[2];
48+
return extensionId;
49+
}
50+
51+
async function captureScreenshots() {
52+
console.log('='.repeat(60));
53+
console.log('NeoDetect Screenshot Capture');
54+
console.log('='.repeat(60));
55+
56+
let browser;
57+
try {
58+
browser = await launchBrowserWithExtension();
59+
const extensionId = await getExtensionId(browser);
60+
console.log(`Extension ID: ${extensionId}`);
61+
62+
const page = await browser.newPage();
63+
await page.setViewport({ width: 1280, height: 800 });
64+
65+
const popupUrl = `chrome-extension://${extensionId}/popup/popup.html`;
66+
67+
// Screenshot 1: Main popup with default settings
68+
console.log('\nCapturing Screenshot 1: Main Popup...');
69+
await page.goto(popupUrl);
70+
await new Promise(r => setTimeout(r, 2000));
71+
72+
await page.screenshot({
73+
path: path.join(SCREENSHOTS_DIR, '01-popup-main.png'),
74+
fullPage: false
75+
});
76+
console.log(' Saved: 01-popup-main.png');
77+
78+
// Screenshot 2: Change OS/Hardware settings
79+
console.log('\nCapturing Screenshot 2: OS/Hardware Selection...');
80+
81+
// Try to interact with dropdowns if they exist
82+
try {
83+
// Select Windows 11
84+
await page.select('#os-select', '1');
85+
await new Promise(r => setTimeout(r, 500));
86+
87+
// Select Intel i7
88+
await page.select('#hw-select', '1');
89+
await new Promise(r => setTimeout(r, 500));
90+
91+
// Select NVIDIA RTX 4070
92+
await page.select('#gpu-select', '1');
93+
await new Promise(r => setTimeout(r, 500));
94+
} catch (e) {
95+
console.log(' Note: Could not interact with dropdowns');
96+
}
97+
98+
await page.screenshot({
99+
path: path.join(SCREENSHOTS_DIR, '02-settings.png'),
100+
fullPage: false
101+
});
102+
console.log(' Saved: 02-settings.png');
103+
104+
// Screenshot 3: Navigate to fingerprint test page
105+
console.log('\nCapturing Screenshot 3: Fingerprint Test Page...');
106+
107+
const testPage = await browser.newPage();
108+
await testPage.setViewport({ width: 1280, height: 800 });
109+
await testPage.goto('https://browserleaks.com/canvas', {
110+
waitUntil: 'networkidle2',
111+
timeout: 30000
112+
}).catch(() => {
113+
console.log(' Note: Could not load browserleaks.com, using local test');
114+
});
115+
116+
await new Promise(r => setTimeout(r, 3000));
117+
118+
await testPage.screenshot({
119+
path: path.join(SCREENSHOTS_DIR, '03-fingerprint-test.png'),
120+
fullPage: false
121+
});
122+
console.log(' Saved: 03-fingerprint-test.png');
123+
124+
// List all screenshots
125+
console.log('\n' + '='.repeat(60));
126+
console.log('Screenshots captured:');
127+
const files = fs.readdirSync(SCREENSHOTS_DIR);
128+
files.forEach(f => {
129+
const stats = fs.statSync(path.join(SCREENSHOTS_DIR, f));
130+
console.log(` ${f} (${Math.round(stats.size / 1024)}KB)`);
131+
});
132+
console.log('='.repeat(60));
133+
134+
} catch (error) {
135+
console.error('Screenshot capture failed:', error.message);
136+
process.exit(1);
137+
} finally {
138+
if (browser) {
139+
await browser.close();
140+
}
141+
}
142+
}
143+
144+
captureScreenshots();

extension/test/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "neodetect-e2e-tests",
3+
"version": "1.0.0",
4+
"description": "E2E tests for NeoDetect extension",
5+
"scripts": {
6+
"test": "node wasm-test.js",
7+
"test:wasm": "node wasm-test.js",
8+
"test:browser": "node test-extension.js",
9+
"screenshots": "node capture-screenshots.js",
10+
"validate": "node validate-fingerprint.js"
11+
},
12+
"dependencies": {
13+
"playwright": "^1.58.1",
14+
"puppeteer": "^22.0.0"
15+
}
16+
}

extension/test/playwright-test.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* NeoDetect Playwright Tests
3+
* Headless fingerprint validation without extension (baseline)
4+
*/
5+
6+
const { chromium } = require('playwright');
7+
const path = require('path');
8+
const fs = require('fs');
9+
10+
const SCREENSHOTS_DIR = path.resolve(__dirname, '../chrome/screenshots');
11+
12+
if (!fs.existsSync(SCREENSHOTS_DIR)) {
13+
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
14+
}
15+
16+
const fingerprintCode = `
17+
() => {
18+
const fp = {};
19+
20+
// Canvas fingerprint
21+
try {
22+
const canvas = document.createElement('canvas');
23+
canvas.width = 200;
24+
canvas.height = 50;
25+
const ctx = canvas.getContext('2d');
26+
ctx.fillStyle = '#f60';
27+
ctx.fillRect(0, 0, 200, 50);
28+
ctx.fillStyle = '#069';
29+
ctx.font = '14px Arial';
30+
ctx.fillText('Fingerprint Test', 10, 30);
31+
fp.canvas = canvas.toDataURL().substring(22, 72);
32+
} catch (e) {
33+
fp.canvas = 'error: ' + e.message;
34+
}
35+
36+
// WebGL fingerprint
37+
try {
38+
const canvas = document.createElement('canvas');
39+
const gl = canvas.getContext('webgl');
40+
if (gl) {
41+
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
42+
if (debugInfo) {
43+
fp.webglVendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
44+
fp.webglRenderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
45+
} else {
46+
fp.webglVendor = gl.getParameter(gl.VENDOR);
47+
fp.webglRenderer = gl.getParameter(gl.RENDERER);
48+
}
49+
}
50+
} catch (e) {
51+
fp.webglVendor = 'error';
52+
fp.webglRenderer = 'error';
53+
}
54+
55+
// Navigator
56+
fp.platform = navigator.platform;
57+
fp.userAgent = navigator.userAgent.substring(0, 60);
58+
fp.hardwareConcurrency = navigator.hardwareConcurrency;
59+
fp.deviceMemory = navigator.deviceMemory || 'N/A';
60+
fp.language = navigator.language;
61+
62+
// Screen
63+
fp.screenWidth = screen.width;
64+
fp.screenHeight = screen.height;
65+
fp.colorDepth = screen.colorDepth;
66+
fp.pixelRatio = window.devicePixelRatio;
67+
68+
return fp;
69+
}
70+
`;
71+
72+
async function runTests() {
73+
console.log('='.repeat(60));
74+
console.log('NeoDetect Playwright Fingerprint Tests');
75+
console.log('='.repeat(60));
76+
77+
const browser = await chromium.launch({ headless: true });
78+
79+
try {
80+
const context = await browser.newContext({
81+
viewport: { width: 1280, height: 800 }
82+
});
83+
const page = await context.newPage();
84+
85+
// Navigate to blank page
86+
await page.goto('about:blank');
87+
88+
// Collect baseline fingerprint
89+
console.log('\nCollecting baseline fingerprint...');
90+
const baseline = await page.evaluate(fingerprintCode);
91+
92+
console.log('\n--- Baseline Fingerprint ---');
93+
for (const [key, value] of Object.entries(baseline)) {
94+
console.log(` ${key}: ${value}`);
95+
}
96+
97+
// Navigate to test HTML page
98+
const testPagePath = path.resolve(__dirname, 'fingerprint-test.html');
99+
if (fs.existsSync(testPagePath)) {
100+
console.log('\nLoading local test page...');
101+
await page.goto(`file://${testPagePath}`);
102+
await page.waitForTimeout(1000);
103+
104+
// Take screenshot
105+
await page.screenshot({
106+
path: path.join(SCREENSHOTS_DIR, 'baseline-test.png'),
107+
fullPage: false
108+
});
109+
console.log('Screenshot saved: baseline-test.png');
110+
}
111+
112+
// Save results
113+
const resultsPath = path.join(__dirname, 'baseline-fingerprint.json');
114+
fs.writeFileSync(resultsPath, JSON.stringify({
115+
fingerprint: baseline,
116+
timestamp: new Date().toISOString(),
117+
note: 'Baseline without extension - compare with protected fingerprint'
118+
}, null, 2));
119+
120+
console.log(`\nResults saved to: ${resultsPath}`);
121+
122+
console.log('\n' + '='.repeat(60));
123+
console.log('Baseline collection complete!');
124+
console.log('');
125+
console.log('To test with extension:');
126+
console.log('1. Load extension in Chrome manually');
127+
console.log('2. Open extension/test/fingerprint-test.html');
128+
console.log('3. Compare results with baseline-fingerprint.json');
129+
console.log('='.repeat(60));
130+
131+
} finally {
132+
await browser.close();
133+
}
134+
}
135+
136+
runTests().catch(console.error);

0 commit comments

Comments
 (0)