UNPKG

chrome-extension-test-framework

Version:

Universal testing framework for Chrome extensions - Fast static analysis without browser dependencies

335 lines (286 loc) 9.43 kB
/** * TestSuite - 関連するテストケースをグループ化 */ const fs = require('fs'); const path = require('path'); const PerformanceMonitor = require('./PerformanceMonitor'); class TestSuite { constructor(options = {}) { this.name = options.name || 'Unnamed Suite'; this.description = options.description || ''; this.tests = []; this.beforeAll = options.beforeAll; this.afterAll = options.afterAll; this.beforeEach = options.beforeEach; this.afterEach = options.afterEach; this.enabled = options.enabled !== false; this.config = options.config || {}; } /** * テストケースを追加 */ addTest(testCase) { if (typeof testCase === 'function') { // 関数から簡易的にテストケースを作成 this.tests.push({ name: testCase.name || 'Anonymous Test', test: testCase }); } else { // TestCaseオブジェクトまたはプレーンオブジェクト this.tests.push(testCase); } return this; } /** * 複数のテストケースを一度に追加 */ addTests(testCases) { testCases.forEach(testCase => this.addTest(testCase)); return this; } /** * テストケースを定義(fluent API) */ test(name, testFn, options = {}) { this.addTest({ name, test: testFn, ...options }); return this; } /** * スキップするテスト */ skip(name, testFn, options = {}) { this.addTest({ name, test: testFn, skip: true, ...options }); return this; } /** * 条件付きテスト */ testIf(condition, name, testFn, options = {}) { this.addTest({ name, test: testFn, condition, ...options }); return this; } /** * beforeAllフックを設定 */ before(fn) { this.beforeAll = fn; return this; } /** * afterAllフックを設定 */ after(fn) { this.afterAll = fn; return this; } /** * beforeEachフックを設定 */ beforeEachTest(fn) { this.beforeEach = fn; return this; } /** * afterEachフックを設定 */ afterEachTest(fn) { this.afterEach = fn; return this; } /** * スイートを有効/無効化 */ enable() { this.enabled = true; return this; } disable() { this.enabled = false; return this; } /** * テストケースの数を取得 */ get testCount() { return this.tests.length; } /** * スイートが空かどうか */ get isEmpty() { return this.tests.length === 0; } // =========== ヘルパーメソッド =========== /** * 拡張機能のファイルを読み込み */ async loadFile(filePath) { const fullPath = path.join(this.config.extensionPath, filePath); if (!fs.existsSync(fullPath)) { throw new Error(`File not found: ${filePath}`); } return fs.readFileSync(fullPath, 'utf8'); } /** * 拡張機能のJSONファイルを読み込み */ async loadJSON(filePath) { const content = await this.loadFile(filePath); return JSON.parse(content); } /** * manifest.jsonを読み込み */ async loadManifest(config) { const configToUse = config || this.config; const manifestPath = path.join(configToUse.extensionPath, 'manifest.json'); if (!fs.existsSync(manifestPath)) { throw new Error('manifest.json not found'); } const content = fs.readFileSync(manifestPath, 'utf8'); return JSON.parse(content); } /** * ファイルの存在確認 */ async fileExists(filePath) { const fullPath = path.join(this.config.extensionPath, filePath); return fs.existsSync(fullPath); } /** * ディレクトリの内容を取得 */ async readDirectory(dirPath) { const fullPath = path.join(this.config.extensionPath, dirPath); if (!fs.existsSync(fullPath)) { return []; } return fs.readdirSync(fullPath); } /** * ファイルサイズを取得(バイト) */ async getFileSize(filePath) { const fullPath = path.join(this.config.extensionPath, filePath); if (!fs.existsSync(fullPath)) { throw new Error(`File not found: ${filePath}`); } const stats = fs.statSync(fullPath); return stats.size; } /** * ファイル内容を検索 */ async searchInFile(filePath, pattern) { const content = await this.loadFile(filePath); const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern, 'g'); return content.match(regex) || []; } /** * 全ファイルを再帰的に取得 */ async getAllFiles(dirPath = '', fileList = [], options = {}) { // パフォーマンスモニターを初期化(最初の呼び出し時のみ) if (!this._perfMonitor) { this._perfMonitor = new PerformanceMonitor(); this._fileScanStats = { totalFiles: 0, scannedFiles: 0, excludedFiles: 0, excludedByNodeModules: 0 }; this._perfMonitor.start('file-scanning'); } // パフォーマンス最適化: node_modules を早期除外 if (dirPath.includes('node_modules') || dirPath.includes('.git')) { this._fileScanStats.excludedByNodeModules++; return fileList; } const fullPath = path.join(this.config.extensionPath, dirPath); if (!fs.existsSync(fullPath)) { return fileList; } // フレームワーク自身のディレクトリを除外 const frameworkPath = path.resolve(__dirname, '..'); // Only exclude if it's exactly the framework path or its core directories if (fullPath === frameworkPath || fullPath === path.join(frameworkPath, 'lib') || fullPath === path.join(frameworkPath, 'suites') || fullPath === path.join(frameworkPath, 'bin')) { return fileList; } const files = fs.readdirSync(fullPath); for (const file of files) { // パフォーマンス最適化: node_modules と .git を早期スキップ if (file === 'node_modules' || file === '.git') { this._fileScanStats.excludedByNodeModules++; continue; } const filePath = path.join(dirPath, file); const fullFilePath = path.join(this.config.extensionPath, filePath); // ファイルが存在しない場合はスキップ(シンボリックリンクなど) if (!fs.existsSync(fullFilePath)) { continue; } const stat = fs.statSync(fullFilePath); this._fileScanStats.totalFiles++; // ExcludeManagerがある場合は除外チェック(オプションでスキップ可能) if (!options.skipExclude && this.config.excludeManager) { // ExcludeManagerには相対パスを渡す if (stat.isDirectory() && this.config.excludeManager.shouldExcludeDirectory(filePath)) { this._fileScanStats.excludedFiles++; continue; } if (!stat.isDirectory() && this.config.excludeManager.shouldExclude(filePath)) { this._fileScanStats.excludedFiles++; continue; } } if (stat.isDirectory()) { await this.getAllFiles(filePath, fileList, options); } else { this._fileScanStats.scannedFiles++; fileList.push(filePath); } } // ルートディレクトリに戻ってきたら(最初の呼び出しが完了したら)統計を記録 if (dirPath === '') { this._perfMonitor.end('file-scanning'); this._perfMonitor.recordFileScan(this._fileScanStats); // 次回の呼び出しのためにリセット this._perfMonitor = null; this._fileScanStats = null; } return fileList; } /** * 簡易的なglobマッチング */ simpleGlobMatch(pattern, str) { // 基本的なglobパターンを正規表現に変換 let regexPattern = pattern .replace(/\*/g, '.*') .replace(/\?/g, '.') .replace(/\[/g, '\\[') .replace(/\]/g, '\\]'); // 完全一致で検証 const regex = new RegExp(`^${regexPattern}$`); return regex.test(str); } } module.exports = TestSuite;