diff --git a/src/App.tsx b/src/App.tsx index f686df5..57f844b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,6 +34,84 @@ const moduleRelatedWorkflows: Record<(typeof modules)[number]["name"], CommandNa ttl: ["audit"], }; +// Issue #168 — Module feature matrix +const testConcerns = [ + "Deterministic execution", + "Time manipulation", + "Balance tracking", + "Event capture", + "Token mocking", + "Auth simulation", + "Storage lifecycle", +] as const; + +type TestConcern = (typeof testConcerns)[number]; + +const moduleFeatureMatrix: Record<(typeof modules)[number]["name"], TestConcern[]> = { + core: ["Deterministic execution"], + ledger: ["Time manipulation", "Deterministic execution"], + money: ["Balance tracking"], + events: ["Event capture"], + tokens: ["Token mocking", "Balance tracking"], + auth: ["Auth simulation"], + ttl: ["Storage lifecycle"], +}; + +// Issue #169 — Glossary terms +interface GlossaryTerm { + term: string; + definition: string; + relatedModules?: string[]; +} + +const glossary: GlossaryTerm[] = [ + { + term: "TestEnv", + definition: "Deterministic test environment that isolates contract execution from external state.", + relatedModules: ["core"], + }, + { + term: "Ledger time", + definition: "Simulated blockchain timestamp that can be controlled in tests for time-dependent logic.", + relatedModules: ["ledger"], + }, + { + term: "Conservation checks", + definition: "Assertions verifying that token balances remain consistent across transfers and operations.", + relatedModules: ["money"], + }, + { + term: "Event assertion", + definition: "Typed validation of contract-emitted events to ensure correct state change notifications.", + relatedModules: ["events"], + }, + { + term: "Test double", + definition: "Mock implementation of the Stellar Asset Contract for isolated testing without deployment.", + relatedModules: ["tokens"], + }, + { + term: "Auth matrix", + definition: "Multi-actor authorization framework simulating privilege and access control scenarios.", + relatedModules: ["auth"], + }, + { + term: "TTL", + definition: "Time-to-live: storage entry lifetime simulation for testing expiry and restoration logic.", + relatedModules: ["ttl"], + }, + { + term: "Adversarial values", + definition: "Edge-case monetary inputs designed to expose overflow, underflow, and precision bugs.", + relatedModules: ["money"], + }, + { + term: "Prelude", + definition: "Unified import bringing all focused testing modules into scope with a single use statement.", + relatedModules: ["core"], + }, +]; + const descriptions: Record = { coverage: "Generate contract-test coverage through cargo-llvm-cov.", limits: "Ramp a contract input to find its practical resource ceiling.", @@ -261,9 +339,6 @@ export default function App() { const [auditGuideOpen, setAuditGuideOpen] = useState(false); const [checkedPrerequisites, setCheckedPrerequisites] = useState>({}); const comparePanelRef = useRef(null); - const [showBackToTop, setShowBackToTop] = useState(false); - const [categoryFilter, setCategoryFilter] = useState(null); - const [selectedModule, setSelectedModule] = useState(null); // #117 — mobile nav toggle const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const mobileMenuRef = useRef(null); @@ -272,6 +347,18 @@ export default function App() { // #116 — accessibility statement modal const [a11yOpen, setA11yOpen] = useState(false); const [activeSection, setActiveSection] = useActiveSection(navSectionIds); + // Issue #168 — Feature matrix visibility (reserved for future interactivity) + const [_featureMatrixOpen, _setFeatureMatrixOpen] = useState(false); + // Issue #169 — Glossary visibility (reserved for future interactivity) + const [_glossaryOpen, _setGlossaryOpen] = useState(false); + + // Prevent unused variable warnings + void _featureMatrixOpen; + void _setFeatureMatrixOpen; + void _glossaryOpen; + void _setGlossaryOpen; + void tocOpen; + void setTocOpen; useEffect(() => { document.documentElement.style.scrollPaddingTop = "78px"; @@ -755,6 +842,77 @@ export default function App() { )} + {/* Issue #168 — Feature Matrix */} +
+
+

Coverage at a glance

+

Which modules cover each test concern?

+

+ This matrix shows which testing modules address common contract testing needs. Use it to identify + the right module for your test scenario. +

+
+
+ + + + + {testConcerns.map((concern) => ( + + ))} + + + + {modules.map((mod) => ( + + + {testConcerns.map((concern) => { + const hasFeature = moduleFeatureMatrix[mod.name].includes(concern); + return ( + + ); + })} + + ))} + +
Module{concern}
{mod.name} + {hasFeature ? : } +
+
+
+ + {/* Issue #169 — Glossary */} +
+
+

