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",
"sourceMap": true,
"statsJson": true,
"extractCss": true,
"extractLicenses": true,
"namedChunks": true,
"vendorChunk": false,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,18 @@
import { ApiPage } from './api.po';
describe('Api pages', () => {
it('should show direct subclasses of a class', () => {
const page = new ApiPage('api/forms/AbstractControlDirective');
expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl']);
let page: ApiPage;
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', () => {
const page = new ApiPage('api/forms/AbstractControlDirective');
expect(page.getDescendants('class')).toEqual([
it('should show direct and indirect subclasses of a class', async () => {
await page.navigateTo('api/forms/AbstractControlDirective');
expect(await page.getDescendants('class')).toEqual([
'ControlContainer',
'AbstractFormGroupDirective',
'NgModelGroup',
@ -23,53 +27,52 @@ describe('Api pages', () => {
]);
});
it('should show child interfaces that extend an interface', () => {
const page = new ApiPage('api/forms/Validator');
expect(page.getDescendants('interface')).toEqual(['AsyncValidator']);
it('should show child interfaces that extend an interface', async () => {
await page.navigateTo('api/forms/Validator');
expect(await page.getDescendants('interface')).toEqual(['AsyncValidator']);
});
it('should show classes that implement an interface', () => {
const page = new ApiPage('api/animations/AnimationPlayer');
expect(page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer']);
it('should show classes that implement an interface', async () => {
await page.navigateTo('api/animations/AnimationPlayer');
expect(await page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer']);
});
it('should show type params of type-aliases', () => {
const page = new ApiPage('api/common/http/HttpEvent');
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
it('should show type params of type-aliases', async () => {
await page.navigateTo('api/common/http/HttpEvent');
expect(await page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
});
it('should not show parenthesis for getters', () => {
const page = new ApiPage('api/core/NgModuleRef');
expect(page.getOverview('class').getText()).toContain('injector: Injector');
it('should not show parenthesis for getters', async () => {
await page.navigateTo('api/core/NgModuleRef');
expect(await page.getOverview('class').getText()).toContain('injector: Injector');
});
it('should show both type and initializer if set', () => {
const page = new ApiPage('api/common/HashLocationStrategy');
expect(page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string');
it('should show both type and initializer if set', async () => {
await page.navigateTo('api/common/HashLocationStrategy');
expect(await page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string');
});
it('should show a "Properties" section if there are public properties', () => {
const page = new ApiPage('api/core/ViewContainerRef');
expect(page.getSection('instance-properties').isPresent()).toBe(true);
it('should show a "Properties" section if there are public properties', async () => {
await page.navigateTo('api/core/ViewContainerRef');
expect(await page.getSection('instance-properties').isPresent()).toBe(true);
});
it('should not show a "Properties" section if there are only internal properties', () => {
const page = new ApiPage('api/forms/FormControl');
expect(page.getSection('instance-properties').isPresent()).toBe(false);
it('should not show a "Properties" section if there are only internal properties', async () => {
await page.navigateTo('api/forms/FormControl');
expect(await page.getSection('instance-properties').isPresent()).toBe(false);
});
it('should show "impure" badge if pipe is not pure', () => {
const page = new ApiPage('api/common/AsyncPipe');
const impureBadge = page.getBadge('impure-pipe');
expect(impureBadge.isPresent()).toBe(true);
it('should show "impure" badge if pipe is not pure', async () => {
await page.navigateTo('api/common/AsyncPipe');
expect(await page.getBadge('impure-pipe').isPresent()).toBe(true);
});
it('should show links to github', () => {
const page = new ApiPage('api/core/EventEmitter');
it('should show links to github', async () => {
await page.navigateTo('api/core/EventEmitter');
/* 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+/);
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+/);
/* tslint:enable:max-line-length */
});

View File

@ -2,11 +2,6 @@ import { element, by } from 'protractor';
import { SitePage } from './app.po';
export class ApiPage extends SitePage {
constructor(url: string) {
super();
this.navigateTo(url);
}
getDescendants(docType: string, onlyDirect = false) {
// 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
// if `onlyDirect` is set to `true`.
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) {

View File

@ -4,127 +4,127 @@ import { SitePage } from './app.po';
describe('site App', () => {
let page: SitePage;
beforeEach(() => {
SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side.
beforeEach(async () => {
await SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side.
page = new SitePage();
});
it('should show features text after clicking "Features"', () => {
page.navigateTo('');
page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
it('should show features text after clicking "Features"', async () => {
await page.navigateTo('');
await page.click(page.getTopMenuLink('features'));
expect(await page.getDocViewerText()).toMatch(/Progressive web apps/i);
});
it('should set appropriate window titles', () => {
page.navigateTo('');
expect(browser.getTitle()).toBe('Angular');
it('should set appropriate window titles', async () => {
await page.navigateTo('');
expect(await browser.getTitle()).toBe('Angular');
page.click(page.getTopMenuLink('features'));
expect(browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
await page.click(page.getTopMenuLink('features'));
expect(await browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
page.click(page.homeLink);
expect(browser.getTitle()).toBe('Angular');
await page.click(page.homeLink);
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.
page.navigateTo('docs');
expect(page.locationPath()).toBe('/docs');
await page.navigateTo('docs');
expect(await page.locationPath()).toBe('/docs');
// Get the top-level nav-item headings (sub-menu toggles).
const navItemHeadings = page.getNavItemHeadings(page.sidenav, 1);
// Test all headings (and sub-headings).
expect(navItemHeadings.count()).toBeGreaterThan(0);
navItemHeadings.each(heading => testNavItemHeading(heading!, 1));
expect(await navItemHeadings.count()).toBeGreaterThan(0);
await navItemHeadings.each(heading => testNavItemHeading(heading!, 1));
// Helpers
function expectToBeCollapsed(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).not.toMatch(/\bexpanded\b/);
async function expectToBeCollapsed(elementFinder: ElementFinder) {
expect(await elementFinder.getAttribute('class')).toMatch(/\bcollapsed\b/);
expect(await elementFinder.getAttribute('class')).not.toMatch(/\bexpanded\b/);
}
function expectToBeExpanded(elementFinder: ElementFinder) {
expect(elementFinder.getAttribute('class')).not.toMatch(/\bcollapsed\b/);
expect(elementFinder.getAttribute('class')).toMatch(/\bexpanded\b/);
async function expectToBeExpanded(elementFinder: ElementFinder) {
expect(await elementFinder.getAttribute('class')).not.toMatch(/\bcollapsed\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);
// Headings are initially collapsed.
expectToBeCollapsed(children);
await expectToBeCollapsed(children);
// Ensure heading does not cause navigation when expanding.
page.click(heading);
expectToBeExpanded(children);
expect(page.locationPath()).toBe('/docs');
await page.click(heading);
await expectToBeExpanded(children);
expect(await page.locationPath()).toBe('/docs');
// Recursively test child-headings (while this heading is expanded).
const nextLevel = level + 1;
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.
page.click(heading);
expectToBeCollapsed(children);
expect(page.locationPath()).toBe('/docs');
await page.click(heading);
await expectToBeCollapsed(children);
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
page.navigateTo('tutorial');
expect(page.getDocViewerText()).toMatch(/Tour of Heroes App and Tutorial/i);
await page.navigateTo('tutorial');
expect(await page.getDocViewerText()).toMatch(/Tour of Heroes App and Tutorial/i);
// navigate to a different page
page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
await page.click(page.getTopMenuLink('features'));
expect(await page.getDocViewerText()).toMatch(/Progressive web apps/i);
// 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
// Navigate to the tutorial introduction via a link in the sidenav
page.click(page.getNavItem(/The Hero Editor/i));
expect(page.getDocViewerText()).toMatch(/The Hero Editor/i);
await page.click(page.getNavItem(/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', () => {
page.navigateTo('guide/component-styles');
it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', async () => {
await page.navigateTo('guide/component-styles');
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', () => {
it('should scroll to the top when navigating to another page', () => {
page.navigateTo('guide/security');
page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0);
it('should scroll to the top when navigating to another page', async () => {
await page.navigateTo('guide/security');
await page.scrollTo('bottom');
expect(await page.getScrollTop()).toBeGreaterThan(0);
// Navigate to Reference section, then check
// Find the navigation item that has the text "api"
page.click(page.getNavItem(/reference/i));
page.click(page.getNavItem(/api/i));
expect(page.locationPath()).toBe('/api');
expect(page.getScrollTop()).toBe(0);
await page.click(page.getNavItem(/reference/i));
await page.click(page.getNavItem(/api/i));
expect(await page.locationPath()).toBe('/api');
expect(await page.getScrollTop()).toBe(0);
});
it('should scroll to the top when navigating to the same page', () => {
page.navigateTo('guide/security');
it('should scroll to the top when navigating to the same page', async () => {
await page.navigateTo('guide/security');
page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0);
await page.scrollTo('bottom');
expect(await page.getScrollTop()).toBeGreaterThan(0);
page.click(page.getNavItem(/security/i));
expect(page.locationPath()).toBe('/guide/security');
expect(page.getScrollTop()).toBe(0);
await page.click(page.getNavItem(/security/i));
expect(await page.locationPath()).toBe('/guide/security');
expect(await page.getScrollTop()).toBe(0);
});
});
describe('tutorial docs', () => {
it('should not render a paragraph element inside the h1 element', () => {
page.navigateTo('tutorial/toh-pt1');
expect(element(by.css('h1 p')).isPresent()).toBeFalsy();
it('should not render a paragraph element inside the h1 element', async () => {
await page.navigateTo('tutorial/toh-pt1');
expect(await element(by.css('h1 p')).isPresent()).toBeFalsy();
});
});
@ -134,40 +134,44 @@ describe('site App', () => {
beforeAll(() => page.navigateTo('about'));
it('should have the expected groups', () => {
expect(groupButtons.count()).toBe(3);
it('should have the expected groups', async () => {
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']);
});
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
// 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
// unclickable. To avoid this, we click the element directly using JavaScript instead.
const clickButton = (elementFinder: ElementFinder) => elementFinder.getWebElement().then(
webElement => browser.executeScript('arguments[0].click()', webElement));
const clickButton =
(elementFinder: ElementFinder) => browser.executeScript('arguments[0].click()', elementFinder);
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();
expect(contributors.count()).toBeGreaterThan(1);
const names1 = await getContributorNames();
expect(await contributors.count()).toBeGreaterThan(1);
expect(names1.length).toBeGreaterThan(1);
clickButton(groupButtons.get(1));
const names2 = getContributorNames();
expect(contributors.count()).toBeGreaterThan(1);
await clickButton(groupButtons.get(1));
const names2 = await getContributorNames();
expect(await contributors.count()).toBeGreaterThan(1);
expect(names2.length).toBeGreaterThan(1);
expect(names2).not.toEqual(names1);
clickButton(groupButtons.get(2));
const names3 = getContributorNames();
expect(contributors.count()).toBeGreaterThan(1);
await clickButton(groupButtons.get(2));
const names3 = await getContributorNames();
expect(await contributors.count()).toBeGreaterThan(1);
expect(names2.length).toBeGreaterThan(1);
expect(names3).not.toEqual(names2);
expect(names3).not.toEqual(names1);
clickButton(groupButtons.get(0));
const names4 = getContributorNames();
expect(contributors.count()).toBeGreaterThan(1);
await clickButton(groupButtons.get(0));
const names4 = await getContributorNames();
expect(await contributors.count()).toBeGreaterThan(1);
expect(names4.length).toBeGreaterThan(1);
expect(names4).not.toEqual(names3);
expect(names4).not.toEqual(names2);
expect(names4).toEqual(names1);
@ -176,51 +180,45 @@ describe('site App', () => {
describe('google analytics', () => {
it('should call ga with initial URL', done => {
let path: string;
page.navigateTo('api');
page.locationPath()
.then(p => path = p)
.then(() => page.ga())
.then(calls => {
it('should call ga with initial URL', async () => {
await page.navigateTo('api');
const path = await page.locationPath();
const calls = await page.ga();
// 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
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
});
});
it('should call ga with new URL on navigation', done => {
let path: string;
page.navigateTo('');
page.click(page.getTopMenuLink('features'));
page.locationPath()
.then(p => path = p)
.then(() => page.ga())
.then(calls => {
it('should call ga with new URL on navigation', async () => {
await page.navigateTo('');
await page.click(page.getTopMenuLink('features'));
const path = await page.locationPath();
const calls = await page.ga();
// 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
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
});
});
});
describe('404 page', () => {
it('should add or remove the "noindex" meta tag depending upon the validity of the page', () => {
page.navigateTo('');
expect(element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
it('should add or remove the "noindex" meta tag depending upon the validity of the page', async () => {
await page.navigateTo('');
expect(await element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
page.navigateTo('does/not/exist');
expect(element(by.css('meta[name="robots"][content="noindex"]')).isPresent()).toBeTruthy();
await page.navigateTo('does/not/exist');
expect(await element(by.css('meta[name="robots"][content="noindex"]')).isPresent()).toBeTruthy();
page.click(page.getTopMenuLink('features'));
expect(element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
await page.click(page.getTopMenuLink('features'));
expect(await element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
});
it('should search the index for words found in the url', () => {
page.navigateTo('http/router');
const results = page.getSearchResults();
it('should search the index for words found in the url', async () => {
await page.navigateTo('http/router');
const results = await page.getSearchResults();
expect(results).toContain('HttpRequest');
expect(results).toContain('Router');
@ -228,23 +226,23 @@ describe('site App', () => {
});
describe('suggest edit link', () => {
it('should be present on all docs pages', () => {
page.navigateTo('tutorial/toh-pt1');
expect(page.ghLinks.count()).toEqual(1);
it('should be present on all docs pages', async () => {
await page.navigateTo('tutorial/toh-pt1');
expect(await page.ghLinks.count()).toEqual(1);
/* 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\.\.\./);
page.navigateTo('guide/http');
expect(page.ghLinks.count()).toEqual(1);
await page.navigateTo('guide/http');
expect(await page.ghLinks.count()).toEqual(1);
/* 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\.\.\./);
});
it('should not be present on top level pages', () => {
page.navigateTo('features');
expect(page.ghLinks.count()).toEqual(0);
it('should not be present on top level pages', async () => {
await page.navigateTo('features');
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\//;
@ -12,16 +12,17 @@ export class SitePage {
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
ghLinks = this.docViewer
.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();
return win.getSize().then(oldSize => win.setSize(newWidth, oldSize.height));
const oldSize = await win.getSize();
await win.setSize(newWidth, oldSize.height);
}
getNavItem(pattern: RegExp) {
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();
}
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}"]`)); }
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; }
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
ga() { return browser.executeScript<any[][]>('return window["ga"].q'); }
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.
return browser.get('/' + pageUrl)
.then(() => browser.executeScript('document.body.classList.add(\'no-animations\')'))
.then(() => browser.waitForAngular());
await browser.get(`/${pageUrl}`);
await browser.executeScript('document.body.classList.add(\'no-animations\')');
await browser.waitForAngular();
}
getDocViewerText() {
@ -68,31 +69,32 @@ export class SitePage {
`);
}
click(elementFinder: ElementFinder) {
return elementFinder.click().then(() => browser.waitForAngular());
async click(elementFinder: ElementFinder) {
await elementFinder.click();
await browser.waitForAngular();
}
enterSearch(query: string) {
async enterSearch(query: string) {
const input = element(by.css('.search-container input[type=search]'));
input.clear();
input.sendKeys(query);
await input.clear();
await input.sendKeys(query);
}
getSearchResults() {
async getSearchResults() {
const results = element.all(by.css('.search-results li'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results.map(link => link && link.getText());
await browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results.map(link => link?.getText());
}
getApiSearchResults() {
async getApiSearchResults() {
const results = element.all(by.css('aio-api-list .api-item'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 2000);
return results.map(elem => elem && elem.getText());
await browser.wait(ExpectedConditions.presenceOf(results.first()), 2000);
return results.map(elem => elem?.getText());
}
clickDropdownItem(dropdown: ElementFinder, itemName: string){
dropdown.element(by.css('.form-select-button')).click();
async clickDropdownItem(dropdown: ElementFinder, itemName: string){
await dropdown.element(by.css('.form-select-button')).click();
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', () => {
let page: SitePage;
beforeAll(() => {
beforeAll(async () => {
page = new SitePage();
page.navigateTo('');
await page.navigateTo('');
});

View File

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

View File

@ -6,7 +6,6 @@
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"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": [
true,
"debug",
@ -66,7 +64,6 @@
// TODO(gkalpak): Fix the code and enable this to align with CLI. (Failures: 59)
// "no-non-null-assertion": true,
"no-redundant-jsdoc": true,
// TODO(gkalpak): Fix the code and remove this to align with CLI.
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [

File diff suppressed because it is too large Load Diff

View File

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