5 lines
10 KiB
JSON
5 lines
10 KiB
JSON
{
|
|
"id": "guide/testing-attribute-directives",
|
|
"title": "Testing Attribute Directives",
|
|
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/guide/testing-attribute-directives.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <a id=\"attribute-directive\"></a>\n<h1 id=\"testing-attribute-directives\">Testing Attribute Directives<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/testing-attribute-directives#testing-attribute-directives\"><i class=\"material-icons\">link</i></a></h1>\n<p>An <em>attribute directive</em> modifies the behavior of an element, component or another directive.\nIts name reflects the way the directive is applied: as an attribute on a host element.</p>\n<div class=\"alert is-helpful\">\n<p> For the sample app that the testing guides describe, see the <live-example name=\"testing\" embedded-style=\"\" nodownload=\"\">sample app</live-example>.</p>\n<p> For the tests features in the testing guides, see <live-example name=\"testing\" stackblitz=\"specs\" nodownload=\"\">tests</live-example>.</p>\n</div>\n<h2 id=\"testing-the-highlightdirective\">Testing the <code>HighlightDirective</code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/testing-attribute-directives#testing-the-highlightdirective\"><i class=\"material-icons\">link</i></a></h2>\n<p>The sample application's <code>HighlightDirective</code> sets the background color of an element\nbased on either a data bound color or a default color (lightgray).\nIt also sets a custom property of the element (<code>customProperty</code>) to <code>true</code>\nfor no reason other than to show that it can.</p>\n<code-example path=\"testing/src/app/shared/highlight.directive.ts\" header=\"app/shared/highlight.directive.ts\">\nimport { <a href=\"api/core/Directive\" class=\"code-anchor\">Directive</a>, <a href=\"api/core/ElementRef\" class=\"code-anchor\">ElementRef</a>, <a href=\"api/core/Input\" class=\"code-anchor\">Input</a>, <a href=\"api/core/OnChanges\" class=\"code-anchor\">OnChanges</a> } from '@angular/core';\n\n@<a href=\"api/core/Directive\" class=\"code-anchor\">Directive</a>({ selector: '[highlight]' })\n/**\n * Set backgroundColor for the attached element to highlight color\n * and set the element's customProperty to true\n */\nexport class HighlightDirective implements <a href=\"api/core/OnChanges\" class=\"code-anchor\">OnChanges</a> {\n\n defaultColor = 'rgb(211, 211, 211)'; // lightgray\n\n @<a href=\"api/core/Input\" class=\"code-anchor\">Input</a>('highlight') bgColor: string;\n\n constructor(private el: <a href=\"api/core/ElementRef\" class=\"code-anchor\">ElementRef</a>) {\n el.nativeElement.style.customProperty = true;\n }\n\n ngOnChanges() {\n this.el.nativeElement.style.backgroundColor = this.bgColor || this.defaultColor;\n }\n}\n\n\n</code-example>\n<p>It's used throughout the application, perhaps most simply in the <code>AboutComponent</code>:</p>\n<code-example path=\"testing/src/app/about/about.component.ts\" header=\"app/about/about.component.ts\">\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a> } from '@angular/core';\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n template: `\n <h2 highlight=\"skyblue\">About</h2>\n <h3>Quote of the day:</h3>\n <twain-quote></twain-quote>\n `\n})\nexport class AboutComponent { }\n\n\n</code-example>\n<p>Testing the specific use of the <code>HighlightDirective</code> within the <code>AboutComponent</code> requires only the techniques explored in the <a href=\"guide/testing-components-scenarios#nested-component-tests\">\"Nested component tests\"</a> section of <a href=\"guide/testing-components-scenarios\">Component testing scenarios</a>.</p>\n<code-example path=\"testing/src/app/about/about.component.spec.ts\" region=\"tests\" header=\"app/about/about.component.spec.ts\">\nbeforeEach(() => {\n fixture = TestBed.configureTestingModule({\n declarations: [ AboutComponent, HighlightDirective ],\n schemas: [ <a href=\"api/core/NO_ERRORS_SCHEMA\" class=\"code-anchor\">NO_ERRORS_SCHEMA</a> ]\n })\n .createComponent(AboutComponent);\n fixture.detectChanges(); // initial binding\n});\n\nit('should have skyblue <h2>', () => {\n const h2: HTMLElement = fixture.nativeElement.querySelector('h2');\n const bgColor = h2.style.backgroundColor;\n expect(bgColor).toBe('skyblue');\n});\n\n</code-example>\n<p>However, testing a single use case is unlikely to explore the full range of a directive's capabilities.\nFinding and testing all components that use the directive is tedious, brittle, and almost as unlikely to afford full coverage.</p>\n<p><em>Class-only tests</em> might be helpful,\nbut attribute directives like this one tend to manipulate the DOM.\nIsolated unit tests don't touch the DOM and, therefore,\ndo not inspire confidence in the directive's efficacy.</p>\n<p>A better solution is to create an artificial test component that demonstrates all ways to apply the directive.</p>\n<code-example path=\"testing/src/app/shared/highlight.directive.spec.ts\" region=\"test-component\" header=\"app/shared/highlight.directive.spec.ts (TestComponent)\">\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n template: `\n <h2 highlight=\"yellow\">Something Yellow</h2>\n <h2 highlight>The Default (Gray)</h2>\n <h2>No Highlight</h2>\n <input #box [highlight]=\"box.value\" value=\"cyan\"/>`\n})\nclass TestComponent { }\n\n</code-example>\n<div class=\"lightbox\">\n <img src=\"generated/images/guide/testing/highlight-directive-spec.png\" alt=\"HighlightDirective spec in action\" width=\"200\" height=\"159\">\n</div>\n<div class=\"alert is-helpful\">\n<p>The <code><input></code> case binds the <code>HighlightDirective</code> to the name of a color value in the input box.\nThe initial value is the word \"cyan\" which should be the background color of the input box.</p>\n</div>\n<p>Here are some tests of this component:</p>\n<code-example path=\"testing/src/app/shared/highlight.directive.spec.ts\" region=\"selected-tests\" header=\"app/shared/highlight.directive.spec.ts (selected tests)\">\nbeforeEach(() => {\n fixture = TestBed.configureTestingModule({\n declarations: [ HighlightDirective, TestComponent ]\n })\n .createComponent(TestComponent);\n\n fixture.detectChanges(); // initial binding\n\n // all elements with an attached HighlightDirective\n des = fixture.debugElement.queryAll(By.directive(HighlightDirective));\n\n // the h2 without the HighlightDirective\n bareH2 = fixture.debugElement.query(By.css('h2:not([highlight])'));\n});\n\n// color tests\nit('should have three highlighted elements', () => {\n expect(des.length).toBe(3);\n});\n\nit('should color 1st <h2> background \"yellow\"', () => {\n const bgColor = des[0].nativeElement.style.backgroundColor;\n expect(bgColor).toBe('yellow');\n});\n\nit('should color 2nd <h2> background w/ default color', () => {\n const dir = des[1].injector.get(HighlightDirective) as HighlightDirective;\n const bgColor = des[1].nativeElement.style.backgroundColor;\n expect(bgColor).toBe(dir.defaultColor);\n});\n\nit('should bind <input> background to value color', () => {\n // easier to work with nativeElement\n const input = des[2].nativeElement as HTMLInputElement;\n expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');\n\n input.value = 'green';\n\n // Dispatch a DOM event so that Angular responds to the input value change.\n // In older browsers, such as IE, you might need a CustomEvent instead. See\n // https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill\n input.dispatchEvent(new <a href=\"api/router/Event\" class=\"code-anchor\">Event</a>('input'));\n fixture.detectChanges();\n\n expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');\n});\n\n\nit('bare <h2> should not have a customProperty', () => {\n expect(bareH2.properties.customProperty).toBeUndefined();\n});\n\n</code-example>\n<p>A few techniques are noteworthy:</p>\n<ul>\n<li>\n<p>The <code>By.directive</code> predicate is a great way to get the elements that have this directive <em>when their element types are unknown</em>.</p>\n</li>\n<li>\n<p>The <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/:not\"><code>:not</code> pseudo-class</a>\nin <code>By.css('h2:not([highlight])')</code> helps find <code><h2></code> elements that <em>do not</em> have the directive.\n<code>By.css('*:not([highlight])')</code> finds <em>any</em> element that does not have the directive.</p>\n</li>\n<li>\n<p><code><a href=\"api/core/DebugElement#styles\" class=\"code-anchor\">DebugElement.styles</a></code> affords access to element styles even in the absence of a real browser, thanks to the <code><a href=\"api/core/DebugElement\" class=\"code-anchor\">DebugElement</a></code> abstraction.\nBut feel free to exploit the <code>nativeElement</code> when that seems easier or more clear than the abstraction.</p>\n</li>\n<li>\n<p>Angular adds a directive to the injector of the element to which it is applied.\nThe test for the default color uses the injector of the second <code><h2></code> to get its <code>HighlightDirective</code> instance\nand its <code>defaultColor</code>.</p>\n</li>\n<li>\n<p><code><a href=\"api/core/DebugElement#properties\" class=\"code-anchor\">DebugElement.properties</a></code> affords access to the artificial custom property that is set by the directive.</p>\n</li>\n</ul>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/testing\n-->\n<!-- links from this doc:\n - api/core/Component\n - api/core/DebugElement\n - api/core/DebugElement#properties\n - api/core/DebugElement#styles\n - api/core/Directive\n - api/core/ElementRef\n - api/core/Input\n - api/core/NO_ERRORS_SCHEMA\n - api/core/OnChanges\n - api/router/Event\n - guide/testing-attribute-directives#testing-attribute-directives\n - guide/testing-attribute-directives#testing-the-highlightdirective\n - guide/testing-components-scenarios\n - guide/testing-components-scenarios#nested-component-tests\n - https://developer.mozilla.org/en-US/docs/Web/CSS/:not\n - https://github.com/angular/angular/edit/master/aio/content/guide/testing-attribute-directives.md?message=docs%3A%20describe%20your%20change...\n-->"
|
|
} |