UNPKG

tradingview-screener-ts

Version:

TypeScript port of TradingView Screener with 100% Python parity - Based on the original Python library by shner-elmo (https://github.com/shner-elmo/TradingView-Screener)

365 lines 11.7 kB
"use strict"; /** * Advanced testing utilities and mock data * * This module provides comprehensive testing utilities, mock data generators, * and test helpers to achieve 100% test coverage and quality assurance. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Testing = exports.TestFixtures = exports.MockHttpClient = exports.TestUtils = exports.MockDataGenerator = void 0; /** * Mock data generator for testing */ class MockDataGenerator { /** * Generate mock stock data */ static generateMockStockData(count = 10) { const data = Array.from({ length: count }, (_, index) => ({ name: `Stock ${index + 1}`, close: Math.random() * 1000 + 10, volume: Math.floor(Math.random() * 10000000) + 100000, market_cap_basic: Math.floor(Math.random() * 100000000000) + 1000000000, 'P/E': Math.random() * 50 + 5, 'P/B': Math.random() * 10 + 0.5, RSI: Math.random() * 100, 'MACD.macd': Math.random() * 2 - 1, 'MACD.signal': Math.random() * 2 - 1, change: Math.random() * 20 - 10, sector: ['Technology', 'Healthcare', 'Finance', 'Energy'][Math.floor(Math.random() * 4)], country: ['US', 'India', 'UK', 'Germany'][Math.floor(Math.random() * 4)], })); return { totalCount: count, data, }; } /** * Generate mock crypto data */ static generateMockCryptoData(count = 10) { const cryptos = ['Bitcoin', 'Ethereum', 'Cardano', 'Solana', 'Polygon', 'Chainlink']; const data = Array.from({ length: count }, (_, index) => ({ name: cryptos[index % cryptos.length] || `Crypto ${index + 1}`, close: Math.random() * 50000 + 100, volume: Math.floor(Math.random() * 1000000000) + 10000000, market_cap_calc: Math.floor(Math.random() * 1000000000000) + 1000000000, change: Math.random() * 40 - 20, change_7d: Math.random() * 60 - 30, change_30d: Math.random() * 100 - 50, volatility: Math.random() * 0.1 + 0.01, })); return { totalCount: count, data, }; } /** * Generate mock India stock data */ static generateMockIndiaStockData(count = 10) { const indianStocks = ['Reliance', 'TCS', 'HDFC Bank', 'Infosys', 'ICICI Bank', 'Bharti Airtel']; const data = Array.from({ length: count }, (_, index) => ({ name: indianStocks[index % indianStocks.length] || `Indian Stock ${index + 1}`, close: Math.random() * 5000 + 100, // INR prices volume: Math.floor(Math.random() * 5000000) + 50000, market_cap_basic: Math.floor(Math.random() * 500000000000) + 10000000000, // INR market cap 'P/E': Math.random() * 40 + 8, 'P/B': Math.random() * 8 + 0.5, debt_to_equity: Math.random() * 2, RSI: Math.random() * 100, change: Math.random() * 15 - 7.5, sector: ['IT', 'Banking', 'Oil & Gas', 'Telecom', 'Pharma'][Math.floor(Math.random() * 5)], exchange: ['NSE', 'BSE'][Math.floor(Math.random() * 2)], })); return { totalCount: count, data, }; } /** * Generate mock filter operation */ static generateMockFilter() { const operations = ['greater', 'less', 'equal', 'between', 'in_range']; const fields = ['close', 'volume', 'market_cap_basic', 'P/E', 'RSI']; return { left: fields[Math.floor(Math.random() * fields.length)], operation: operations[Math.floor(Math.random() * operations.length)], right: Math.random() * 1000, }; } /** * Generate mock query */ static generateMockQuery() { return { markets: ['america', 'india'][Math.floor(Math.random() * 2)] ? ['america'] : ['india'], columns: ['name', 'close', 'volume', 'market_cap_basic'], sort: { sortBy: 'volume', sortOrder: 'desc', }, range: [0, 50], filter: [this.generateMockFilter()], }; } } exports.MockDataGenerator = MockDataGenerator; /** * Test utilities for assertions and validations */ class TestUtils { /** * Assert that data matches expected structure */ static assertScreenerDataStructure(data) { if (typeof data !== 'object' || data === null) { throw new Error('Data must be an object'); } if (typeof data.totalCount !== 'number') { throw new Error('totalCount must be a number'); } if (!Array.isArray(data.data)) { throw new Error('data must be an array'); } // Validate each row data.data.forEach((row, index) => { if (typeof row !== 'object' || row === null) { throw new Error(`Row ${index} must be an object`); } }); } /** * Assert that filter operation is valid */ static assertValidFilter(filter) { if (typeof filter !== 'object' || filter === null) { throw new Error('Filter must be an object'); } if (typeof filter.left !== 'string') { throw new Error('Filter left must be a string'); } if (typeof filter.operation !== 'string') { throw new Error('Filter operation must be a string'); } const validOperations = [ 'greater', 'egreater', 'less', 'eless', 'equal', 'nequal', 'in_range', 'not_in_range', 'match', 'nmatch', 'crosses', ]; if (!validOperations.includes(filter.operation)) { throw new Error(`Invalid operation: ${filter.operation}`); } } /** * Measure execution time */ static async measureExecutionTime(fn) { const start = performance.now(); const result = await fn(); const end = performance.now(); return { result, executionTime: end - start, }; } /** * Create performance benchmark */ static async benchmark(name, fn, iterations = 10) { const times = []; for (let i = 0; i < iterations; i++) { const { executionTime } = await this.measureExecutionTime(fn); times.push(executionTime); } const totalTime = times.reduce((sum, time) => sum + time, 0); const averageTime = totalTime / iterations; const minTime = Math.min(...times); const maxTime = Math.max(...times); return { name, iterations, averageTime, minTime, maxTime, totalTime, }; } /** * Validate API response structure */ static validateApiResponse(response) { const errors = []; const warnings = []; try { this.assertScreenerDataStructure(response); } catch (error) { errors.push(error instanceof Error ? error.message : 'Unknown error'); } // Check for reasonable data if (response?.totalCount > 10000) { warnings.push('Large totalCount may indicate overly broad query'); } if (response?.data?.length === 0) { warnings.push('No data returned - query may be too restrictive'); } // Check for data consistency if (response?.data?.length > 0) { const firstRow = response.data[0]; const keys = Object.keys(firstRow); response.data.forEach((row, index) => { const rowKeys = Object.keys(row); if (rowKeys.length !== keys.length) { warnings.push(`Row ${index} has different number of fields`); } }); } return { isValid: errors.length === 0, errors, warnings, }; } /** * Generate test report */ static generateTestReport(results) { const totalTests = results.length; const passed = results.filter(r => r.status === 'passed').length; const failed = results.filter(r => r.status === 'failed').length; const skipped = results.filter(r => r.status === 'skipped').length; const duration = results.reduce((sum, r) => sum + (r.duration || 0), 0); // Mock coverage calculation (in real scenario, this would come from coverage tools) const coverage = (passed / totalTests) * 100; const summary = `${passed}/${totalTests} tests passed (${((passed / totalTests) * 100).toFixed(1)}%)`; return { totalTests, passed, failed, skipped, coverage, duration, summary, }; } } exports.TestUtils = TestUtils; /** * Mock HTTP client for testing */ class MockHttpClient { constructor() { this.responses = new Map(); this.requestLog = []; } /** * Set mock response for URL */ setMockResponse(url, response) { this.responses.set(url, response); } /** * Mock HTTP request */ async request(url, config = {}) { this.requestLog.push({ url, config, timestamp: Date.now(), }); const response = this.responses.get(url); if (!response) { throw new Error(`No mock response configured for ${url}`); } // Simulate network delay await new Promise(resolve => setTimeout(resolve, Math.random() * 100)); return response; } /** * Get request log */ getRequestLog() { return [...this.requestLog]; } /** * Clear request log */ clearLog() { this.requestLog.length = 0; } /** * Reset all mocks */ reset() { this.responses.clear(); this.clearLog(); } } exports.MockHttpClient = MockHttpClient; /** * Test data fixtures */ exports.TestFixtures = { /** * Sample stock data */ stockData: MockDataGenerator.generateMockStockData(50), /** * Sample crypto data */ cryptoData: MockDataGenerator.generateMockCryptoData(20), /** * Sample India stock data */ indiaStockData: MockDataGenerator.generateMockIndiaStockData(30), /** * Sample error responses */ errorResponses: { networkError: new Error('Network connection failed'), timeoutError: new Error('Request timeout'), authError: new Error('Authentication required'), rateLimitError: new Error('Rate limit exceeded'), serverError: new Error('Server error'), }, /** * Sample queries */ sampleQueries: { basicStock: { markets: ['america'], columns: ['name', 'close', 'volume'], range: [0, 50], }, indiaStock: { markets: ['india'], columns: ['name', 'close', 'volume', 'market_cap_basic'], range: [0, 30], }, crypto: { markets: ['crypto'], columns: ['name', 'close', 'volume', 'market_cap_calc'], range: [0, 20], }, }, }; /** * Export testing utilities */ exports.Testing = { MockDataGenerator, TestUtils, MockHttpClient, TestFixtures: exports.TestFixtures, }; //# sourceMappingURL=testing.js.map