wip
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.kt
index 6e3002a..7ef3cfa 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/test/JSTestGenerator.kt
@@ -42,25 +42,39 @@
         return JsNameRef(name, JsNameRef("Kotlin"))
     }
 
-    fun generateTestCalls(moduleDescriptor: ModuleDescriptor) = generateTestCalls(moduleDescriptor, FqName.ROOT)
+    fun generateTestCalls(moduleDescriptor: ModuleDescriptor) {
+        val rootFunction = JsFunction(context.scope(), JsBlock(), "root suite function")
 
-    private fun generateTestCalls(moduleDescriptor: ModuleDescriptor, packageName: FqName) {
+        generateTestCalls(moduleDescriptor, FqName.ROOT, rootFunction)
+
+        if (!rootFunction.body.isEmpty) {
+            context.addTopLevelStatement(JsInvocation(suiteRef, JsStringLiteral(""), rootFunction).makeStmt())
+        }
+    }
+
+    private fun generateTestCalls(moduleDescriptor: ModuleDescriptor, packageName: FqName, parentFun: JsFunction) {
         for (packageDescriptor in moduleDescriptor.getPackage(packageName).fragments) {
             if (DescriptorUtils.getContainingModule(packageDescriptor) !== moduleDescriptor) continue
 
             packageDescriptor.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS, MemberScope.ALL_NAME_FILTER).forEach {
                 if (it is ClassDescriptor) {
-                    generateTestFunctions(it)
+                    generateTestFunctions(it, parentFun)
                 }
             }
         }
 
         for (subpackageName in moduleDescriptor.getSubPackagesOf(packageName, MemberScope.ALL_NAME_FILTER)) {
-            generateTestCalls(moduleDescriptor, subpackageName)
+            val subPackageFunction = JsFunction(context.scope(), JsBlock(), "${subpackageName.asString()} package suite function")
+
+            generateTestCalls(moduleDescriptor, subpackageName, subPackageFunction)
+
+            if (!subPackageFunction.body.isEmpty) {
+                parentFun.body.statements += JsInvocation(suiteRef, JsStringLiteral(subpackageName.shortName().asString()), subPackageFunction).makeStmt()
+            }
         }
     }
 
-    private fun generateTestFunctions(classDescriptor: ClassDescriptor) {
+    private fun generateTestFunctions(classDescriptor: ClassDescriptor, parentFun: JsFunction) {
         if (classDescriptor.modality === Modality.ABSTRACT) return
 
         val suiteFunction = JsFunction(context.scope(), JsBlock(), "suite function")
@@ -74,7 +88,7 @@
         if (!suiteFunction.body.isEmpty) {
             val suiteName = JsStringLiteral(classDescriptor.name.toString())
 
-            context.addTopLevelStatement(JsInvocation(classDescriptor.ref, suiteName, suiteFunction).makeStmt())
+            parentFun.body.statements += JsInvocation(classDescriptor.ref, suiteName, suiteFunction).makeStmt()
         }
     }
 
@@ -124,7 +138,7 @@
      * }
      */
     private val FunctionDescriptor.isTest
-            get() = annotationFinder("Test", "kotlin.test", "org.junit")
+            get() = annotationFinder("Test", "kotlin.test", "org.junit") // Support both ways for now.
 
     private val DeclarationDescriptor.isIgnored
             get() = annotationFinder("Ignore", "kotlin.test")
@@ -132,10 +146,7 @@
     private val DeclarationDescriptor.isFocused
             get() = annotationFinder("Only", "kotlin.test")
 
-    private fun DeclarationDescriptor.annotationFinder(shortName: String, vararg packages: String) = this.annotations.any { annotation: AnnotationDescriptor ->
-        annotation.type.toString() == shortName && packages.any { packageName ->
-            val descriptor = annotation.type.constructor.declarationDescriptor
-            descriptor != null && FqNameUnsafe("$packageName.$shortName") == DescriptorUtils.getFqName(descriptor)
-        }
+    private fun DeclarationDescriptor.annotationFinder(shortName: String, vararg packages: String) = packages.any { packageName ->
+        annotations.hasAnnotation(FqName("$packageName.$shortName"))
     }
 }
