Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fire turbo:click event when submitting <form method="get"> #421

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions src/core/drive/navigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,10 @@ export class Navigator {
this.currentVisit.start()
}

submitForm(form: HTMLFormElement, submitter?: HTMLElement) {
submitForm(formSubmission: FormSubmission) {
this.stop()
this.formSubmission = new FormSubmission(this, form, submitter, true)

if (this.formSubmission.isIdempotent) {
this.proposeVisit(this.formSubmission.fetchRequest.url, { action: this.getActionForFormSubmission(this.formSubmission) })
} else {
this.formSubmission.start()
}
this.formSubmission = formSubmission
this.formSubmission.start()
}

stop() {
Expand Down
20 changes: 16 additions & 4 deletions src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Action, Position, StreamSource, isAction } from "./types"
import { dispatch } from "../util"
import { PageView, PageViewDelegate } from "./drive/page_view"
import { Visit, VisitOptions } from "./drive/visit"
import { FormSubmission } from "./drive/form_submission"
import { PageSnapshot } from "./drive/page_snapshot"
import { FrameElement } from "../elements/frame_element"
import { FetchResponse } from "../http/fetch_response"
Expand Down Expand Up @@ -135,7 +136,7 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin
}

followedLinkToLocation(link: Element, location: URL) {
const action = this.getActionForLink(link)
const action = this.getActionForLink(link) || "advance"
this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action })
}

Expand Down Expand Up @@ -197,7 +198,18 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin
}

formSubmitted(form: HTMLFormElement, submitter?: HTMLElement) {
this.navigator.submitForm(form, submitter)
const formSubmission = new FormSubmission(this.navigator, form, submitter, true)
const { isIdempotent, fetchRequest: { url } } = formSubmission

if (isIdempotent) {
const action = submitter && this.applicationAllowsFollowingLinkToLocation(submitter, url) ?
this.getActionForLink(submitter) || this.getActionForLink(form) :
this.getActionForLink(form)

this.visit(url, { action: action || "advance" })
} else {
this.navigator.submitForm(formSubmission)
}
}

// Page observer delegate
Expand Down Expand Up @@ -330,9 +342,9 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin

// Private

