1+ import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors" ;
12import { wait } from "@stackframe/stack-shared/dist/utils/promises" ;
23import { deindent } from "@stackframe/stack-shared/dist/utils/strings" ;
34import { describe } from "vitest" ;
45import { it } from "../../../../../helpers" ;
56import { withPortPrefix } from "../../../../../helpers/ports" ;
6- import { Project , backendContext , bumpEmailAddress , niceBackendFetch , waitForOutboxEmailWithStatus } from "../../../../backend-helpers" ;
7+ import { Project , backendContext , bumpEmailAddress , getOutboxEmails , niceBackendFetch , waitForOutboxEmailWithStatus } from "../../../../backend-helpers" ;
78
89const testEmailConfig = {
910 type : "standard" ,
@@ -601,10 +602,8 @@ describe("email outbox API", () => {
601602 expect ( sendResponse . status ) . toBe ( 200 ) ;
602603
603604 // Poll until we find the email and can pause it (with timeout)
604- let emailId : string | null = null ;
605- let pauseSucceeded = false ;
606-
607- for ( let i = 0 ; i < 20 ; i ++ ) {
605+ let emailId : string ;
606+ for ( let i = 0 ; ; i ++ ) {
608607 const listResponse = await niceBackendFetch ( "/api/v1/emails/outbox" , {
609608 method : "GET" ,
610609 accessType : "server" ,
@@ -622,19 +621,61 @@ describe("email outbox API", () => {
622621 } ,
623622 } ) ;
624623
625- if ( pauseResponse . status === 200 && pauseResponse . body . status === "paused" ) {
626- pauseSucceeded = true ;
627- break ;
624+ expect ( pauseResponse ) . toMatchInlineSnapshot ( `
625+ NiceResponse {
626+ "status": 200,
627+ "body": {
628+ "created_at_millis": <stripped field 'created_at_millis'>,
629+ "has_delivered": false,
630+ "has_rendered": false,
631+ "id": "<stripped UUID>",
632+ "is_paused": true,
633+ "scheduled_at_millis": <stripped field 'scheduled_at_millis'>,
634+ "simple_status": "in-progress",
635+ "skip_deliverability_check": false,
636+ "status": "paused",
637+ "theme_id": null,
638+ "to": {
639+ "type": "user-primary-email",
640+ "user_id": "<stripped UUID>",
641+ },
642+ "tsx_source": deindent\`
643+ import { Container } from "@react-email/components";
644+ import { Subject, NotificationCategory, Props } from "@stackframe/emails";
645+
646+ // Artificial delay to make the email slow to render
647+ const startTime = performance.now();
648+ while (performance.now() - startTime < 500) {
649+ // Busy wait - 500ms delay
650+ }
651+
652+ export function EmailTemplate({ user, project }) {
653+ return (
654+ <Container>
655+ <Subject value="Slow Render Cancel Test" />
656+ <NotificationCategory value="Transactional" />
657+ <div>Slow email content</div>
658+ </Container>
659+ );
660+ }
661+ \`,
662+ "updated_at_millis": <stripped field 'updated_at_millis'>,
663+ "variables": {},
664+ },
665+ "headers": Headers { <some fields may have been hidden> },
666+ }
667+ ` ) ;
668+ break ;
669+ } else {
670+ if ( i >= 20 ) {
671+ throw new StackAssertionError ( `Timeout waiting for email in the outbox` , {
672+ outboxEmails : await getOutboxEmails ( ) ,
673+ } ) ;
628674 }
675+ await wait ( 25 ) ;
629676 }
630-
631- await wait ( 25 ) ;
632677 }
633678
634- // These assertions must always run - test fails if we couldn't pause
635- expect ( emailId ) . not . toBeNull ( ) ;
636- expect ( pauseSucceeded ) . toBe ( true ) ;
637-
638679 // Now edit the scheduled_at_millis
639680 const newScheduleTime = Date . now ( ) + 3600000 ; // 1 hour from now
640681 const editResponse = await niceBackendFetch ( `/api/v1/emails/outbox/${ emailId } ` , {
0 commit comments