~statianzo/angularjs-testing-library

9ea5d678df098c4fa2684543f689c2f3a1a81b64 — Jason Staten 4 years ago 14271ad
feat: render throws on unknown elements

BREAKING CHANGE: `render` will throw if any custom elements (tag name
containing `-`) are found not registered in the injector.

This helps avoid the scenario where a child component/directive under
test isn't getting tested.

The behavior can be disabled via the `ignoreUnknownElements` option.
2 files changed, 61 insertions(+), 1 deletions(-)

M src/__tests__/render.js
M src/pure.js
M src/__tests__/render.js => src/__tests__/render.js +27 -0
@@ 41,3 41,30 @@ test('assigns to scope', () => {
    'Unable to find an element with the text: Hello World.',
  )
})

test('throws on unknown custom elements', () => {
  angular.module('atl').component('atlParent', {
    template: `
      <h1>Hi</hz>
      <atl-child></atl-child>
    `,
  })

  expect(() => render(`<atl-parent></atl-parent>`)).toThrow(
    'Unknown component/directive "ATL-CHILD"',
  )
})

test('suppresses unknown custom elements error', () => {
  angular.module('atl').component('atlParent', {
    template: `
      <h1>Hi</hz>
      <atl-child></atl-child>
    `,
  })

  const {container} = render(`<atl-parent></atl-parent>`, {
    ignoreUnknownElements: true,
  })
  expect(container.querySelector('atl-child')).toBeDefined()
})

M src/pure.js => src/pure.js +34 -1
@@ 10,7 10,16 @@ import {
const mountedContainers = new Set()
const mountedScopes = new Set()

function render(ui, {container, baseElement = container, queries, scope} = {}) {
function render(
  ui,
  {
    container,
    baseElement = container,
    queries,
    scope,
    ignoreUnknownElements,
  } = {},
) {
  if (!baseElement) {
    // default to document.body instead of documentElement to avoid output of potentially-large
    // head elements (such as JSS style blocks) in debug output


@@ 37,6 46,10 @@ function render(ui, {container, baseElement = container, queries, scope} = {}) {

  $scope.$digest()

  if (!ignoreUnknownElements) {
    assertNoUnknownElements(container)
  }

  return {
    container,
    baseElement,


@@ 85,6 98,26 @@ function cleanupScope(scope) {
  mountedScopes.delete(scope)
}

function toCamel(s) {
  return s
    .toLowerCase()
    .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
}

function assertNoUnknownElements(element) {
  const {tagName} = element
  if (tagName.includes('-')) {
    const $injector = getAngularService('$injector')
    const directiveName = `${toCamel(tagName)}Directive`
    if (!$injector.has(directiveName)) {
      throw Error(
        `Unknown component/directive "${tagName}". Are you missing an import?`,
      )
    }
  }
  Array.from(element.children).forEach(assertNoUnknownElements)
}

function getAngularService(name) {
  let service
  angular.mock.inject([