getActionForLink(link: Element): Action {
private getActionForLink(link: Element): Action | null {
const action = link.getAttribute("data-turbo-action")
return isAction(action) ? action : "advance"
return isAction(action) ? action : null
}

get snapshot() {
Expand Down
7 changes: 4 additions & 3 deletions src/tests/fixtures/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@
<section id="main" style="height: 200vh">
<h1>Navigation</h1>
<p><a id="same-origin-unannotated-link" href="/src/tests/fixtures/one.html">Same-origin unannotated link</a></p>
<p><form id="same-origin-unannotated-form" method="get" action="/src/tests/fixtures/one.html"><button>Same-origin unannotated form</button></form></p>
<p><form id="same-origin-unannotated-form" method="get" action="/src/tests/fixtures/one.html"><button id="same-origin-unannotated-submitter">Same-origin unannotated form</button></form></p>
<p><form id="same-origin-unannotated-form-no-action"><button id="same-origin-submitter-formaction" formaction="/src/tests/fixtures/one.html">Same-origin unannotated form formaction</button></form></p>
<p><a id="same-origin-replace-link" href="/src/tests/fixtures/one.html" data-turbo-action="replace">Same-origin data-turbo-action=replace link</a></p>
<p><form id="same-origin-replace-form" method="get" action="/src/tests/fixtures/one.html" data-turbo-action="replace"><button>Same-origin data-turbo-action=replace form</button></form></p>
<p><form id="same-origin-replace-form-submitter" method="get" action="/src/tests/fixtures/one.html"><button data-turbo-action="replace">Same-origin data-turbo-action=replace form</button></form></p>
<p><form id="same-origin-replace-form" method="get" action="/src/tests/fixtures/one.html" data-turbo-action="replace"><button>Same-origin form[data-turbo-action=replace]</button></form></p>
<p><form id="same-origin-replace-form-submitter" method="get" action="/src/tests/fixtures/one.html"><button data-turbo-action="replace">Same-origin form button[data-turbo-action=replace]</button></form></p>
<p><a id="same-origin-false-link" href="/src/tests/fixtures/one.html" data-turbo="false">Same-origin data-turbo=false link</a></p>
<p data-turbo="false"><a id="same-origin-unannotated-link-inside-false-container" href="/src/tests/fixtures/one.html">Same-origin unannotated link inside data-turbo=false container</a></p>
<p data-turbo="false"><a id="same-origin-true-link-inside-false-container" href="/src/tests/fixtures/one.html" data-turbo="true">Same-origin data-turbo=true link inside data-turbo=false container</a></p>
Expand Down
1 change: 1 addition & 0 deletions src/tests/fixtures/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
}
}).observe(document, { subtree: true, childList: true, attributes: true })
})([
"turbo:click",
"turbo:before-cache",
"turbo:before-render",
"turbo:before-visit",
Expand Down
52 changes: 49 additions & 3 deletions src/tests/functional/navigation_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ export class NavigationTests extends TurboDriveTestCase {
this.assert.equal(await this.visitAction, "advance")
}

async "test following a same-origin location link"() {
await this.drainEventLog

const link = await this.querySelector("#same-origin-unannotated-link")
const href = await link.getProperty("href")
await link.click()
await this.nextBody

const [ eventName, { url }, id ] = await this.nextEvent()

this.assert.equal(eventName, "turbo:click")
this.assert.equal(url, href, "turbo:click detail.url is href")
this.assert.equal(id, "same-origin-unannotated-link", "turbo:click target is link")
}

async "test following a same-origin unannotated custom element link"() {
await this.nextBeat
await this.remote.execute(() => {
Expand All @@ -49,13 +64,44 @@ export class NavigationTests extends TurboDriveTestCase {
this.assert.equal(await this.visitAction, "advance")
}

async "test following a same-origin unannotated form[method=GET]"() {
this.clickSelector("#same-origin-unannotated-form button")
async "test submitting a same-origin unannotated form[method=GET]"() {
this.clickSelector("#same-origin-unannotated-submitter")
await this.nextBody
this.assert.equal(await this.pathname, "/src/tests/fixtures/one.html")
this.assert.equal(await this.visitAction, "advance")
}

async "test submitting a same-origin form by clicking a submitter"() {
await this.drainEventLog

const form = await this.querySelector("#same-origin-unannotated-form")
const action = await form.getProperty("action")
const button = await this.querySelector("#same-origin-unannotated-submitter")
await button.click()
await this.nextBody

const [ eventName, { url }, id ] = await this.nextEvent()

this.assert.equal(eventName, "turbo:click")
this.assert.equal(url, action, "turbo:click detail.url is href")
this.assert.equal(id, "same-origin-unannotated-submitter", "turbo:click target is submitter")
}

async "test submitting a same-origin form by clicking a submitter with formaction"() {
await this.drainEventLog

const button = await this.querySelector("#same-origin-submitter-formaction")
const action = await this.expandURL(await button.getProperty("formAction"))
await button.click()
await this.nextBody

const [ eventName, { url }, id ] = await this.nextEvent()

this.assert.equal(eventName, "turbo:click")
this.assert.equal(url, action, "turbo:click detail.url is formaction")
this.assert.equal(id, "same-origin-submitter-formaction", "turbo:click target is submitter")
}

async "test following a same-origin data-turbo-action=replace link"() {
this.clickSelector("#same-origin-replace-link")
await this.nextBody
Expand Down Expand Up @@ -232,6 +278,7 @@ export class NavigationTests extends TurboDriveTestCase {

async "test same-page anchor visits do not trigger visit events"() {
const events = [
"turbo:click",
"turbo:before-visit",
"turbo:visit",
"turbo:before-cache",
Expand All @@ -241,7 +288,6 @@ export class NavigationTests extends TurboDriveTestCase {
]

for (const eventName in events) {
await this.goToLocation("/src/tests/fixtures/navigation.html")
await this.clickSelector('a[href="#main"]')
this.assert.ok(await this.noNextEventNamed(eventName), `same-page links do not trigger ${eventName} events`)
}
Expand Down
8 changes: 8 additions & 0 deletions src/tests/helpers/functional_test_case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,18 @@ export class FunctionalTestCase extends InternTestCase {
return await this.remote.execute(callback, args)
}

async expandURL(pathname: string | null | undefined) {
return await this.evaluate((pathname) => new URL(pathname || "", document.baseURI), [pathname])
}

get head(): Promise<Element> {
return this.evaluate(() => document.head as any)
}

get url(): Promise<URL> {
return this.evaluate(() => new URL(location.href))
}

get body(): Promise<Element> {
return this.evaluate(() => document.body as any)
}
Expand Down
8 changes: 8 additions & 0 deletions src/tests/helpers/turbo_drive_test_case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export class TurboDriveTestCase extends FunctionalTestCase {
})()
}

async nextEvent(): Promise<EventLog> {
let record: EventLog | undefined
while (!record) {
[ record ] = await this.eventLogChannel.read(1)
}
return record
}

async nextEventNamed(eventName: string): Promise<any> {
let record: EventLog | undefined
while (!record) {
Expand Down