build(docs-infra): update @angular/* and @angular/cli to 11.0.0-rc.2 (#39600)

This commit updates `@angular/*` and `@angular/cli` (and related
packages) to version 11.0.0-rc.2. Apart from the automatic migrations,
this commit also tries to align `aio/` with new apps generated by the
latest CLI. (See [here][1] for a diff between a v10.1.3 and a
v11.0.0-rc.2 CLI app.)

[1]: https://github.com/cexbrayat/angular-cli-diff/compare/10.1.3...11.0.0-rc.2

PR Close #39600
This commit is contained in:
George Kalpakas 2020-11-18 12:36:06 +02:00 committed by Andrew Kushnir
parent 3ab4c8313d
commit 18110a8ab0
18 changed files with 1613 additions and 1666 deletions

View File

@ -37,7 +37,6 @@
"outputHashing": "all", "outputHashing": "all",
"sourceMap": true, "sourceMap": true,
"statsJson": true, "statsJson": true,
"extractCss": true,
"extractLicenses": true, "extractLicenses": true,
"namedChunks": true, "namedChunks": true,
"vendorChunk": false, "vendorChunk": false,

View File

@ -10,7 +10,7 @@ module.exports = function (config) {
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'), require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'), require('@angular-devkit/build-angular/plugins/karma'),
{'reporter:jasmine-seed': ['type', JasmineSeedReporter]}, {'reporter:jasmine-seed': ['type', JasmineSeedReporter]},
], ],
@ -24,10 +24,13 @@ module.exports = function (config) {
seed: '', seed: '',
}, },
}, },
coverageIstanbulReporter: { coverageReporter: {
dir: require('path').join(__dirname, './coverage/site'), dir: require('path').join(__dirname, './coverage/site'),
reports: ['html', 'lcovonly', 'text-summary'], subdir: '.',
fixWebpackSourcePaths: true, reporters: [
{ type: 'html' },
{ type: 'text-summary' }
],
}, },
reporters: ['progress', 'kjhtml', 'jasmine-seed'], reporters: ['progress', 'kjhtml', 'jasmine-seed'],
port: 9876, port: 9876,

View File

@ -88,29 +88,28 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "10.1.3", "@angular/animations": "11.0.0-rc.2",
"@angular/cdk": "10.2.2", "@angular/cdk": "10.2.2",
"@angular/common": "10.1.3", "@angular/common": "11.0.0-rc.2",
"@angular/compiler": "10.1.3", "@angular/compiler": "11.0.0-rc.2",
"@angular/core": "10.1.3", "@angular/core": "11.0.0-rc.2",
"@angular/elements": "10.1.3", "@angular/elements": "11.0.0-rc.2",
"@angular/forms": "10.1.3", "@angular/forms": "11.0.0-rc.2",
"@angular/material": "10.2.2", "@angular/material": "10.2.2",
"@angular/platform-browser": "10.1.3", "@angular/platform-browser": "11.0.0-rc.2",
"@angular/platform-browser-dynamic": "10.1.3", "@angular/platform-browser-dynamic": "11.0.0-rc.2",
"@angular/router": "10.1.3", "@angular/router": "11.0.0-rc.2",
"@angular/service-worker": "10.1.3", "@angular/service-worker": "11.0.0-rc.2",
"@webcomponents/custom-elements": "1.2.1", "@webcomponents/custom-elements": "1.2.1",
"rxjs": "^6.5.3", "rxjs": "^6.5.3",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"zone.js": "~0.11.3" "zone.js": "~0.11.3"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "0.1001.3", "@angular-devkit/build-angular": "0.1100.0-rc.2",
"@angular/cli": "10.1.3", "@angular/cli": "11.0.0-rc.2",
"@angular/compiler-cli": "10.1.3", "@angular/compiler-cli": "11.0.0-rc.2",
"@types/jasmine": "^3.4.2", "@types/jasmine": "~3.6.0",
"@types/jasminewd2": "^2.0.8",
"@types/lunr": "^2.3.2", "@types/lunr": "^2.3.2",
"@types/node": "^12.7.9", "@types/node": "^12.7.9",
"@types/xregexp": "^3.0.30", "@types/xregexp": "^3.0.30",
@ -142,9 +141,9 @@
"jsdom": "^9.12.0", "jsdom": "^9.12.0",
"json-schema-traverse": "^0.4.1", "json-schema-traverse": "^0.4.1",
"json5": "^1.0.1", "json5": "^1.0.1",
"karma": "~5.0.0", "karma": "~5.2.3",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.1.0", "karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.4.2", "karma-jasmine-html-reporter": "^1.4.2",
"light-server": "^2.6.2", "light-server": "^2.6.2",
@ -153,7 +152,7 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lunr": "^2.1.0", "lunr": "^2.1.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"protractor": "~5.4.4", "protractor": "~7.0.0",
"puppeteer": "5.1.0", "puppeteer": "5.1.0",
"rehype": "^6.0.0", "rehype": "^6.0.0",
"rehype-slug": "^2.0.0", "rehype-slug": "^2.0.0",

View File

@ -21,6 +21,7 @@ exports.config = {
}, },
}, },
directConnect: true, directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
framework: 'jasmine', framework: 'jasmine',
jasmineNodeOpts: { jasmineNodeOpts: {
showColors: true, showColors: true,

View File

@ -67,8 +67,8 @@ describe(browser.baseUrl, () => {
await page.goTo(unknownPagePath); await page.goTo(unknownPagePath);
await browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded. await browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded.
expect(aioShell.isPresent()).toBe(true); expect(await aioShell.isPresent()).toBe(true);
expect(heading.getText()).toMatch(/page not found/i); expect(await heading.getText()).toMatch(/page not found/i);
}); });
it('should serve a custom 404 page for unknown resources', async () => { it('should serve a custom 404 page for unknown resources', async () => {
@ -76,15 +76,15 @@ describe(browser.baseUrl, () => {
const heading = aioShell.element(by.css('h1')); const heading = aioShell.element(by.css('h1'));
await page.goTo(unknownResourcePath); await page.goTo(unknownResourcePath);
expect(aioShell.isPresent()).toBe(true); expect(await aioShell.isPresent()).toBe(true);
expect(heading.getText()).toMatch(/resource not found/i); expect(await heading.getText()).toMatch(/resource not found/i);
}); });
it('should include a link to the home page in custom 404 page', async () => { it('should include a link to the home page in custom 404 page', async () => {
const homeNavLink = element(by.css('.nav-link.home')); const homeNavLink = element(by.css('.nav-link.home'));
await page.goTo(unknownResourcePath); await page.goTo(unknownResourcePath);
expect(homeNavLink.isPresent()).toBe(true); expect(await homeNavLink.isPresent()).toBe(true);
await homeNavLink.click(); await homeNavLink.click();
const expectedUrl = page.baseUrl; const expectedUrl = page.baseUrl;

View File

@ -14,9 +14,9 @@ describe(browser.baseUrl, () => {
}); });
describe('(smoke tests)', () => { describe('(smoke tests)', () => {
it('should show the home page', () => { it('should show the home page', async () => {
page.goTo(''); await page.goTo('');
const text = page.getDocViewerText(); const text = await page.getDocViewerText();
expect(text).toContain('one framework'); expect(text).toContain('one framework');
expect(text).toContain('mobile & desktop'); expect(text).toContain('mobile & desktop');
@ -31,11 +31,11 @@ describe(browser.baseUrl, () => {
}; };
Object.keys(textPerUrl).forEach(url => { Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => { it(`should show the page at '${url}'`, async () => {
page.goTo(url); await page.goTo(url);
browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded. await browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded.
expect(page.getDocViewerText()).toContain(textPerUrl[url]); expect(await page.getDocViewerText()).toContain(textPerUrl[url]);
}); });
}); });
}); });
@ -51,11 +51,11 @@ describe(browser.baseUrl, () => {
}; };
Object.keys(textPerUrl).forEach(url => { Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => { it(`should show the page at '${url}'`, async () => {
page.goTo(url); await page.goTo(url);
browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded. await browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded.
expect(page.getDocViewerText()).toContain(textPerUrl[url]); expect(await page.getDocViewerText()).toContain(textPerUrl[url]);
}); });
}); });
}); });
@ -74,11 +74,11 @@ describe(browser.baseUrl, () => {
}; };
Object.keys(textPerUrl).forEach(url => { Object.keys(textPerUrl).forEach(url => {
it(`should show the page at '${url}'`, () => { it(`should show the page at '${url}'`, async () => {
page.goTo(url); await page.goTo(url);
browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded. await browser.wait(() => page.getDocViewerText(), 5000); // Wait for the document to be loaded.
expect(page.getDocViewerText()).toContain(textPerUrl[url]); expect(await page.getDocViewerText()).toContain(textPerUrl[url]);
}); });
}); });
}); });
@ -86,25 +86,25 @@ describe(browser.baseUrl, () => {
describe('(search results)', () => { describe('(search results)', () => {
beforeEach(() => page.goTo('')); beforeEach(() => page.goTo(''));
it('should find pages when searching by a partial word in the title', () => { it('should find pages when searching by a partial word in the title', async () => {
page.enterSearch('ngCont'); await page.enterSearch('ngCont');
expect(page.getSearchResults()).toContain('NgControl'); expect(await page.getSearchResults()).toContain('NgControl');
}); });
it('should find API docs when searching for an instance member name', () => { it('should find API docs when searching for an instance member name', async () => {
page.enterSearch('writeValue'); await page.enterSearch('writeValue');
expect(page.getSearchResults()).toContain('ControlValueAccessor'); expect(await page.getSearchResults()).toContain('ControlValueAccessor');
}); });
it('should find API docs when searching for a static member name', () => { it('should find API docs when searching for a static member name', async () => {
page.enterSearch('compose'); await page.enterSearch('compose');
expect(page.getSearchResults()).toContain('Validators'); expect(await page.getSearchResults()).toContain('Validators');
}); });
}); });
it('should show relevant results on 404', () => { it('should show relevant results on 404', async () => {
page.goTo('http/router'); await page.goTo('http/router');
const results = page.getSearchResults(); const results = await page.getSearchResults();
expect(results).toContain('HttpClient'); expect(results).toContain('HttpClient');
expect(results).toContain('Router'); expect(results).toContain('Router');

View File

@ -21,6 +21,7 @@ exports.config = {
}, },
}, },
directConnect: true, directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/', baseUrl: 'http://localhost:4200/',
framework: 'jasmine', framework: 'jasmine',
jasmineNodeOpts: { jasmineNodeOpts: {

View File

@ -7,46 +7,46 @@ describe('api-list', () => {
const apiTypeDropdown = element(by.css('aio-api-list aio-select[label="Type:"]')); const apiTypeDropdown = element(by.css('aio-api-list aio-select[label="Type:"]'));
let page: SitePage; let page: SitePage;
beforeEach(() => { beforeEach(async () => {
page = new SitePage(); page = new SitePage();
page.navigateTo('api'); await page.navigateTo('api');
}); });
it('should find AnimationSequenceMetadata when searching by partial word anima', () => { it('should find AnimationSequenceMetadata when searching by partial word anima', async () => {
expect(page.getApiSearchResults()).toContain('HttpEventType'); expect(await page.getApiSearchResults()).toContain('HttpEventType');
apiSearchInput.clear(); await apiSearchInput.clear();
apiSearchInput.sendKeys('anima'); await apiSearchInput.sendKeys('anima');
expect(page.getApiSearchResults()).not.toContain('HttpEventType'); expect(await page.getApiSearchResults()).not.toContain('HttpEventType');
expect(page.getApiSearchResults()).toContain('AnimationSequenceMetadata'); expect(await page.getApiSearchResults()).toContain('AnimationSequenceMetadata');
}); });
it('should find getLocaleDateTimeFormat when searching by partial word date', () => { it('should find getLocaleDateTimeFormat when searching by partial word date', async () => {
expect(page.getApiSearchResults()).toContain('formatCurrency'); expect(await page.getApiSearchResults()).toContain('formatCurrency');
apiSearchInput.clear(); await apiSearchInput.clear();
apiSearchInput.sendKeys('date'); await apiSearchInput.sendKeys('date');
expect(page.getApiSearchResults()).not.toContain('formatCurrency'); expect(await page.getApiSearchResults()).not.toContain('formatCurrency');
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat'); expect(await page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
}); });
it('should find LowerCasePipe when searching for type pipe', () => { it('should find LowerCasePipe when searching for type pipe', async () => {
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat'); expect(await page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
page.clickDropdownItem(apiTypeDropdown, 'Pipe'); await page.clickDropdownItem(apiTypeDropdown, 'Pipe');
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat'); expect(await page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
expect(page.getApiSearchResults()).toContain('LowerCasePipe'); expect(await page.getApiSearchResults()).toContain('LowerCasePipe');
}); });
it('should find ElementRef when searching for status Security Risk', () => { it('should find ElementRef when searching for status Security Risk', async () => {
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat'); expect(await page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
page.clickDropdownItem(apiStatusDropdown, 'Security Risk'); await page.clickDropdownItem(apiStatusDropdown, 'Security Risk');
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat'); expect(await page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
expect(page.getApiSearchResults()).toContain('ElementRef'); expect(await page.getApiSearchResults()).toContain('ElementRef');
}); });
}); });

View File

@ -1,14 +1,18 @@
import { ApiPage } from './api.po'; import { ApiPage } from './api.po';
describe('Api pages', () => { describe('Api pages', () => {
it('should show direct subclasses of a class', () => { let page: ApiPage;
const page = new ApiPage('api/forms/AbstractControlDirective');
expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl']); beforeEach(() => page = new ApiPage());
it('should show direct subclasses of a class', async () => {
await page.navigateTo('api/forms/AbstractControlDirective');
expect(await page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl']);
}); });
it('should show direct and indirect subclasses of a class', () => { it('should show direct and indirect subclasses of a class', async () => {
const page = new ApiPage('api/forms/AbstractControlDirective'); await page.navigateTo('api/forms/AbstractControlDirective');
expect(page.getDescendants('class')).toEqual([ expect(await page.getDescendants('class')).toEqual([
'ControlContainer', 'ControlContainer',
'AbstractFormGroupDirective', 'AbstractFormGroupDirective',
'NgModelGroup', 'NgModelGroup',
@ -23,53 +27,52 @@ describe('Api pages', () => {
]); ]);
}); });
it('should show child interfaces that extend an interface', () => { it('should show child interfaces that extend an interface', async () => {
const page = new ApiPage('api/forms/Validator'); await page.navigateTo('api/forms/Validator');
expect(page.getDescendants('interface')).toEqual(['AsyncValidator']); expect(await page.getDescendants('interface')).toEqual(['AsyncValidator']);
}); });
it('should show classes that implement an interface', () => { it('should show classes that implement an interface', async () => {
const page = new ApiPage('api/animations/AnimationPlayer'); await page.navigateTo('api/animations/AnimationPlayer');
expect(page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer']); expect(await page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer']);
}); });
it('should show type params of type-aliases', () => { it('should show type params of type-aliases', async () => {
const page = new ApiPage('api/common/http/HttpEvent'); await page.navigateTo('api/common/http/HttpEvent');
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>'); expect(await page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
}); });
it('should not show parenthesis for getters', () => { it('should not show parenthesis for getters', async () => {
const page = new ApiPage('api/core/NgModuleRef'); await page.navigateTo('api/core/NgModuleRef');
expect(page.getOverview('class').getText()).toContain('injector: Injector'); expect(await page.getOverview('class').getText()).toContain('injector: Injector');
}); });
it('should show both type and initializer if set', () => { it('should show both type and initializer if set', async () => {
const page = new ApiPage('api/common/HashLocationStrategy'); await page.navigateTo('api/common/HashLocationStrategy');
expect(page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string'); expect(await page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string');
}); });
it('should show a "Properties" section if there are public properties', () => { it('should show a "Properties" section if there are public properties', async () => {
const page = new ApiPage('api/core/ViewContainerRef'); await page.navigateTo('api/core/ViewContainerRef');
expect(page.getSection('instance-properties').isPresent()).toBe(true); expect(await page.getSection('instance-properties').isPresent()).toBe(true);
}); });
it('should not show a "Properties" section if there are only internal properties', () => { it('should not show a "Properties" section if there are only internal properties', async () => {
const page = new ApiPage('api/forms/FormControl'); await page.navigateTo('api/forms/FormControl');
expect(page.getSection('instance-properties').isPresent()).toBe(false); expect(await page.getSection('instance-properties').isPresent()).toBe(false);
}); });
it('should show "impure" badge if pipe is not pure', () => { it('should show "impure" badge if pipe is not pure', async () => {
const page = new ApiPage('api/common/AsyncPipe'); await page.navigateTo('api/common/AsyncPipe');
const impureBadge = page.getBadge('impure-pipe'); expect(await page.getBadge('impure-pipe').isPresent()).toBe(true);
expect(impureBadge.isPresent()).toBe(true);
}); });
it('should show links to github', () => { it('should show links to github', async () => {
const page = new ApiPage('api/core/EventEmitter'); await page.navigateTo('api/core/EventEmitter');
/* tslint:disable:max-line-length */ /* tslint:disable:max-line-length */
expect(page.ghLinks.get(0).getAttribute('href')) expect(await page.ghLinks.get(0).getAttribute('href'))
.toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/packages\/core\/src\/event_emitter\.ts\?message=docs\(core\)%3A%20describe%20your%20change\.\.\.#L\d+-L\d+/); .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/packages\/core\/src\/event_emitter\.ts\?message=docs\(core\)%3A%20describe%20your%20change\.\.\.#L\d+-L\d+/);
expect(page.ghLinks.get(1).getAttribute('href')) expect(await page.ghLinks.get(1).getAttribute('href'))
.toMatch(/https:\/\/github\.com\/angular\/angular\/tree\/[^/]+\/packages\/core\/src\/event_emitter\.ts#L\d+-L\d+/); .toMatch(/https:\/\/github\.com\/angular\/angular\/tree\/[^/]+\/packages\/core\/src\/event_emitter\.ts#L\d+-L\d+/);
/* tslint:enable:max-line-length */ /* tslint:enable:max-line-length */
}); });

