diff --git a/editor/src/Icons.h b/editor/src/Icons.h index cec898a..e74e34c 100644 --- a/editor/src/Icons.h +++ b/editor/src/Icons.h @@ -11,6 +11,8 @@ namespace WhetstoneIcons { constexpr const char* FileSource = "\xF0\x9F\x93\x84"; // document constexpr const char* FileHeader = "\xF0\x9F\x93\x83"; // page with curl constexpr const char* FileConfig = "\xE2\x9A\x99"; // gear +constexpr const char* FileImage = "\xF0\x9F\x96\xBC"; // framed picture +constexpr const char* FileGeneric = "\xF0\x9F\x93\x84"; // generic document constexpr const char* Folder = "\xF0\x9F\x93\x81"; // folder constexpr const char* FolderOpen = "\xF0\x9F\x93\x82"; // open folder @@ -46,7 +48,7 @@ inline std::string fallback(const std::string& icon) { {DiagError, "[ERR]"}, {DiagWarning, "[WARN]"}, {DiagInfo, "[INFO]"}, {DiagHint, "[HINT]"}, {Pending, "[...]"}, {InProgress, "[>>]"}, - {Complete, "[OK]"}, {Rejected, "[X]"}, {Review, "[?]"}, + {Complete, "[OK]"}, {Rejected, "[FAIL]"}, {Review, "[?]"}, {NodeFunction, "fn"}, {NodeClass, "cls"}, {NodeVariable, "var"}, {NodeModule, "mod"}, {FileSource, "[]"}, {Folder, "/"}, {FolderOpen, "/"}, @@ -68,7 +70,7 @@ inline std::string iconForNodeType(const std::string& conceptType) { return NodeModule; // default } -// Get icon for diagnostic severity +// Get icon for diagnostic severity (by int: 1=error, 2=warning, 3=info, 4=hint) inline std::string iconForSeverity(int severity) { switch (severity) { case 1: return DiagError; @@ -79,6 +81,15 @@ inline std::string iconForSeverity(int severity) { } } +// Get icon for diagnostic severity (by name) +inline std::string iconForSeverity(const std::string& severity) { + if (severity == "error") return DiagError; + if (severity == "warning") return DiagWarning; + if (severity == "info") return DiagInfo; + if (severity == "hint") return DiagHint; + return DiagInfo; +} + // Get icon for workflow status inline std::string iconForWorkflowStatus(const std::string& status) { if (status == "pending" || status == "ready") return Pending; diff --git a/editor/tests/step349_test.cpp b/editor/tests/step349_test.cpp new file mode 100644 index 0000000..4d03071 --- /dev/null +++ b/editor/tests/step349_test.cpp @@ -0,0 +1,167 @@ +// Step 349: Icon System + Visual Indicators (12 tests) +// Tests icon constants, file/node/severity/workflow icons, fallbacks, completeness + +#include +#include +#include +#include +#include "Icons.h" +#include "ThemeData.h" + +int main() { + int passed = 0; + + // Test 1: All file type icons are non-empty + { + assert(std::string(WhetstoneIcons::FileSource).size() > 0); + assert(std::string(WhetstoneIcons::FileHeader).size() > 0); + assert(std::string(WhetstoneIcons::FileConfig).size() > 0); + assert(std::string(WhetstoneIcons::FileImage).size() > 0); + assert(std::string(WhetstoneIcons::FileGeneric).size() > 0); + assert(std::string(WhetstoneIcons::Folder).size() > 0); + assert(std::string(WhetstoneIcons::FolderOpen).size() > 0); + std::cout << "Test 1 PASSED: All file type icons non-empty\n"; + passed++; + } + + // Test 2: Diagnostic severity icons distinct + { + std::set icons; + icons.insert(WhetstoneIcons::DiagError); + icons.insert(WhetstoneIcons::DiagWarning); + icons.insert(WhetstoneIcons::DiagInfo); + icons.insert(WhetstoneIcons::DiagHint); + assert(icons.size() == 4); // all different + std::cout << "Test 2 PASSED: 4 distinct diagnostic icons\n"; + passed++; + } + + // Test 3: Workflow status icons cover all states + { + auto pending = WhetstoneIcons::iconForWorkflowStatus("pending"); + auto progress = WhetstoneIcons::iconForWorkflowStatus("in-progress"); + auto complete = WhetstoneIcons::iconForWorkflowStatus("complete"); + auto rejected = WhetstoneIcons::iconForWorkflowStatus("rejected"); + auto review = WhetstoneIcons::iconForWorkflowStatus("review"); + assert(std::string(pending).size() > 0); + assert(std::string(progress).size() > 0); + assert(std::string(complete).size() > 0); + assert(std::string(rejected).size() > 0); + assert(std::string(review).size() > 0); + std::cout << "Test 3 PASSED: Workflow status icons cover all 5 states\n"; + passed++; + } + + // Test 4: AST node type icons + { + assert(std::string(WhetstoneIcons::iconForNodeType("Function")) == WhetstoneIcons::NodeFunction); + assert(std::string(WhetstoneIcons::iconForNodeType("ClassDeclaration")) == WhetstoneIcons::NodeClass); + assert(std::string(WhetstoneIcons::iconForNodeType("Variable")) == WhetstoneIcons::NodeVariable); + assert(std::string(WhetstoneIcons::iconForNodeType("Module")) == WhetstoneIcons::NodeModule); + std::cout << "Test 4 PASSED: AST node type icons\n"; + passed++; + } + + // Test 5: Severity icon mapping + { + assert(std::string(WhetstoneIcons::iconForSeverity("error")) == WhetstoneIcons::DiagError); + assert(std::string(WhetstoneIcons::iconForSeverity("warning")) == WhetstoneIcons::DiagWarning); + assert(std::string(WhetstoneIcons::iconForSeverity("info")) == WhetstoneIcons::DiagInfo); + assert(std::string(WhetstoneIcons::iconForSeverity("hint")) == WhetstoneIcons::DiagHint); + std::cout << "Test 5 PASSED: Severity icon mapping\n"; + passed++; + } + + // Test 6: Fallback text representations + { + assert(WhetstoneIcons::fallback(WhetstoneIcons::DiagError) == "[ERR]"); + assert(WhetstoneIcons::fallback(WhetstoneIcons::DiagWarning) == "[WARN]"); + assert(WhetstoneIcons::fallback(WhetstoneIcons::DiagInfo) == "[INFO]"); + assert(WhetstoneIcons::fallback(WhetstoneIcons::Complete) == "[OK]"); + assert(WhetstoneIcons::fallback(WhetstoneIcons::Rejected) == "[FAIL]"); + std::cout << "Test 6 PASSED: Fallback text representations\n"; + passed++; + } + + // Test 7: Navigation icons defined + { + assert(std::string(WhetstoneIcons::ArrowRight).size() > 0); + assert(std::string(WhetstoneIcons::ArrowDown).size() > 0); + assert(std::string(WhetstoneIcons::Search).size() > 0); + assert(std::string(WhetstoneIcons::Settings).size() > 0); + std::cout << "Test 7 PASSED: Navigation icons defined\n"; + passed++; + } + + // Test 8: Unknown node type returns generic icon + { + auto icon = WhetstoneIcons::iconForNodeType("UnknownNodeType"); + assert(std::string(icon).size() > 0); // should still return something + std::cout << "Test 8 PASSED: Unknown node type returns generic icon\n"; + passed++; + } + + // Test 9: Unknown severity returns generic icon + { + auto icon = WhetstoneIcons::iconForSeverity("unknown"); + assert(std::string(icon).size() > 0); + std::cout << "Test 9 PASSED: Unknown severity returns generic icon\n"; + passed++; + } + + // Test 10: Unknown workflow status returns generic icon + { + auto icon = WhetstoneIcons::iconForWorkflowStatus("unknown"); + assert(std::string(icon).size() > 0); + std::cout << "Test 10 PASSED: Unknown workflow status returns generic\n"; + passed++; + } + + // Test 11: Icon count — at least 20 distinct icon constants + { + std::set all; + all.insert(WhetstoneIcons::FileSource); + all.insert(WhetstoneIcons::FileHeader); + all.insert(WhetstoneIcons::FileConfig); + all.insert(WhetstoneIcons::FileImage); + all.insert(WhetstoneIcons::FileGeneric); + all.insert(WhetstoneIcons::Folder); + all.insert(WhetstoneIcons::FolderOpen); + all.insert(WhetstoneIcons::DiagError); + all.insert(WhetstoneIcons::DiagWarning); + all.insert(WhetstoneIcons::DiagInfo); + all.insert(WhetstoneIcons::DiagHint); + all.insert(WhetstoneIcons::Pending); + all.insert(WhetstoneIcons::InProgress); + all.insert(WhetstoneIcons::Complete); + all.insert(WhetstoneIcons::Rejected); + all.insert(WhetstoneIcons::Review); + all.insert(WhetstoneIcons::NodeFunction); + all.insert(WhetstoneIcons::NodeClass); + all.insert(WhetstoneIcons::NodeVariable); + all.insert(WhetstoneIcons::NodeModule); + all.insert(WhetstoneIcons::ArrowRight); + all.insert(WhetstoneIcons::ArrowDown); + all.insert(WhetstoneIcons::Search); + all.insert(WhetstoneIcons::Settings); + assert(all.size() >= 20); + std::cout << "Test 11 PASSED: " << all.size() << " distinct icon constants\n"; + passed++; + } + + // Test 12: Diagnostic icon colors match theme severity colors + { + auto theme = getDefaultDarkTheme(); + // Verify diagnostic colors are semantic: error=red, warning=amber, info=blue + assert(theme.diagError.r > 0.5f); // red-ish + assert(theme.diagWarning.r > 0.5f && theme.diagWarning.g > 0.3f); // amber + assert(theme.diagInfo.b > 0.5f); // blue-ish + assert(theme.diagHint.b > 0.5f || theme.diagHint.r > 0.5f); // purple + std::cout << "Test 12 PASSED: Diagnostic colors match severity semantics\n"; + passed++; + } + + std::cout << "\nResults: " << passed << "/12\n"; + assert(passed == 12); + return 0; +}