@@ -104,35 +104,129 @@ Deno.test("MyLayout - renders heading and content", async () => {
104104});
105105```
106106
107- ## Testing with file routes and islands
107+ ## Testing routes and handlers
108108
109- [ File routes] ( /docs/concepts/file-routing ) are collected by the
110- [ ` Builder ` ] ( /docs/advanced/builder ) class and not just by
111- [ ` App ` ] ( /docs/concepts/app ) alone. We can generate a snapshot and re-use it for
112- many app instances in our test suite.
109+ For testing your route handlers and business logic, you can use the same
110+ [ ` App ` ] ( /docs/concepts/app ) pattern shown above. Fresh 2.0 makes it easy to test
111+ individual routes without needing a full build process:
113112
114- ``` ts my-app.test.ts
113+ ``` ts my-routes.test.ts
114+ import { expect } from " @std/expect" ;
115+ import { App } from " fresh" ;
116+
117+ // Import your route handlers
118+ import { handler as indexHandler } from " ./routes/index.ts" ;
119+ import { handler as apiHandler } from " ./routes/api/users.ts" ;
120+
121+ Deno .test (" Index route returns homepage" , async () => {
122+ const app = new App ().get (" /" , indexHandler );
123+ const handler = app .handler ();
124+
125+ const response = await handler (new Request (" http://localhost/" ));
126+ const text = await response .text ();
127+
128+ expect (text ).toContain (" Welcome" );
129+ });
130+
131+ Deno .test (" API route returns JSON" , async () => {
132+ const app = new App ().get (" /api/users" , apiHandler );
133+ const handler = app .handler ();
134+
135+ const response = await handler (new Request (" http://localhost/api/users" ));
136+ const json = await response .json ();
137+
138+ expect (json ).toEqual ({ users: [] });
139+ });
140+ ```
141+
142+ ## Testing islands
143+
144+ Testing islands requires different approaches for server-side and client-side
145+ behavior:
146+
147+ ### Server-side rendering of islands
148+
149+ You can test that your islands render correctly on the server using the same
150+ [ ` App ` ] ( /docs/concepts/app ) pattern. Note: this requires a ` .tsx ` file extension
151+ to use JSX:
152+
153+ ``` tsx island-ssr.test.tsx
154+ import { expect } from " @std/expect" ;
155+ import { App } from " fresh" ;
156+ import Counter from " ./islands/Counter.tsx" ;
157+
158+ Deno .test (" Counter page renders island" , async () => {
159+ const app = new App ().get (" /counter" , (ctx ) => {
160+ return ctx .render (
161+ <div className = " p-8" >
162+ <h1 >Counter Test Page</h1 >
163+ <Counter />
164+ </div >,
165+ );
166+ });
167+ const handler = app .handler ();
168+
169+ const response = await handler (new Request (" http://localhost/counter" ));
170+ const html = await response .text ();
171+
172+ // Verify the island's initial HTML is present
173+ expect (html ).toContain (' class="counter"' );
174+ expect (html ).toContain (" count: 0" );
175+ });
176+ ```
177+
178+ ### Client-side island interactivity
179+
180+ For testing client-side island behavior (clicks, state changes, etc.), you need
181+ a full build and browser environment. You can use the approach similar to
182+ Fresh's own tests:
183+
184+ ``` tsx island-client.test.tsx
185+ import { expect } from " @std/expect" ;
115186import { createBuilder } from " vite" ;
116- // Best to do this once instead of for every test case for
117- // performance reasons.
187+ import * as path from " @std/path" ;
188+
189+ // Create a production build
118190const builder = await createBuilder ({
119- root: " ./path/to/app" ,
120- build: {
121- emptyOutDir: true ,
191+ logLevel: " error" ,
192+ root: " ./" ,
193+ build: { emptyOutDir: true },
194+ environments: {
195+ ssr: { build: { outDir: path .join (" _fresh" , " server" ) } },
196+ client: { build: { outDir: path .join (" _fresh" , " client" ) } },
122197 },
123198});
124- await builder .build ();
199+ await builder .buildApp ();
125200
126- const { app } = await import (" ./path/to/app /_fresh/server.js" );
201+ const app = await import (" ./_fresh/server.js" );
127202
128- Deno .test (" My Test" , async () => {
129- const handler = app .handler ();
203+ Deno .test (" Counter island renders correctly" , async () => {
204+ // Start production server
205+ const server = Deno .serve ({
206+ port: 0 ,
207+ handler: app .default .fetch ,
208+ });
130209
131- const response = await handler (new Request (" http://localhost" ));
132- const text = await response .text ();
210+ const { port } = server .addr as Deno .NetAddr ;
211+ const address = ` http://localhost:${port } ` ;
212+
213+ try {
214+ // Basic smoke test: verify the island HTML is served
215+ const response = await fetch (` ${address }/counter ` );
216+ const html = await response .text ();
133217
134- if (text !== " hello" ) {
135- throw new Error (" fail" );
218+ expect (html ).toContain (' class="counter"' );
219+ expect (html ).toContain (" count: 0" );
220+
221+ // For full browser interactivity testing, you would need:
222+ // - Browser automation tools (Puppeteer, Playwright)
223+ // - withBrowser utility from Fresh's test suite
224+ } finally {
225+ await server .shutdown ();
136226 }
137227});
138228```
229+
230+ ** Note:** For most applications, testing the server-side rendering is
231+ sufficient. Only test client-side interactivity if you have complex island logic
232+ that needs verification.
0 commit comments