All files / src/scripts/instrumentor/patcher/form observer.ts

100% Statements 19/19
100% Branches 14/14
100% Functions 8/8
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66          1922x       1714x                 89x 790x   9168x 208x 208x       9168x 5806x 5806x   1714x 341x 341x       1714x 1714x 144x 144x               89x                             693x    
import { injectSignalsElement } from './injectSignalsElement'
import { PatcherContext } from '../context'
import { logger } from '../../../shared/logger'
 
function isForm(element: Node): element is HTMLFormElement {
  return element instanceof HTMLFormElement
}
 
function isElement(element: Node): element is HTMLElement {
  return element instanceof HTMLElement
}
 
/**
 * Observes and tracks changes to forms on the document, such as modifications to the "action" attribute or the addition of new form elements.
 *
 * @param {PatcherContext} ctx - The context object used in the handler for observed form changes.
 */
export function observeForms(ctx: PatcherContext) {
  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      // Track changes to the "action" or "method" attributes in existing forms
      if (mutation.type === 'attributes' && isForm(mutation.target)) {
        logger.debug('Form action changed')
        onFormChange(mutation.target, ctx)
      }
 
      // Track new forms added to the page
      if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
        mutation.addedNodes.forEach((node) => {
          if (node.nodeType === Node.ELEMENT_NODE && isElement(node)) {
            // Check if the added node itself is a form
            if (isForm(node)) {
              logger.debug('New form added')
              onFormChange(node, ctx)
            }
 
            // Check if the added node contains any forms
            const forms = node.querySelectorAll('form')
            forms.forEach((form) => {
              logger.debug('New form added (nested):')
              onFormChange(form, ctx)
            })
          }
        })
      }
    })
  })
 
  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeFilter: ['action', 'method', 'enctype'],
  })
}
 
/**
 * Handles changes to a given form element and applies the necessary patches.
 *
 * @param {HTMLFormElement} form - The form element that has changed.
 * @param {PatcherContext} ctx - Context information used to apply the patch.
 */
function onFormChange(form: HTMLFormElement, ctx: PatcherContext) {
  injectSignalsElement(form, ctx)
}