-
Notifications
You must be signed in to change notification settings - Fork 441
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
Handle <turbo-stream>
name collisions with <form>
controls
#1037
base: main
Are you sure you want to change the base?
Conversation
Closes [hotwired#929][] At the very least, we should provide guidance based on MDN's [Issues with Naming Elements][]: > Some names will interfere with JavaScript access to the form's > properties and elements. > > For example: > > `<input name="id">` will take precedence over `<form id="…">`. This > means that `form.id` will not refer to the form's `id`, but to the > element whose `name` is `"id"`. This will be the case with any other > form properties, such as `<input name="action">` or `<input > name="post">`. `<input name="elements">` will render the form's > `elements` collection inaccessible. The reference `form.elements` will > now refer to the individual element. > > To avoid such problems with element names: > > * Always use the `elements` collection to avoid ambiguity between an > element `name` and a form property. > * Never use `"elements"` as an element name. To guard against future regressions, this commit adds coverage for the `stream.html` fixture page by rendering a `<form>` element with `<input type="hidden">` elements whose names match each `StreamActions` function. [hotwired#929]: hotwired#929 [Issues with Naming Elements]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement#issues_with_naming_elements
To re-iterate #929 (comment): I'm not sure if there's a predictable way to work around the underlying problem. One thing we could do is detect a collision (with something like It seems that any other counter-measure is kicking the can down the road. For example, |
test("test receiving an [action=after] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="after" target="form_with_action_names"> | ||
<template><p id="after_form_with_action_names">After</p></template> | ||
</turbo-stream> | ||
`) | ||
) | ||
|
||
assert.equal(await page.textContent("#after_form_with_action_names"), "After") | ||
}) | ||
|
||
test("test receiving an [action=append] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="append" target="form_with_action_names"> | ||
<template>Append</template> | ||
</turbo-stream> | ||
`) | ||
) | ||
await nextBeat() | ||
|
||
assert.include(await page.textContent("#form_with_action_names"), "Append") | ||
}) | ||
|
||
test("test receiving an [action=before] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="before" target="form_with_action_names"> | ||
<template><p id="before_form_with_action_names">Before</p></template> | ||
</turbo-stream> | ||
`) | ||
) | ||
|
||
assert.equal(await page.textContent("#before_form_with_action_names"), "Before") | ||
}) | ||
|
||
test("test receiving an [action=prepend] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="prepend" target="form_with_action_names"> | ||
<template>Prepend</template> | ||
</turbo-stream> | ||
`) | ||
) | ||
await nextBeat() | ||
|
||
assert.include(await page.textContent("#form_with_action_names"), "Prepend") | ||
}) | ||
|
||
test("test receiving an [action=remove] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="remove" target="form_with_action_names"></turbo-stream> | ||
`) | ||
) | ||
|
||
assert.notOk(await waitUntilNoSelector(page, "#form_with_action_names"), "removes target element") | ||
}) | ||
|
||
test("test receiving an [action=replace] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="replace" target="form_with_action_names"> | ||
<template><div id="form_with_action_names"></div></template> | ||
</turbo-stream> | ||
`) | ||
) | ||
|
||
assert.notOk(await hasSelector(page, "form#form_with_action_names")) | ||
assert.ok(await hasSelector(page, "div#form_with_action_names")) | ||
}) | ||
|
||
test("test receiving an [action=update] targeting a form", async ({ page }) => { | ||
await page.evaluate(() => | ||
window.Turbo.renderStreamMessage(` | ||
<turbo-stream action="update" target="form_with_action_names"> | ||
<template><input name="updated_field"></template> | ||
</turbo-stream> | ||
`) | ||
) | ||
await nextBeat() | ||
|
||
assert.ok(await hasSelector(page, "#form_with_action_names [name=updated_field]")) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests should probably be covered by unit tests, like in https://github.com/hotwired/turbo/pull/880/files#diff-f9df76d4df4de3858e7298e7f36d9e3acc8e7f07af6759c4f66c80bcc309b465.
#880 is trying to solve this too |
Closes #879
Closes #929
At the very least, we should provide guidance based on MDN's Issues with Naming Elements:
To guard against future regressions, this commit adds coverage for the
stream.html
fixture page by rendering a<form>
element with<input type="hidden">
elements whose names match eachStreamActions
function.