0
# JavaScript Platform Support
1
2
Lightweight JavaScript test runner with master/slave architecture for distributed test execution and inter-process communication through serialized summaries. Optimized for Scala.js compilation and browser/Node.js environments.
3
4
## Capabilities
5
6
### ZTestRunnerJS Base Class
7
8
Abstract base class for JavaScript test runners that provides common functionality for both master and slave execution modes.
9
10
```scala { .api }
11
/**
12
* Base JavaScript test runner with master/slave architecture support
13
* Provides lightweight execution optimized for JavaScript environments
14
*/
15
sealed abstract class ZTestRunnerJS(
16
val args: Array[String],
17
val remoteArgs: Array[String],
18
testClassLoader: ClassLoader,
19
runnerType: String
20
) extends Runner {
21
/** Abstract method for summary handling strategy */
22
def sendSummary: SendSummary
23
24
/** Mutable buffer for collecting test summaries */
25
val summaries: mutable.Buffer[Summary]
26
27
/**
28
* Create test tasks from discovered test definitions
29
* @param defs Array of SBT task definitions
30
* @return Array of executable JavaScript test tasks
31
*/
32
def tasks(defs: Array[TaskDef]): Array[Task]
33
34
/**
35
* Complete test execution and return formatted results
36
* @return Formatted test results or "No tests executed" message
37
*/
38
def done(): String
39
40
/**
41
* Receive serialized summaries from distributed execution
42
* @param summary Serialized summary string from SummaryProtocol
43
* @return Optional response message (always None for JS)
44
*/
45
override def receiveMessage(summary: String): Option[String]
46
47
/**
48
* Serialize task for distributed execution
49
* @param task Task to serialize
50
* @param serializer SBT-provided serialization function
51
* @return Serialized task string
52
*/
53
override def serializeTask(task: Task, serializer: TaskDef => String): String
54
55
/**
56
* Deserialize task from distributed execution
57
* @param task Serialized task string
58
* @param deserializer SBT-provided deserialization function
59
* @return Reconstructed test task
60
*/
61
override def deserializeTask(task: String, deserializer: String => TaskDef): Task
62
}
63
```
64
65
### ZMasterTestRunnerJS
66
67
Master runner for single-process JavaScript execution, typically used when running tests in a single JavaScript context.
68
69
```scala { .api }
70
/**
71
* Master JavaScript test runner for single-process execution
72
* Collects summaries directly without inter-process communication
73
*/
74
final class ZMasterTestRunnerJS(
75
args: Array[String],
76
remoteArgs: Array[String],
77
testClassLoader: ClassLoader
78
) extends ZTestRunnerJS(args, remoteArgs, testClassLoader, "master") {
79
/**
80
* Summary sender that collects results locally
81
* Used when all tests run in the same JavaScript context
82
*/
83
override val sendSummary: SendSummary
84
}
85
```
86
87
### ZSlaveTestRunnerJS
88
89
Slave runner for distributed JavaScript execution, used when tests are executed across multiple JavaScript contexts or processes.
90
91
```scala { .api }
92
/**
93
* Slave JavaScript test runner for distributed execution
94
* Sends summaries to master process via serialization protocol
95
*/
96
final class ZSlaveTestRunnerJS(
97
args: Array[String],
98
remoteArgs: Array[String],
99
testClassLoader: ClassLoader,
100
val sendSummary: SendSummary
101
) extends ZTestRunnerJS(args, remoteArgs, testClassLoader, "slave")
102
```
103
104
### ZTestTask (JavaScript)
105
106
JavaScript-specific test task implementation with asynchronous execution support.
107
108
```scala { .api }
109
/**
110
* JavaScript-specific test task with async execution
111
* Optimized for JavaScript event loop and browser/Node.js environments
112
*/
113
sealed class ZTestTask(
114
taskDef: TaskDef,
115
testClassLoader: ClassLoader,
116
runnerType: String,
117
sendSummary: SendSummary,
118
testArgs: TestArgs,
119
spec: ZIOSpecAbstract
120
) extends BaseTestTask(
121
taskDef, testClassLoader, sendSummary, testArgs, spec,
122
Runtime.default, zio.Console.ConsoleLive
123
) {
124
/**
125
* Execute test with async continuation support
126
* Required for JavaScript's asynchronous execution model
127
* @param eventHandler SBT event handler for reporting
128
* @param loggers Array of SBT loggers
129
* @param continuation Continuation function called when execution completes
130
*/
131
def execute(eventHandler: EventHandler, loggers: Array[Logger], continuation: Array[Task] => Unit): Unit
132
}
133
```
134
135
### ZTestTask Companion Object (JavaScript)
136
137
Factory methods for creating JavaScript test tasks.
138
139
```scala { .api }
140
object ZTestTask {
141
/**
142
* Create a JavaScript test task instance
143
* @param taskDef SBT task definition
144
* @param testClassLoader ClassLoader for loading test classes
145
* @param runnerType Runner type identifier ("master" or "slave")
146
* @param sendSummary Summary collection effect
147
* @param args Parsed test arguments
148
* @return Configured JavaScript ZTestTask instance
149
*/
150
def apply(
151
taskDef: TaskDef,
152
testClassLoader: ClassLoader,
153
runnerType: String,
154
sendSummary: SendSummary,
155
args: TestArgs
156
): ZTestTask
157
}
158
```
159
160
### SummaryProtocol
161
162
Serialization protocol for inter-process communication in distributed JavaScript execution.
163
164
```scala { .api }
165
/**
166
* Serialization protocol for test summaries in JavaScript environments
167
* Enables communication between master and slave processes
168
*/
169
object SummaryProtocol {
170
/**
171
* Serialize test summary to string for transmission
172
* @param summary Test execution summary to serialize
173
* @return Tab-separated string representation
174
*/
175
def serialize(summary: Summary): String
176
177
/**
178
* Deserialize string back to test summary
179
* @param s Serialized summary string
180
* @return Optional Summary if deserialization succeeds
181
*/
182
def deserialize(s: String): Option[Summary]
183
184
/**
185
* Escape tab characters in tokens for serialization
186
* @param token String token to escape
187
* @return Escaped string safe for tab-separated format
188
*/
189
def escape(token: String): String
190
191
/**
192
* Unescape tab characters from serialized tokens
193
* @param token Escaped string token
194
* @return Unescaped original string
195
*/
196
def unescape(token: String): String
197
}
198
```
199
200
### Asynchronous Execution
201
202
JavaScript test tasks use asynchronous execution with continuations:
203
204
```scala { .api }
205
def execute(eventHandler: EventHandler, loggers: Array[Logger], continuation: Array[Task] => Unit): Unit = {
206
// Fork execution on default runtime
207
val fiber = Runtime.default.unsafe.fork {
208
val logic = ZIO.consoleWith { console =>
209
(for {
210
summary <- spec.runSpecAsApp(FilteredSpec(spec.spec, args), args, console)
211
_ <- sendSummary.provide(ZLayer.succeed(summary))
212
_ <- ZIO.when(summary.status == Summary.Failure) {
213
ZIO.attempt(eventHandler.handle(ZTestEvent(/* failure event */)))
214
}
215
} yield ()).provideLayer(sharedFilledTestLayer +!+ (Scope.default >>> spec.bootstrap))
216
}
217
logic
218
}
219
220
// Add observer for completion handling
221
fiber.unsafe.addObserver { exit =>
222
exit match {
223
case Exit.Failure(cause) => Console.err.println(s"$runnerType failed. $cause")
224
case _ =>
225
}
226
continuation(Array())
227
}
228
}
229
```
230
231
### Master/Slave Architecture
232
233
**Master Process Flow:**
234
1. Receives test definitions from SBT
235
2. Creates test tasks for local execution
236
3. Collects summaries directly in local buffer
237
4. Aggregates results and reports to SBT
238
239
**Slave Process Flow:**
240
1. Receives serialized tasks from master
241
2. Executes tests in isolated JavaScript context
242
3. Serializes summaries using `SummaryProtocol`
243
4. Sends results back to master via `send` function
244
245
### Platform Optimizations
246
247
JavaScript platform provides several optimizations:
248
249
- **Lightweight Runtime**: Uses default ZIO runtime without heavy JVM features
250
- **Async Execution**: Leverages JavaScript event loop for non-blocking execution
251
- **Serialization Protocol**: Efficient string-based communication for distributed execution
252
- **Browser Compatibility**: Works in both Node.js and browser environments
253
- **Small Bundle Size**: Minimal overhead for Scala.js compilation
254
255
### Usage Example
256
257
```scala
258
// Framework automatically creates appropriate runner type
259
val framework = new ZTestFramework()
260
261
// For single-process execution
262
val masterRunner = framework.runner(args, remoteArgs, classLoader)
263
264
// For distributed execution
265
val slaveRunner = framework.slaveRunner(args, remoteArgs, classLoader, summaryCallback)
266
267
// SBT handles task serialization/deserialization automatically
268
val serializedTask = masterRunner.serializeTask(task, serializer)
269
val deserializedTask = slaveRunner.deserializeTask(serializedTask, deserializer)
270
```