benkuly
03/19/2024, 12:01 PMLeoColman
03/19/2024, 12:23 PMLeoColman
03/19/2024, 12:23 PMLeoColman
03/19/2024, 12:24 PMLeoColman
03/19/2024, 12:25 PMLeoColman
03/19/2024, 12:26 PMOliver.O
03/19/2024, 12:29 PMEmil Kantis
03/19/2024, 1:03 PMEmil Kantis
03/19/2024, 1:05 PMOliver.O
03/19/2024, 1:16 PMHow can I test if my changes have affected Wasm? When I runThe problem here is that testing withit doesn't trigger anything...gradle wasmJsTest
kotest-tests-js
it not integrated with the current local Kotest build but uses a repository version instead.
Many folks (me included) would like to see nested tests working on JS, but failed attempts of getting this done properly and lots of discussions spread across PRs and issues suggest that it's not easily done.Adam S
03/19/2024, 2:31 PMkotest-tests-js
subproject and tested the latest version, and it works as expected! (Which is still rough, but it's better than what we have now.)Adam S
03/19/2024, 2:32 PMAdam S
03/19/2024, 2:33 PMOliver.O
03/19/2024, 2:41 PMAdam S
03/19/2024, 2:47 PMAdam S
03/19/2024, 2:48 PMcd kotest-tests/kotest-tests-js-standalone
idea .
Adam S
03/19/2024, 2:49 PMAdam S
03/19/2024, 2:55 PMOliver.O
03/26/2024, 5:34 PMAdam S
03/26/2024, 5:46 PMAdam S
03/26/2024, 5:46 PMOliver.O
03/26/2024, 5:49 PMkotest-tests-js
existed before your PR's changes. So it was already somewhat messy before. No worries. 😉Adam S
03/26/2024, 5:53 PMOliver.O
03/26/2024, 6:06 PMOliver.O
03/28/2024, 3:22 PMthis
is not this
, and lots of duck typing obscures what actually gets used and how). Anyway, we now have this new PR which contains some reasoning on how we should use the Kotlin test infra APIs, and a bunch of comments pointing to references. Hopefully, it helps to understand things better and build solid solutions on top of that.
Next on my list is the little project to explore nested tests directly without Kotest (scheduled for later today). For now, I'd appreciate if you could look into the PR and tell me what you think.Oliver.O
03/30/2024, 8:52 PMAdam S
03/31/2024, 12:42 PMOliver.O
03/31/2024, 8:35 PMAdam S
04/01/2024, 11:57 AMmain()
and startUnitTests()
functions, which it then uses to initialize and invoke the tests
2. Kotlin Test mimics a JS test framework, and prints TeamCity formatted messages to stdout.
3. KGP updates the Gradle test tasks to intercept standard out.
4. 'instruction' messages (like suite started/finished, test started/finished/failed/skipped) are forwarded to IJ (so IJ can pretty render the tests)
5. 'standard output' messages (like when a test has println("x")
) are forwarded to stdout (so that when running via console or in CI, the test output is visible)
And then the plan is this?
1. Kotest also injects some functions for initializing and invoking tests
2. Kotest will also log TeamCity messages to stdout
3. And then KGP can continue to handle the TeamCity messages,
But the trick is that Kotest has to print out exactly the same TeamCity messages, otherwise the KGP test handler breaks.Adam S
04/01/2024, 11:58 AMAdam S
04/01/2024, 11:58 AMAdam S
04/01/2024, 12:07 PMkarmaTC-197179531948718148
to n1
.Oliver.O
04/01/2024, 12:38 PMmain
.
◦ Set up Wasm test targets to call startUnitTests
.
• kotlin-test
provides the functions suite
and test
, as well as startUnitTests
(Wasm only).
• TheKotlin compiler
◦ finds the annotated unit tests and creates calls to the functions suite
and test
,
◦ injects code into main
and startUnitTests
(Wasm only).
• TeamCity messages and other output can, in principle, share a common stdout channel to communicate with IntelliJ IDEA. For browsers, Karma has to make this happen.
Mocha, as far as I am aware, has its own test output format. The Kotlin test infra seems to translate everything to TeamCity format (but I haven't looked into the specifics here). In the end, IntelliJ IDEA always understands the TeamCity format, which – in principle – also supports nesting.
A changing flowId
means that a new test or suite has started, which is entirely OK if I understand the docs right. The problem in the above output is that a suite is started inside a test, which the format seemingly does not allow (tests must be leaves, only suites can nest). Unfortunately, the only way to get async support with Mocha is inside a test, and that's what you see above as the top-level test is used to enter the async world:
[org.jetbrains.kotlin.gradle.tasks.testing] [KOTLIN] TCSM: ##teamcity[testStarted name='nestable async (via JS/Mocha/transformed)' captureStandardOutput='true' flowId='karmaTC-197179531948718148']
Kotest already has integrated TeamCity reporting. So you could compare how this looks on the JVM.
My impression is that the TeamCity format in general allows much more: For example, with parallel tests, several test start messages may appear consecutively.Adam S
04/01/2024, 12:42 PMAdam S
04/01/2024, 12:42 PMAdam S
04/01/2024, 12:44 PMOliver.O
04/01/2024, 12:45 PMAdam S
04/01/2024, 12:47 PMAdam S
04/01/2024, 12:47 PMAdam S
04/01/2024, 12:48 PMOliver.O
04/01/2024, 12:49 PMAdam S
04/01/2024, 12:51 PMOliver.O
04/01/2024, 12:53 PMAdam S
04/01/2024, 12:53 PMAdam S
04/01/2024, 1:00 PMAdam S
04/01/2024, 1:17 PMgradle :with-kotlin-test:jsBrowserTest --debug
, and filter for ##teamcity
Adam S
04/01/2024, 1:30 PMOliver.O
04/01/2024, 1:31 PMflowId
. It also looks buggy with %s
appearing as flowId
and duration
values.Adam S
04/01/2024, 1:33 PMAdam S
04/01/2024, 1:34 PM%s
is a valid ID, but that's probably not intentional. I think it's a bug in the Kotlin JS lib, which uses varargs and string formatting.Oliver.O
04/01/2024, 1:34 PMAdam S
04/01/2024, 1:37 PMOliver.O
04/01/2024, 1:40 PMAdam S
04/01/2024, 1:44 PMOliver.O
04/01/2024, 1:52 PMstandaloneJsFlatTestFramework
on Node.js, disabling the transformation in FrameworkAdapter.kt#L28. It just outputs TC messages as you can see with gradlew jsNodeDevelopmentRun
. The is no middleman to convert it into anything else. And running it via gradlew jsNodeTest
makes IJ report just fine.Adam S
04/01/2024, 1:53 PMAdam S
04/01/2024, 1:54 PM./gradlew jsNodeDevelopmentRun --debug
and search for <ijLog><event type='onOutput'>
do you see anything?Oliver.O
04/01/2024, 1:54 PMAdam S
04/01/2024, 1:55 PMAdam S
04/01/2024, 1:56 PMjsNodeDevelopmentRun
...
/Users/dev/projects/external/kotlin-js-wasm-testing/src/jsMain/kotlin/KotlinJsTestFramework.js.kt:32
js("describe(description, function () { this.timeout(0); suiteFn(); })")
^
ReferenceError: describe is not defined
Oliver.O
04/01/2024, 1:56 PMAdam S
04/01/2024, 1:57 PMOliver.O
04/01/2024, 1:57 PMHmmm... if you runCould not findand search for./gradlew jsNodeDevelopmentRun --debug
do you see anything?<ijLog><event type='onOutput'>
ijLog
Oliver.O
04/01/2024, 2:00 PM2024-04-01T15:55:09.632+0200 [INFO] [org.gradle.process.internal.DefaultExecHandle] Starting process 'command '/home/oliver/.gradle/nodejs/node-v22.0.0-nightly2024010568c8472ed9-linux-x64/bin/node''. Working directory: /home/oliver/Repositories/experimental/Kotlin/kotlin-js-wasm-testing/build/js/packages/kotlin-js-wasm-testing Command: /home/oliver/.gradle/nodejs/node-v22.0.0-nightly2024010568c8472ed9-linux-x64/bin/node --require /home/oliver/Repositories/experimental/Kotlin/kotlin-js-wasm-testing/build/js/node_modules/source-map-support/register.js /home/oliver/Repositories/experimental/Kotlin/kotlin-js-wasm-testing/build/js/packages/kotlin-js-wasm-testing/kotlin/kotlin-js-wasm-testing.js
2024-04-01T15:55:09.632+0200 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTING
2024-04-01T15:55:09.644+0200 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Waiting until process started: command '/home/oliver/.gradle/nodejs/node-v22.0.0-nightly2024010568c8472ed9-linux-x64/bin/node'.
2024-04-01T15:55:09.671+0200 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: STARTED
2024-04-01T15:55:09.671+0200 [DEBUG] [org.gradle.process.internal.ExecHandleRunner] waiting until streams are handled...
2024-04-01T15:55:09.671+0200 [INFO] [org.gradle.process.internal.DefaultExecHandle] Successfully started process 'command '/home/oliver/.gradle/nodejs/node-v22.0.0-nightly2024010568c8472ed9-linux-x64/bin/node''
2024-04-01T15:55:09.754+0200 [QUIET] [system.out] ##teamcity[testSuiteStarted name='nestable async (via JS/standalone/transformed)' flowId='s1']
2024-04-01T15:55:09.755+0200 [QUIET] [system.out] ##teamcity[testSuiteStarted name='nestable async (via JS/standalone/transformed)' flowId='s2']
2024-04-01T15:55:09.770+0200 [QUIET] [system.out] ##teamcity[testSuiteStarted name='container' flowId='n1']
2024-04-01T15:55:09.771+0200 [QUIET] [system.out] ##teamcity[testStarted name='should pass' captureStandardOutput='true' flowId='n2']
2024-04-01T15:55:10.778+0200 [QUIET] [system.out] ##teamcity[testFinished name='should pass' duration='2' flowId='n2']
2024-04-01T15:55:10.778+0200 [QUIET] [system.out] ##teamcity[testSuiteFinished name='container' flowId='n1']
2024-04-01T15:55:10.778+0200 [QUIET] [system.out] ##teamcity[testStarted name='should fail' captureStandardOutput='true' flowId='n3']
2024-04-01T15:55:12.784+0200 [QUIET] [system.out] ##teamcity[testFailed name='should fail' message='AssertionError: this is a failure' details='(details)' flowId='n3']
2024-04-01T15:55:12.784+0200 [QUIET] [system.out] ##teamcity[testFinished name='should fail' duration='2' flowId='n3']
2024-04-01T15:55:12.785+0200 [QUIET] [system.out] ##teamcity[testSuiteFinished name='nestable async (via JS/standalone/transformed)' flowId='s2']
2024-04-01T15:55:12.785+0200 [QUIET] [system.out] ##teamcity[testSuiteFinished name='nestable async (via JS/standalone/transformed)' flowId='s1']
2024-04-01T15:55:12.796+0200 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Changing state to: SUCCEEDED
2024-04-01T15:55:12.796+0200 [DEBUG] [org.gradle.process.internal.DefaultExecHandle] Process 'command '/home/oliver/.gradle/nodejs/node-v22.0.0-nightly2024010568c8472ed9-linux-x64/bin/node'' finished with exit value 0 (state: SUCCEEDED)
2024-04-01T15:55:12.797+0200 [LIFECYCLE] [org.gradle.internal.operations.DefaultBuildOperationRunner]
2024-04-01T15:55:12.797+0200 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Execute exec for :jsNodeDevelopmentRun'
2024-04-01T15:55:12.797+0200 [DEBUG] [org.gradle.internal.operations.DefaultBuildOperationRunner] Completing Build operation 'Executing task ':jsNodeDevelopmentRun''
2024-04-01T15:55:12.724+0200 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger]
2024-04-01T15:55:12.724+0200 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] > Task :jsNodeDevelopmentRun
Adam S
04/01/2024, 2:02 PMOliver.O
04/01/2024, 2:04 PMAdam S
04/01/2024, 2:05 PMOliver.O
04/01/2024, 2:05 PMOliver.O
04/01/2024, 2:06 PMAdam S
04/01/2024, 2:06 PMgradle :jsNodeTest --debug
then the tests are rendered in IntelliJ, and I see <ijLog><event type='beforeSuite'>
events in the logsOliver.O
04/01/2024, 2:07 PMAdam S
04/01/2024, 2:11 PMOliver.O
04/01/2024, 2:20 PMAdam S
04/01/2024, 2:34 PMprintln("\n##teamcity[${args}]\n")
Adam S
04/01/2024, 2:34 PMOliver.O
04/01/2024, 3:13 PMAdam S
04/01/2024, 6:21 PMAdam S
04/01/2024, 7:20 PMAdam S
04/01/2024, 7:57 PMgradle :v2:jsBrowserTest --rerun-tasks
in debug mode in IJ then it seems to render the results betterOliver.O
04/01/2024, 8:20 PMOliver.O
04/01/2024, 8:20 PMOliver.O
04/01/2024, 8:35 PMAdam S
04/02/2024, 8:21 AMOliver.O
04/02/2024, 9:04 AMAdam S
04/02/2024, 9:31 AMOliver.O
04/02/2024, 9:36 AMAdam S
04/02/2024, 9:58 AMAdam S
04/02/2024, 10:04 AMAdam S
04/02/2024, 10:08 AMOliver.O
04/02/2024, 11:08 AMAdam S
04/02/2024, 11:58 AMAdam S
04/02/2024, 12:02 PMAdam S
04/02/2024, 12:03 PMOliver.O
04/02/2024, 12:12 PMOliver.O
04/02/2024, 12:17 PMAdam S
04/03/2024, 8:50 AMAlso, an intermediate file would block any real-time test progress reporting.Yeah, it's not a great option :)
Adam S
04/03/2024, 8:58 AMOliver.O
04/03/2024, 3:30 PMAdam S
04/04/2024, 9:08 AMAdam S
04/04/2024, 9:26 AM@Test
class and add it to the jsTest Task classpath. This @Test
class would then launch KotestJsLauncher. And then also Kotest would need a custom JUnit engine that KotestJsLauncher can report tests to.Oliver.O
04/04/2024, 9:46 AMOliver.O
04/04/2024, 9:49 AMIf you are running Kotest via Gradle's Junit Platform support, and if you are using a nested spec style, you will notice that only the leaf test name is included in output and test reports. This is a limitation of gradle which is designed around class.method test frameworks.
Oliver.O
04/04/2024, 9:52 AMdisplayFullTestPath
.Adam S
04/04/2024, 12:45 PMCould we use the existing Kotest Junit5 extension for that?Yes, although isn't there an idea for Kotest to remove the JUnit engine?
Oliver.O
04/04/2024, 12:48 PMOliver.O
04/04/2024, 12:49 PMAdam S
04/04/2024, 1:44 PMAdam S
04/04/2024, 3:27 PMAdam S
04/04/2024, 3:33 PM.html
file that loads the compiled .js file.
3. Kotest GP adds the KotestJUnitEngine to the jsTest task classpath.
4. KotestJUnitEngine opens the html page using Playwright (the browsers could be configured via the Kotest GP DSL).
5. The tests run, and print output (e.g. TCSM) to the browser console log, which gets picked up by KotestJUnitEngine and reported as JUnit tests.Oliver.O
04/04/2024, 3:44 PMAdam S
04/06/2024, 12:28 PMOliver.O
04/06/2024, 3:12 PMAdam S
04/06/2024, 4:03 PMAdam S
04/06/2024, 4:04 PMgradle testOutputLoggingIJ
, then IJ will render the tests, but not if you run gradle testOutputLoggingTCSM
Adam S
04/07/2024, 9:09 AMgradle :v3:jsTestKotest
, and some of the tests will be shown in IntelliJ (see screenshot). I'm not sure why they don't all render, I guess something isn't quite right with the IJLog XML, but it's fixable.
How it works:
• Tests are defined in src/jsTest using a custom test DSL (suspended nesting is supported)
• KGP compiles src/jsTest/kotlin to .js
• a custom Gradle Test task uses a custom JUnit TestEngine
• The custom JUnit TestEngine hosts uses Ktor to host the .js files, and an index.html
• Playwright visits the hosted index.html
• The tests run (which is magic to me, I guess they run automatically because they're in a main function?)
• The custom test DSL logs test/suite started/finished events (to the browser console)
• Playwright has a console listener, which parses the event, and converts them to IJLog XMLOliver.O
04/09/2024, 8:16 PMAdam S
04/10/2024, 12:25 PMAdam S
04/10/2024, 12:27 PM