effect-start 0.17.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/Development.d.ts +7 -2
  2. package/dist/Development.js +12 -6
  3. package/dist/PlatformRuntime.d.ts +4 -0
  4. package/dist/PlatformRuntime.js +9 -0
  5. package/dist/Route.d.ts +6 -2
  6. package/dist/Route.js +22 -0
  7. package/dist/RouteHttp.d.ts +1 -1
  8. package/dist/RouteHttp.js +12 -19
  9. package/dist/RouteMount.d.ts +2 -1
  10. package/dist/Start.d.ts +1 -5
  11. package/dist/Start.js +1 -8
  12. package/dist/Unique.d.ts +50 -0
  13. package/dist/Unique.js +187 -0
  14. package/dist/bun/BunHttpServer.js +5 -6
  15. package/dist/bun/BunRoute.d.ts +1 -1
  16. package/dist/bun/BunRoute.js +2 -2
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/node/Effectify.d.ts +209 -0
  20. package/dist/node/Effectify.js +19 -0
  21. package/dist/node/FileSystem.d.ts +3 -5
  22. package/dist/node/FileSystem.js +42 -62
  23. package/dist/node/PlatformError.d.ts +46 -0
  24. package/dist/node/PlatformError.js +43 -0
  25. package/dist/testing/TestLogger.js +1 -1
  26. package/package.json +10 -5
  27. package/src/Development.ts +13 -18
  28. package/src/PlatformRuntime.ts +11 -0
  29. package/src/Route.ts +31 -2
  30. package/src/RouteHttp.ts +15 -31
  31. package/src/RouteMount.ts +1 -1
  32. package/src/Start.ts +1 -15
  33. package/src/Unique.ts +232 -0
  34. package/src/bun/BunHttpServer.ts +6 -9
  35. package/src/bun/BunRoute.ts +3 -3
  36. package/src/index.ts +1 -0
  37. package/src/node/Effectify.ts +262 -0
  38. package/src/node/FileSystem.ts +59 -97
  39. package/src/node/PlatformError.ts +102 -0
  40. package/src/testing/TestLogger.ts +1 -1
  41. package/dist/Random.d.ts +0 -5
  42. package/dist/Random.js +0 -49
  43. package/src/Commander.test.ts +0 -1639
  44. package/src/ContentNegotiation.test.ts +0 -603
  45. package/src/Development.test.ts +0 -119
  46. package/src/Entity.test.ts +0 -592
  47. package/src/FileRouterPattern.test.ts +0 -147
  48. package/src/FileRouter_files.test.ts +0 -64
  49. package/src/FileRouter_path.test.ts +0 -145
  50. package/src/FileRouter_tree.test.ts +0 -132
  51. package/src/Http.test.ts +0 -319
  52. package/src/HttpAppExtra.test.ts +0 -103
  53. package/src/HttpUtils.test.ts +0 -85
  54. package/src/PathPattern.test.ts +0 -648
  55. package/src/Random.ts +0 -59
  56. package/src/RouteBody.test.ts +0 -232
  57. package/src/RouteHook.test.ts +0 -40
  58. package/src/RouteHttp.test.ts +0 -2909
  59. package/src/RouteMount.test.ts +0 -481
  60. package/src/RouteSchema.test.ts +0 -427
  61. package/src/RouteSse.test.ts +0 -249
  62. package/src/RouteTree.test.ts +0 -494
  63. package/src/RouteTrie.test.ts +0 -322
  64. package/src/RouterPattern.test.ts +0 -676
  65. package/src/Values.test.ts +0 -263
  66. package/src/bun/BunBundle.test.ts +0 -268
  67. package/src/bun/BunBundle_imports.test.ts +0 -48
  68. package/src/bun/BunHttpServer.test.ts +0 -251
  69. package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
  70. package/src/bun/BunRoute.test.ts +0 -162
  71. package/src/bundler/BundleHttp.test.ts +0 -132
  72. package/src/effect/HttpRouter.test.ts +0 -548
  73. package/src/experimental/EncryptedCookies.test.ts +0 -488
  74. package/src/hyper/HyperHtml.test.ts +0 -209
  75. package/src/hyper/HyperRoute.test.tsx +0 -197
  76. package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
  77. package/src/testing/TestHttpClient.test.ts +0 -83
  78. package/src/testing/TestLogger.test.ts +0 -51
  79. package/src/x/datastar/Datastar.test.ts +0 -266
  80. package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
