/** * RPG Companion Integration Test Helper * * This module provides functions for testing JSON parsing and validation * within SillyTavern. It can be loaded in the browser console or integrated * into the extension's debug mode. * * Usage in browser console: * 1. Open SillyTavern with RPG Companion enabled * 2. Open browser dev tools (F12) * 3. Copy/paste this file's contents into console * 4. Run: RPGTestHelper.validateLastResponse() or other methods */ window.RPGTestHelper = { /** * Validates the last generated tracker data against expected JSON structure */ validateLastResponse() { console.log('๐Ÿ” Validating last generated tracker data...\n'); // Access extension settings (this assumes RPG Companion is loaded) const settings = window.extension_settings?.['rpg-companion']; if (!settings) { console.error('โŒ RPG Companion not found in extension_settings'); return false; } const results = { inventoryV3: this.validateInventory(settings.inventoryV3), skillsV2: this.validateSkills(settings.skillsV2), questsV2: this.validateQuests(settings.questsV2), infoBoxData: this.validateInfoBox(settings.infoBoxData), charactersData: this.validateCharacters(settings.charactersData) }; console.log('\n๐Ÿ“Š Validation Results:'); Object.entries(results).forEach(([key, valid]) => { console.log(` ${valid ? 'โœ…' : 'โŒ'} ${key}`); }); return Object.values(results).every(v => v); }, /** * Validates inventory structure */ validateInventory(inv) { console.log('\n๐Ÿ“ฆ Validating Inventory...'); if (!inv) { console.log(' โš ๏ธ inventoryV3 is null/undefined'); return true; // Not an error if not populated yet } let valid = true; // Check onPerson array if (inv.onPerson && !Array.isArray(inv.onPerson)) { console.log(' โŒ onPerson should be an array'); valid = false; } else if (inv.onPerson?.length > 0) { const item = inv.onPerson[0]; if (typeof item !== 'object' || !item.name) { console.log(' โŒ onPerson items should be objects with name property'); valid = false; } else { console.log(` โœ… onPerson: ${inv.onPerson.length} items (e.g., "${item.name}")`); } } // Check stored object if (inv.stored && typeof inv.stored !== 'object') { console.log(' โŒ stored should be an object'); valid = false; } else if (inv.stored) { const locations = Object.keys(inv.stored); console.log(` โœ… stored: ${locations.length} locations`); } // Check assets array if (inv.assets && !Array.isArray(inv.assets)) { console.log(' โŒ assets should be an array'); valid = false; } else if (inv.assets?.length > 0) { console.log(` โœ… assets: ${inv.assets.length} items`); } // Check simplified array if (inv.simplified && !Array.isArray(inv.simplified)) { console.log(' โŒ simplified should be an array'); valid = false; } else if (inv.simplified?.length > 0) { console.log(` โœ… simplified: ${inv.simplified.length} items`); } return valid; }, /** * Validates skills structure */ validateSkills(skills) { console.log('\nโš”๏ธ Validating Skills...'); if (!skills) { console.log(' โš ๏ธ skillsV2 is null/undefined'); return true; } if (typeof skills !== 'object') { console.log(' โŒ skillsV2 should be an object'); return false; } let valid = true; for (const [category, abilities] of Object.entries(skills)) { if (!Array.isArray(abilities)) { console.log(` โŒ ${category} should be an array`); valid = false; continue; } abilities.forEach((ability, i) => { if (typeof ability !== 'object' || !ability.name) { console.log(` โŒ ${category}[${i}] should be an object with name`); valid = false; } }); console.log(` โœ… ${category}: ${abilities.length} abilities`); } return valid; }, /** * Validates quests structure */ validateQuests(quests) { console.log('\n๐Ÿ“œ Validating Quests...'); if (!quests) { console.log(' โš ๏ธ questsV2 is null/undefined'); return true; } let valid = true; if (quests.main !== null && quests.main !== undefined) { if (typeof quests.main === 'string') { console.log(` โœ… main: "${quests.main}"`); } else if (typeof quests.main === 'object' && quests.main.name) { console.log(` โœ… main: "${quests.main.name}" (structured)`); } else { console.log(' โŒ main should be string or {name, description}'); valid = false; } } if (quests.optional) { if (!Array.isArray(quests.optional)) { console.log(' โŒ optional should be an array'); valid = false; } else { console.log(` โœ… optional: ${quests.optional.length} quests`); } } return valid; }, /** * Validates info box structure */ validateInfoBox(info) { console.log('\n๐Ÿ“ Validating Info Box...'); if (!info) { console.log(' โš ๏ธ infoBoxData is null/undefined'); return true; } const fields = ['date', 'weather', 'temperature', 'time', 'location']; let valid = true; fields.forEach(field => { if (info[field] !== undefined && info[field] !== null) { if (typeof info[field] !== 'string') { console.log(` โŒ ${field} should be a string`); valid = false; } else { console.log(` โœ… ${field}: "${info[field]}"`); } } }); if (info.recentEvents) { if (!Array.isArray(info.recentEvents)) { console.log(' โŒ recentEvents should be an array'); valid = false; } else { console.log(` โœ… recentEvents: ${info.recentEvents.length} events`); } } return valid; }, /** * Validates characters structure */ validateCharacters(chars) { console.log('\n๐Ÿ‘ฅ Validating Characters...'); if (!chars) { console.log(' โš ๏ธ charactersData is null/undefined'); return true; } if (!Array.isArray(chars)) { console.log(' โŒ charactersData should be an array'); return false; } let valid = true; chars.forEach((char, i) => { if (typeof char !== 'object' || !char.name) { console.log(` โŒ character[${i}] should have name`); valid = false; } else { console.log(` โœ… ${char.name}: ${char.relationship || 'no relationship'}`); } }); return valid; }, /** * Tests JSON extraction from a raw response string */ testJSONExtraction(responseText) { console.log('\n๐Ÿ”ฌ Testing JSON Extraction...\n'); const jsonRegex = /```(?:json)?\s*([\s\S]*?)```/i; const match = responseText.match(jsonRegex); if (!match) { console.log('โŒ No JSON code block found'); return null; } console.log('โœ… Found JSON code block'); try { const parsed = JSON.parse(match[1].trim()); console.log('โœ… JSON parsed successfully'); console.log('๐Ÿ“‹ Structure:', Object.keys(parsed).join(', ')); return parsed; } catch (e) { console.log('โŒ JSON parse failed:', e.message); // Try to fix common issues console.log('๐Ÿ”ง Attempting to fix JSON...'); const fixed = match[1].trim() .replace(/,\s*}/g, '}') .replace(/,\s*]/g, ']'); try { const fixedParsed = JSON.parse(fixed); console.log('โœ… Fixed JSON parsed successfully'); return fixedParsed; } catch (e2) { console.log('โŒ Could not fix JSON:', e2.message); return null; } } }, /** * Simulates a full parse cycle with a sample response */ simulateParseResponse(sampleResponse) { console.log('\n๐Ÿ”„ Simulating Parse Response...\n'); const parsed = this.testJSONExtraction(sampleResponse); if (parsed) { console.log('\n๐Ÿ“Š Validating parsed structure:'); if (parsed.userStats) { console.log(' โœ… userStats present'); } if (parsed.skills) { console.log(' โœ… skills present'); } if (parsed.inventory) { console.log(' โœ… inventory present'); } if (parsed.quests) { console.log(' โœ… quests present'); } if (parsed.infoBox) { console.log(' โœ… infoBox present'); } if (parsed.presentCharacters) { console.log(' โœ… presentCharacters present'); } } return parsed; }, /** * Prints current extension settings for debugging */ printCurrentState() { const settings = window.extension_settings?.['rpg-companion']; if (!settings) { console.error('RPG Companion not found'); return; } console.log('๐Ÿ“‹ Current RPG Companion State:\n'); console.log('inventoryV3:', JSON.stringify(settings.inventoryV3, null, 2)); console.log('skillsV2:', JSON.stringify(settings.skillsV2, null, 2)); console.log('questsV2:', JSON.stringify(settings.questsV2, null, 2)); console.log('infoBoxData:', JSON.stringify(settings.infoBoxData, null, 2)); console.log('charactersData:', JSON.stringify(settings.charactersData, null, 2)); }, /** * Help message */ help() { console.log(` ๐Ÿงช RPG Companion Test Helper Commands: RPGTestHelper.validateLastResponse() - Validate current structured data RPGTestHelper.testJSONExtraction(text) - Test JSON extraction from text RPGTestHelper.simulateParseResponse(text) - Full parse simulation RPGTestHelper.printCurrentState() - Print current extension state RPGTestHelper.help() - Show this help message Example: RPGTestHelper.validateLastResponse() `); } }; // Print help on load console.log('๐Ÿงช RPG Companion Test Helper loaded. Run RPGTestHelper.help() for commands.');