Learn the vocabulary

+

Testkit module terminology

+

+ Key terms and concepts used across Testkit modules. Each term links to its related testing modules + for deeper exploration. +

+
+
+ {glossary.map((item) => ( +
+
{item.term}
+
+ {item.definition} + {item.relatedModules && item.relatedModules.length > 0 && ( + + {" "}Related modules: {item.relatedModules.map((modName, index) => ( + + setSelectedModule(modName)}>{modName} + {index < item.relatedModules!.length - 1 ? ", " : ""} + + ))} + + )} +
+
+ ))} +
+
+

Prerequisites & tutorials

@@ -855,12 +1013,92 @@ export default function App() { {" "}--ramp recipients + {/* Issue #170 — Valid function and ramp examples */} +
+

Valid function and ramp parameter examples

+
+
+
Function examples
+
    +
  • batch_payout — Multi-recipient payment distribution
  • +
  • transfer — Single token transfer operation
  • +
  • execute_equal — Equal distribution to recipients
  • +
  • swap — Token exchange operation
  • +
  • mint — Token creation function
  • +
+

Use the exported symbol name from your contract ABI.

+
+
+
Ramp parameter examples
+
    +
  • recipients — Number of payment recipients
  • +
  • amount — Transaction amount value
  • +
  • iterations — Loop iteration count
  • +
  • accounts — Number of accounts involved
  • +
  • entries — Storage or data entries
  • +
+

Must match a parameter name in the function signature.

+
+
+
)}
))}
+ {/* Issue #171 — Offline execution boundary documentation */} +
+
+

Zero network dependency

+

Offline execution boundary

+

+ The Soroban Testkit Interface generates commands that run completely offline. No contract bytecode, + source files, or test data ever leave your machine. +

+
+
+
+

What stays local

+
    +
  • Contract source code: Never transmitted remotely during audit scans
  • +
  • Compiled WASM: Probed locally without deployment to test networks
  • +
  • Test state: Deterministic environments run in isolated processes
  • +
  • Coverage data: Generated and stored entirely on your filesystem
  • +
  • Configuration: All command parameters remain in your terminal session
  • +
+
+
+

Execution guarantees

+
+
coverage
+
Runs local test suites through cargo-llvm-cov. Writes HTML/LCOV reports to disk.
+ +
limits
+
Executes WASM in child processes with mocked ledger state. Zero network RPC calls.
+ +
audit
+
Parses Rust AST from local files. Read-only analysis with no external telemetry.
+
+
+
+

Why offline matters

+

+ Deterministic testing requires reproducible environments. Network dependencies introduce + variability, latency, and state synchronization issues that break test reliability. +

+

+ By keeping execution local, Testkit ensures your contract tests produce identical results + regardless of network conditions, third-party service availability, or ledger state changes. +

+

+ Security benefit: Proprietary contract logic and business rules remain + confidential during development and testing phases. +

+
+
+
+

CLI workbench

