1717import { describe , expect , it } from "bun:test" ;
1818import {
1919 FONT_FETCH_FAILED ,
20+ FONT_FETCH_UNAVAILABLE ,
2021 FontFetchError ,
22+ FontFetchUnavailableError ,
2123 injectDeterministicFontFaces ,
2224} from "./deterministicFonts.js" ;
2325
@@ -75,6 +77,15 @@ function makeGoogleFontFetch(cssRequests: string[]): typeof fetch {
7577 } ) as unknown as typeof fetch ;
7678}
7779
80+ async function rejectedError ( promise : Promise < unknown > ) : Promise < unknown > {
81+ try {
82+ await promise ;
83+ } catch ( error ) {
84+ return error ;
85+ }
86+ throw new Error ( "Expected promise to reject" ) ;
87+ }
88+
7889describe ( "injectDeterministicFontFaces — failClosedFontFetch: false (default)" , ( ) => {
7990 it ( "swallows a network failure and returns the original HTML (no throw)" , async ( ) => {
8091 const result = await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
@@ -141,19 +152,18 @@ describe("injectDeterministicFontFaces — failClosedFontFetch: true", () => {
141152 } ) ;
142153 }
143154
144- it ( "throws FontFetchError on a network failure" , async ( ) => {
145- let caught : unknown ;
146- try {
147- await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
155+ it ( "throws FontFetchUnavailableError after retrying a network failure" , async ( ) => {
156+ const caught = await rejectedError (
157+ injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
148158 failClosedFontFetch : true ,
149159 fetchImpl : makeFailingFetch ( ) ,
150- } ) ;
151- } catch ( err ) {
152- caught = err ;
153- }
160+ fontFetchRetryPolicy : { baseDelayMs : 0 } ,
161+ } ) ,
162+ ) ;
154163 expect ( caught ) . toBeInstanceOf ( FontFetchError ) ;
155- expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_FAILED ) ;
156- expect ( ( caught as FontFetchError ) . code ) . toBe ( "FONT_FETCH_FAILED" ) ;
164+ expect ( caught ) . toBeInstanceOf ( FontFetchUnavailableError ) ;
165+ expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_UNAVAILABLE ) ;
166+ expect ( ( caught as FontFetchError ) . code ) . toBe ( "FONT_FETCH_UNAVAILABLE" ) ;
157167 expect ( ( caught as FontFetchError ) . familyName ) . toBe ( "NotARealFontFamilyForTest" ) ;
158168 expect ( ( caught as Error ) . message ) . toContain ( "simulated network failure" ) ;
159169 } ) ;
@@ -164,62 +174,53 @@ describe("injectDeterministicFontFaces — failClosedFontFetch: true", () => {
164174 // unresolvable (no alias, no Google Fonts, no system capture) which IS
165175 // a fail-closed error — the render would use a fallback font, producing
166176 // non-deterministic output across machines.
167- let caught : unknown ;
168- try {
169- await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
177+ const caught = await rejectedError (
178+ injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
170179 failClosedFontFetch : true ,
171180 allowSystemFontCapture : false ,
172181 fetchImpl : makeHttp400Fetch ( ) ,
173- } ) ;
174- } catch ( err ) {
175- caught = err ;
176- }
182+ } ) ,
183+ ) ;
177184 expect ( caught ) . toBeInstanceOf ( FontFetchError ) ;
178185 expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_FAILED ) ;
179186 expect ( ( caught as FontFetchError ) . familyName ) . toBe ( "NotARealFontFamilyForTest" ) ;
180187 } ) ;
181188
182189 it ( "throws on a 404 response when font is completely unresolvable" , async ( ) => {
183- let caught : unknown ;
184- try {
185- await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
190+ const caught = await rejectedError (
191+ injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
186192 failClosedFontFetch : true ,
187193 allowSystemFontCapture : false ,
188194 fetchImpl : makeHttp404Fetch ( ) ,
189- } ) ;
190- } catch ( err ) {
191- caught = err ;
192- }
195+ } ) ,
196+ ) ;
193197 expect ( caught ) . toBeInstanceOf ( FontFetchError ) ;
194198 expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_FAILED ) ;
195199 } ) ;
196200
197- it ( "throws FontFetchError on a 5xx response — non-deterministic, could differ on retry" , async ( ) => {
198- let caught : unknown ;
199- try {
200- await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
201+ it ( "throws FontFetchUnavailableError on an exhausted 5xx response" , async ( ) => {
202+ const caught = await rejectedError (
203+ injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
201204 failClosedFontFetch : true ,
202205 fetchImpl : makeHttp503Fetch ( ) ,
203- } ) ;
204- } catch ( err ) {
205- caught = err ;
206- }
206+ fontFetchRetryPolicy : { baseDelayMs : 0 } ,
207+ } ) ,
208+ ) ;
207209 expect ( caught ) . toBeInstanceOf ( FontFetchError ) ;
208- expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_FAILED ) ;
210+ expect ( caught ) . toBeInstanceOf ( FontFetchUnavailableError ) ;
211+ expect ( ( caught as FontFetchError ) . code ) . toBe ( FONT_FETCH_UNAVAILABLE ) ;
209212 expect ( ( caught as Error ) . message ) . toContain ( "HTTP 503" ) ;
210213 expect ( ( caught as Error ) . message ) . toContain ( "NotARealFontFamilyForTest" ) ;
211214 } ) ;
212215
213216 it ( "includes the requested URL in 5xx errors" , async ( ) => {
214- let caught : unknown ;
215- try {
216- await injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
217+ const caught = await rejectedError (
218+ injectDeterministicFontFaces ( HTML_REQUESTING_UNRESOLVED_FONT , {
217219 failClosedFontFetch : true ,
218220 fetchImpl : makeHttp503Fetch ( ) ,
219- } ) ;
220- } catch ( err ) {
221- caught = err ;
222- }
221+ fontFetchRetryPolicy : { baseDelayMs : 0 } ,
222+ } ) ,
223+ ) ;
223224 expect ( ( caught as FontFetchError ) . url ) . toContain ( "fonts.googleapis.com" ) ;
224225 expect ( ( caught as FontFetchError ) . url ) . toContain ( "NotARealFontFamilyForTest" ) ;
225226 } ) ;
0 commit comments