View File

@ -2,11 +2,6 @@ import { element, by } from 'protractor';
import { SitePage } from './app.po'; import { SitePage } from './app.po';
export class ApiPage extends SitePage { export class ApiPage extends SitePage {
constructor(url: string) {
super();
this.navigateTo(url);
}
getDescendants(docType: string, onlyDirect = false) { getDescendants(docType: string, onlyDirect = false) {
// This selector is horrible because we have potentially recursive HTML lists // This selector is horrible because we have potentially recursive HTML lists
// //
@ -25,7 +20,7 @@ export class ApiPage extends SitePage {
// and we want to be able to pull out the code elements from only the first level // and we want to be able to pull out the code elements from only the first level
// if `onlyDirect` is set to `true`. // if `onlyDirect` is set to `true`.
const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} ul > li > code`; const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} ul > li > code`;
return element.all(by.css(selector)).map<string>(item => item && item.getText()); return element.all(by.css(selector)).map<string>(item => item?.getText());
} }
getOverview(docType: string) { getOverview(docType: string) {

View File

@ -4,127 +4,127 @@ import { SitePage } from './app.po';
describe('site App', () => { describe('site App', () => {
let page: SitePage; let page: SitePage;
beforeEach(() => { beforeEach(async () => {
SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side. await SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side.
page = new SitePage(); page = new SitePage();
}); });
it('should show features text after clicking "Features"', () => { it('should show features text after clicking "Features"', async () => {
page.navigateTo(''); await page.navigateTo('');
page.click(page.getTopMenuLink('features')); await page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i); expect(await page.getDocViewerText()).toMatch(/Progressive web apps/i);
}); });
it('should set appropriate window titles', () => { it('should set appropriate window titles', async () => {
page.navigateTo(''); await page.navigateTo('');
expect(browser.getTitle()).toBe('Angular'); expect(await browser.getTitle()).toBe('Angular');
page.click(page.getTopMenuLink('features')); await page.click(page.getTopMenuLink('features'));
expect(browser.getTitle()).toBe('Angular - FEATURES & BENEFITS'); expect(await browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
page.click(page.homeLink); await page.click(page.homeLink);
expect(browser.getTitle()).toBe('Angular'); expect(await browser.getTitle()).toBe('Angular');
}); });
it('should not navigate when clicking on nav-item headings (sub-menu toggles)', () => { it('should not navigate when clicking on nav-item headings (sub-menu toggles)', async () => {
// Show the sidenav. // Show the sidenav.
page.navigateTo('docs'); await page.navigateTo('docs');
expect(page.locationPath()).toBe('/docs'); expect(await page.locationPath()).toBe('/docs');
// Get the top-level nav-item headings (sub-menu toggles). // Get the top-level nav-item headings (sub-menu toggles).
const navItemHeadings = page.getNavItemHeadings(page.sidenav, 1); const navItemHeadings = page.getNavItemHeadings(page.sidenav, 1);
// Test all headings (and sub-headings). // Test all headings (and sub-headings).
expect(navItemHeadings.count()).toBeGreaterThan(0); expect(await navItemHeadings.count()).toBeGreaterThan(0);
navItemHeadings.each(heading => testNavItemHeading(heading!, 1)); await navItemHeadings.each(heading => testNavItemHeading(heading!, 1));
// Helpers // Helpers
function expectToBeCollapsed(elementFinder: ElementFinder) { async function expectToBeCollapsed(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).toMatch(/\bcollapsed\b/); expect(await elementFinder.getAttribute('class')).toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).not.toMatch(/\bexpanded\b/); expect(await elementFinder.getAttribute('class')).not.toMatch(/\bexpanded\b/);
} }
function expectToBeExpanded(elementFinder: ElementFinder) { async function expectToBeExpanded(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).not.toMatch(/\bcollapsed\b/); expect(await elementFinder.getAttribute('class')).not.toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).toMatch(/\bexpanded\b/); expect(await elementFinder.getAttribute('class')).toMatch(/\bexpanded\b/);
} }
function testNavItemHeading(heading: ElementFinder, level: number) { async function testNavItemHeading(heading: ElementFinder, level: number) {
const children = page.getNavItemHeadingChildren(heading, level); const children = page.getNavItemHeadingChildren(heading, level);
// Headings are initially collapsed. // Headings are initially collapsed.
expectToBeCollapsed(children); await expectToBeCollapsed(children);
// Ensure heading does not cause navigation when expanding. // Ensure heading does not cause navigation when expanding.
page.click(heading); await page.click(heading);
expectToBeExpanded(children); await expectToBeExpanded(children);
expect(page.locationPath()).toBe('/docs'); expect(await page.locationPath()).toBe('/docs');
// Recursively test child-headings (while this heading is expanded). // Recursively test child-headings (while this heading is expanded).
const nextLevel = level + 1; const nextLevel = level + 1;
const childNavItemHeadings = page.getNavItemHeadings(children, nextLevel); const childNavItemHeadings = page.getNavItemHeadings(children, nextLevel);
childNavItemHeadings.each(childHeading => testNavItemHeading(childHeading!, nextLevel)); await childNavItemHeadings.each(childHeading => testNavItemHeading(childHeading!, nextLevel));
// Ensure heading does not cause navigation when collapsing. // Ensure heading does not cause navigation when collapsing.
page.click(heading); await page.click(heading);
expectToBeCollapsed(children); await expectToBeCollapsed(children);
expect(page.locationPath()).toBe('/docs'); expect(await page.locationPath()).toBe('/docs');
} }
}); });
it('should show the tutorial index page at `/tutorial` after jitterbugging through features', () => { it('should show the tutorial index page at `/tutorial` after jitterbugging through features', async () => {
// check that we can navigate directly to the tutorial page // check that we can navigate directly to the tutorial page
page.navigateTo('tutorial'); await page.navigateTo('tutorial');
expect(page.getDocViewerText()).toMatch(/Tour of Heroes App and Tutorial/i); expect(await page.getDocViewerText()).toMatch(/Tour of Heroes App and Tutorial/i);
// navigate to a different page // navigate to a different page
page.click(page.getTopMenuLink('features')); await page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i); expect(await page.getDocViewerText()).toMatch(/Progressive web apps/i);
// Show the menu // Show the menu
page.click(page.docsMenuLink); await page.click(page.docsMenuLink);
// Tutorial folder should still be expanded because this test runs in wide mode // Tutorial folder should still be expanded because this test runs in wide mode
// Navigate to the tutorial introduction via a link in the sidenav // Navigate to the tutorial introduction via a link in the sidenav
page.click(page.getNavItem(/The Hero Editor/i)); await page.click(page.getNavItem(/The Hero Editor/i));
expect(page.getDocViewerText()).toMatch(/The Hero Editor/i); expect(await page.getDocViewerText()).toMatch(/The Hero Editor/i);
}); });
it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', () => { it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', async () => {
page.navigateTo('guide/component-styles'); await page.navigateTo('guide/component-styles');
const codeExample = element.all(by.css('code-example')).first(); const codeExample = element.all(by.css('code-example')).first();
expect(page.getInnerHtml(codeExample)).toContain('&lt;h1&gt;Tour of Heroes&lt;/h1&gt;'); expect(await page.getInnerHtml(codeExample)).toContain('&lt;h1&gt;Tour of Heroes&lt;/h1&gt;');
}); });
describe('scrolling to the top', () => { describe('scrolling to the top', () => {
it('should scroll to the top when navigating to another page', () => { it('should scroll to the top when navigating to another page', async () => {
page.navigateTo('guide/security'); await page.navigateTo('guide/security');
page.scrollTo('bottom'); await page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0); expect(await page.getScrollTop()).toBeGreaterThan(0);
// Navigate to Reference section, then check // Navigate to Reference section, then check
// Find the navigation item that has the text "api" // Find the navigation item that has the text "api"
page.click(page.getNavItem(/reference/i)); await page.click(page.getNavItem(/reference/i));
page.click(page.getNavItem(/api/i)); await page.click(page.getNavItem(/api/i));
expect(page.locationPath()).toBe('/api'); expect(await page.locationPath()).toBe('/api');
expect(page.getScrollTop()).toBe(0); expect(await page.getScrollTop()).toBe(0);
}); });
it('should scroll to the top when navigating to the same page', () => { it('should scroll to the top when navigating to the same page', async () => {
page.navigateTo('guide/security'); await page.navigateTo('guide/security');
page.scrollTo('bottom'); await page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0); expect(await page.getScrollTop()).toBeGreaterThan(0);
page.click(page.getNavItem(/security/i)); await page.click(page.getNavItem(/security/i));
expect(page.locationPath()).toBe('/guide/security'); expect(await page.locationPath()).toBe('/guide/security');
expect(page.getScrollTop()).toBe(0); expect(await page.getScrollTop()).toBe(0);
}); });
}); });
describe('tutorial docs', () => { describe('tutorial docs', () => {
it('should not render a paragraph element inside the h1 element', () => { it('should not render a paragraph element inside the h1 element', async () => {
page.navigateTo('tutorial/toh-pt1'); await page.navigateTo('tutorial/toh-pt1');
expect(element(by.css('h1 p')).isPresent()).toBeFalsy(); expect(await element(by.css('h1 p')).isPresent()).toBeFalsy();
}); });
}); });
@ -134,40 +134,44 @@ describe('site App', () => {
beforeAll(() => page.navigateTo('about')); beforeAll(() => page.navigateTo('about'));
it('should have the expected groups', () => { it('should have the expected groups', async () => {
expect(groupButtons.count()).toBe(3); expect(await groupButtons.count()).toBe(3);
const texts = groupButtons.map<string>(btn => btn && btn.getText()); const texts = await groupButtons.map<string>(btn => btn?.getText());
expect(texts).toEqual(['ANGULAR', 'COLLABORATORS', 'GDE']); expect(texts).toEqual(['ANGULAR', 'COLLABORATORS', 'GDE']);
}); });
it('should have contributors listed in each group', () => { it('should have contributors listed in each group', async () => {
// WebDriver calls `scrollIntoView()` on the element to bring it into the visible area of the // WebDriver calls `scrollIntoView()` on the element to bring it into the visible area of the
// browser, before clicking it. By default, this aligns the top of the element to the top of // browser, before clicking it. By default, this aligns the top of the element to the top of
// the window. As a result, the element may end up behing the fixed top menu, thus being // the window. As a result, the element may end up behing the fixed top menu, thus being
// unclickable. To avoid this, we click the element directly using JavaScript instead. // unclickable. To avoid this, we click the element directly using JavaScript instead.
const clickButton = (elementFinder: ElementFinder) => elementFinder.getWebElement().then( const clickButton =
webElement => browser.executeScript('arguments[0].click()', webElement)); (elementFinder: ElementFinder) => browser.executeScript('arguments[0].click()', elementFinder);
const getContributorNames = const getContributorNames =
() => contributors.all(by.css('h3')).map<string>(c => c && c.getText()); () => contributors.all(by.css('h3')).map<string>(c => c?.getText());
const names1 = getContributorNames(); const names1 = await getContributorNames();
expect(contributors.count()).toBeGreaterThan(1); expect(await contributors.count()).toBeGreaterThan(1);
expect(names1.length).toBeGreaterThan(1);
clickButton(groupButtons.get(1)); await clickButton(groupButtons.get(1));
const names2 = getContributorNames(); const names2 = await getContributorNames();
expect(contributors.count()).toBeGreaterThan(1); expect(await contributors.count()).toBeGreaterThan(1);
expect(names2.length).toBeGreaterThan(1);
expect(names2).not.toEqual(names1); expect(names2).not.toEqual(names1);
clickButton(groupButtons.get(2)); await clickButton(groupButtons.get(2));
const names3 = getContributorNames(); const names3 = await getContributorNames();
expect(contributors.count()).toBeGreaterThan(1); expect(await contributors.count()).toBeGreaterThan(1);
expect(names2.length).toBeGreaterThan(1);
expect(names3).not.toEqual(names2); expect(names3).not.toEqual(names2);
expect(names3).not.toEqual(names1); expect(names3).not.toEqual(names1);
clickButton(groupButtons.get(0)); await clickButton(groupButtons.get(0));
const names4 = getContributorNames(); const names4 = await getContributorNames();
expect(contributors.count()).toBeGreaterThan(1); expect(await contributors.count()).toBeGreaterThan(1);
expect(names4.length).toBeGreaterThan(1);
expect(names4).not.toEqual(names3); expect(names4).not.toEqual(names3);
expect(names4).not.toEqual(names2); expect(names4).not.toEqual(names2);
expect(names4).toEqual(names1); expect(names4).toEqual(names1);
@ -176,51 +180,45 @@ describe('site App', () => {
describe('google analytics', () => { describe('google analytics', () => {
it('should call ga with initial URL', done => { it('should call ga with initial URL', async () => {
let path: string; await page.navigateTo('api');
page.navigateTo('api');
page.locationPath() const path = await page.locationPath();
.then(p => path = p) const calls = await page.ga();
.then(() => page.ga())
.then(calls => { // The last call (length-1) will be the `send` command
// The last call (length-1) will be the `send` command // The second to last call (length-2) will be the command to `set` the page url
// The second to last call (length-2) will be the command to `set` the page url expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
});
}); });
it('should call ga with new URL on navigation', done => { it('should call ga with new URL on navigation', async () => {
let path: string; await page.navigateTo('');
page.navigateTo(''); await page.click(page.getTopMenuLink('features'));
page.click(page.getTopMenuLink('features'));
page.locationPath() const path = await page.locationPath();
.then(p => path = p) const calls = await page.ga();
.then(() => page.ga())
.then(calls => { // The last call (length-1) will be the `send` command
// The last call (length-1) will be the `send` command // The second to last call (length-2) will be the command to `set` the page url
// The second to last call (length-2) will be the command to `set` the page url expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
});
}); });
}); });
describe('404 page', () => { describe('404 page', () => {
it('should add or remove the "noindex" meta tag depending upon the validity of the page', () => { it('should add or remove the "noindex" meta tag depending upon the validity of the page', async () => {
page.navigateTo(''); await page.navigateTo('');
expect(element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy(); expect(await element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
page.navigateTo('does/not/exist'); await page.navigateTo('does/not/exist');
expect(element(by.css('meta[name="robots"][content="noindex"]')).isPresent()).toBeTruthy(); expect(await element(by.css('meta[name="robots"][content="noindex"]')).isPresent()).toBeTruthy();
page.click(page.getTopMenuLink('features')); await page.click(page.getTopMenuLink('features'));
expect(element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy(); expect(await element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
}); });
it('should search the index for words found in the url', () => { it('should search the index for words found in the url', async () => {
page.navigateTo('http/router'); await page.navigateTo('http/router');
const results = page.getSearchResults(); const results = await page.getSearchResults();
expect(results).toContain('HttpRequest'); expect(results).toContain('HttpRequest');
expect(results).toContain('Router'); expect(results).toContain('Router');
@ -228,23 +226,23 @@ describe('site App', () => {
}); });
describe('suggest edit link', () => { describe('suggest edit link', () => {
it('should be present on all docs pages', () => { it('should be present on all docs pages', async () => {
page.navigateTo('tutorial/toh-pt1'); await page.navigateTo('tutorial/toh-pt1');
expect(page.ghLinks.count()).toEqual(1); expect(await page.ghLinks.count()).toEqual(1);
/* tslint:disable:max-line-length */ /* tslint:disable:max-line-length */
expect(page.ghLinks.get(0).getAttribute('href')) expect(await page.ghLinks.get(0).getAttribute('href'))
.toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/tutorial\/toh-pt1\.md\?message=docs%3A%20describe%20your%20change\.\.\./); .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/tutorial\/toh-pt1\.md\?message=docs%3A%20describe%20your%20change\.\.\./);
page.navigateTo('guide/http'); await page.navigateTo('guide/http');
expect(page.ghLinks.count()).toEqual(1); expect(await page.ghLinks.count()).toEqual(1);
/* tslint:disable:max-line-length */ /* tslint:disable:max-line-length */
expect(page.ghLinks.get(0).getAttribute('href')) expect(await page.ghLinks.get(0).getAttribute('href'))
.toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/guide\/http\.md\?message=docs%3A%20describe%20your%20change\.\.\./); .toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/guide\/http\.md\?message=docs%3A%20describe%20your%20change\.\.\./);
}); });
it('should not be present on top level pages', () => { it('should not be present on top level pages', async () => {
page.navigateTo('features'); await page.navigateTo('features');
expect(page.ghLinks.count()).toEqual(0); expect(await page.ghLinks.count()).toEqual(0);
}); });
}); });
}); });

View File

@ -1,4 +1,4 @@
import { browser, element, by, promise, ElementFinder, ExpectedConditions } from 'protractor'; import { browser, element, by, ElementFinder, ExpectedConditions } from 'protractor';
const githubRegex = /https:\/\/github.com\/angular\/angular\//; const githubRegex = /https:\/\/github.com\/angular\/angular\//;
@ -12,16 +12,17 @@ export class SitePage {
codeExample = element.all(by.css('aio-doc-viewer pre > code')); codeExample = element.all(by.css('aio-doc-viewer pre > code'));
ghLinks = this.docViewer ghLinks = this.docViewer
.all(by.css('a')) .all(by.css('a'))
.filter((a: ElementFinder) => a.getAttribute('href').then(href => githubRegex.test(href))); .filter(async a => githubRegex.test(await a.getAttribute('href')));
static setWindowWidth(newWidth: number) { static async setWindowWidth(newWidth: number) {
const win = browser.driver.manage().window(); const win = browser.driver.manage().window();
return win.getSize().then(oldSize => win.setSize(newWidth, oldSize.height)); const oldSize = await win.getSize();
await win.setSize(newWidth, oldSize.height);
} }
getNavItem(pattern: RegExp) { getNavItem(pattern: RegExp) {
return element.all(by.css('aio-nav-item .vertical-menu-item')) return element.all(by.css('aio-nav-item .vertical-menu-item'))
.filter(elementFinder => elementFinder.getText().then(text => pattern.test(text))) .filter(async elementFinder => pattern.test(await elementFinder.getText()))
.first(); .first();
} }
getNavItemHeadings(parent: ElementFinder, level: number) { getNavItemHeadings(parent: ElementFinder, level: number) {
@ -35,14 +36,14 @@ export class SitePage {
} }
getTopMenuLink(path: string) { return element(by.css(`aio-top-menu a[href="${path}"]`)); } getTopMenuLink(path: string) { return element(by.css(`aio-top-menu a[href="${path}"]`)); }
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; } ga() { return browser.executeScript<any[][]>('return window["ga"].q'); }
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; } locationPath() { return browser.executeScript<string>('return document.location.pathname'); }
navigateTo(pageUrl: string) { async navigateTo(pageUrl: string) {
// Navigate to the page, disable animations, and wait for Angular. // Navigate to the page, disable animations, and wait for Angular.
return browser.get('/' + pageUrl) await browser.get(`/${pageUrl}`);
.then(() => browser.executeScript('document.body.classList.add(\'no-animations\')')) await browser.executeScript('document.body.classList.add(\'no-animations\')');
.then(() => browser.waitForAngular()); await browser.waitForAngular();
} }
getDocViewerText() { getDocViewerText() {
@ -68,31 +69,32 @@ export class SitePage {
`); `);
} }
click(elementFinder: ElementFinder) { async click(elementFinder: ElementFinder) {
return elementFinder.click().then(() => browser.waitForAngular()); await elementFinder.click();
await browser.waitForAngular();
} }
enterSearch(query: string) { async enterSearch(query: string) {
const input = element(by.css('.search-container input[type=search]')); const input = element(by.css('.search-container input[type=search]'));
input.clear(); await input.clear();
input.sendKeys(query); await input.sendKeys(query);
} }
getSearchResults() { async getSearchResults() {
const results = element.all(by.css('.search-results li')); const results = element.all(by.css('.search-results li'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000); await browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results.map(link => link && link.getText()); return results.map(link => link?.getText());
} }
getApiSearchResults() { async getApiSearchResults() {
const results = element.all(by.css('aio-api-list .api-item')); const results = element.all(by.css('aio-api-list .api-item'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 2000); await browser.wait(ExpectedConditions.presenceOf(results.first()), 2000);
return results.map(elem => elem && elem.getText()); return results.map(elem => elem?.getText());
} }
clickDropdownItem(dropdown: ElementFinder, itemName: string){ async clickDropdownItem(dropdown: ElementFinder, itemName: string){
dropdown.element(by.css('.form-select-button')).click(); await dropdown.element(by.css('.form-select-button')).click();
const menuItem = dropdown.element(by.cssContainingText('.form-select-dropdown li', itemName)); const menuItem = dropdown.element(by.cssContainingText('.form-select-dropdown li', itemName));
menuItem.click(); await menuItem.click();
} }
} }

View File

@ -6,9 +6,9 @@ import { SitePage } from './app.po';
describe('onerror handler', () => { describe('onerror handler', () => {
let page: SitePage; let page: SitePage;
beforeAll(() => { beforeAll(async () => {
page = new SitePage(); page = new SitePage();
page.navigateTo(''); await page.navigateTo('');
}); });

View File

@ -3,26 +3,26 @@ import { SitePage } from './app.po';
describe('site search', () => { describe('site search', () => {
let page: SitePage; let page: SitePage;
beforeEach(() => { beforeEach(async () => {
page = new SitePage(); page = new SitePage();
page.navigateTo(''); await page.navigateTo('');
}); });
it('should find pages when searching by a partial word in the title', () => { it('should find pages when searching by a partial word in the title', async () => {
page.enterSearch('ngCont'); await page.enterSearch('ngCont');
expect(page.getSearchResults()).toContain('NgControl'); expect(await page.getSearchResults()).toContain('NgControl');
page.enterSearch('valueaccess'); await page.enterSearch('valueaccess');
expect(page.getSearchResults()).toContain('ControlValueAccessor'); expect(await page.getSearchResults()).toContain('ControlValueAccessor');
}); });
it('should find API docs whose instance member name matches the search query', () => { it('should find API docs whose instance member name matches the search query', async () => {
page.enterSearch('decode'); await page.enterSearch('decode');
expect(page.getSearchResults()).toContain('HttpParameterCodec'); expect(await page.getSearchResults()).toContain('HttpParameterCodec');
}); });
it('should find API docs whose static method name matches the search query', () => { it('should find API docs whose static method name matches the search query', async () => {
page.enterSearch('compose'); await page.enterSearch('compose');
expect(page.getSearchResults()).toContain('Validators'); expect(await page.getSearchResults()).toContain('Validators');
}); });
}); });

View File

@ -6,7 +6,6 @@
"target": "es2018", "target": "es2018",
"types": [ "types": [
"jasmine", "jasmine",
"jasminewd2",
"node" "node"
] ]
} }

View File

@ -48,8 +48,6 @@
] ]
} }
], ],
// TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 114)
// "no-any": true,
"no-console": [ "no-console": [
true, true,
"debug", "debug",
@ -66,7 +64,6 @@
// TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 59) // TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 59)
// "no-non-null-assertion": true, // "no-non-null-assertion": true,
"no-redundant-jsdoc": true, "no-redundant-jsdoc": true,
// TODO(gkalpak): Fix the code and remove this to align with CLI.
"no-switch-case-fall-through": true, "no-switch-case-fall-through": true,
"no-var-requires": false, "no-var-requires": false,
"object-literal-key-quotes": [ "object-literal-key-quotes": [

File diff suppressed because it is too large Load Diff

View File

@ -2,27 +2,27 @@
"aio": { "aio": {
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 3037, "runtime-es2015": 3033,
"main-es2015": 448285, "main-es2015": 447582,
"polyfills-es2015": 52407 "polyfills-es2015": 52343
} }
} }
}, },
"aio-local": { "aio-local": {
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 3037, "runtime-es2015": 3033,
"main-es2015": 449116, "main-es2015": 448385,
"polyfills-es2015": 52557 "polyfills-es2015": 52493
} }
} }
}, },
"aio-local-viewengine": { "aio-local-viewengine": {
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 3157, "runtime-es2015": 3153,
"main-es2015": 432424, "main-es2015": 431575,
"polyfills-es2015": 52557 "polyfills-es2015": 52493
} }
} }
} }