mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-04-05 07:17:52 +10:00
wip major changes
This commit is contained in:
116
archivebox/plugins/ublock/on_Snapshot__03_ublock.js
Executable file
116
archivebox/plugins/ublock/on_Snapshot__03_ublock.js
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* uBlock Origin Extension Plugin
|
||||
*
|
||||
* Installs and configures the uBlock Origin Chrome extension for ad blocking
|
||||
* and privacy protection during page archiving.
|
||||
*
|
||||
* Extension: https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm
|
||||
*
|
||||
* Priority: 03 (early) - Must install before Chrome session starts
|
||||
* Hook: on_Snapshot
|
||||
*
|
||||
* This extension automatically:
|
||||
* - Blocks ads, trackers, and malware domains
|
||||
* - Reduces page load time and bandwidth usage
|
||||
* - Improves privacy during archiving
|
||||
* - Removes clutter from archived pages
|
||||
* - Uses efficient blocking with filter lists
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Import extension utilities
|
||||
const extensionUtils = require('../chrome_extensions/chrome_extension_utils.js');
|
||||
|
||||
// Extension metadata
|
||||
const EXTENSION = {
|
||||
webstore_id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm',
|
||||
name: 'ublock',
|
||||
};
|
||||
|
||||
// Get extensions directory from environment or use default
|
||||
const EXTENSIONS_DIR = process.env.CHROME_EXTENSIONS_DIR ||
|
||||
path.join(process.env.DATA_DIR || './data', 'personas', process.env.ACTIVE_PERSONA || 'Default', 'chrome_extensions');
|
||||
|
||||
/**
|
||||
* Install the uBlock Origin extension
|
||||
*/
|
||||
async function installUblockExtension() {
|
||||
console.log('[*] Installing uBlock Origin extension...');
|
||||
|
||||
// Install the extension
|
||||
const extension = await extensionUtils.loadOrInstallExtension(EXTENSION, EXTENSIONS_DIR);
|
||||
|
||||
if (!extension) {
|
||||
console.error('[❌] Failed to install uBlock Origin extension');
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('[+] uBlock Origin extension installed');
|
||||
console.log('[+] Ads and trackers will be blocked during archiving');
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: uBlock Origin works automatically with default filter lists.
|
||||
* No configuration needed - blocks ads, trackers, and malware domains out of the box.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main entry point - install extension before archiving
|
||||
*/
|
||||
async function main() {
|
||||
// Check if extension is already cached
|
||||
const cacheFile = path.join(EXTENSIONS_DIR, 'ublock.extension.json');
|
||||
|
||||
if (fs.existsSync(cacheFile)) {
|
||||
try {
|
||||
const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
|
||||
const manifestPath = path.join(cached.unpacked_path, 'manifest.json');
|
||||
|
||||
if (fs.existsSync(manifestPath)) {
|
||||
console.log('[*] uBlock Origin extension already installed (using cache)');
|
||||
return cached;
|
||||
}
|
||||
} catch (e) {
|
||||
// Cache file corrupted, re-install
|
||||
console.warn('[⚠️] Extension cache corrupted, re-installing...');
|
||||
}
|
||||
}
|
||||
|
||||
// Install extension
|
||||
const extension = await installUblockExtension();
|
||||
|
||||
// Export extension metadata for chrome_session to load
|
||||
if (extension) {
|
||||
// Write extension info to a cache file that chrome_session can read
|
||||
await fs.promises.mkdir(EXTENSIONS_DIR, { recursive: true });
|
||||
await fs.promises.writeFile(
|
||||
cacheFile,
|
||||
JSON.stringify(extension, null, 2)
|
||||
);
|
||||
console.log(`[+] Extension metadata written to ${cacheFile}`);
|
||||
}
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
// Export functions for use by other plugins
|
||||
module.exports = {
|
||||
EXTENSION,
|
||||
installUblockExtension,
|
||||
};
|
||||
|
||||
// Run if executed directly
|
||||
if (require.main === module) {
|
||||
main().then(() => {
|
||||
console.log('[✓] uBlock Origin extension setup complete');
|
||||
process.exit(0);
|
||||
}).catch(err => {
|
||||
console.error('[❌] uBlock Origin extension setup failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
321
archivebox/plugins/ublock/tests/test_ublock.js
Normal file
321
archivebox/plugins/ublock/tests/test_ublock.js
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* Unit tests for ublock plugin
|
||||
*
|
||||
* Run with: node --test tests/test_ublock.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { describe, it, before, after, beforeEach, afterEach } = require('node:test');
|
||||
|
||||
// Test fixtures
|
||||
const TEST_DIR = path.join(__dirname, '.test_fixtures');
|
||||
const TEST_EXTENSIONS_DIR = path.join(TEST_DIR, 'chrome_extensions');
|
||||
|
||||
describe('ublock plugin', () => {
|
||||
before(() => {
|
||||
if (!fs.existsSync(TEST_DIR)) {
|
||||
fs.mkdirSync(TEST_DIR, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (fs.existsSync(TEST_DIR)) {
|
||||
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe('EXTENSION metadata', () => {
|
||||
it('should have correct webstore_id for uBlock Origin', () => {
|
||||
const { EXTENSION } = require('../on_Snapshot__03_ublock.js');
|
||||
|
||||
assert.strictEqual(EXTENSION.webstore_id, 'cjpalhdlnbpafiamejdnhcphjbkeiagm');
|
||||
});
|
||||
|
||||
it('should have correct name', () => {
|
||||
const { EXTENSION } = require('../on_Snapshot__03_ublock.js');
|
||||
|
||||
assert.strictEqual(EXTENSION.name, 'ublock');
|
||||
});
|
||||
});
|
||||
|
||||
describe('installUblockExtension', () => {
|
||||
beforeEach(() => {
|
||||
process.env.CHROME_EXTENSIONS_DIR = TEST_EXTENSIONS_DIR;
|
||||
|
||||
if (!fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.mkdirSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.rmSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
delete process.env.CHROME_EXTENSIONS_DIR;
|
||||
});
|
||||
|
||||
it('should use cached extension if available', async () => {
|
||||
const { installUblockExtension } = require('../on_Snapshot__03_ublock.js');
|
||||
|
||||
// Create fake cache
|
||||
const cacheFile = path.join(TEST_EXTENSIONS_DIR, 'ublock.extension.json');
|
||||
const fakeExtensionDir = path.join(TEST_EXTENSIONS_DIR, 'fake_ublock');
|
||||
|
||||
fs.mkdirSync(fakeExtensionDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(fakeExtensionDir, 'manifest.json'),
|
||||
JSON.stringify({ version: '1.67.0' })
|
||||
);
|
||||
|
||||
const fakeCache = {
|
||||
webstore_id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm',
|
||||
name: 'ublock',
|
||||
unpacked_path: fakeExtensionDir,
|
||||
version: '1.67.0'
|
||||
};
|
||||
|
||||
fs.writeFileSync(cacheFile, JSON.stringify(fakeCache));
|
||||
|
||||
const result = await installUblockExtension();
|
||||
|
||||
assert.notStrictEqual(result, null);
|
||||
assert.strictEqual(result.webstore_id, 'cjpalhdlnbpafiamejdnhcphjbkeiagm');
|
||||
});
|
||||
|
||||
it('should not require any configuration', async () => {
|
||||
// uBlock Origin works out of the box with default filter lists
|
||||
const { EXTENSION } = require('../on_Snapshot__03_ublock.js');
|
||||
|
||||
assert.ok(EXTENSION);
|
||||
// No config fields should be required
|
||||
});
|
||||
|
||||
it('should have large download size (filter lists)', () => {
|
||||
// uBlock Origin is typically larger than other extensions
|
||||
// due to included filter lists (usually 3-5 MB)
|
||||
|
||||
const typicalSize = 4 * 1024 * 1024; // ~4 MB
|
||||
const minExpectedSize = 2 * 1024 * 1024; // Minimum 2 MB
|
||||
|
||||
// Just verify we understand the expected size
|
||||
assert.ok(typicalSize > minExpectedSize);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cache file creation', () => {
|
||||
beforeEach(() => {
|
||||
process.env.CHROME_EXTENSIONS_DIR = TEST_EXTENSIONS_DIR;
|
||||
|
||||
if (!fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.mkdirSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.rmSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
delete process.env.CHROME_EXTENSIONS_DIR;
|
||||
});
|
||||
|
||||
it('should create cache file with correct structure', async () => {
|
||||
const cacheFile = path.join(TEST_EXTENSIONS_DIR, 'ublock.extension.json');
|
||||
|
||||
const mockExtension = {
|
||||
webstore_id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm',
|
||||
name: 'ublock',
|
||||
version: '1.68.0',
|
||||
unpacked_path: path.join(TEST_EXTENSIONS_DIR, 'test_ublock'),
|
||||
crx_path: path.join(TEST_EXTENSIONS_DIR, 'test_ublock.crx')
|
||||
};
|
||||
|
||||
await fs.promises.writeFile(cacheFile, JSON.stringify(mockExtension, null, 2));
|
||||
|
||||
assert.ok(fs.existsSync(cacheFile));
|
||||
|
||||
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
|
||||
assert.strictEqual(cache.name, 'ublock');
|
||||
assert.strictEqual(cache.webstore_id, 'cjpalhdlnbpafiamejdnhcphjbkeiagm');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extension functionality', () => {
|
||||
it('should work automatically with default filter lists', () => {
|
||||
const features = {
|
||||
automaticBlocking: true,
|
||||
requiresConfiguration: false,
|
||||
requiresApiKey: false,
|
||||
defaultFilterLists: true,
|
||||
blocksAds: true,
|
||||
blocksTrackers: true,
|
||||
blocksMalware: true
|
||||
};
|
||||
|
||||
assert.strictEqual(features.automaticBlocking, true);
|
||||
assert.strictEqual(features.requiresConfiguration, false);
|
||||
assert.strictEqual(features.requiresApiKey, false);
|
||||
assert.strictEqual(features.defaultFilterLists, true);
|
||||
});
|
||||
|
||||
it('should not require runtime configuration', () => {
|
||||
// uBlock Origin works purely via filter lists and content scripts
|
||||
// No API keys or runtime configuration needed
|
||||
|
||||
const requiresRuntimeConfig = false;
|
||||
const requiresApiKey = false;
|
||||
|
||||
assert.strictEqual(requiresRuntimeConfig, false);
|
||||
assert.strictEqual(requiresApiKey, false);
|
||||
});
|
||||
|
||||
it('should support standard filter list formats', () => {
|
||||
const supportedFormats = [
|
||||
'EasyList',
|
||||
'EasyPrivacy',
|
||||
'Malware Domains',
|
||||
'Peter Lowe\'s List',
|
||||
'uBlock Origin filters'
|
||||
];
|
||||
|
||||
assert.ok(supportedFormats.length > 0);
|
||||
// Should support multiple filter list formats
|
||||
});
|
||||
});
|
||||
|
||||
describe('priority and execution order', () => {
|
||||
it('should have priority 03 (early)', () => {
|
||||
const filename = 'on_Snapshot__03_ublock.js';
|
||||
|
||||
const match = filename.match(/on_Snapshot__(\d+)_/);
|
||||
assert.ok(match);
|
||||
|
||||
const priority = parseInt(match[1]);
|
||||
assert.strictEqual(priority, 3);
|
||||
});
|
||||
|
||||
it('should run before chrome_session (priority 20)', () => {
|
||||
const extensionPriority = 3;
|
||||
const chromeSessionPriority = 20;
|
||||
|
||||
assert.ok(extensionPriority < chromeSessionPriority);
|
||||
});
|
||||
|
||||
it('should run after cookie dismissal extension', () => {
|
||||
const ublockPriority = 3;
|
||||
const cookiesPriority = 2;
|
||||
|
||||
assert.ok(ublockPriority > cookiesPriority);
|
||||
});
|
||||
});
|
||||
|
||||
describe('performance considerations', () => {
|
||||
it('should benefit from caching due to large size', () => {
|
||||
// uBlock Origin's large size makes caching especially important
|
||||
|
||||
const averageDownloadTime = 10; // seconds
|
||||
const averageCacheCheckTime = 0.01; // seconds
|
||||
|
||||
const performanceGain = averageDownloadTime / averageCacheCheckTime;
|
||||
|
||||
// Should be at least 100x faster with cache
|
||||
assert.ok(performanceGain > 100);
|
||||
});
|
||||
|
||||
it('should not impact page load time significantly', () => {
|
||||
// While extension is large, it uses efficient blocking
|
||||
|
||||
const efficientBlocking = true;
|
||||
const minimalOverhead = true;
|
||||
|
||||
assert.strictEqual(efficientBlocking, true);
|
||||
assert.strictEqual(minimalOverhead, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
beforeEach(() => {
|
||||
process.env.CHROME_EXTENSIONS_DIR = TEST_EXTENSIONS_DIR;
|
||||
|
||||
if (!fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.mkdirSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(TEST_EXTENSIONS_DIR)) {
|
||||
fs.rmSync(TEST_EXTENSIONS_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
delete process.env.CHROME_EXTENSIONS_DIR;
|
||||
});
|
||||
|
||||
it('should handle corrupted cache gracefully', async () => {
|
||||
const cacheFile = path.join(TEST_EXTENSIONS_DIR, 'ublock.extension.json');
|
||||
|
||||
// Create corrupted cache
|
||||
fs.writeFileSync(cacheFile, 'invalid json content');
|
||||
|
||||
const { installUblockExtension } = require('../on_Snapshot__03_ublock.js');
|
||||
|
||||
// Mock loadOrInstallExtension to avoid actual download
|
||||
const extensionUtils = require('../../chrome_extensions/chrome_extension_utils.js');
|
||||
const originalFunc = extensionUtils.loadOrInstallExtension;
|
||||
|
||||
extensionUtils.loadOrInstallExtension = async () => ({
|
||||
webstore_id: 'cjpalhdlnbpafiamejdnhcphjbkeiagm',
|
||||
name: 'ublock',
|
||||
version: '1.68.0'
|
||||
});
|
||||
|
||||
const result = await installUblockExtension();
|
||||
|
||||
extensionUtils.loadOrInstallExtension = originalFunc;
|
||||
|
||||
assert.notStrictEqual(result, null);
|
||||
});
|
||||
|
||||
it('should handle download timeout gracefully', () => {
|
||||
// For large extension like uBlock, timeout handling is important
|
||||
|
||||
const timeoutSeconds = 120; // 2 minutes
|
||||
const minTimeout = 30; // Should allow at least 30 seconds
|
||||
|
||||
assert.ok(timeoutSeconds > minTimeout);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter list validation', () => {
|
||||
it('should have valid filter list format', () => {
|
||||
// Example filter list entry
|
||||
const sampleFilters = [
|
||||
'||ads.example.com^',
|
||||
'||tracker.example.com^$third-party',
|
||||
'##.advertisement'
|
||||
];
|
||||
|
||||
// All filters should follow standard format
|
||||
sampleFilters.forEach(filter => {
|
||||
assert.ok(typeof filter === 'string');
|
||||
assert.ok(filter.length > 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support cosmetic filters', () => {
|
||||
const cosmeticFilter = '##.banner-ad';
|
||||
|
||||
// Should start with ## for cosmetic filters
|
||||
assert.ok(cosmeticFilter.startsWith('##'));
|
||||
});
|
||||
|
||||
it('should support network filters', () => {
|
||||
const networkFilter = '||ads.example.com^';
|
||||
|
||||
// Network filters typically start with || or contain ^
|
||||
assert.ok(networkFilter.includes('||') || networkFilter.includes('^'));
|
||||
});
|
||||
});
|
||||
});
|
||||
148
archivebox/plugins/ublock/tests/test_ublock.py
Normal file
148
archivebox/plugins/ublock/tests/test_ublock.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
Unit tests for ublock plugin
|
||||
|
||||
Tests invoke the plugin hook as an external process and verify outputs/side effects.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
PLUGIN_DIR = Path(__file__).parent.parent
|
||||
INSTALL_SCRIPT = PLUGIN_DIR / "on_Snapshot__03_ublock.js"
|
||||
|
||||
|
||||
def test_install_script_exists():
|
||||
"""Verify install script exists"""
|
||||
assert INSTALL_SCRIPT.exists(), f"Install script not found: {INSTALL_SCRIPT}"
|
||||
|
||||
|
||||
def test_extension_metadata():
|
||||
"""Test that uBlock Origin extension has correct metadata"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
env = os.environ.copy()
|
||||
env["CHROME_EXTENSIONS_DIR"] = str(Path(tmpdir) / "chrome_extensions")
|
||||
|
||||
result = subprocess.run(
|
||||
["node", "-e", f"const ext = require('{INSTALL_SCRIPT}'); console.log(JSON.stringify(ext.EXTENSION))"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env
|
||||
)
|
||||
|
||||
assert result.returncode == 0, f"Failed to load extension metadata: {result.stderr}"
|
||||
|
||||
metadata = json.loads(result.stdout)
|
||||
assert metadata["webstore_id"] == "cjpalhdlnbpafiamejdnhcphjbkeiagm"
|
||||
assert metadata["name"] == "ublock"
|
||||
|
||||
|
||||
def test_install_creates_cache():
|
||||
"""Test that install creates extension cache"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ext_dir = Path(tmpdir) / "chrome_extensions"
|
||||
ext_dir.mkdir(parents=True)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CHROME_EXTENSIONS_DIR"] = str(ext_dir)
|
||||
|
||||
result = subprocess.run(
|
||||
["node", str(INSTALL_SCRIPT)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
timeout=120 # uBlock is large, may take longer to download
|
||||
)
|
||||
|
||||
# Check output mentions installation
|
||||
assert "uBlock" in result.stdout or "ublock" in result.stdout
|
||||
|
||||
# Check cache file was created
|
||||
cache_file = ext_dir / "ublock.extension.json"
|
||||
assert cache_file.exists(), "Cache file should be created"
|
||||
|
||||
# Verify cache content
|
||||
cache_data = json.loads(cache_file.read_text())
|
||||
assert cache_data["webstore_id"] == "cjpalhdlnbpafiamejdnhcphjbkeiagm"
|
||||
assert cache_data["name"] == "ublock"
|
||||
|
||||
|
||||
def test_install_uses_existing_cache():
|
||||
"""Test that install uses existing cache when available"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ext_dir = Path(tmpdir) / "chrome_extensions"
|
||||
ext_dir.mkdir(parents=True)
|
||||
|
||||
# Create fake cache
|
||||
fake_extension_dir = ext_dir / "cjpalhdlnbpafiamejdnhcphjbkeiagm__ublock"
|
||||
fake_extension_dir.mkdir(parents=True)
|
||||
|
||||
manifest = {"version": "1.68.0", "name": "uBlock Origin"}
|
||||
(fake_extension_dir / "manifest.json").write_text(json.dumps(manifest))
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CHROME_EXTENSIONS_DIR"] = str(ext_dir)
|
||||
|
||||
result = subprocess.run(
|
||||
["node", str(INSTALL_SCRIPT)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
# Should use cache or install successfully
|
||||
assert result.returncode == 0
|
||||
|
||||
|
||||
def test_no_configuration_required():
|
||||
"""Test that uBlock Origin works without configuration"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ext_dir = Path(tmpdir) / "chrome_extensions"
|
||||
ext_dir.mkdir(parents=True)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CHROME_EXTENSIONS_DIR"] = str(ext_dir)
|
||||
# No API keys needed - works with default filter lists
|
||||
|
||||
result = subprocess.run(
|
||||
["node", str(INSTALL_SCRIPT)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
# Should not require any API keys
|
||||
combined_output = result.stdout + result.stderr
|
||||
assert "API" not in combined_output or result.returncode == 0
|
||||
|
||||
|
||||
def test_large_extension_size():
|
||||
"""Test that uBlock Origin is downloaded successfully despite large size"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
ext_dir = Path(tmpdir) / "chrome_extensions"
|
||||
ext_dir.mkdir(parents=True)
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CHROME_EXTENSIONS_DIR"] = str(ext_dir)
|
||||
|
||||
result = subprocess.run(
|
||||
["node", str(INSTALL_SCRIPT)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
# If extension was downloaded, verify it's substantial size
|
||||
crx_file = ext_dir / "cjpalhdlnbpafiamejdnhcphjbkeiagm__ublock.crx"
|
||||
if crx_file.exists():
|
||||
# uBlock Origin with filter lists is typically 2-5 MB
|
||||
size_bytes = crx_file.stat().st_size
|
||||
assert size_bytes > 1_000_000, f"uBlock Origin should be > 1MB, got {size_bytes} bytes"
|
||||
Reference in New Issue
Block a user