356 lines
12 KiB
JavaScript
356 lines
12 KiB
JavaScript
/**
|
|
* 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.');
|
|
|