fix(docs-infra): fix the `component-interaction` example e2e tests to run in prod mode (#39001)

Previously, the `component-interaction` docs example was configured to
run e2e tests on CI in development mode (in contrast to the default for
all docs examples, which is to run e2e tests in production mode). This
was necessary due to the following reasons:
- One of the components, `CountdownTimerComponent`, which is used by
  `CountdownLocalVarParentComponent` and
  `CountdownViewChildParentComponent`, was triggering a periodic
  asynchronous task (via `setInterval()`), which prevented the app from
  stabilizing and caused tests to fail.
- In order to prevent this from happening, the example's `AppModule` had
  special provisioning to not include the problematic components in its
  declarations when testing.
- Since this had to be determined dynamically at runtime (via inspecting
  the URL query params), the `AppModule`'s config could not be
  statically evaluated in AOT compilation.

This commit fixes the example to make it compatible with AOT compilation
and removes the custom test command from its `example-config.json`
(allowing it to be run with the default e2e test command, i.e. in
production mode).

PR Close #39001
This commit is contained in:
George Kalpakas 2020-09-25 16:31:31 +03:00 committed by Alex Rickabaugh
parent c5e178997c
commit 2b6b9c95f9
5 changed files with 27 additions and 55 deletions

View File

@ -2,11 +2,7 @@ import { browser, element, by } from 'protractor';
describe('Component Communication Cookbook Tests', () => { describe('Component Communication Cookbook Tests', () => {
// Note: '?e2e' which app can read to know it is running in protractor beforeAll(() => browser.get(browser.baseUrl));
// e.g. `if (!/e2e/.test(location.search)) { ...`
beforeAll(() => {
browser.get('?e2e');
});
describe('Parent-to-child communication', () => { describe('Parent-to-child communication', () => {
// #docregion parent-to-child // #docregion parent-to-child
@ -156,11 +152,11 @@ describe('Component Communication Cookbook Tests', () => {
// Can't run timer tests in protractor because // Can't run timer tests in protractor because
// interaction w/ zones causes all tests to freeze & timeout. // interaction w/ zones causes all tests to freeze & timeout.
xdescribe('Parent calls child via local var', () => { xdescribe('Parent calls child via local var', () => {
countDownTimerTests('countdown-parent-lv'); countDownTimerTests('app-countdown-parent-lv');
}); });
xdescribe('Parent calls ViewChild', () => { xdescribe('Parent calls ViewChild', () => {
countDownTimerTests('countdown-parent-vc'); countDownTimerTests('app-countdown-parent-vc');
}); });
function countDownTimerTests(parentTag: string) { function countDownTimerTests(parentTag: string) {
@ -168,11 +164,15 @@ describe('Component Communication Cookbook Tests', () => {
// ... // ...
it('timer and parent seconds should match', () => { it('timer and parent seconds should match', () => {
const parent = element(by.tagName(parentTag)); const parent = element(by.tagName(parentTag));
const startButton = parent.element(by.tagName('button')).get(0);
const message = parent.element(by.tagName('app-countdown-timer')).getText(); const message = parent.element(by.tagName('app-countdown-timer')).getText();
startButton.click().then(() => {
browser.sleep(10); // give `seconds` a chance to catchup with `message` browser.sleep(10); // give `seconds` a chance to catchup with `message`
const seconds = parent.element(by.className('seconds')).getText(); const seconds = parent.element(by.className('seconds')).getText();
expect(message).toContain(seconds); expect(message).toContain(seconds);
}); });
});
it('should stop the countdown', () => { it('should stop the countdown', () => {
const parent = element(by.tagName(parentTag)); const parent = element(by.tagName(parentTag));

View File

@ -1,13 +0,0 @@
{
"tests": [
{
"cmd": "yarn",
"args": [
"e2e",
"--protractor-config=e2e/protractor-puppeteer.conf.js",
"--no-webdriver-update",
"--port={PORT}"
]
}
]
}

View File

@ -30,22 +30,21 @@
<app-vote-taker></app-vote-taker> <app-vote-taker></app-vote-taker>
</div> </div>
<a href="#top" class="to-top">Back to Top</a> <a href="#top" class="to-top">Back to Top</a>
<hr>
<hr>
<div id="parent-to-child-local-var"> <div id="parent-to-child-local-var">
<app-countdown-parent-lv></app-countdown-parent-lv> <app-countdown-parent-lv></app-countdown-parent-lv>
</div> </div>
<a href="#top" class="to-top">Back to Top</a> <a href="#top" class="to-top">Back to Top</a>
<hr>
<hr>
<div id="parent-to-view-child"> <div id="parent-to-view-child">
<app-countdown-parent-vc></app-countdown-parent-vc> <app-countdown-parent-vc></app-countdown-parent-vc>
</div> </div>
<a href="#top" class="to-top">Back to Top</a> <a href="#top" class="to-top">Back to Top</a>
<hr>
<hr>
<div id="bidirectional-service"> <div id="bidirectional-service">
<app-mission-control></app-mission-control> <app-mission-control></app-mission-control>
</div> </div>
<a href="#top" class="to-top">Back to Top</a> <a href="#top" class="to-top">Back to Top</a>
<hr>

View File

@ -1,4 +1,4 @@
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component';
import { VoterComponent } from './voter.component'; import { VoterComponent } from './voter.component';
import { VoteTakerComponent } from './votetaker.component'; import { VoteTakerComponent } from './votetaker.component';
const directives: any[] = [
@NgModule({
imports: [
BrowserModule,
],
declarations: [
AppComponent, AppComponent,
AstronautComponent, AstronautComponent,
CountdownLocalVarParentComponent,
CountdownTimerComponent, CountdownTimerComponent,
CountdownViewChildParentComponent,
HeroChildComponent, HeroChildComponent,
HeroParentComponent, HeroParentComponent,
MissionControlComponent, MissionControlComponent,
@ -27,28 +34,8 @@ const directives: any[] = [
VersionChildComponent, VersionChildComponent,
VersionParentComponent, VersionParentComponent,
VoterComponent, VoterComponent,
VoteTakerComponent VoteTakerComponent,
];
const schemas: any[] = [];
// Include Countdown examples
// unless in e2e tests which they break.
if (!/e2e/.test(location.search)) {
console.log('adding countdown timer examples');
directives.push(CountdownLocalVarParentComponent);
directives.push(CountdownViewChildParentComponent);
} else {
// In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors
schemas.push(CUSTOM_ELEMENTS_SCHEMA);
}
@NgModule({
imports: [
BrowserModule
], ],
declarations: directives,
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],
schemas
}) })
export class AppModule { } export class AppModule { }

View File

@ -1,19 +1,16 @@
// #docregion // #docregion
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
@Component({ @Component({
selector: 'app-countdown-timer', selector: 'app-countdown-timer',
template: '<p>{{message}}</p>' template: '<p>{{message}}</p>'
}) })
export class CountdownTimerComponent implements OnInit, OnDestroy { export class CountdownTimerComponent implements OnDestroy {
intervalId = 0; intervalId = 0;
message = ''; message = '';
seconds = 11; seconds = 11;
clearTimer() { clearInterval(this.intervalId); }
ngOnInit() { this.start(); }
ngOnDestroy() { this.clearTimer(); } ngOnDestroy() { this.clearTimer(); }
start() { this.countDown(); } start() { this.countDown(); }
@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
this.message = `Holding at T-${this.seconds} seconds`; this.message = `Holding at T-${this.seconds} seconds`;
} }
private clearTimer() { clearInterval(this.intervalId); }
private countDown() { private countDown() {
this.clearTimer(); this.clearTimer();
this.intervalId = window.setInterval(() => { this.intervalId = window.setInterval(() => {