Skip to content

Commit 173dca6

Browse files
authored
Merge pull request #31 from sbt/fallback
Handle multiple extracted versions of same webjar
2 parents 3421d83 + a0fc735 commit 173dca6

File tree

18 files changed

+302
-2
lines changed

18 files changed

+302
-2
lines changed

build.sbt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ developers += Developer(
1515

1616
addSbtJsEngine("1.3.5")
1717

18-
libraryDependencies += "org.webjars" % "mocha" % "1.17.1"
18+
libraryDependencies ++= Seq(
19+
"org.webjars.npm" % "node-require-fallback" % "1.0.0",
20+
"org.webjars" % "mocha" % "1.17.1", // sync with src/main/resources/com/typesafe/sbt/mocha/mocha.js
21+
)
1922

2023
// Customise sbt-dynver's behaviour to make it work with tags which aren't v-prefixed
2124
ThisBuild / dynverVTagPrefix := false

src/main/resources/com/typesafe/sbt/mocha/mocha.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
var Mocha = require("mocha");
1+
var requireIfExists = require('node-require-fallback');
2+
3+
var Mocha = requireIfExists("mocha/1.17.1", "mocha"); // sync with build.sbt
24

35
var mocha = new Mocha();
46

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
enablePlugins(SbtWeb)
2+
3+
Keys.libraryDependencies += "org.specs2" %% "specs2-core" % "4.20.4" % "test"
4+
5+
MochaKeys.requires += "Setup"
6+
scalaVersion := "2.13.12"
7+
8+
Global / Keys.extraLoggers := { scope =>
9+
// Configure extra loggers just for the mocha and test tasks
10+
if (scope.scope.task.fold({ tsk =>
11+
Seq(
12+
MochaKeys.mochaOnly.key,
13+
MochaKeys.mocha.key,
14+
MochaKeys.mochaExecuteTests,
15+
Keys.test.key
16+
).contains(tsk)
17+
}, false, false)) {
18+
Seq(TestLogger)
19+
} else {
20+
Nil
21+
}
22+
}
23+
24+
InputKey[Unit]("logged") := {
25+
val args = Def.spaceDelimited("<level> <msg>").parsed
26+
val level = Level.withName(args.head)
27+
val msg = args.tail.head
28+
29+
val msgs = TestLogger.messages.collect {
30+
case (l, m) if m.trim == msg => l
31+
}
32+
33+
if (msgs.contains(level)) {
34+
// Pass
35+
} else if (msgs.isEmpty) {
36+
throw new RuntimeException("No test logged with content '" + msg + "' messages were:\n" + TestLogger.messages.reverse.mkString("\n"))
37+
} else {
38+
throw new RuntimeException("No test logged with content '" + msg + "' at level " + level + ", but some matched at level(s) " + msgs)
39+
}
40+
}
41+
42+
TaskKey[Unit]("resetLogs") := {
43+
TestLogger.messages = Nil
44+
TestLogger.exceptions = Nil
45+
MockListener.gotAnEvent = false
46+
}
47+
48+
TaskKey[Unit]("noErrors") := {
49+
val errors = TestLogger.messages.filter {
50+
case (l, m) => l == Level.Error || l == Level.Warn
51+
}
52+
if (!errors.isEmpty) {
53+
throw new RuntimeException("Expected no errors, but got:\n" + errors.reverse.mkString("\n"))
54+
}
55+
if (!TestLogger.exceptions.isEmpty) {
56+
throw new RuntimeException("Expected no exceptions, but got:\n" + TestLogger.exceptions.reverse.mkString("\n"))
57+
}
58+
}
59+
60+
TaskKey[Unit]("mockListenerInvoked") := {
61+
if (!MockListener.gotAnEvent) {
62+
throw new RuntimeException("Mock test report listener was not invoked")
63+
}
64+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import sbt._
2+
3+
object MockListener extends TestReportListener {
4+
5+
@volatile var gotAnEvent = false
6+
7+
def testEvent(event: TestEvent) = gotAnEvent = true
8+
9+
def startGroup(name: String) = ()
10+
11+
def endGroup(name: String, t: Throwable) = ()
12+
13+
def endGroup(name: String, result: TestResult) = ()
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
addSbtPlugin("com.github.sbt" % "sbt-mocha" % sys.props("project.version"))
2+
3+
// When the same webjar (= same name) from different groupIds (org.webjars[.npm|bower]?)
4+
// are are on the classpath, those webjars get extracted into subfolders which are named by the version of the webjar.
5+
// However, if there is just one type (npm, bower, classic) of a webjar on the classpath, NO subfolders gets created.
6+
// Now, because in a project we don't know which other webjars get pulled in from other dependencies, it can happen
7+
// that subfolders get created, or may not. However, require(...) can't know that and therefore has to look in both places.
8+
// To test that the lookup is correct, we force this scripted test to create subfolders by pulling in the same webjar
9+
// but from different groupIds (the other scripted test does not do that and therefore no subfolders get created there)
10+
// btw: dependency eviction within the same type of webjar still works correctly, so e.g. 0.2 wins over 0.1 within the same type of webjar
11+
// and no subfolder will be forced for that case but the newest version will be choosen. Like normal dependency resolution.
12+
libraryDependencies ++= Seq(
13+
// Pulling in the classic and the npm webjar to subfolders for this webjar will be created
14+
("org.webjars.npm" % "mocha" % "3.0.0-2")
15+
.exclude("org.webjars.npm", "debug")
16+
.exclude("org.webjars.npm", "diff"),
17+
"org.webjars" % "mocha" % "1.17.1",
18+
)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import org.apache.logging.log4j.core._
2+
import org.apache.logging.log4j.core.appender.AbstractAppender
3+
import org.apache.logging.log4j.Level
4+
import org.apache.logging.log4j.core.layout.PatternLayout
5+
import org.apache.logging.log4j.message.ObjectMessage
6+
import sbt.internal.util.{ObjectEvent, StringEvent}
7+
import sbt.{Level => SbtLevel}
8+
9+
object TestLogger extends AbstractAppender("TestLogger", null, PatternLayout.createDefaultLayout(), true) {
10+
11+
override def append(event: LogEvent): Unit = {
12+
val level = event.getLevel match {
13+
case Level.ERROR => SbtLevel.Error
14+
case Level.WARN => SbtLevel.Warn
15+
case Level.INFO => SbtLevel.Info
16+
}
17+
val msg = event.getMessage match {
18+
case o: ObjectMessage =>
19+
o.getParameter match {
20+
case e: ObjectEvent[_] => e.message.toString
21+
case s: StringEvent => s.message
22+
case _ => event.getMessage.getFormattedMessage
23+
}
24+
case _ => event.getMessage.getFormattedMessage
25+
}
26+
messages = (level, stripAnsiCodes(msg)) :: messages
27+
28+
if (event.getMessage.getThrowable != null) {
29+
exceptions = event.getMessage.getThrowable :: exceptions
30+
}
31+
}
32+
33+
override def error(msg: String): Unit = messages = (SbtLevel.Error, msg) :: messages
34+
override def error(msg: String, t: Throwable): Unit = {
35+
exceptions = t :: exceptions
36+
error(msg)
37+
}
38+
override def error(msg: String, event: LogEvent, t: Throwable): Unit = {
39+
exceptions = t :: exceptions
40+
append(event)
41+
}
42+
43+
def stripAnsiCodes(s: String): String = {
44+
s.replaceAll("\u001b[^m]*m", "")
45+
}
46+
47+
var messages = List.empty[(SbtLevel.Value, String)]
48+
var exceptions = List.empty[Throwable]
49+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports.foo = function() {
2+
console.log("I am imported");
3+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
describe("a test asset", function() {
2+
it("should be happy", function() {
3+
console.log("I am happy");
4+
});
5+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports.foo = function() {
2+
console.log("I am imported");
3+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
describe("a spec", function() {
2+
it("should be able to fail with a generic error", function() {
3+
throw new Error("not happy");
4+
});
5+
});

0 commit comments

Comments
 (0)