3

I have a simple project in Kotlin JavaScript using React. I've added unit tests, but when I run them, they seem to call main method of production code, and fail on initialization, when trying to reach unexsistent DOM structure. Despite the fact that the tested class does not reference React or DOM in any way.

The error looks the same when run from Intelij IDEA or by gradlew build (I've replaced the full path to my project with /APP/ for clarity):

Testing started at 17:51 ...
> Task :cleanBrowserTest
> Task :packageJson UP-TO-DATE
> Task :testPackageJson UP-TO-DATE
> Task :kotlinNodeJsSetup SKIPPED
> Task :kotlinNpmInstall
> Task :compileKotlinJs
> Task :processResources
> Task :mainClasses
> Task :compileTestKotlinJs
> Task :testProcessResources NO-SOURCE
> Task :testClasses

> Task :browserTest

(...)

Error: Target container is not a DOM element.

    at render (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:25359:13)

    at render_0 (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:29868:5)

    at main (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146311:5)

    at Object.<anonymous> (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146315:3)

    at http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146289:37

    at Object.../example/kotlin/example.js (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146292:2)

    at __webpack_require__ (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:20:30)

    at http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146346:134

    at Object../kotlin/example-test.js (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:146351:2)

    at __webpack_require__ (http://localhost:9876/absoluteD:/APP/build/js/packages/example-test/adapter-browser.js?92ccabfdcfa982960828b65b2f4e2683080859b4:20:30)

HeadlessChrome 81.0.4044 (Windows 10.0.0) ERROR

  Uncaught Error: Target container is not a DOM element.

  at d:/APP/build/js/node_modules/react-dom/cjs/react-dom.development.js:24828:1 <- D:/APP/build/js/packages/example-test/adapter-browser.js:25359:7

(...)


> Task :browserTest FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':browserTest'.
> command 'C:\Users\Arsen\.gradle\nodejs\node-v12.14.0-win-x64\node.exe' exited with errors (exit code: 1)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 10s
8 actionable tasks: 6 executed, 2 up-to-date

Minimal example:

./build.gradle.kts

plugins {
    id("org.jetbrains.kotlin.js") version "1.3.70-eap-184"
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
    maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
    mavenCentral()
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib-js"))

    implementation("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70")
    implementation("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70")
    implementation(npm("react", "16.13.1"))
    implementation(npm("react-dom", "16.13.1"))
    testImplementation(kotlin("test-js"))
}

kotlin.target.browser {
}

./settings.gradle.kts

pluginManagement {
    repositories {
        maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }

        mavenCentral()

        maven { setUrl("https://plugins.gradle.org/m2/") }
    }
}
rootProject.name = "example"

./src/main/resources/index.html

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <div id="root"></div>
        <script src="example.js"></script>
    </body>
</html>

./src/main/kotlin/Main.kt

import react.dom.*
import kotlin.browser.document

fun main(args: Array<String>) {
    render(document.getElementById("root")) {

    }
}

./src/main/kotlin/DummyClass.kt

class DummyClass {
    fun foo(): String {
        return "foo"
    }
}

./src/test/kotlin/ExampleTest.kt

import kotlin.test.*

class ExampleTest {
    @Test
    fun foo() {
        assertEquals(DummyClass().foo(), "foo")
    }
}

The error does not occur if I don't reference production code at all (remove DummyClass().foo() from the test), or when main method does not call render(document.getElementById("root")).

PS: If it matters I run the code on Windows

Arsen
  • 542
  • 1
  • 9
  • 26

2 Answers2

2

This is not the right answer - but something that will unblock you immediately.

This answer pointed out that

the test environment doesn't supply the DOM with an app id.

So too is the case with our test framework - it is not supplying an element with id 'root'. This led me to believe that the main/resources/index.html is not being used by the test framework. The exception trace confirms that - notice how it starts from __webpack_require__

The ideal solution would be to either have karma or webpack supply our code with the correct index.html; but I don't know how to accomplish that.

In the meantime, you can

  • Remove <div id="root"></div> as part of index.html
  • Have your kotlin code generate it, as below:

    ./src/main/kotlin/Main.kt

fun main(args: Array<String>) {
    document.body!!.insertAdjacentHTML("afterbegin", "<div id='root'></div>" )
    render(document.getElementById("root")) {

    }
}

Hope that helps

Yogesh Nachnani
  • 281
  • 2
  • 3
  • 1
    Thanks. This is a very good workaround. :) I'm gonna wait a little for a non-workaround solution but if it doesn't happen before the bounty expires, you're getting it. ;) – Arsen May 28 '20 at 21:56
  • Thanks for the workaround. However, did sommeone find the solution to this issue ? Because (for me at least) no files from resources can be used and, for instance, it prevents me to make tests based on translation files. – Paul Souteyrat Dec 08 '20 at 12:55
0

I had the same issue and I fixed it. Thanks to the workaround of @Yogesh Nachnani I understood what was missing. In fact all resources files are not included. So, first I created a new workaround using fs-extra (copying files from resources to the local directory) and then as I was exploring Karma I figured out that the cleanest solution was to use proxies to redirect requests to resources. Therefore I used the following code into karma.config.js:

const path = require('path');
const resourcesSourcePath = path.resolve(__dirname, '../../../../build/processedResources/js/main');
const setupFile = path.resolve(__dirname, '../../../../src/test/setup.js');
config.files.unshift(setupFile);
config.proxies = {
    "/strings/": "absolute" + resourcesSourcePath + "/strings/",
    "/css/": "absolute" + resourcesSourcePath + "/css/",
    "/images/": "absolute" + resourcesSourcePath + "/images/"
}

This way, if you have a lot of resources you won't have to wait for the copy of the content. However, I didn't find a way to launch karma on index.html, but as I can now access all resources, I just used the workaround proposed by @Yogesh Nachnani because the HTML file usually doesn't have much influence into tests (at least for me). Therefore I added a setup.js file which only contains:

document.body.insertAdjacentHTML('afterbegin', "<div id='root'></div>");

It prevents the rendering target error when you are using render(document.getElementById("root")){} for React.

Paul Souteyrat
  • 320
  • 3
  • 13