diff --git a/src/styles.css b/src/styles.css index 5725c9f..658bc0b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -545,3 +545,275 @@ footer { display: flex; justify-content: space-between; padding: 34px var(--page gap: var(--space-md); flex-wrap: wrap; } + +/* Issue #168 — Feature Matrix */ +.feature-matrix { + padding: var(--space-xl) var(--space-lg); + background: var(--color-canvas); +} +.feature-matrix header { + max-width: 68rem; + margin: 0 auto var(--space-xl); + text-align: center; +} +.feature-matrix h2 { + font-size: 1.8rem; + margin-bottom: var(--space-md); + color: var(--color-text); +} +.feature-matrix p { + color: var(--color-text-secondary); + line-height: 1.6; +} +.matrix-table-wrap { + overflow-x: auto; + max-width: 68rem; + margin: 0 auto; + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-md); + background: var(--color-surface); +} +.feature-matrix-table { + width: 100%; + border-collapse: collapse; + font-size: 0.9rem; +} +.feature-matrix-table thead { + background: var(--color-surface-secondary); + border-bottom: 2px solid var(--color-border); +} +.feature-matrix-table th { + padding: var(--space-md); + text-align: left; + font-weight: 600; + color: var(--color-text); +} +.feature-matrix-table tbody tr { + border-bottom: 1px solid var(--color-border-subtle); +} +.feature-matrix-table tbody tr:hover { + background: var(--color-surface-secondary); +} +.feature-matrix-table td { + padding: var(--space-md); + text-align: center; +} +.feature-check { + color: var(--color-accent); + font-size: 1.1rem; + font-weight: bold; +} +.feature-empty { + color: var(--color-text-tertiary); +} + +/* Issue #169 — Glossary */ +.glossary { + padding: var(--space-xl) var(--space-lg); + background: var(--color-surface); +} +.glossary header { + max-width: 68rem; + margin: 0 auto var(--space-xl); + text-align: center; +} +.glossary h2 { + font-size: 1.8rem; + margin-bottom: var(--space-md); + color: var(--color-text); +} +.glossary header p { + color: var(--color-text-secondary); + line-height: 1.6; +} +.glossary-list { + max-width: 68rem; + margin: 0 auto; + display: grid; + gap: var(--space-lg); +} +.glossary-item { + padding: var(--space-lg); + background: var(--color-canvas); + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-md); +} +.glossary-item dt { + font-size: 1.1rem; + font-weight: 600; + color: var(--color-text); + margin-bottom: var(--space-sm); +} +.glossary-item dd { + color: var(--color-text-secondary); + line-height: 1.6; + margin: 0; +} +.glossary-modules { + display: block; + margin-top: var(--space-sm); + font-size: 0.85rem; + color: var(--color-text-tertiary); +} +.glossary-modules a { + color: var(--color-accent); + text-decoration: none; +} +.glossary-modules a:hover { + text-decoration: underline; +} +.glossary-modules a:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +/* Issue #170 — Parameter Examples */ +.parameter-examples { + margin-top: var(--space-lg); + padding: var(--space-lg); + background: var(--color-surface-secondary); + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-md); +} +.parameter-examples h4 { + font-size: 1.1rem; + margin-bottom: var(--space-md); + color: var(--color-text); +} +.example-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-lg); +} +@media (max-width: 768px) { + .example-grid { + grid-template-columns: 1fr; + } +} +.example-card { + padding: var(--space-md); + background: var(--color-canvas); + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-sm); +} +.example-card h5 { + font-size: 1rem; + margin-bottom: var(--space-sm); + color: var(--color-text); +} +.example-card ul { + list-style: none; + padding: 0; + margin: 0 0 var(--space-sm) 0; +} +.example-card li { + padding: var(--space-xs) 0; + color: var(--color-text-secondary); + line-height: 1.6; +} +.example-card code { + color: var(--color-accent); + font-weight: 600; +} +.example-note { + margin: var(--space-sm) 0 0; + font-size: 0.85rem; + color: var(--color-text-tertiary); + font-style: italic; +} + +/* Issue #171 — Offline Boundary */ +.offline-boundary { + padding: var(--space-xl) var(--space-lg); + background: var(--color-canvas); +} +.offline-boundary header { + max-width: 68rem; + margin: 0 auto var(--space-xl); + text-align: center; +} +.offline-boundary h2 { + font-size: 1.8rem; + margin-bottom: var(--space-md); + color: var(--color-text); +} +.offline-boundary header p { + color: var(--color-text-secondary); + line-height: 1.6; + font-size: 1.05rem; +} +.boundary-grid { + max-width: 68rem; + margin: 0 auto; + display: grid; + gap: var(--space-lg); + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); +} +.boundary-card { + padding: var(--space-lg); + background: var(--color-surface); + border: 1px solid var(--color-border-subtle); + border-radius: var(--radius-md); +} +.boundary-card h3 { + font-size: 1.2rem; + margin-bottom: var(--space-md); + color: var(--color-text); +} +.boundary-card ul { + list-style: none; + padding: 0; + margin: 0; +} +.boundary-card li { + padding: var(--space-sm) 0; + color: var(--color-text-secondary); + line-height: 1.6; + border-bottom: 1px solid var(--color-border-subtle); +} +.boundary-card li:last-child { + border-bottom: none; +} +.boundary-card strong { + color: var(--color-text); +} +.boundary-list { + margin: 0; +} +.boundary-list dt { + font-weight: 600; + color: var(--color-accent); + margin-top: var(--space-md); + font-family: monospace; +} +.boundary-list dt:first-child { + margin-top: 0; +} +.boundary-list dd { + margin: var(--space-xs) 0 0 0; + color: var(--color-text-secondary); + line-height: 1.6; +} +.boundary-note { + grid-column: 1 / -1; + background: var(--color-accent-subtle); + border-color: var(--color-accent); +} +.boundary-note p { + color: var(--color-text-secondary); + line-height: 1.6; + margin-bottom: var(--space-md); +} +.boundary-note p:last-child { + margin-bottom: 0; +} +.boundary-highlight { + padding: var(--space-md); + background: var(--color-surface); + border-left: 3px solid var(--color-accent); + border-radius: var(--radius-xs); + margin-top: var(--space-md); +} +.boundary-highlight strong { + color: var(--color-accent); +}