The API freeze ahead of 1.0. The supported surface is now sealed by the compiler rather than by convention, so that 1.0 can commit to semantic versioning over something we actually mean. See COMPATIBILITY.md for the full policy and support matrix.
Breaking:
- Implementation packages are now
internal. The parser, runtime, state machine, sentence scanner, output writers and utility code indev.kensa.parse,dev.kensa.state,dev.kensa.output,dev.kensa.service,dev.kensa.utiland the implementation parts ofdev.kensa.contextcan no longer be imported by Kotlin consumers. Nothing in the documented API is affected. Note thatinternalis a Kotlin concept with no bytecode equivalent, so Java consumers are not blocked from these types, but they are equally unsupported. - Upgrade core and the framework adapters together. Kotlin mangles the JVM names of
internalfunctions, so mixing a 0.9.0 core with an olderkensa-junit5/kensa-testng/kensa-kotestcan fail at runtime rather than at compile time. findAnnotationNamesmoved. Now a top-leveldev.kensa.parse.kotlin.findAnnotationNamesinstead of a member ofKotlinParserDelegate.Companion, which is itself nowinternal. Affects custom framework adapters only.@KensaInternalApiis an error, not a warning. Code reaching for the integration SPI (FrameworkDescriptor,KensaLifecycleManager, the invocation-context hooks) must now opt in explicitly with@file:OptIn(dev.kensa.KensaInternalApi::class).
New features:
- Two opt-in markers with opposite meanings.
@KensaInternalApimarks plumbing that is public only because Kensa's adapters and compiler plugin span Gradle modules: do not call it.@KensaExperimentalmarks features still being designed and open to feedback, currently the org-flow surfaces only. Opting in to one does not opt you in to the other. COMPATIBILITY.md. States what semver covers, what the two markers exclude, and the support matrix of Kensa version against required Kotlin and minimum JDK, including why a Kotlin bump is a documented compatibility note rather than an API-major.
Changed:
- Tab services are supported API.
Configuration.registerTabServiceandKensaTabServiceswere tagged experimental despite being documented, so registering a log-tab service demanded an opt-in. They are now part of the stable surface and need none. TestContextandTestContextHolderare stable. The thread-local accessor pair that application code uses is now documented as frozen rather than disclaimed in prose.
New features:
- Qualified enum constants read cleanly. When two enums share a constant name and Kotlin forces qualification (
OrderStatus.PENDING), the sentence rendered the camel-split qualifier as noise words ("the order is order status PENDING"). A qualified reference whose qualifier provably resolves (via the file's imports) to an enum constant or nested object — including sealed-class data objects (OrderStatus.Pending) — now renders just the member name as a value token, with the type's simple name as a hover hint. Unresolvable qualifiers render as before. Kotlin and Java (#180). - Chained references through value containers.
@RenderedValueContainernow works on test-method parameters as well as fields, and references through the container (useCase.stub,useCase.ref.name) render as resolved values, gated on the member carrying@RenderedValue. The chain may be followed by a call taking arguments —whenever(useCase.stub.sends(request))renders the resolved stub value followed by "sends" and the request as ordinary sentence words, where previously the whole phrase collapsed into the container's toString. Kotlin covers.and?.chains; Java covers the chain-before-call shape. Display values come from a registered value renderer or the member'stoString. - Fixture values from shared objects:
by fixtures(fx). A Kotlin property delegate that resolves a fixture's value from the currently executing test's context on every access. Shared parameterised-test data (e.g.@MethodSourceuse-case objects) can expose per-invocation fixture values as plain properties with no mutation of shared state; a single instance is safe under parallel execution, and access outside an active test fails with an error naming the property and fixture. - Fixture-delegated properties render as fixture tokens. A reference terminating in a
by fixtures(...)property — via a container chain, a bare reference insidewith(container) { }, or a property on the test class itself — renders with the same styling and highlighting as a directfixtures[...]reference. Paths continuing past the delegated property render as derived values as before.
Fixes:
- Diagram labels keep natural glyph shapes. The 0.8.15 Safari fix swapped every diagram
<text>element tolengthAdjust="spacingAndGlyphs", which scales glyphs horizontally to fit PlantUML's computedtextLength— short labels (e.g. 4-character participant names) read as faux-bold where browser font metrics diverge from PlantUML's. The Safari corruption (#174) is now fixed at source instead: consecutive whitespace in sequence diagram labels (interaction names, dividers, time markers, participant labels, group names) is collapsed to a single space before PlantUML measures them, so the renderedtextLengthmatches what the browser draws and the SVG keeps PlantUML's nativelengthAdjust="spacing"(#179). A group namedSetupwith stray whitespace also now receives the setup colour rather than the test-group gold. - Delegated Kotlin properties in rendered paths. A path segment backed by a property delegate (
by lazy, a customReadOnlyProperty) resolved to the delegate object itself, rendering its type name (e.g. "Read Only Property") instead of the value. Delegated properties now resolve through their getter, including private delegated properties.
Changed:
- Gradle 9.7.0. Wrapper bump from 9.6.1; no API changes.
New features:
- Negative matcher assertions. The kotest and hamkrest test-support cores gain
noneMatching(matcher)— aMatcher<Collection<T>>that passes when no element matches, failing with the offending elements listed.WithKotest/WithHamkrestgain directthenContinually(spec)overloads, so aThenSpeccan be asserted over a window (delegating to the existingPollingScopeform: the matcher must hold on every poll;onMatchfires once). Pair negative assertions withthenorthenContinually, notthenEventually, which passes trivially on the first poll. - MCP server in the CLI.
kensa mcpruns a Model Context Protocol server over stdio, giving an AI agent structured access to your test results. Four tools read a completed run (list_tests,list_failures,get_test,failure_evidence), so a failure reaches the agent as the Given/When/Then sentence Kensa parsed from your source rather than a stack trace it has to reverse-engineer. A fifth,style_profile, catalogues how the project writes Kensa tests (framework, fixture containers, MatcherFields, stub helpers, conventions and an exemplar) so a proposed fix follows your idiom. The bundle argument accepts a single bundle, a site-mode root, a test folder name from.kensa-properties, or nothing at all when only one folder is configured. Registration and usage are documented on kensa.dev.
Fixes:
- Expandable sentence hover popup sizes to its content. The hover preview was a fixed 450px wide, forcing ugly wrapping of the nested sentences it previews. It now grows to fit its content, keeping 450px as a minimum and capping at the available viewport width; a malformed max-width class on the multiline parameter block was also repaired (#176).
- Expandable sentence parameter names are tokenised. Parameters shown beside an expandable sentence rendered identifier names raw (
withConversationId = value). Identifiers in the parameter list now pass through the same token scanner as a normal sentence — camel-split into words, with protected phrases and acronyms honoured (with conversation id = value) (#177). - Test card corners no longer bleed. Test headers showed square corners outside the card's rounded border — all four when collapsed, where the header is the whole card. The card cannot clip with
overflow-hiddenbecause that stops the sticky parameter matrix pinning, so the header now carries its own matching radius: full when collapsed, top-only when expanded, where the body already rounds its bottom (#175). - Sequence and component diagrams render correctly in Safari. A message label containing consecutive spaces (e.g. from interaction naming with an empty segment) corrupted in Safari into interleaved, overlapping letters: XML whitespace collapsing renders the run as one space, but PlantUML computes
textLengthfor the raw string, and WebKit reconciles the mismatch underlengthAdjust="spacing"by overlapping glyph runs. Rendered SVGs now uselengthAdjust="spacingAndGlyphs", which WebKit lays out correctly and leaves Chrome visually unchanged (#174). - Given/When/Then keywords recognised in expandable sentence names. An
@ExpandableSentence(or nested sentence) call likethenAllTheVerificationsAreCorrect()rendered its whole name as one flat header — keyword scanning was disabled on that path and token types were discarded. The leading keyword is now emitted as its own keyword token (title-cased, rendered blue,whenevernormalising toWhen) ahead of the expandable header; expandables not at the start of a sentence are unchanged (#173).
Changed:
- Kotlin 2.4.10, Gradle 9.6.1. Toolchain bump from Kotlin 2.4.0 / Gradle 9.5.1; no API changes. Consumers using the build plugins get the matching compiler-plugin pairing via the next build-plugins release.
Fixes:
- Non-null assertion (
!!) in rendered paths. Fixture, output and chained-call expressions containing Kotlin's!!operator —fixtures(MyFixture)!!.name,outputs[MyOutput].foo!!.bar— were not recognised, so the raw source words leaked into the sentence instead of the value. All path patterns now accept!!after the accessor and after any chain segment, and path resolution strips it (#168). - Kotlin stdlib extension functions in rendered paths. A path segment calling a stdlib extension —
fixtures[X]!!.uppercase(),.first()— silently renderednull, because only Java members of the receiver were searched. Resolution now falls back to the stdlib extension facades (StringsKt,CollectionsKt,MapsKt,ArraysKt), including@InlineOnlyfunctions. User-defined extensions remain unresolvable (#169). - By-key outputs with non-word keys.
outputs("my-key")and other keys containing hyphens, dots or spaces were not recognised; the quoted key may now be any string (#170). - Explicit type arguments in rendered expressions.
outputs<String>("myKey")— which the compiler forces whenever the type can't be inferred — and generic chain calls like.first<String>()were not recognised. Type arguments are now accepted on thefixtures/outputsaccessors and on chain segments, and erased during path resolution (#171). - Factory fixture navigation paths resolve to the navigated value.
fixtures[productFor(p)].namerendered the whole fixture value rather than the.nameproperty, making sentences silently misleading. The trailing path is now carried through parsing and applied when rendering (#172). - Fixture factory calls inside a
fixtures[...]subscript render as a single value.fixtures[productFor(provideType)](the documented@Fixturefactory-call syntax) previously leaked the literal word "fixtures" before the fixture value, because only the inner factory call was recognised. The wholefixtures[factory(args)]span, including any trailing navigation, is now recognised as one fixture-factory expression, so the accessor is absorbed. - Parameterised test header hover no longer truncates. The invocation header showed the full parameterised display name through a native
titlehover, which browsers cap at roughly 1024 characters, clipping long names mid-string. It now uses the built-in tooltip, which wraps and scrolls with no cap; the collapsed "N params" chip gained a hover listing the parameters, and long parameter names in the expanded table now show a hover too (#165). - Protected phrases no longer match inside longer words. A protected phrase matched case-insensitively at any position, so a phrase like
Closebit intoClosedDateand split it mid-word, rendering the display name asClose d Dateinstead ofClosed Date; rendered sentences mangled the same way. Phrase matches now only apply when they align to camel-word or punctuation boundaries, in both test display names and sentences (#166).
Fixes:
@RenderedValueidentifier as afixtures[...]/outputs[...]subscript key. A rendered parameter or field used inside a fixtures or outputs subscript, such asfixtures[productFor(provideType)]oroutputs[trackingId], crashed the source parser and rendered the whole statement as "Could not parse this statement". The parser now keeps its expression stack balanced for these nested rendered identifiers (#163).Namedparameterized-test arguments show their label. JUnit unwrapsNamed.of("label", value)to the raw payload before Kensa sees the invocation, so the parameter table showed the payload instead of the label. Kensa now recovers the label positionally from the test display name and applies it to opaque payloads, falling back to the raw value when the argument count does not match the parsed parameters (#162).- Opaque lambda and builder arguments render by type name. Lambda and builder parameter values that fell through to
Object.toString()produced non-deterministic noise likeMyTest$$Lambda/0x...@6f77685in parameter tables. They now render as their functional interface simple name (for exampleFunction1, or a custom SAM name), withlambdaas a fallback. Registered value renderers still take precedence.
Performance:
- Fewer test-explorer re-renders in large reports. Explorer rows no longer re-render on every selection or URL change, because only System-View rows subscribe to the router now. This cuts per-click main-thread work in reports with many test rows.
Fixes:
@ExpandableSentencewith value class parameters. The Kotlin compiler mangles the JVM name of a function that takes a value class parameter (someActionbecomessomeAction-abc123), so Kensa could not find the method and reported the test as unparseable. Thanks to Michael Orr (#160).@RenderedValuewith value class parameters or return types. The same name mangling meant the captured invocation was recorded under the mangled JVM name and never matched the parsed sentence, so the rendered value was silently dropped.- Array parameter types in parameterized tests. Tests declaring array parameters —
Array<Pair<String, String>>and otherArray<T>(including nested, variance and nullable components), primitive arrays (IntArray, ...), Java arrays (String[]),varargparams — previously failed withDid not find method declaration for test method; plainFloat/Short/Byte/Charparams now match too. Array values also render like lists ([(a, b)]) everywhere instead ofObject.toString()identity hashes (#161).
Performance:
- No reflective method lookup on instrumented calls. The compiler-plugin hooks now record invocations by source name directly instead of deriving a
java.lang.reflect.Methodon every@ExpandableSentence/@RenderedValuecall.
New features:
- Multi-assertion
thenEventually/thenContinuallyblocks. Both polling forms now accept a block ofthen/andassertions evaluated in parallel within a single polling window —thenEventuallylocks in each assertion independently as it starts passing,thenContinuallyrequires all assertions to hold on every tick. Single failures are rethrown as-is; multiple failures are aggregated with the rest suppressed. Available in the Kotest and Hamkrest bridges.
Fixes:
ReplaceSentenceandIgnorehints in Java test sources. The Java lexer now recognises the documented/*+ ... */hint form (it previously only matched javadoc-style comments, which then failed the prefix check), and the Java body parser now emitsIgnorehints, matching the Kotlin parser (#151).
New features:
@Fixturefactory functions. Annotate a function in aFixtureContainerwith@Fixture("Key")and call it inline viafixtures[myFixture(arg)]; the compiler plugin rewrites its no-namefixture { }to inject the key and arguments, giving each(key, args)a distinct memoized identity that renders as a fixture token.
Fixes:
- Chained reference to a parameterised
@RenderedValuemethod.productFor(p).stringValuenow renders the captured value instead of the call words /null(#149). - Top-level
@RenderedValuefunction. Resolves the file-facade FQN (honouring@file:JvmName), fixing aClassNotFoundException(#150).
New features:
- Null-safe paths in rendered values. Rendered chained paths now accept the
?.safe-call operator —order?.customer?.name,fixtures[X]?.foo,outputs("k")?.bar— which were previously dropped from value substitution (#144). - Test parameters rendered through registered renderers. Parameter values, the parameterised test description shown in the test header, and the suite search index now all honour registered value renderers, and each parameter is rendered exactly once (no more double-rendering when a
Stringrenderer is registered) (#145). - Parameter-derived fixtures. A
parameterFixture("key", from = "paramName") { … }(Kotlin) /createParameterFixture(...)(Java) is registered by name — so it resolves infixtures[…]interpolation — and derives its value per invocation from a named parameterised-test argument, seeded before the test body so it's usable both in the test and in the rendered sentence. Secondary fixtures can derive from one (#148).
Changed:
- Consistent lowercase
nullrendering. Captured null values now render as lowercasenulleverywhere, matching the source-literalnullkeyword and the UI's null styling (previously an inconsistentNULL, which made an actual null display as a quoted string) (#146).
Fixes:
- No ConcurrentModificationException in sequence diagrams under parallel execution.
SequenceDiagramFactoryis now safe when tests run in parallel (#142). dataOnlyis authoritative overoutputDirin site mode.
New features:
- Suite-wide fixture search. Modifier-click (find by fixture name) or right-click (find by value or name) any fixture token in a report to search the whole suite. Matches dock in a resizable, non-modal panel grouped project → class → method → invocation, auto-revealing the active result in the left explorer and highlighting it in violet across sentences and interaction payloads, landing on the exact invocation. Backed by a new per-source
search-index.json— a value-anywhere index over rendered sentences, givens/outputs and interaction payloads, with short, boolean and small-numeric values filtered out as noise — emitted in both data-only and full output modes.
Changed:
- Built against Kotlin 2.4.0.. Kensa now compiles against Kotlin 2.4.0 and requires consumers to be on Kotlin 2.4.0; projects still on Kotlin 2.3.x should stay on the previous Kensa release. Context parameters are stable in 2.4.0, so the deprecated
-Xcontext-parametersflag has been dropped from the build.
New features:
- Expression-bodied
@ExpandableSentenceand test functions now render. A function written with an expression body —fun theDetails() = arrayOf(aField of "John", ...)— previously produced an empty expandable popup, because an expression body emits no statement for the source parser to turn into a sentence. Expression bodies now render through the same path as block bodies. When the whole body is a single wrapping call (arrayOf(...),listOf(...), or anybuilder(args)) the call itself is stripped so only its contents render — a matcher/element list reads as a clean list without the surrounding builder. Lambda-delegating bodies (= with(context) { ... },= test { ... }) are untouched and keep rendering their lambda statements; block bodies are unchanged. /*+ Ignore */source hint. A/*+ Ignore */comment on its own line drops the rendered tokens on the next source line from the report sentence;/*+ Ignore:n */drops the nextnlines. It works anywhere in a test or@ExpandableSentencebody — a manual way to hide distracting plumbing (a wrappingreturn arrayOf(line, a gnarly builder chain) from the sentence without changing the test. Complements the existing/*+ ReplaceSentence: ... */hint.
Changed:
primaryparticipant now renders as the leftmost participant. Previouslyprimary.participant(...)was only emitted as an empty-diagram fallback for marker-only tests — configuringprimary.actor("SUT")alongside other participants required re-declaring "SUT" viaparticipant("SUT")to actually see it.SequenceDiagramFactorynow prepends the primary's line to the participants list whenever it is set, soprimarymeans what it reads as: the leftmost participant. When the same name is already declared (top-level or inside abox { }) the primary is suppressed so the explicit declaration wins and keeps its position. Docs.- Test headers cap inline issue badges. A test card header now renders at most three
@Issuebadges inline; any beyond that fold into a+N morepopover listing the full set in a scrollable grid, so long issue lists no longer push the header layout around. Badges still link to the issue tracker, and the popover trigger does not toggle the test card. - Tidier parameterised invocation headers. Long backend display names now truncate to a single line (full text on hover) instead of wrapping and growing the header box, and a leading JUnit-style
[N]index prefix is stripped from the name since the card already carries a#Nbadge (a bare[123]with no trailing space — e.g. a Kotest listtoString— is left intact). - Sidebar test leaves read as leaves. Test rows are inset past the absent-chevron gutter and the leaf diamond's stroke is thickened, so individual tests no longer blend in with the container folders above them.
Performance:
- Parsing no longer serialises on hash-bin collisions. The per-method and per-class parser caches moved off
ConcurrentHashMap.computeIfAbsent, which holds the bin lock for the entire duration of a (potentially slow) parse and blocks unrelated threads whose keys land in the same bin. They now use aCompletableFuture-based memoize that reserves the slot and runs the parse outside the lock, so concurrent first-time parses of different classes/methods proceed in parallel. - Lower retained heap from cached parse trees.
MethodDeclarationContextnow severs the parsed method body from its parent chain, so a cachedParsedMethodno longer pins the entire enclosing AST in memory.
Fixes:
- No report written when no tests ran.
writeAllResultsforced the lazyResultWritereven with an empty test set, which recreated (wiped) the output directory and wrote an empty report — destroying any report from a previous run. It now returns early when no test containers were recorded, leaving the output directory untouched on a zero-test run. Covers every framework adapter (JUnit 5/6, TestNG, Kotest), which all funnel through the same call. - Breadcrumb no longer nests
<li>inside<li>. The package breadcrumb in the detail header rendered its separator as an<li>inside the item<li>, tripping React'svalidateDOMNestingwarning. The separator is now a sibling of the item under the list.
New features:
- Sidebar tree auto-expands while filtering. Typing in the test filter now reveals matches inside user-collapsed folders without touching the persisted shape — clear the filter and your collapse/expand state is restored exactly as it was. Builds on the v0.8.3 expand/collapse toolbar.
Fixes:
- Collapse-all now collapses every folder, not just project roots. The
⌥+[toolbar button only added per-source project roots to the collapsed set; package nodes were built dynamically per-render and never reached the expansion state, so opening a collapsed root revealed every nested package already expanded. The package tree is now materialised before expansion state sees it, matching IntelliJ behaviour: collapse-all → opening the root shows only its top-level entries; expand-all opens every folder. - Persisted tree state is bounded.
kensa-tree-collapsedin localStorage now caps at 5000 entries, dropping the oldest on load. Prevents unbounded growth in long-lived workspaces where folder ids churn (heavy package refactors, frequentpackageDisplaymode switches). - All Kensa modules now appear in the BOM. Modules added after the BOM's last refresh were missing from
kensa-bom, so importing the BOM didn't constrain their versions.
New features:
- Sidebar tree expand/collapse toolbar. Two icon buttons live inline with the Test Explorer group label: expand-all (
⌥+]) and collapse-all (⌥+[). Expansion state persists to localStorage so the tree's open/closed shape survives reload. Newly-seen folder ids that aren't yet in storage stay expanded by default — no surprises after a build adds packages. - Parameterised test display. Invocation card headers no longer truncate parameter lists. A hybrid summary picks one of three shapes per invocation: the backend-supplied
displayNamewhen present (e.g. JUnit's[1] arg1, arg2); an inline comma-join when there are ≤3 parameters and the joined string fits in 80 characters; or aN paramscount chip otherwise. When the header had to drop information the expanded card body grows a structured parameters table; when the header already listed everything the table is suppressed to avoid redundancy. Above the per-invocation cards a cross-invocation matrix renders whenever a test has ≥2 invocations and every invocation carries ≥2 parameters — clicking a row scrolls to and expands the matching card. Values flow through a small client-side classifier that colours numbers / booleans /null/ strings / JSON code-style.
Fixes:
- Console banner now prints an absolute output path. The "Kensa Output:" line emitted at the end of a run is what IntelliJ turns into a clickable hyperlink — but only when the path is fully qualified. Relative
outputDirvalues (common under@KensaTestYAML config or anykonfigure { outputDir = Path(...) }with a relative argument) silently failed to link. Resolved to absolute at print time so the hyperlink works regardless of howoutputDirwas supplied.
New features:
- Framework-native tags surfaced in reports. JUnit
@Tag, Kotest@Tags, and TestNG@Test(groups = ...)now flow through to the HTML report as badges on each test (alongside@Issuebadges) and as a multi-select cloud in the sidebar with OR-logic filtering. Click a badge to filter by that tag; Cmd/Ctrl/Shift-click to add to the current selection. sequenceDiagram { }Kotlin DSL onConfigurationfor declaring participants, boxes, title, hide-unlinked, and a fallback identity. Declaration order is left-to-right on the rendered diagram. Each participant call (participant,actor,boundary,control,entity,database,collections,queue) returns a handle so.withColour(...)and.withAlias(...)chain inline.box(title) { ... }wraps nested participants. Docs.primary.<type>(name)fallback identity. A test that captures only dividers (SD-MARKER/==Something==) or has no participants previously produced markup PlantUML couldn't recognise as a sequence diagram — falling into the error path that pulls in QR-code (zxing) classes stripped from the shadow JAR. ConfiguresequenceDiagram { primary.actor("User") }to inject a single participant for these cases. Only emitted when no participants are declared and no real arrow interactions are captured.
Fixes:
- UI no longer caches
loadJson/loadTextfetches. Browser-side requests forindices.json,results/*.json, and source-file text now go out withcache: 'no-store'so a regenerated report is picked up on reload without a hard refresh. - Late-applied sequence-diagram config is now observed by the factory. When
Kensa.konfigure { ... }runs from a companion-objectinitblock (a common pattern — e.g., your own JUnit extension) it may execute afterKensaLifecycleManager.initialise(...)has already constructedSequenceDiagramFactory. Previously the factory captured a reference to the oldvar umlDirectiveslist, so a subsequentumlDirectives = listOf(...)reassignment was invisible to it — the rendered diagram had noparticipantdeclarations and PlantUML laid out participants from interaction order alone. The factory now reads from the shared list at render time, so post-init reassignment via the deprecated setter (or the newsequenceDiagram { }block) is honoured.
Deprecated:
Configuration.umlDirectives— replaced by thesequenceDiagram { }block. The legacy setter still works but clears the directives list and invalidates any retained participant handle from a priorsequenceDiagram { }block. Java consumers can keep using it until a richer Java-side API lands.
New features:
- Spring Boot starter — two new modules (
kensa-spring-boot-starter,kensa-spring-boot-starter-web) wire Kensa into Spring Boot tests with zero manual configuration:@KensaTestboots the application context with Kensa registered as a JUnit Jupiter extension and configures the runtime fromkensa.*properties inapplication.yml. The-webmodule auto-configuresHandlerInterceptor/ClientHttpRequestInterceptor/ExchangeFilterFunctionbeans that capture HTTP interactions for the sequence diagram. Override any capture point by registering a bean with the canonical name (kensaHandlerInterceptor,kensaClientHttpRequestInterceptor,kensaExchangeFilterFunction) to plug in a party-aware variant. Spring deps arecompileOnly— non-Spring projects onkensa-coresee no transitive Spring dependency. Docs.
Changed:
@ExpandableSentencebodies are now parsed lazily — the parser runs only on first access, so unused expandable methods incur no parse cost.- JUnit framework adapters — softer version pin.
kensa-framework-junit5and-junit6previously consumed thejunit-bomatimplementationscope, which propagated as a hard runtime dep and made Kensa's JUnit version a floor for consumers. The BOM is nowcompileOnly; the published POM still carries it as<scope>import</scope>in<dependencyManagement>(so suggested versions are visible to Maven consumers) but is no longer a runtime dep, letting Spring Boot's dependency-management plugin and similar version-managers win cleanly.junit-jupiter-api,junit-jupiter-params, andjunit-platform-launcherstill ship transitively atimplementationscope, so consumer classpaths are unchanged. The deadjunit-jupiter-enginedeclaration was removed (the adapter's listener usesjunit-platform-enginetypes brought in via the launcher). - TestNG framework adapter —
testngis nowcompileOnly.kensa-framework-testngno longer ships TestNG transitively. TestNG users already declaretestngexplicitly in their build, so the practical impact is nil; the win is that the adapter no longer imposes a TestNG version on consumers.
Fixes:
- Remove
-Xexplicit-backing-fieldsagain (re-introduced unintentionally in 0.8.0). Downstream Kotlin projects no longer need-Xskip-prerelease-checkto consume kensa-core. - Per-source component diagrams in site mode. The HTML UI was loading every per-source
aggregateComponentDiagramfrom eachsources/<id>/indices.jsonbut then only using the first source's diagram as a global System View — every other source's architecture was silently dropped. Site mode now renders one System View per source, surfaced as a "System View" entry inside each source's tree root in the sidebar. Single-sourceset projects are unaffected (one source → one entry). kensa-framework-junit5: per-classresults/*.jsonfiles now write correctly on JUnit Jupiter < 5.13. TheCloseableTestContainerthat hooks end-of-class teardown usedjava.io.Closeable, which the JupiterExtensionContext.Storeonly honours on 5.13+; on 5.12 (forced by Spring Boot 3.5.x) the close callback never fired and test detail files were silently dropped. Switched toExtensionContext.Store.CloseableResource, which is honoured by every 5.x version.
New features:
- Kotest Field Assertion DSL — three new modules (
kensa-kotest-test-support,-xml,-json) providing aMatcherField<T, R>interface and composable, named-field matchers for JSON and XML payloads. Extension functionsof/matching/withListOf/withSetOf/toMatcherproduce standardMatcher<T?>values that compose withthen(collector, matcher). Failure messages are auto-prefixed with the field's description. Docs. - Hamkrest Field Assertion DSL — three new modules (
kensa-hamkrest-test-support,-xml,-json) providing aMatcherField<T, R>interface and composable, named-field matchers for JSON and XML payloads. Extension functionsof/matching/withListOf/withSetOf/toMatcherproduce standardMatcher<T?>values that compose withthen(collector, matcher). Failure messages are auto-prefixed with the field's description. Docs. - Phrasing sugar in both flavours —
with,thatHas(single + vararg),thatIs,shouldHaveAll— for assertions that read like English at the call-site. Docs. @RenderedValueWithHintintegration for field DSL — JSONPointer / XPath surfaced as on-hover hints in the report. Docs.- Site mode for HTML reports — runtime, UI, and CLI integration. Docs.
- Experimental UI test framework with Playwright and Selenium adapters (
framework-playwright,framework-selenium, plus-junit5/-junit6variants). Docs. - Component diagram view. Shows all interactions between components in a test suite. Docs.
- UI Click-to-filter passed / failed / disabled badges on the sidebar footer.
- UI Scrollable expandable popups.
- Missing
andEventuallyoverloads on the kotest assertion entry points.
Changed:
- CLI updated for site mode; warns on Kensa-version drift between the CLI and the report it serves. Docs.
- Local builds now default the version to
snapshot-version.txt.
Fixes:
getFailingLinechoosing the wrong line number.- Default-mode
@ExpandableRenderedValuerendering. - Kotest matcher double-execute inside
andEventually.
Breaking:
- Legacy UI removed (deprecated in 0.7.0;
Configuration.uiModeandKensaConfigurator.withUiMode(...)are gone, along with theUiModeenum).
- Fix #132: don't clear sidebar search on Escape while a dialog is open
- Fix #131: match issue ids exactly, not as substrings
- Build uses Gradle 9.5.0
- Kotlin 2.3.21
Breaking — deprecated APIs removed:
@NestedSentenceannotation removed — use@ExpandableSentenceGivens/GivensBuilder/ActionUnderTest/StateExtractorand the deprecatedgiven(GivensBuilder)&whenever(ActionUnderTest)overloads removed- Deprecated
KensaTestoverloads removed from the JUnit 5 / JUnit 6 adapters WithAssertJand all*StateExtractorhelper classes removed (AssertJ module)WithHamcrestremoved;KotestThenpruned of deprecated entry points- Enum values
Section.ButtonsandTab.Givensremoved SuppressParseErrorsannotation removed — the parser now recovers from errors automatically, making suppression unnecessary
Breaking — renamed parameter extractor → collector on assertion entry points. If you were calling these with named parameters (then(extractor = ...)), update call sites.
Deprecations:
Configuration.uiModeandKensaConfigurator.withUiMode(...)— Modern is now the only supported UI; Legacy UI and theUiModeenum are scheduled for removal in 0.8.0.
New features:
/*+ ReplaceSentence: ... */hint — a comment placed before any test statement replaces its rendered sentence with the hint text. Supports value interpolation via{expr}(fields, fixtures, outputs, chained calls). Discouraged as a long-term pattern; intended as a short-term workaround when Kensa fails to parse a test due to unsupported syntax — please raise an issue if you hit a parser gap.- Parse errors no longer fail the build. The parser recovers and continues, and failed sentences are rendered inline with an error marker in the report.
- Modern UI is now the default
UIMode. @RenderedValueWithHintis now hierarchy-aware — a directive declared on a supertype or interface applies to every subtype unless a more specific directive is declared. Avoids having to repeat the annotation for each member of a sealed hierarchy.
Fixes:
- Revert
-Xexplicit-backing-fieldscompile option. Kensa no longer requires downstream projects to set-Xskip-prerelease-check. - Source file lookup now works when the sources come from the Gradle cache or a local Maven repository and support fat sources with
-sourcesclassifier. - More robust recognition of
fixtures[...],outputs(...), and chained property/method calls in test sources, backed by dedicated regex tests.
- Add annotation
SuppressParseErrorsto allow temporary bypass of parsing errors. Also added instructional sentences in test output. - Fix an issue where log indices were not indexed correctly for consecutive log lines.
- Synchronising with IntelliJ plugin. Allow the plugin to expand individual tests when clicking on the gutter icon.
- Fix issue where failed tests using ExpandableSentence or ExpandableRenderedValue would not render correctly.
- CMD-F binding in Modern UI removed as it was conflicting with default browser search. Replaced with '/' to focus search field.
- Modern UI test explorer now orders packages before tests.
- (Experimental) Custom tabs now support image rendering (for upcoming UI testing functionality).
- Allow regex to be passed to
IndexedLogFileQueryServiceto enable log delimiter lines to more easily matched - Regression - ExpandableSentence defined via Sources annotation were not recognised properly.
- Add 'FixtureSuite' and
WithFixtureSuitealongside new fixture rendering capability usingfixtures { MyFixtureFx }in tests. Intended to reduce the number of import boilerplate in tests that use a large number of fixtures. (Thanks to Ryan Taplin for the suggestion) - Improve recognition of
EpandableSentencethat use lambda parameters.
- Adding overloads for fixtures when using function references.
- Allowing inline notes to be rendered inside Kotlin tests that are expression functions.
- Fixing issue where legacy tests would not render due to changes in the json format.
- Relocating more PlantUml classes which were causing
VerifyErrorin some applications that also use PlantUML.
- Fixture table — parent/child highlighting, hierarchy display, corrected ordering & family grouping. Thanks to Jamie Redding!
- Fixtures can now be specified as highlighted
- Claude Code Skill
- Modern UI overhaul — IBM Plex fonts, larger base size, GWT keyword colour-coding, styled notes, section separators
- Notes annotation — class-level markdown notes rendered above test list; supports bold/italic/links & tables, internal cross-suite navigation (#ClassName.methodName)
- Clickable package breadcrumbs — clicking a package segment updates the search query
- Sentence line numbers — exceptions/failures now highlighted in the report
- Keyboard scrolling — issue/state picker list scrolls with arrow keys
- URL entry — entering the app with a query URL now selects the first matching test
- Protected phrases honoured in test display names
- Custom tabs — HTML content can use secure allow-same-origin iframe
- Dependency pruning — antlr4 → antlr4-runtime (saves ~7MB); kotlinCompilerEmbeddable marked compileOnly; PlantUML JAR shadowed; assertion libs promoted to api() scope
- Kotlin 2.3.20 — includes fix for downstream compiler plugin errors
- Kotest 6.1 — version bump + test fix
- Gradle 9.4.1
- Experimental Kotest and TestNG support (tests pending)
- Report date & Kensa version shown in the modern UI
- Disabled tests & package display fixes
- JUnit 6 BOM no longer leaks into assertion dependencies — Tim W
- Sequence diagram CSS tweaks (light & dark mode improvements)
- Hamcrest/Hamkrest modules aligned with Kotest module structure
- Experimental support for JUnit 6
- Breaking dependency change: Now need to specify junit dependency as either
dev.kensa:kensa-framework-junit5ordev.kensa:kensa-framework-junit6
- Breaking dependency change: Now need to specify junit dependency as either
- Modern UI improvements: Select test and expand first matching method when filtering. Honour the display name
- Improve registration of log services for Modern UI (Thanks to Paul Reynolds)
- Modern UI improvements: Keep focus in the search field after 'Escape' clear Remove package icon. Add keyboard navigation to issue and state pickers. Handle method level issue filtering Allow removing the state/issue badge by clicking on the x Dial down bar colours a bit
- Fix issue where some report lines would not be indented correctly
- Modern UI improvements
- Adding
scopesupport for tabs, alowing single log tab content to be shown across multiple tests. - Add RawLogFileQueryService for displaying raw files without indexing.
- Add ability to jump to and expand a specific test via the 'method' query parameter.
- DockerCliLogQueryService now using Regex for matching.
- LogFileQueryService now using Regex for matching.
- Improvements & fixes to Custom tab rendering in modern UI.
- Experimental LogFileQueryService now using
startsWithfor delimiter line matching.
- Experimental support for custom tabs in Modern UI. Includes ability to query log files and docker logs.
- Modern UI improvements.
- Fixing issue where
ExpandableSentenceannotation was not working properly.
- Improving how nested sentences are matched with source code. Now using full type matching.
- Fixing autoOpen of tabs in modern UI.
- Adding overflow popover panel for issues in test header.
-
Adding support for
Notesin test output. Kensa will recognise///as a note when it is placed before a Given/When/Then keyword. Notes will also be recognised at the end of a line (immediately following a}or))Thanks to Michael Orr.
-
Introduce
ExpandableRenderedValueto allow function markers to be expanded to show the function return value in a table. This is useful for functions that return collections. -
Introduce
RenderedValueWithHintto allow fields to be rendered with a hint tooltip. This can be used to provide additional information about a field, such as a JsonPath or XPath. -
Deprecate
NestedSentencein favour ofExpandableSentence -
Introduce early access version of a new UI. Requires use of a web server to view the output. Use IDEA's or TeamCity's built in server, or use Kensa's own CLI to spin up a server.
- Updates for compatibility with Kotlin 2.3.0
- Kotest version bump for compatibility
- Fix regression with parameterised tests that contained nested sentences.
- Improvements to how the Kotlin compiler plugin handles extension functions and context parameters in nested sentences.
- Allow nested sentences to be defined in Interfaces when using the Kotlin compiler plugin.
- Honour whitespace when displaying field and parameter values (Thanks to Michael Orr)
- Introduce Kotlin compiler plugin to support advanced rendering for @RenderedValue & @NestedSentence annotated functions.
- Fix issue where parameters were not being recognised when closing bracket was on a new line. (Thanks to Neil Massey)
- Fix issue with plural ProtectedPhrases
- Fix issue with Kensa agent and Kotlin 2.2.20 (ByteBuddy Advice problems)
- Fixing more layout issues with nested sentences
- Bump to Kotlin 2.2.20
- Correcting the rendering of nested sentences.
- Improving the rendering & parsing of RenderedValue functions with parameters.
- Tweak alignment of nested sentence floating and embedded block.
- Experimental support for rendering return values of methods/functions that take parameters. Currently only matches on method/function name. Requires kensa-agent.
- Fix for fake plurals of ProtectedPhrases.
- Support for Kotest 6.
- Fix issue with Java record rendering
- Adding used fixtures tab the test output
- Adding ability to search captured interactions
- Improvements to captured interactions ui (with placeholder for raw rendering)
- Experimental CLI to spin up a local server for viewing the test output
- Protected phrases now recognise their plural form
- Adding better support for Kotlin backticked test names.
- Fix issue with Nested Sentences with parameters that are function calls with parameters.
- Fix an issue with Nested Sentences not parsing correctly when top level expression
- Adding a copy button to rendered captured output
- Removing some unused deprecations
- Improve exception handling for
onMatchin ThenSpec - Adding Adoptabot example project to be used in docs
- Adding more docs
- Preserve whitespace in value block of tables
- Add CapturedOutputs to GivensContext to allow chaining of givens steps
- AssertJ with StateCollectors
- TestContextUtil with Action & StateCollector
- Captured Outputs can be rendered by the string key as well as typed name
- Rethinking fixtures. No longer mutable.
- Introduce CapturedOutputs
- Improved nested sentences:
- Parameters are no longer bound to share the parent test arguments (requires kensa-agent)
- Arguments are rendered on the test output as part of the call
- Arguments are rendered within the nested sentence as expected
- Add ability to define private fixtures to use as parents of other secondary fixtures.
- Add some syntactic sugar for SetupSteps.
- Version bumps
- Colour fix for disabled tests
- Intermittent test fail fix due to concurrency
- WithFixtures as receiver for ThenSpec
- Allow secondary fixtures to have multiple primary dependencies
- Chained calls after Fixture expressions are resolved
- Make TestContextFixtures use a getter
- Adding FixturesParameterResolver
- Adding fixtures as parameter to
onMatchforThenSpecWithFixtures - Allow SecondaryFixtures to be parents of other fixtures.
- Adding
hasValuefunction to Fixtures. - TestContext should be a class not an object.
- Fix ui issues.
- Introduce *WithFixtures suffix interfaces for GivensBuilder, ActionUnderTest & StateExtractor.
- Revert GivensBuilder, ActionUnderTest & StateExtractor to remove Fixtures (for backwards compatibility).
- Fixing parsing issue
- Introduce Bom.
- Fixes for Bulma typography.
- Lenient value reflection.
- Breaking changes: Dependencies changed in prep for supporting other test frameworks:
- Now need to choose dependency
dev.kensa:kensa-framework-junitand one of: dev.kensa:kensa-assertions-kotestdev.kensa:kensa-assertions-hamcrestdev.kensa:kensa-assertions-hamkrestdev.kensa:kensa-assertions-assertj
- Now need to choose dependency
- Breaking changes:
JavaKensaTesthas been removed - useKensaTestfrom JUnit framework dependency - Breaking changes:
KotlinKensaTesthas been removed - useKensaTestfrom JUnit framework dependency - Breaking changes: Introduce
fixturesparameter to GivensBuilder & ActionUnderTest - Breaking changes: Rename annotation
SentenceValuetoRenderedValue - Breaking changes: Rename annotation
ScenarioHoldertoRenderedValueHolder - Breaking changes: Remove annotation
Scenario(replaced by call chaining capability) - Breaking changes: Remove interface GivensWithInteractionsBuilder
- Rendered values will now follow chained calls eg
myField.myPropery - Introduce
Fixtures. Tests now have access to a built-in fixture container with custom factories. Fixture values will be rendered by default in test output
- Drop webpack. Introduce Vite. UI layout changes. Parsing Changes. Test Fixtures.
- #45 : Ignore type arguments when building sentences
- #43 : Display the test class's package as part of the header (thanks to Michael Orr)
- #39,#40 : Introduce
flattenOutputPackagesconfiguration flag (defaults tofalse), to allow package folder structure to be maintained or flattened. Ability to use custom test or index writers is now removed. - #41 : JUnit's
ParameterizedTestannotation is correctly handled with thenametemplate becoming the test's display name.
- #38 : Allow setup to specify which Tab to open (Givens, Parameters, Captured Interactions or Sequence Diagram)
- #37 : WIP - Introduce SetupSteps
- #35 : Quick 'fix' for Sources annotation use with Java
- #34 : Support Java 20 Grammar (Fix scenarios)
- #34 : Support Java 20 Grammar (Fix scenarios)
- #35 : Add Source annotation and allow source files to be specified (WIP)
- #34 : Support Java 20 Grammar (Fix noisy output)
- #34 : Support Java 20 Grammar
- #33 : Add sequence diagram filtering via clickable actor names (thanks to Michael Orr)
- #32 : Replace HighlightedIdentifier with ProtectedPhrase
- #31 : Add floating headers to sequence diagrams (thanks to Michael Orr)
- #29 : Allow configuration of
initialDelay&intervalforthenEventuallyfunctions. - #30 : Add additional parsing events for Kotlin tokens (
Boolean,Char,null& hexadecimal numbers)
- #27 : Update UI to use modern ReactJS function components. Allow filtering by issue. Remove showOnSequenceDiagram for RenderedAttributes as they were legacy.
- #27 : Fixes for issue rendering
- Bump some versions
- #27 : Allow manual filtering of test classes by issue number
- #25 : Wide sequence diagrams now scrollable (thanks to michaelomichael)
- #24 : Ensure extractor is called only once in KotestThen#then
- #23 : UmlDirective to hide unlinked participants
- #22 : Fix stacktrace overflow issues.
- #21 : Fix stacktrace overflow issues.
- #20 : Introduce
ParameterizedTestDescriptionannotation. Tidy.
- #19 : More experimentation with Kotest. Support
thenContinuously. IntroduceThenSpec
- #19 : Starting some work on
thenEventuallyimprovements for Kotlin/Kotest
- #18 : Improve handling of expression function tests
- #17 : Introduce ScenarioHolder
- #15 : Add timestamp to KensaMap entries to ease sequence diagram order issues
- #14 : Complete thenEventually for Kotest
- #13 : Add rendering of equals & arrow operator to Kotlin tests
- #13 : Add rendering of equals & arrow operator to Kotlin tests
- #8 : Improve rendering of attributes and values - changes after feedback
- #8 : Improve rendering of attributes and values - changes after feedback
- #10 : Allow rendering of collections using the registered renderer of the content objects
- #8 : Improve rendering of attributes and values
- #6 : Synchronise read access for KensaMap.
- Bump Kotlin & Kotest versions
- #4 : Allow Java AssertJ tests to specify own assertion
- #5 : No longer exposing Kotlin's Function1 via Java api in WithAssertJ.java
- Clean up
- #3 : Reintroduce some capability of customising the test output via
TestWriter&IndexWriterinterfaces
- #2 : Reinstate transparent background & white group box in sequence diagram
- #3 : Improve total output rendering time by making individual test containers render their output when JUnit closes their context
- Fix issue with
whenkeywords not being recognised - Various NPM updates
- Kotlin 1.7.10
- Kotest Assertions
- First version for maven central