@@ -417,4 +417,194 @@ 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+ debugger;
453+ let msg;
454+ if (isRouteErrorResponse(error)) {
455+ console.error(" Error: " + error.status + " " + error.statusText);
456+ } else if (error instanceof Error) {
457+ console.error(" Error: " + error.message);
458+ console.error(" Stack: " + error.stack);
459+ } else {
460+ console.error("Dunno what this is");
461+ }
462+ }
463+ ` ,
464+ ...routeFiles ,
465+ } ,
466+ } ,
467+ ServerMode . Production
468+ ) ;
469+ } ) ;
470+
471+ test ( "renders document without errors" , async ( ) => {
472+ let response = await fixture . requestDocument ( "/" ) ;
473+ let html = await response . text ( ) ;
474+ expect ( html ) . toMatch ( "Index Route" ) ;
475+ expect ( html ) . toMatch ( "LOADER" ) ;
476+ expect ( html ) . not . toMatch ( "MESSAGE:" ) ;
477+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
478+ } ) ;
479+
480+ test ( "sanitizes loader errors in document requests" , async ( ) => {
481+ let response = await fixture . requestDocument ( "/?loader" ) ;
482+ let html = await response . text ( ) ;
483+ expect ( html ) . toMatch ( "Index Error" ) ;
484+ expect ( html ) . not . toMatch ( "LOADER" ) ;
485+ expect ( html ) . toMatch ( "MESSAGE:Unexpected Server Error" ) ;
486+ expect ( html ) . toMatch (
487+ '{"routes/index":{"message":"Unexpected Server Error","__type":"Error"}}'
488+ ) ;
489+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
490+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
491+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual ( " Request: GET test://test/?loader" ) ;
492+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
493+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
494+ expect ( errorLogs . length ) . toBe ( 4 ) ;
495+ } ) ;
496+
497+ test ( "sanitizes render errors in document requests" , async ( ) => {
498+ let response = await fixture . requestDocument ( "/?render" ) ;
499+ let html = await response . text ( ) ;
500+ expect ( html ) . toMatch ( "Index Error" ) ;
501+ expect ( html ) . toMatch ( "MESSAGE:Unexpected Server Error" ) ;
502+ expect ( html ) . toMatch (
503+ '{"routes/index":{"message":"Unexpected Server Error","__type":"Error"}}'
504+ ) ;
505+ expect ( html ) . not . toMatch ( / s t a c k / i) ;
506+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
507+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual ( " Request: GET test://test/?render" ) ;
508+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Render Error" ) ;
509+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
510+ expect ( errorLogs . length ) . toBe ( 4 ) ;
511+ } ) ;
512+
513+ test ( "renders deferred document without errors" , async ( ) => {
514+ let response = await fixture . requestDocument ( "/defer" ) ;
515+ let html = await response . text ( ) ;
516+ expect ( html ) . toMatch ( "Defer Route" ) ;
517+ expect ( html ) . toMatch ( "RESOLVED" ) ;
518+ expect ( html ) . not . toMatch ( "MESSAGE:" ) ;
519+ // Defer errors are not not part of the JSON blob but rather rejected
520+ // against a pending promise and therefore are inlined JS.
521+ expect ( html ) . not . toMatch ( "x.stack=e.stack;" ) ;
522+ } ) ;
523+
524+ test ( "sanitizes defer errors in document requests" , async ( ) => {
525+ let response = await fixture . requestDocument ( "/defer?loader" ) ;
526+ let html = await response . text ( ) ;
527+ expect ( html ) . toMatch ( "Defer Error" ) ;
528+ expect ( html ) . not . toMatch ( "RESOLVED" ) ;
529+ expect ( html ) . toMatch ( '{"message":"Unexpected Server Error"}' ) ;
530+ // Defer errors are not not part of the JSON blob but rather rejected
531+ // against a pending promise and therefore are inlined JS.
532+ expect ( html ) . toMatch ( "x.stack=undefined;" ) ;
533+ // defer errors are not logged to the server console since the request
534+ // has "succeeded"
535+ expect ( errorLogs . length ) . toBe ( 0 ) ;
536+ } ) ;
537+
538+ test ( "returns data without errors" , async ( ) => {
539+ let response = await fixture . requestData ( "/" , "routes/index" ) ;
540+ let text = await response . text ( ) ;
541+ expect ( text ) . toMatch ( "LOADER" ) ;
542+ expect ( text ) . not . toMatch ( "MESSAGE:" ) ;
543+ expect ( text ) . not . toMatch ( / s t a c k / i) ;
544+ } ) ;
545+
546+ test ( "sanitizes loader errors in data requests" , async ( ) => {
547+ let response = await fixture . requestData ( "/?loader" , "routes/index" ) ;
548+ let text = await response . text ( ) ;
549+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
550+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
551+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
552+ " Request: GET test://test/?loader=&_data=routes%2Findex"
553+ ) ;
554+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
555+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
556+ expect ( errorLogs . length ) . toBe ( 4 ) ;
557+ } ) ;
558+
559+ test ( "returns deferred data without errors" , async ( ) => {
560+ let response = await fixture . requestData ( "/defer" , "routes/defer" ) ;
561+ let text = await response . text ( ) ;
562+ expect ( text ) . toMatch ( "RESOLVED" ) ;
563+ expect ( text ) . not . toMatch ( "REJECTED" ) ;
564+ expect ( text ) . not . toMatch ( / s t a c k / i) ;
565+ } ) ;
566+
567+ test ( "sanitizes loader errors in deferred data requests" , async ( ) => {
568+ let response = await fixture . requestData ( "/defer?loader" , "routes/defer" ) ;
569+ let text = await response . text ( ) ;
570+ expect ( text ) . toBe (
571+ '{"lazy":"__deferred_promise:lazy"}\n\n' +
572+ 'error:{"lazy":{"message":"Unexpected Server Error"}}\n\n'
573+ ) ;
574+ // defer errors are not logged to the server console since the request
575+ // has "succeeded"
576+ expect ( errorLogs . length ) . toBe ( 0 ) ;
577+ } ) ;
578+
579+ test ( "sanitizes loader errors in resource requests" , async ( ) => {
580+ let response = await fixture . requestData (
581+ "/resource?loader" ,
582+ "routes/resource"
583+ ) ;
584+ let text = await response . text ( ) ;
585+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
586+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
587+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
588+ " Request: GET test://test/resource?loader=&_data=routes%2Fresource"
589+ ) ;
590+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual ( " Error: Loader Error" ) ;
591+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
592+ expect ( errorLogs . length ) . toBe ( 4 ) ;
593+ } ) ;
594+
595+ test ( "sanitizes mismatched route errors in data requests" , async ( ) => {
596+ let response = await fixture . requestData ( "/" , "not-a-route" ) ;
597+ let text = await response . text ( ) ;
598+ expect ( text ) . toBe ( '{"message":"Unexpected Server Error"}' ) ;
599+ expect ( errorLogs [ 0 ] [ 0 ] ) . toEqual ( "App Specific Error Logging:" ) ;
600+ expect ( errorLogs [ 1 ] [ 0 ] ) . toEqual (
601+ " Request: GET test://test/?_data=not-a-route"
602+ ) ;
603+ expect ( errorLogs [ 2 ] [ 0 ] ) . toEqual (
604+ ' Error: Route "not-a-route" does not match URL "/"'
605+ ) ;
606+ expect ( errorLogs [ 3 ] [ 0 ] ) . toMatch ( " at " ) ;
607+ expect ( errorLogs . length ) . toBe ( 4 ) ;
608+ } ) ;
609+ } ) ;
420610} ) ;
0 commit comments