From 9ea5d678df098c4fa2684543f689c2f3a1a81b64 Mon Sep 17 00:00:00 2001 From: Jason Staten Date: Mon, 4 Nov 2019 16:02:13 -0700 Subject: [PATCH] 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. --- src/__tests__/render.js | 27 +++++++++++++++++++++++++++ src/pure.js | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/__tests__/render.js b/src/__tests__/render.js index d288b0a..4e1bc05 100644 --- a/src/__tests__/render.js +++ b/src/__tests__/render.js @@ -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: ` +

Hi + + `, + }) + + expect(() => render(``)).toThrow( + 'Unknown component/directive "ATL-CHILD"', + ) +}) + +test('suppresses unknown custom elements error', () => { + angular.module('atl').component('atlParent', { + template: ` +

Hi + + `, + }) + + const {container} = render(``, { + ignoreUnknownElements: true, + }) + expect(container.querySelector('atl-child')).toBeDefined() +}) diff --git a/src/pure.js b/src/pure.js index dfa6f46..ab26012 100644 --- a/src/pure.js +++ b/src/pure.js @@ -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([