From d98d5b756648ad696eef30e1fdaba55197b6bd7d Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Fri, 16 Oct 2026 02:32:32 +0900 Subject: [PATCH] fix(browse): no action and method for a form that JavaScript handles --- README.md | 2 +- src/commands/browse/browser.test.ts | 12 ++++++++++++ src/commands/browse/browser.ts | 11 ++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 51c30d6..11159bc 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ happy-dom is not a dependency of Hono CLI. Install it in the project; without it } ``` -`console` has the page's console messages and its uncaught errors. An action that finds nothing stops the run with `"ok": false` and an `error`. +A form that JavaScript handles (e.g. a React form action) has only `fields`, with no `action` and `method`: it never submits itself. A field without a `name` or label is keyed by its placeholder, `aria-label`, or `id`. `console` has the page's console messages and its uncaught errors. An action that finds nothing stops the run with `"ok": false` and an `error`. ### `snapshot` diff --git a/src/commands/browse/browser.test.ts b/src/commands/browse/browser.test.ts index 5fc1227..e69bd29 100644 --- a/src/commands/browse/browser.test.ts +++ b/src/commands/browse/browser.test.ts @@ -30,6 +30,13 @@ const app = () => { const { title } = await c.req.parseBody() return c.redirect(`/?added=${title}`, 303) }) + app.get('/react-form', (c) => + c.html( + page( + `
` + ) + ) + ) app.get('/module', (c) => c.html(page('

waiting

')) ) @@ -80,6 +87,11 @@ describe('browse', () => { expect(after.text).toContain('milk') }) + it('reports no action and method for a form that JavaScript handles', async () => { + const result = await browse(happyDom, app(), '/react-form', []) + expect(result.forms).toEqual([{ fields: { Search: '' } }]) + }) + it('stops at an action that finds nothing', async () => { const result = await browse(happyDom, app(), '/', [ { type: 'fill', target: 'nothing', value: 'x' }, diff --git a/src/commands/browse/browser.ts b/src/commands/browse/browser.ts index f1f57f2..852e2f8 100644 --- a/src/commands/browse/browser.ts +++ b/src/commands/browse/browser.ts @@ -19,7 +19,8 @@ export interface BrowseResult { url: string title: string text: string - forms: { action: string; method: string; fields: Record }[] + /** No action and method when JavaScript handles the submit */ + forms: { action?: string; method?: string; fields: Record }[] console: { type: string; text: string }[] actions: { action: string; ok: boolean; error?: string }[] note: string @@ -354,9 +355,13 @@ export const browse = async ( fields[namesOf(el)[0] ?? el.tagName.toLowerCase()] = field.value } const action = form.getAttribute('action') ?? '' + // React puts this placeholder when a function handles the submit: + // the form never sends itself, so it has no method to report + if (action.startsWith('javascript:')) { + return { fields } + } return { - // React puts a placeholder here when a function handles the submit - action: action.startsWith('javascript:') ? '' : action, + action, method: (form.getAttribute('method') ?? 'get').toUpperCase(), fields, }