From 88d479295b848431d5d6fc3610c6fc36d76474ac Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Thu, 4 Feb 2021 11:24:02 -0500 Subject: [PATCH] Add test coverage for turbo-frame[busy] During the request lifecycle, `` elements will toggle the `[busy]` boolean attribute to true when the request starts, and then remove it when the request ends. This commit adds functional test coverage for that behavior. --- src/core/frames/frame_controller.ts | 6 ++++-- src/tests/fixtures/form.html | 7 ++++++- src/tests/fixtures/test.js | 9 +++++++++ src/tests/functional/form_submission_tests.ts | 19 ++++++++++++++++++- src/tests/functional/frame_tests.ts | 7 +++++++ src/tests/helpers/turbo_drive_test_case.ts | 11 +++++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/core/frames/frame_controller.ts b/src/core/frames/frame_controller.ts index 61a316b85..be181ea53 100644 --- a/src/core/frames/frame_controller.ts +++ b/src/core/frames/frame_controller.ts @@ -178,7 +178,8 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest // Form submission delegate formSubmissionStarted(formSubmission: FormSubmission) { - + const frame = this.findFrameElement(formSubmission.formElement) + frame.setAttribute("busy", "") } formSubmissionSucceededWithResponse(formSubmission: FormSubmission, response: FetchResponse) { @@ -195,7 +196,8 @@ export class FrameController implements AppearanceObserverDelegate, FetchRequest } formSubmissionFinished(formSubmission: FormSubmission) { - + const frame = this.findFrameElement(formSubmission.formElement) + frame.removeAttribute("busy") } // View delegate diff --git a/src/tests/fixtures/form.html b/src/tests/fixtures/form.html index f37246f5a..d724d5ad0 100644 --- a/src/tests/fixtures/form.html +++ b/src/tests/fixtures/form.html @@ -115,10 +115,15 @@
-
+
+ +
+ + +

Frame: Form

diff --git a/src/tests/fixtures/test.js b/src/tests/fixtures/test.js index 40031bd16..c1d14a267 100644 --- a/src/tests/fixtures/test.js +++ b/src/tests/fixtures/test.js @@ -10,6 +10,15 @@ eventLogs.push([event.type, event.detail]) } + window.mutationLogs = [] + + new MutationObserver((mutations) => { + for (const { attributeName, oldValue, target } of mutations.filter(({ type }) => type == "attributes")) { + if (target instanceof HTMLElement) { + mutationLogs.push([attributeName, target.id, target.getAttribute(attributeName)]) + } + } + }).observe(document, { subtree: true, childList: true, attributes: true }) })([ "turbo:before-cache", "turbo:before-render", diff --git a/src/tests/functional/form_submission_tests.ts b/src/tests/functional/form_submission_tests.ts index f39c68c4a..1652111a0 100644 --- a/src/tests/functional/form_submission_tests.ts +++ b/src/tests/functional/form_submission_tests.ts @@ -185,6 +185,23 @@ export class FormSubmissionTests extends TurboDriveTestCase { this.assert.equal(await this.attributeForSelector("#frame", "src"), url.href, "redirects the target frame") } + async "test frame form submission toggles the ancestor frame's [busy] attribute"() { + await this.clickSelector("#frame form.redirect input[type=submit]") + + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), "", "sets [busy] on the #frame") + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), null, "removes [busy] from the #frame") + } + + async "test frame form submission toggles the target frame's [busy] attribute"() { + await this.clickSelector('#targets-frame form.frame [type="submit"]') + + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), "", "sets [busy] on the #frame") + + const title = await this.querySelector("#frame h2") + this.assert.equal(await title.getVisibleText(), "Frame: Loaded") + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), null, "removes [busy] from the #frame") + } + async "test frame form submission with empty created response"() { const htmlBefore = await this.outerHTMLForSelector("#frame") const button = await this.querySelector("#frame form.created input[type=submit]") @@ -292,7 +309,7 @@ export class FormSubmissionTests extends TurboDriveTestCase { async "test form submission targets disabled frame"() { this.remote.execute(() => document.getElementById("frame")?.setAttribute("disabled", "")) - await this.clickSelector('#targets-frame [type="submit"]') + await this.clickSelector('#targets-frame form.one [type="submit"]') await this.nextBody this.assert.equal(await this.pathname, "/src/tests/fixtures/one.html") diff --git a/src/tests/functional/frame_tests.ts b/src/tests/functional/frame_tests.ts index 5173f7d10..36c7d7da1 100644 --- a/src/tests/functional/frame_tests.ts +++ b/src/tests/functional/frame_tests.ts @@ -25,6 +25,13 @@ export class FrameTests extends TurboDriveTestCase { this.assert.equal(otherEvents.length, 0, "no more events") } + async "test following a link driving a frame toggles the [busy] attribute"() { + await this.clickSelector("#hello a") + + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), "", "sets [busy] on the #frame") + this.assert.equal(await this.nextAttributeMutationNamed("frame", "busy"), null, "removes [busy] from the #frame") + } + async "test following a link to a page without a matching frame results in an empty frame"() { await this.clickSelector("#missing a") await this.nextBeat diff --git a/src/tests/helpers/turbo_drive_test_case.ts b/src/tests/helpers/turbo_drive_test_case.ts index 5124e0708..5d852b70b 100644 --- a/src/tests/helpers/turbo_drive_test_case.ts +++ b/src/tests/helpers/turbo_drive_test_case.ts @@ -3,9 +3,11 @@ import { RemoteChannel } from "./remote_channel" import { Element } from "@theintern/leadfoot" type EventLog = [string, any] +type MutationLog = [string, string, string | null] export class TurboDriveTestCase extends FunctionalTestCase { eventLogChannel: RemoteChannel = new RemoteChannel(this.remote, "eventLogs") + mutationLogChannel: RemoteChannel = new RemoteChannel(this.remote, "mutationLogs") lastBody?: Element async beforeTest() { @@ -33,6 +35,15 @@ export class TurboDriveTestCase extends FunctionalTestCase { return record[1] } + async nextAttributeMutationNamed(elementId: string, attributeName: string): Promise { + let record: MutationLog | undefined + while (!record) { + const records = await this.mutationLogChannel.read(1) + record = records.find(([attribute, id]) => id == elementId && attribute == attributeName) + } + return record[2] + } + get nextBody(): Promise { return (async () => { let body