diff --git a/src/core/drive/form_submission.ts b/src/core/drive/form_submission.ts index 6ff0d336e..d1676d2bd 100644 --- a/src/core/drive/form_submission.ts +++ b/src/core/drive/form_submission.ts @@ -3,6 +3,7 @@ import { FetchResponse } from "../../http/fetch_response" import { expandURL } from "../url" import { dispatch, getMetaContent } from "../../util" import { StreamMessage } from "../streams/stream_message" +import { TurboFetchRequestErrorEvent } from "../session" export interface FormSubmissionDelegate { formSubmissionStarted(formSubmission: FormSubmission): void @@ -199,6 +200,10 @@ export class FormSubmission { requestErrored(request: FetchRequest, error: Error) { this.result = { success: false, error } + dispatch("turbo:fetch-request-error", { + target: this.formElement, + detail: { request, error }, + }) this.delegate.formSubmissionErrored(this, error) } diff --git a/src/core/frames/frame_controller.ts b/src/core/frames/frame_controller.ts index d5ddd45fe..b3bb376fb 100644 --- a/src/core/frames/frame_controller.ts +++ b/src/core/frames/frame_controller.ts @@ -29,7 +29,7 @@ import { FrameRenderer } from "./frame_renderer" import { session } from "../index" import { isAction, Action } from "../types" import { VisitOptions } from "../drive/visit" -import { TurboBeforeFrameRenderEvent } from "../session" +import { TurboBeforeFrameRenderEvent, TurboFetchRequestErrorEvent } from "../session" import { StreamMessage } from "../streams/stream_message" export class FrameController @@ -247,6 +247,10 @@ export class FrameController requestErrored(request: FetchRequest, error: Error) { console.error(error) + dispatch("turbo:fetch-request-error", { + target: this.element, + detail: { request, error }, + }) this.resolveVisitPromise() } diff --git a/src/core/index.ts b/src/core/index.ts index 721d2989c..54b033821 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -19,6 +19,7 @@ export { TurboBeforeRenderEvent, TurboBeforeVisitEvent, TurboClickEvent, + TurboFetchRequestErrorEvent, TurboFrameLoadEvent, TurboFrameRenderEvent, TurboLoadEvent, diff --git a/src/core/session.ts b/src/core/session.ts index 992f49deb..a1a36fb1a 100644 --- a/src/core/session.ts +++ b/src/core/session.ts @@ -21,6 +21,7 @@ import { FrameElement } from "../elements/frame_element" import { FrameViewRenderOptions } from "./frames/frame_view" import { FetchResponse } from "../http/fetch_response" import { Preloader, PreloaderDelegate } from "./drive/preloader" +import { FetchRequest } from "../http/fetch_request" export type FormMode = "on" | "off" | "optin" export type TimingData = unknown @@ -30,6 +31,7 @@ export type TurboBeforeVisitEvent = CustomEvent<{ url: string }> export type TurboClickEvent = CustomEvent<{ url: string; originalEvent: MouseEvent }> export type TurboFrameLoadEvent = CustomEvent export type TurboBeforeFrameRenderEvent = CustomEvent<{ newFrame: FrameElement } & FrameViewRenderOptions> +export type TurboFetchRequestErrorEvent = CustomEvent<{ request: FetchRequest; error: Error }> export type TurboFrameRenderEvent = CustomEvent<{ fetchResponse: FetchResponse }> export type TurboLoadEvent = CustomEvent<{ url: string; timing: TimingData }> export type TurboRenderEvent = CustomEvent diff --git a/src/tests/fixtures/tabs.html b/src/tests/fixtures/tabs.html index cc9666c29..702cea0a1 100644 --- a/src/tests/fixtures/tabs.html +++ b/src/tests/fixtures/tabs.html @@ -1,5 +1,5 @@ - + Tabs diff --git a/src/tests/fixtures/test.js b/src/tests/fixtures/test.js index acb543061..b22b09248 100644 --- a/src/tests/fixtures/test.js +++ b/src/tests/fixtures/test.js @@ -48,6 +48,7 @@ "turbo:before-fetch-response", "turbo:visit", "turbo:before-frame-render", + "turbo:fetch-request-error", "turbo:frame-load", "turbo:frame-render", "turbo:reload" diff --git a/src/tests/functional/frame_navigation_tests.ts b/src/tests/functional/frame_navigation_tests.ts index 9dc1ae98c..a38e27b85 100644 --- a/src/tests/functional/frame_navigation_tests.ts +++ b/src/tests/functional/frame_navigation_tests.ts @@ -23,6 +23,13 @@ test("test frame navigation with exterior link", async ({ page }) => { await nextEventOnTarget(page, "frame", "turbo:frame-load") }) +test("test frame navigation emits fetch-request-error event when offline", async ({ page }) => { + await page.goto("/src/tests/fixtures/tabs.html") + await page.context().setOffline(true) + await page.click("#tab-2") + await nextEventNamed(page, "turbo:fetch-request-error") +}) + test("test promoted frame navigation updates the URL before rendering", async ({ page }) => { await page.goto("/src/tests/fixtures/tabs.html")