@@ -417,4 +417,193 @@ test.describe("Error Sanitization", () => {
417417 expect ( errorLogs [ 0 ] [ 0 ] . stack ) . toMatch ( " at " ) ;
418418 } ) ;
419419 } ) ;
420+
421+ test . describe ( "serverMode=production (user-provided handleError)" , ( ) => {
422+ test . beforeAll ( async ( ) => {
423+ fixture = await createFixture (
424+ {
425+ files : {
426+ "app/entry.server.tsx" : js `
427+ import type { EntryContext } from "@remix-run/node";
428+ import { RemixServer, isRouteErrorResponse } from "@remix-run/react";
429+ import { renderToString } from "react-dom/server";
430+
431+ export default function handleRequest(
432+ request: Request,
433+ responseStatusCode: number,
434+ responseHeaders: Headers,
435+ remixContext: EntryContext
436+ ) {
437+ let markup = renderToString(
438+ <RemixServer context={remixContext} url={request.url} />
439+ );
440+
441+ responseHeaders.set("Content-Type", "text/html");
442+
443+ return new Response("<!DOCTYPE html>" + markup, {
444+ status: responseStatusCode,
445+ headers: responseHeaders,
446+ });
447+ }
448+
449+ export function handleError(error: unknown, { request }: { request: Request }) {
450+ console.error("App Specific Error Logging:");
451+ console.error(" Request: " + request.method + " " + request.url);
452+ let msg;
453+ if (isRouteErrorResponse(error)) {
454+ console.error(" Error: " + error.status + " " + error.statusText);
455+ } else if (error instanceof Error) {
456+ console.error(" Error: " + error.message);
457+ console.error(" Stack: " + error.stack);
458+ } else {
459+ console.error("Dunno what this is");
460+ }
461+ }
462+ ` ,
463+ ...routeFiles ,
464+ } ,
465+ } ,
466+ ServerMode . Production
467+ ) ;
468+ } ) ;
469+
470+ test ( "renders document without errors" , async ( ) => {
471+ let response = await fixture . requestDocument ( "/" ) ;
472+ let html = await response . text ( ) ;
473+ expect ( html ) . toMatch ( "Index Route" ) ;
474+ expect ( html ) . toMatch ( "LOADER" ) ;
475+ expect ( html ) . not . toMatch ( "MESSAGE:" ) ;
476+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
477+ } ) ;
478+
479+ test ( "sanitizes loader errors in document requests" , async ( ) => {
480+ let response = await fixture . requestDocument ( "/?loader" ) ;
481+ let html = await response . text ( ) ;
482+ expect ( html ) . toMatch ( "Index Error" ) ;
483+ expect ( html ) . not . toMatch ( "LOADER" ) ;
484+ expect ( html ) . toMatch ( "MESSAGE:Unexpected Server Error" ) ;
485+ expect ( html ) . toMatch (
486+ '{"routes/index":{"message":"Unexpected Server Error","__type":"Error"}}'
487+ ) ;
488+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
489+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
490+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual ( " Request: GET test://test/?loader" ) ;
491+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
492+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
493+ expect ( errorLogs . length ) . toBe ( 4 ) ;
494+ } ) ;
495+
496+ test ( "sanitizes render errors in document requests" , async ( ) => {
497+ let response = await fixture . requestDocument ( "/?render" ) ;
498+ let html = await response . text ( ) ;
499+ expect ( html ) . toMatch ( "Index Error" ) ;
500+ expect ( html ) . toMatch ( "MESSAGE:Unexpected Server Error" ) ;
501+ expect ( html ) . toMatch (
502+ '{"routes/index":{"message":"Unexpected Server Error","__type":"Error"}}'
503+ ) ;
504+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
505+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
506+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual ( " Request: GET test://test/?render" ) ;
507+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Render Error" ) ;
508+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
509+ expect ( errorLogs . length ) . toBe ( 4 ) ;
510+ } ) ;
511+
512+ test ( "renders deferred document without errors" , async ( ) => {
513+ let response = await fixture . requestDocument ( "/defer" ) ;
514+ let html = await response . text ( ) ;
515+ expect ( html ) . toMatch ( "Defer Route" ) ;
516+ expect ( html ) . toMatch ( "RESOLVED" ) ;
517+ expect ( html ) . not . toMatch ( "MESSAGE:" ) ;
518+ // Defer errors are not not part of the JSON blob but rather rejected
519+ // against a pending promise and therefore are inlined JS.
520+ expect ( html ) . not . toMatch ( "x.stack=e.stack;" ) ;
521+ } ) ;
522+
523+ test ( "sanitizes defer errors in document requests" , async ( ) => {
524+ let response = await fixture . requestDocument ( "/defer?loader" ) ;
525+ let html = await response . text ( ) ;
526+ expect ( html ) . toMatch ( "Defer Error" ) ;
527+ expect ( html ) . not . toMatch ( "RESOLVED" ) ;
528+ expect ( html ) . toMatch ( '{"message":"Unexpected Server Error"}' ) ;
529+ // Defer errors are not not part of the JSON blob but rather rejected
530+ // against a pending promise and therefore are inlined JS.
531+ expect ( html ) . toMatch ( "x.stack=undefined;" ) ;
532+ // defer errors are not logged to the server console since the request
533+ // has "succeeded"
534+ expect ( errorLogs . length ) . toBe ( 0 ) ;
535+ } ) ;
536+
537+ test ( "returns data without errors" , async ( ) => {
538+ let response = await fixture . requestData ( "/" , "routes/index" ) ;
539+ let text = await response . text ( ) ;
540+ expect ( text ) . toMatch ( "LOADER" ) ;
541+ expect ( text ) . not . toMatch ( "MESSAGE:" ) ;
542+ expect ( text ) . not . toMatch ( / s t a c k / i) ;
543+ } ) ;
544+
545+ test ( "sanitizes loader errors in data requests" , async ( ) => {
546+ let response = await fixture . requestData ( "/?loader" , "routes/index" ) ;
547+ let text = await response . text ( ) ;
548+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
549+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
550+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
551+ " Request: GET test://test/?loader=&_data=routes%2Findex"
552+ ) ;
553+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
554+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
555+ expect ( errorLogs . length ) . toBe ( 4 ) ;
556+ } ) ;
557+
558+ test ( "returns deferred data without errors" , async ( ) => {
559+ let response = await fixture . requestData ( "/defer" , "routes/defer" ) ;
560+ let text = await response . text ( ) ;
561+ expect ( text ) . toMatch ( "RESOLVED" ) ;
562+ expect ( text ) . not . toMatch ( "REJECTED" ) ;
563+ expect ( text ) . not . toMatch ( / s t a c k / i) ;
564+ } ) ;
565+
566+ test ( "sanitizes loader errors in deferred data requests" , async ( ) => {
567+ let response = await fixture . requestData ( "/defer?loader" , "routes/defer" ) ;
568+ let text = await response . text ( ) ;
569+ expect ( text ) . toBe (
570+ '{"lazy":"__deferred_promise:lazy"}\n\n' +
571+ 'error:{"lazy":{"message":"Unexpected Server Error"}}\n\n'
572+ ) ;
573+ // defer errors are not logged to the server console since the request
574+ // has "succeeded"
575+ expect ( errorLogs . length ) . toBe ( 0 ) ;
576+ } ) ;
577+
578+ test ( "sanitizes loader errors in resource requests" , async ( ) => {
579+ let response = await fixture . requestData (
580+ "/resource?loader" ,
581+ "routes/resource"
582+ ) ;
583+ let text = await response . text ( ) ;
584+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
585+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
586+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
587+ " Request: GET test://test/resource?loader=&_data=routes%2Fresource"
588+ ) ;
589+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
590+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
591+ expect ( errorLogs . length ) . toBe ( 4 ) ;
592+ } ) ;
593+
594+ test ( "sanitizes mismatched route errors in data requests" , async ( ) => {
595+ let response = await fixture . requestData ( "/" , "not-a-route" ) ;
596+ let text = await response . text ( ) ;
597+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
598+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
599+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
600+ " Request: GET test://test/?_data=not-a-route"
601+ ) ;
602+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual (
603+ ' Error: Route "not-a-route" does not match URL "/"'
604+ ) ;
605+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
606+ expect ( errorLogs . length ) . toBe ( 4 ) ;
607+ } ) ;
608+ } ) ;
420609} ) ;
0 commit comments