@@ -1,251 +0,0 @@
1
- import * as test from "bun:test"
2
- import * as Effect from "effect/Effect"
3
- import * as Layer from "effect/Layer"
4
- import * as Route from "../Route.ts"
5
- import * as BunHttpServer from "./BunHttpServer.ts"
6
-
7
- test.describe("smart port selection", () => {
8
- // Skip when running in TTY because the random port logic requires !isTTY && CLAUDECODE,
9
- // and process.stdout.isTTY cannot be mocked
10
- test.test.skipIf(process.stdout.isTTY)(
11
- "uses random port when PORT not set, isTTY=false, CLAUDECODE set",
12
- async () => {
13
- const originalPort = process.env.PORT
14
- const originalClaudeCode = process.env.CLAUDECODE
15
-
16
- try {
17
- delete process.env.PORT
18
- process.env.CLAUDECODE = "1"
19
-
20
- const port = await Effect.runPromise(
21
- Effect.scoped(
22
- Effect.gen(function*() {
23
- const bunServer = yield* BunHttpServer.make({})
24
- return bunServer.server.port
25
- }),
26
- ),
27
- )
28
-
29
- test
30
- .expect(port)
31
- .not
32
- .toBe(3000)
33
- } finally {
34
- if (originalPort !== undefined) {
35
- process.env.PORT = originalPort
36
- } else {
37
- delete process.env.PORT
38
- }
39
- if (originalClaudeCode !== undefined) {
40
- process.env.CLAUDECODE = originalClaudeCode
41
- } else {
42
- delete process.env.CLAUDECODE
43
- }
44
- }
45
- },
46
- )
47
-
48
- test.test("uses explicit PORT even when CLAUDECODE is set", async () => {
49
- const originalPort = process.env.PORT
50
- const originalClaudeCode = process.env.CLAUDECODE
51
-
52
- try {
53
- process.env.PORT = "5678"
54
- process.env.CLAUDECODE = "1"
55
-
56
- const port = await Effect.runPromise(
57
- Effect.scoped(
58
- Effect.gen(function*() {
59
- const bunServer = yield* BunHttpServer.make({})
60
- return bunServer.server.port
61
- }),
62
- ),
63
- )
64
-
65
- test
66
- .expect(port)
67
- .toBe(5678)
68
- } finally {
69
- if (originalPort !== undefined) {
70
- process.env.PORT = originalPort
71
- } else {
72
- delete process.env.PORT
73
- }
74
- if (originalClaudeCode !== undefined) {
75
- process.env.CLAUDECODE = originalClaudeCode
76
- } else {
77
- delete process.env.CLAUDECODE
78
- }
79
- }
80
- })
81
- })
82
-
83
- const BunHttpServerTest = Layer.scoped(
84
- BunHttpServer.BunHttpServer,
85
- BunHttpServer.make({ port: 0 }),
86
- )
87
-
88
- const testLayer = (routes: ReturnType<typeof Route.tree>) =>
89
- Layer.provideMerge(
90
- BunHttpServer.layerRoutes(routes),
91
- BunHttpServerTest,
92
- )
93
-
94
- test.describe("routes", () => {
95
- test.test("serves static text route", async () => {
96
- const routes = Route.tree({
97
- "/": Route.get(Route.text("Hello, World!")),
98
- })
99
-
100
- const response = await Effect.runPromise(
101
- Effect.scoped(
102
- Effect
103
- .gen(function*() {
104
- const bunServer = yield* BunHttpServer.BunHttpServer
105
- return yield* Effect.promise(() =>
106
- fetch(`http://localhost:${bunServer.server.port}/`)
107
- )
108
- })
109
- .pipe(Effect.provide(testLayer(routes))),
110
- ),
111
- )
112
-
113
- test.expect(response.status).toBe(200)
114
- test.expect(await response.text()).toBe("Hello, World!")
115
- })
116
-
117
- test.test("serves JSON route", async () => {
118
- const routes = Route.tree({
119
- "/api/data": Route.get(Route.json({ message: "success", value: 42 })),
120
- })
121
-
122
- const response = await Effect.runPromise(
123
- Effect.scoped(
124
- Effect
125
- .gen(function*() {
126
- const bunServer = yield* BunHttpServer.BunHttpServer
127
- return yield* Effect.promise(() =>
128
- fetch(`http://localhost:${bunServer.server.port}/api/data`)
129
- )
130
- })
131
- .pipe(Effect.provide(testLayer(routes))),
132
- ),
133
- )
134
-
135
- test.expect(response.status).toBe(200)
136
- test.expect(response.headers.get("Content-Type")).toBe("application/json")
137
- test.expect(await response.json()).toEqual({
138
- message: "success",
139
- value: 42,
140
- })
141
- })
142
-
143
- test.test("returns 404 for unknown routes", async () => {
144
- const routes = Route.tree({
145
- "/": Route.get(Route.text("Home")),
146
- })
147
-
148
- const response = await Effect.runPromise(
149
- Effect.scoped(
150
- Effect
151
- .gen(function*() {
152
- const bunServer = yield* BunHttpServer.BunHttpServer
153
- return yield* Effect.promise(() =>
154
- fetch(`http://localhost:${bunServer.server.port}/unknown`)
155
- )
156
- })
157
- .pipe(Effect.provide(testLayer(routes))),
158
- ),
159
- )
160
-
161
- test.expect(response.status).toBe(404)
162
- })
163
-
164
- test.test("handles content negotiation", async () => {
165
- const routes = Route.tree({
166
- "/data": Route
167
- .get(Route.json({ type: "json" }))
168
- .get(Route.html("<div>html</div>")),
169
- })
170
-
171
- const [jsonResponse, htmlResponse] = await Effect.runPromise(
172
- Effect.scoped(
173
- Effect
174
- .gen(function*() {
175
- const bunServer = yield* BunHttpServer.BunHttpServer
176
- const baseUrl = `http://localhost:${bunServer.server.port}`
177
-
178
- const json = yield* Effect.promise(() =>
179
- fetch(`${baseUrl}/data`, {
180
- headers: { Accept: "application/json" },
181
- })
182
- )
183
-
184
- const html = yield* Effect.promise(() =>
185
- fetch(`${baseUrl}/data`, {
186
- headers: { Accept: "text/html" },
187
- })
188
- )
189
-
190
- return [json, html] as const
191
- })
192
- .pipe(Effect.provide(testLayer(routes))),
193
- ),
194
- )
195
-
196
- test.expect(jsonResponse.headers.get("Content-Type")).toBe(
197
- "application/json",
198
- )
199
- test.expect(await jsonResponse.json()).toEqual({ type: "json" })
200
-
201
- test.expect(htmlResponse.headers.get("Content-Type")).toBe(
202
- "text/html; charset=utf-8",
203
- )
204
- test.expect(await htmlResponse.text()).toBe("<div>html</div>")
205
- })
206
-
207
- test.test("returns 406 for unacceptable content type", async () => {
208
- const routes = Route.tree({
209
- "/data": Route.get(Route.json({ type: "json" })),
210
- })
211
-
212
- const response = await Effect.runPromise(
213
- Effect.scoped(
214
- Effect
215
- .gen(function*() {
216
- const bunServer = yield* BunHttpServer.BunHttpServer
217
- return yield* Effect.promise(() =>
218
- fetch(`http://localhost:${bunServer.server.port}/data`, {
219
- headers: { Accept: "image/png" },
220
- })
221
- )
222
- })
223
- .pipe(Effect.provide(testLayer(routes))),
224
- ),
225
- )
226
-
227
- test.expect(response.status).toBe(406)
228
- })
229
-
230
- test.test("handles parameterized routes", async () => {
231
- const routes = Route.tree({
232
- "/users/:id": Route.get(Route.text("user")),
233
- })
234
-
235
- const response = await Effect.runPromise(
236
- Effect.scoped(
237
- Effect
238
- .gen(function*() {
239
- const bunServer = yield* BunHttpServer.BunHttpServer
240
- return yield* Effect.promise(() =>
241
- fetch(`http://localhost:${bunServer.server.port}/users/123`)
242
- )
243
- })
244
- .pipe(Effect.provide(testLayer(routes))),
245
- ),
246
- )
247
-
248
- test.expect(response.status).toBe(200)
249
- test.expect(await response.text()).toBe("user")
250
- })
251
- })
@@ -1,77 +0,0 @@
1
- import * as test from "bun:test"
2
- import * as BunImportTrackerPlugin from "./BunImportTrackerPlugin.ts"
3
- import * as BunVirtualFilesPlugin from "./BunVirtualFilesPlugin.ts"
4
-
5
- const Files = {
6
- "index.html": `
7
- <!DOCTYPE html>
8
- <html>
9
- <head>
10
- <title>Dashboard</title>
11
- </head>
12
- <body>
13
- <div id="root"></div>
14
- <script src="client.tsx" />
15
- </body>
16
- </html>
17
- `,
18
-
19
- "client.ts": `
20
- import { message } from "./config.ts"
21
-
22
- alert(message)
23
- `,
24
-
25
- ".config.ts": `
26
- export const message = "Hello, World!"
27
- `,
28
- }
29
-
30
- test.it("virtual import", async () => {
31
- const trackerPlugin = BunImportTrackerPlugin.make({
32
- baseDir: Bun.fileURLToPath(import.meta.resolve("../..")),
33
- })
34
-
35
- await Bun.build({
36
- target: "bun",
37
- entrypoints: [
38
- import.meta.path,
39
- ],
40
- plugins: [
41
- trackerPlugin,
42
- ],
43
- })
44
-
45
- test
46
- .expect(
47
- [...trackerPlugin.state.entries()],
48
- )
49
- .toEqual([
50
- [
51
- "src/bun/BunImportTrackerPlugin.test.ts",
52
- [
53
- {
54
- kind: "import-statement",
55
- path: "bun:test",
56
- },
57
- {
58
- kind: "import-statement",
59
- path: "src/bun/BunImportTrackerPlugin.ts",
60
- },
61
- {
62
- kind: "import-statement",
63
- path: "src/bun/BunVirtualFilesPlugin.ts",
64
- },
65
- ],
66
- ],
67
- [
68
- "src/bun/BunImportTrackerPlugin.ts",
69
- [
70
- {
71
- kind: "import-statement",
72
- path: "node:path",
73
- },
74
- ],
75
- ],
76
- ])
77
- })
@@ -1,162 +0,0 @@
1
- import * as test from "bun:test"
2
- import * as Effect from "effect/Effect"
3
- import * as Layer from "effect/Layer"
4
- import * as Option from "effect/Option"
5
- import * as Route from "../Route.ts"
6
- import * as BunHttpServer from "./BunHttpServer.ts"
7
- import * as BunRoute from "./BunRoute.ts"
8
-
9
- const layerServer = Layer.scoped(
10
- BunHttpServer.BunHttpServer,
11
- BunHttpServer.make({ port: 0 }),
12
- )
13
-
14
- const testLayer = (routes: ReturnType<typeof Route.tree>) =>
15
- Layer.provideMerge(
16
- BunHttpServer.layerRoutes(routes),
17
- layerServer,
18
- )
19
-
20
- test.describe(BunRoute.htmlBundle, () => {
21
- test.test("wraps child content with layout", async () => {
22
- const routes = Route.tree({
23
- "/": Route.get(
24
- BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
25
- Route.html("<p>Hello World</p>"),
26
- ),
27
- })
28
-
29
- const response = await Effect.runPromise(
30
- Effect.scoped(
31
- Effect
32
- .gen(function*() {
33
- const bunServer = yield* BunHttpServer.BunHttpServer
34
- return yield* Effect.promise(() =>
35
- fetch(`http://localhost:${bunServer.server.port}/`)
36
- )
37
- })
38
- .pipe(Effect.provide(testLayer(routes))),
39
- ),
40
- )
41
-
42
- test.expect(response.status).toBe(200)
43
- const html = await response.text()
44
- test.expect(html).toContain("<p>Hello World</p>")
45
- test.expect(html).toContain("<body>")
46
- test.expect(html).toContain("</body>")
47
- })
48
-
49
- test.test("replaces %yield% with child content", async () => {
50
- const routes = Route.tree({
51
- "/page": Route.get(
52
- BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
53
- Route.html("<div>Page Content</div>"),
54
- ),
55
- })
56
-
57
- const response = await Effect.runPromise(
58
- Effect.scoped(
59
- Effect
60
- .gen(function*() {
61
- const bunServer = yield* BunHttpServer.BunHttpServer
62
- return yield* Effect.promise(() =>
63
- fetch(`http://localhost:${bunServer.server.port}/page`)
64
- )
65
- })
66
- .pipe(Effect.provide(testLayer(routes))),
67
- ),
68
- )
69
-
70
- const html = await response.text()
71
- test.expect(html).toContain("<div>Page Content</div>")
72
- test.expect(html).not.toContain("%children%")
73
- })
74
-
75
- test.test("works with use() for wildcard routes", async () => {
76
- const routes = Route.tree({
77
- "*": Route.use(
78
- BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
79
- ),
80
- "/:path*": Route.get(Route.html("<section>Catch All</section>")),
81
- })
82
-
83
- const response = await Effect.runPromise(
84
- Effect.scoped(
85
- Effect
86
- .gen(function*() {
87
- const bunServer = yield* BunHttpServer.BunHttpServer
88
- return yield* Effect.promise(() =>
89
- fetch(`http://localhost:${bunServer.server.port}/any/path`)
90
- )
91
- })
92
- .pipe(Effect.provide(testLayer(routes))),
93
- ),
94
- )
95
-
96
- test.expect(response.status).toBe(200)
97
- const html = await response.text()
98
- test.expect(html).toContain("<section>Catch All</section>")
99
- })
100
-
101
- test.test("has format: html descriptor", async () => {
102
- const routes = Route.tree({
103
- "/": Route.get(
104
- BunRoute.htmlBundle(() => import("../../static/LayoutSlots.html")),
105
- Route.html("<p>content</p>"),
106
- ),
107
- })
108
-
109
- const response = await Effect.runPromise(
110
- Effect.scoped(
111
- Effect
112
- .gen(function*() {
113
- const bunServer = yield* BunHttpServer.BunHttpServer
114
- return yield* Effect.promise(() =>
115
- fetch(`http://localhost:${bunServer.server.port}/`)
116
- )
117
- })
118
- .pipe(Effect.provide(testLayer(routes))),
119
- ),
120
- )
121
-
122
- test.expect(response.status).toBe(200)
123
- const contentType = response.headers.get("content-type")
124
- test.expect(contentType).toContain("text/html")
125
- })
126
- })
127
-
128
- test.describe(BunRoute.validateBunPattern, () => {
129
- test.test("returns none for valid patterns", () => {
130
- test
131
- .expect(Option.isNone(BunRoute.validateBunPattern("/users")))
132
- .toBe(true)
133
- test
134
- .expect(Option.isNone(BunRoute.validateBunPattern("/users/[id]")))
135
- .toBe(true)
136
- test
137
- .expect(Option.isNone(BunRoute.validateBunPattern("/[...rest]")))
138
- .toBe(true)
139
- })
140
-
141
- test.test("returns error for prefixed params", () => {
142
- const result = BunRoute.validateBunPattern("/pk_[id]")
143
- test.expect(Option.isSome(result)).toBe(true)
144
- })
145
-
146
- test.test("returns error for suffixed params", () => {
147
- const result = BunRoute.validateBunPattern("/[id]_suffix")
148
- test.expect(Option.isSome(result)).toBe(true)
149
- })
150
- })
151
-
152
- test.describe(BunRoute.isHtmlBundle, () => {
153
- test.test("returns false for non-objects", () => {
154
- test.expect(BunRoute.isHtmlBundle(null)).toBe(false)
155
- test.expect(BunRoute.isHtmlBundle(undefined)).toBe(false)
156
- test.expect(BunRoute.isHtmlBundle("string")).toBe(false)
157
- })
158
-
159
- test.test("returns true for object with index property", () => {
160
- test.expect(BunRoute.isHtmlBundle({ index: "index.html" })).toBe(true)
161
- })
162
- })
@@ -1,132 +0,0 @@
1
- import * as HttpRouter from "@effect/platform/HttpRouter"
2
- import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
3
- import * as test from "bun:test"
4
- import * as Effect from "effect/Effect"
5
- import * as Layer from "effect/Layer"
6
- import IndexHtml from "../../static/react-dashboard.html" with { type: "file" }
7
- import * as BunBundle from "../bun/BunBundle.ts"
8
- import { effectFn } from "../testing"
9
- import * as TestHttpClient from "../testing/TestHttpClient.ts"
10
- import * as Bundle from "./Bundle.ts"
11
- import * as BundleHttp from "./BundleHttp.ts"
12
-
13
- const effect = effectFn(
14
- Layer.effect(
15
- Bundle.ClientBundle,
16
- BunBundle.buildClient(IndexHtml as any),
17
- ),
18
- )
19
-
20
- test.it("entrypoint with specific uri", () =>
21
- effect(function*() {
22
- const App = HttpRouter.empty.pipe(
23
- HttpRouter.get(
24
- "/react-dashboard",
25
- BundleHttp.entrypoint("../static/react-dashboard.html"),
26
- ),
27
- )
28
- const Client = TestHttpClient.make(App)
29
-
30
- const dashboardRes = yield* Client.get("/react-dashboard")
31
- test
32
- .expect(dashboardRes.status)
33
- .toBe(200)
34
- test
35
- .expect(yield* dashboardRes.text)
36
- .toStartWith("<!DOCTYPE html>")
37
- }))
38
-
39
- test.it("entrypoint without uri parameter", () =>
40
- effect(function*() {
41
- const App = HttpRouter.empty.pipe(
42
- HttpRouter.get(
43
- "/",
44
- BundleHttp.entrypoint(),
45
- ),
46
- HttpRouter.get(
47
- "/index",
48
- BundleHttp.entrypoint(),
49
- ),
50
- HttpRouter.get(
51
- "/react-dashboard",
52
- BundleHttp.entrypoint(),
53
- ),
54
- HttpRouter.get(
55
- "/nonexistent",
56
- BundleHttp.entrypoint(),
57
- ),
58
- )
59
- const Client = TestHttpClient.make(App)
60
-
61
- const indexRes = yield* Client.get("/").pipe(
62
- Effect.catchTag(
63
- "RouteNotFound",
64
- () => HttpServerResponse.empty({ status: 404 }),
65
- ),
66
- )
67
- test
68
- .expect(indexRes.status)
69
- .toBe(404)
70
-
71
- const indexPathRes = yield* Client.get("/index").pipe(
72
- Effect.catchTag(
73
- "RouteNotFound",
74
- () => HttpServerResponse.empty({ status: 404 }),
75
- ),
76
- )
77
- test
78
- .expect(indexPathRes.status)
79
- .toBe(404)
80
-
81
- const dashboardRes = yield* Client.get("/react-dashboard")
82
- test
83
- .expect(dashboardRes.status)
84
- .toBe(200)
85
- test
86
- .expect(yield* dashboardRes.text)
87
- .toStartWith("<!DOCTYPE html>")
88
-
89
- const nonexistentRes = yield* Client.get("/nonexistent").pipe(
90
- Effect.catchTag(
91
- "RouteNotFound",
92
- () => HttpServerResponse.empty({ status: 404 }),
93
- ),
94
- )
95
- test
96
- .expect(nonexistentRes.status)
97
- .toBe(404)
98
- }))
99
-
100
- test.it("withEntrypoints middleware", () =>
101
- effect(function*() {
102
- const fallbackApp = Effect.succeed(
103
- HttpServerResponse.text("Fallback", { status: 404 }),
104
- )
105
-
106
- const App = BundleHttp.withEntrypoints()(fallbackApp)
107
- const Client = TestHttpClient.make(App)
108
-
109
- const rootRes = yield* Client.get("/")
110
- test
111
- .expect(rootRes.status)
112
- .toBe(404)
113
- test
114
- .expect(yield* rootRes.text)
115
- .toBe("Fallback")
116
-
117
- const dashboardRes = yield* Client.get("/react-dashboard")
118
- test
119
- .expect(dashboardRes.status)
120
- .toBe(200)
121
- test
122
- .expect(yield* dashboardRes.text)
123
- .toStartWith("<!DOCTYPE html>")
124
-
125
- const nonexistentRes = yield* Client.get("/nonexistent")
126
- test
127
- .expect(nonexistentRes.status)
128
- .toBe(404)
129
- test
130
- .expect(yield* nonexistentRes.text)
131
- .toBe("Fallback")
132
- }))