diff --git a/libraries/kotlin.test/js/it/build.gradle b/libraries/kotlin.test/js/it/build.gradle
index ab9a954..406bd50 100644
--- a/libraries/kotlin.test/js/it/build.gradle
+++ b/libraries/kotlin.test/js/it/build.gradle
@@ -49,27 +49,27 @@
     download = true
 }
 
-task testJest( type: NpmTask, dependsOn: [compileTestKotlin2Js, compileTestKotlin2Js, 'npmInstall']) {
+task testJest(type: NpmTask, dependsOn: [compileTestKotlin2Js, npmInstall]) {
     args = ['run', 'test-jest']
 }
 check.dependsOn testJest
 
-task testJasmine( type: NpmTask, dependsOn: [compileTestKotlin2Js, compileTestKotlin2Js, 'npmInstall']) {
+task testJasmine(type: NpmTask, dependsOn: [compileTestKotlin2Js, npmInstall]) {
     args = ['run', 'test-jasmine']
 }
 check.dependsOn testJasmine
 
-task testMocha( type: NpmTask, dependsOn: [compileTestKotlin2Js, compileTestKotlin2Js, 'npmInstall']) {
+task testMocha(type: NpmTask, dependsOn: [compileTestKotlin2Js, npmInstall]) {
     args = ['run', 'test-mocha']
 }
 check.dependsOn testMocha
 
-task testQunit( type: NpmTask, dependsOn: [compileTestKotlin2Js, compileTestKotlin2Js, 'npmInstall']) {
+task testQunit(type: NpmTask, dependsOn: [compileTestKotlin2Js, npmInstall]) {
     args = ['run', 'test-qunit']
 }
 check.dependsOn testQunit
 
-task testTape( type: NpmTask, dependsOn: [compileTestKotlin2Js, compileTestKotlin2Js, 'npmInstall']) {
+task testTape(type: NpmTask, dependsOn: [compileTestKotlin2Js, npmInstall]) {
     args = ['run', 'test-tape']
 }
 check.dependsOn testTape
diff --git a/libraries/kotlin.test/js/it/js/expected-outcomes.js b/libraries/kotlin.test/js/it/js/expected-outcomes.js
index fbb7ede..3c6f31e 100644
--- a/libraries/kotlin.test/js/it/js/expected-outcomes.js
+++ b/libraries/kotlin.test/js/it/js/expected-outcomes.js
@@ -1,11 +1,22 @@
-module.exports.full = {
-    'SimpleTest testFoo': 'fail',
-    'SimpleTest testBar': 'pass',
-    'SimpleTest testFooWrong': 'pending',
-    'TestTest emptyTest': 'pending'
+var full = {
+    ' SimpleTest testFoo': 'fail',
+    ' SimpleTest testBar': 'pass',
+    ' SimpleTest testFooWrong': 'pending',
+    ' TestTest emptyTest': 'pending',
+    ' org OrgTest test': 'pass',
+    ' org some SomeTest test': 'pass',
+    ' org some name NameTest test': 'pass',
+    ' org other name NameTest test': 'pass'
 };
 
-module.exports.noPending = {
-    'SimpleTest testFoo': 'fail',
-    'SimpleTest testBar': 'pass'
-};
\ No newline at end of file
+// Filter out pending tests for Tape
+var noPending = {};
+for (var name in full) {
+    var result = full[name];
+    if (result !== 'pending') {
+        noPending[name] = result;
+    }
+}
+
+module.exports.full = full;
+module.exports.noPending = noPending;
\ No newline at end of file
diff --git a/libraries/kotlin.test/js/it/package.json b/libraries/kotlin.test/js/it/package.json
index 59e2ed6..f98b126 100644
--- a/libraries/kotlin.test/js/it/package.json
+++ b/libraries/kotlin.test/js/it/package.json
@@ -7,7 +7,7 @@
     "test-jasmine": "jasmine js/paths.js js/jasmine-reporter.js build/classes/test/it_test.js",
     "test-jest": "jest",
     "test-mocha": "mocha -r js/paths.js --reporter js/mocha-reporter.js build/classes/test/it_test.js",
-    "test-qunit": "qunit -c js/paths.js -d js/qunit-reporter.js -t build/classes/test/it_test.js",
+    "test-qunit": "qunit -d js/paths.js -c js/qunit-reporter.js -t build/classes/test/it_test.js",
     "test-tape": "tape js/paths.js js/tape-reporter.js js/tape-plugin.js build/classes/test/it_test.js"
   },
   "author": "",
diff --git a/libraries/kotlin.test/js/it/test/org/OrgTest.kt b/libraries/kotlin.test/js/it/test/org/OrgTest.kt
new file mode 100644
index 0000000..84c114f
--- /dev/null
+++ b/libraries/kotlin.test/js/it/test/org/OrgTest.kt
@@ -0,0 +1,8 @@
+package org
+
+import kotlin.test.*
+
+class OrgTest {
+    @Test fun test() {
+    }
+}
diff --git a/libraries/kotlin.test/js/it/test/org/other/name/NameTest.kt b/libraries/kotlin.test/js/it/test/org/other/name/NameTest.kt
new file mode 100644
index 0000000..5f33526
--- /dev/null
+++ b/libraries/kotlin.test/js/it/test/org/other/name/NameTest.kt
@@ -0,0 +1,8 @@
+package org.other.name
+
+import kotlin.test.*
+
+class NameTest {
+    @Test fun test() {
+    }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/js/it/test/org/some/SomeTest.kt b/libraries/kotlin.test/js/it/test/org/some/SomeTest.kt
new file mode 100644
index 0000000..5487e2b
--- /dev/null
+++ b/libraries/kotlin.test/js/it/test/org/some/SomeTest.kt
@@ -0,0 +1,8 @@
+package org.some
+
+import kotlin.test.*
+
+class SomeTest {
+    @Test fun test() {
+    }
+}
diff --git a/libraries/kotlin.test/js/it/test/org/some/name/NameTest.kt b/libraries/kotlin.test/js/it/test/org/some/name/NameTest.kt
new file mode 100644
index 0000000..de4a162
--- /dev/null
+++ b/libraries/kotlin.test/js/it/test/org/some/name/NameTest.kt
@@ -0,0 +1,8 @@
+package org.some.name
+
+import kotlin.test.*
+
+class NameTest {
+    @Test fun test() {
+    }
+}
diff --git a/libraries/kotlin.test/js/src/main/kotlin/FrameworkAdapter.kt b/libraries/kotlin.test/js/src/main/kotlin/FrameworkAdapter.kt
index 83d17d50..4450c6d 100644
--- a/libraries/kotlin.test/js/src/main/kotlin/FrameworkAdapter.kt
+++ b/libraries/kotlin.test/js/src/main/kotlin/FrameworkAdapter.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import kotlin.test.assertHook
-
 @JsName("suite")
 fun suite(name: String, suiteFn: () -> Unit) {
     currentAdapter.suite(name, suiteFn)
@@ -53,12 +51,10 @@
     if (js("typeof adapter === 'string'")) {
         if (adapter in DefaultAdapters.NAME_TO_ADAPTER) {
             setAdapter(DefaultAdapters.NAME_TO_ADAPTER[adapter])
-        }
-        else {
+        } else {
             throw IllegalArgumentException("Unsupported test framework adapter: '$adapter'")
         }
-    }
-    else {
+    } else {
         currentAdapter = adapter
     }
 }
@@ -86,36 +82,58 @@
 enum class DefaultAdapters : FrameworkAdapter {
 
     QUNIT {
-        override fun suite(name: String, suiteFn: () -> Unit) {
-            QUnit.module(name, suiteFn)
+        var scopeStack = mutableListOf<String>()
+
+        var shouldSkip = false
+
+        val testName: String
+            get() = scopeStack.joinToString(separator = " ")
+
+        private fun withName(name: String, block: () -> Unit) {
+            scopeStack.add(name)
+            block()
+            scopeStack.removeAt(scopeStack.size - 1)
         }
 
+        override fun suite(name: String, suiteFn: () -> Unit) = withName(name, suiteFn)
+
         override fun xsuite(name: String, suiteFn: () -> Unit) {
-            // QUnit doesn't support ignoring modules, so just don't execute it
+            if (shouldSkip) {
+                suiteFn()
+            }
+            else {
+                shouldSkip = true
+                suiteFn()
+                shouldSkip = false
+            }
         }
 
-        override fun fsuite(name: String, suiteFn: () -> Unit) {
-            // QUnit doesn't support focusing on a single module
-            QUnit.module(name, suiteFn)
-        }
+        // QUnit doesn't support focusing on a single module
+        override fun fsuite(name: String, suiteFn: () -> Unit) = withName(name, suiteFn)
 
         override fun test(name: String, testFn: () -> Unit) {
-            QUnit.test(name, wrapTest(testFn))
+            if (shouldSkip) {
+                xtest(name, testFn)
+            }
+            else {
+                withName(name) {
+                    QUnit.test(testName, wrapTest(testFn))
+                }
+            }
         }
 
-        override fun xtest(name: String, testFn: () -> Unit) {
-            QUnit.skip(name, wrapTest(testFn))
+        override fun xtest(name: String, testFn: () -> Unit) = withName(name) {
+            QUnit.skip(testName, wrapTest(testFn))
         }
 
-        override fun ftest(name: String, testFn: () -> Unit) {
-            QUnit.only(name, wrapTest(testFn))
+        override fun ftest(name: String, testFn: () -> Unit) = withName(name) {
+            QUnit.only(testName, wrapTest(testFn))
         }
 
         private fun wrapTest(testFn: () -> Unit): (dynamic) -> Unit = { assert ->
             if (js("typeof assert !== 'function'")) {
                 assertHook = { result, _, _, msgFn -> assert.ok(result, msgFn()) }
-            }
-            else {
+            } else {
                 assertHook = { result, expected, actual, msgFn ->
                     val data = js("{}")
                     data.result = result
@@ -208,13 +226,14 @@
     };
 
     companion object {
-        val AUTO_DETECTED = when {
-            js("typeof QUnit !== 'undefined'") -> QUNIT
-            js("typeof describe === 'function' && typeof it === 'function'") -> {
-                if (js("typeof xit === 'function'")) JASMINE else MOCHA
+        val AUTO_DETECTED: FrameworkAdapter
+            get() = when {
+                js("typeof QUnit !== 'undefined'") -> QUNIT
+                js("typeof describe === 'function' && typeof it === 'function'") -> {
+                    if (js("typeof xit === 'function'")) JASMINE else MOCHA
+                }
+                else -> BARE
             }
-            else -> BARE
-        }
 
         val NAME_TO_ADAPTER = mapOf(
                 "qunit" to QUNIT,
@@ -224,6 +243,12 @@
     }
 }
 
+internal var assertHook: (result: Boolean, expected: Any?, actual: Any?, () -> String?) -> Unit = { result, _, _, msgFun ->
+    if (js("typeof QUnit !== 'undefined'")) {
+        ok(result, msgFun())
+    }
+}
+
 /**
  * The [QUnit](http://qunitjs.com/) API
  */
@@ -241,6 +266,7 @@
  * Jasmine/Mocha API
  */
 external fun describe(name: String, fn: () -> Unit)
+
 external fun xdescribe(name: String, fn: () -> Unit)
 external fun fdescribe(name: String, fn: () -> Unit)
 
diff --git a/libraries/kotlin.test/js/src/main/kotlin/JsImpl.kt b/libraries/kotlin.test/js/src/main/kotlin/JsImpl.kt
index bc44788..47856b5 100644
--- a/libraries/kotlin.test/js/src/main/kotlin/JsImpl.kt
+++ b/libraries/kotlin.test/js/src/main/kotlin/JsImpl.kt
@@ -16,6 +16,7 @@
 
 package kotlin.test
 
+import assertHook
 import kotlin.reflect.KClass
 
 /**
@@ -46,8 +47,6 @@
 
 private val qunitAsserter = QUnitAsserter()
 
-internal var assertHook: (result: Boolean, expected: Any?, actual: Any?, () -> String?) -> Unit = { _, _, _, _ -> }
-
 // TODO: make object in 1.2
 class QUnitAsserter : Asserter {
     private var e: Any? = undefined