All files / src queries.js

98.15% Statements 53/54
92.68% Branches 38/41
100% Functions 19/19
98.15% Lines 53/54

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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156              61x 92x   61x       3x 2x         27x 31x       9x 9x     14x 15x         17x 44x         11x   11x 2x       9x       9x   2x 2x 7x   2x     2x 5x   3x   2x         44x       44x   82x   49x                   5x 5x 1x       4x       2x 2x 1x       1x       8x 8x 3x 3x 2x       1x     5x       13x 13x 1x       12x       2x 2x 1x   1x                                  
import {matches} from './utils'
 
// Here are the queries for the library.
// The queries here should only be things that are accessible to both users who are using a screen reader
// and those who are not using a screen reader (with the exception of the data-testid attribute query).
function query(container, tags, getter, match) {
  const found =
    Array.from(container.findAll(tags).wrappers).find(node =>
      matches(getter(node.element), node.element, match))
 
  return found && found.element ? found.element : null
}
 
function queryByAltText(container, alt) {
  return query (
    container, 'img, input, area', elem => elem.getAttribute('alt'), alt
  )
}
 
function queryByAttribute(attribute, container, text) {
  return query (
    container, `[${attribute}]`, elem => elem.getAttribute(attribute), text
  )
}
 
const queryByPlaceholderText = queryByAttribute.bind(null, 'placeholder')
const queryByTestId = queryByAttribute.bind(null, 'data-testid')
 
function queryLabelByText(container, text) {
  return query (
    container, 'label', elem => elem.textContent, text
  )
}
 
function queryByText(container, text, { selector = '*' } = {}) {
  return query (
    container, selector, elem => getText(elem), text
  )
}
 
function queryByLabelText(container, text, { selector = '*' } = {}) {
  const label = queryLabelByText(container, text)
 
  if (!label) {
    return queryByAttribute('aria-label', container, text)
  }
 
  /* istanbul ignore if */
  if (label.control) {
    // appears to be unsupported in jsdom: https://github.com/jsdom/jsdom/issues/2175
    // but this would be the proper way to do things
    return label.control
  } else if (label.getAttribute('for')) {
    // <label for="someId">text</label><input id="someId" />
    const found = container.find(`#${label.getAttribute('for')}`)
    return found && found.element ? found.element : null
  } else if (label.getAttribute('id')) {
    // <label id="someId">text</label><input aria-labelledby="someId" />
    const found = container.find(
      `[aria-labelledby="${label.getAttribute('id')}"]`,
    )
    return found && found.element ? found.element : null
  } else if (label.childNodes.length) {
    // <label>text: <input /></label>
    return label.querySelector(selector)
  } else {
    return null
  }
}
 
function getText(node) {
  Iif (!node) {
    return null
  }
 
  return Array.from(node.childNodes)
    .filter(
      child => child.nodeType === Node.TEXT_NODE && Boolean(child.textContent),
    )
    .map(c => c.textContent)
    .join(' ')
}
 
// getters
// the reason we're not dynamically generating these functions that look so similar:
// 1. The error messages are specific to each one and depend on arguments
// 2. The stack trace will look better because it'll have a helpful method name.
 
function getByTestId(container, id, ...rest) {
  const el = queryByTestId(container, id, ...rest)
  if (!el) {
    throw new Error(
      `Unable to find an element by: [data-testid="${id}"]`,
    )
  }
  return el
}
 
function getByPlaceholderText(container, text, ...rest) {
  const el = queryByPlaceholderText(container, text, ...rest)
  if (!el) {
    throw new Error(
      `Unable to find an element with the placeholder text of: ${text}`,
    )
  }
  return el
}
 
function getByLabelText(container, text, ...rest) {
  const el = queryByLabelText(container, text, ...rest)
  if (!el) {
    const label = queryLabelByText(container, text)
    if (label) {
      throw new Error(
        `Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`,
      )
    } else {
      throw new Error(`Unable to find a label with the text of: ${text}`)
    }
  }
  return el
}
 
function getByText(container, text, ...rest) {
  const el = queryByText(container, text, ...rest)
  if (!el) {
    throw new Error(
      `Unable to find an element with the text: ${text}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.`,
    )
  }
  return el
}
 
function getByAltText(container, alt) {
  const el = queryByAltText(container, alt)
  if (!el) {
    throw new Error(`Unable to find an element with the alt text: ${alt}`)
  }
  return el
}
 
export {
  queryByPlaceholderText,
  getByPlaceholderText,
  queryByText,
  getByText,
  queryByLabelText,
  getByLabelText,
  queryByAltText,
  getByAltText,
  queryByTestId,
  getByTestId,
}
 
/* eslint complexity:["error", 14] */