1import * as NodeStdio from "@effect/platform-node-shared/NodeStdio"
2import { assert, describe, it } from "@effect/vitest"
3import * as Effect from "effect/Effect"
4import * as Stdio from "effect/Stdio"
5
6const streams = [process.stdin, process.stdout] as const
7
8const setIsTTY = (stdin: boolean, stdout: boolean) =>
9 Effect.sync(() => {
10 Object.defineProperty(streams[0], "isTTY", { configurable: true, value: stdin })
11 Object.defineProperty(streams[1], "isTTY", { configurable: true, value: stdout })
12 })
13
14const withRestoredIsTTY = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
15 Effect.acquireUseRelease(
16 Effect.sync(() => streams.map((stream) => Object.getOwnPropertyDescriptor(stream, "isTTY"))),
17 () => effect,
18 (descriptors) =>
19 Effect.sync(() => {
20 streams.forEach((stream, index) => {
21 const descriptor = descriptors[index]
22 if (descriptor === undefined) {
23 Reflect.deleteProperty(stream, "isTTY")
24 } else {
25 Object.defineProperty(stream, "isTTY", descriptor)
26 }
27 })
28 })
29 )
30
31describe("NodeStdio", () => {
32 it.effect("reads terminal state when the effects run", () =>
33 withRestoredIsTTY(
34 Effect.gen(function*() {
35 const stdio = yield* Stdio.Stdio
36
37 yield* setIsTTY(true, false)
38 assert.deepStrictEqual(
39 yield* Effect.all([stdio.stdinIsTerminal, stdio.stdoutIsTerminal]),
40 [true, false]
41 )
42
43 yield* setIsTTY(false, true)
44 assert.deepStrictEqual(
45 yield* Effect.all([stdio.stdinIsTerminal, stdio.stdoutIsTerminal]),
46 [false, true]
47 )
48 }).pipe(Effect.provide(NodeStdio.layer))
49 ))
50})