Skip to content

Commit 3c74ae0

Browse files
feat(tabs): adds e2e tests for tabs (#650)
closes #549
1 parent bbbc87f commit 3c74ae0

File tree

8 files changed

+156
-21
lines changed

8 files changed

+156
-21
lines changed

e2e/components/tabs/tabs.e2e.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import ElementArrayFinder = protractor.ElementArrayFinder;
2+
import ElementFinder = protractor.ElementFinder;
3+
4+
describe('tabs', () => {
5+
describe('basic behavior', () => {
6+
let tabGroup: ElementFinder;
7+
let tabLabels: ElementArrayFinder;
8+
let tabBodies: ElementArrayFinder;
9+
10+
beforeEach(() => {
11+
browser.get('/tabs');
12+
tabGroup = element(by.css('md-tab-group'));
13+
tabLabels = element.all(by.css('.md-tab-label'));
14+
tabBodies = element.all(by.css('.md-tab-body'));
15+
});
16+
17+
it('should change tabs when the label is clicked', () => {
18+
tabLabels.get(1).click();
19+
expect(getActiveStates(tabLabels)).toEqual([false, true, false]);
20+
expect(getActiveStates(tabBodies)).toEqual([false, true, false]);
21+
22+
tabLabels.get(0).click();
23+
expect(getActiveStates(tabLabels)).toEqual([true, false, false]);
24+
expect(getActiveStates(tabBodies)).toEqual([true, false, false]);
25+
});
26+
27+
it('should change focus with keyboard interaction', () => {
28+
tabLabels.get(0).click();
29+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
30+
31+
pressKey(protractor.Key.RIGHT);
32+
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
33+
34+
pressKey(protractor.Key.RIGHT);
35+
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
36+
37+
pressKey(protractor.Key.RIGHT);
38+
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
39+
40+
pressKey(protractor.Key.LEFT);
41+
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
42+
43+
pressKey(protractor.Key.LEFT);
44+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
45+
46+
pressKey(protractor.Key.LEFT);
47+
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
48+
});
49+
});
50+
});
51+
52+
/**
53+
* A helper function to perform the sendKey action
54+
* @param key
55+
*/
56+
function pressKey(key: string) {
57+
browser.actions().sendKeys(key).perform();
58+
}
59+
60+
/**
61+
* Returns an array of true/false that represents the focus states of the provided elements
62+
* @param elements
63+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
64+
*/
65+
function getFocusStates(elements: ElementArrayFinder) {
66+
return elements.map(element => {
67+
return element.getText().then(elementText => {
68+
return browser.driver.switchTo().activeElement().getText().then(activeText => {
69+
return activeText === elementText;
70+
});
71+
});
72+
});
73+
}
74+
75+
/**
76+
* Returns an array of true/false that represents the active states for the provided elements
77+
* @param elements
78+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
79+
*/
80+
function getActiveStates(elements: ElementArrayFinder) {
81+
return getClassStates(elements, 'md-active');
82+
}
83+
84+
/**
85+
* Returns an array of true/false values that represents whether the provided className is on
86+
* each element
87+
* @param elements
88+
* @param className
89+
* @returns {webdriver.promise.Promise<Promise<boolean>[]>|webdriver.promise.Promise<T[]>}
90+
*/
91+
function getClassStates(elements: ElementArrayFinder, className: string) {
92+
return elements.map(element => {
93+
return element.getAttribute('class').then(classes => {
94+
return classes.split(/ +/g).indexOf(className) >= 0;
95+
});
96+
});
97+
}

src/components/tabs/tab-group.html

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<div class="md-tab-header" role="tablist"
2-
(keydown.arrowRight)="focusNextTab()"
3-
(keydown.arrowLeft)="focusPreviousTab()"
4-
(keydown.enter)="selectedIndex = focusIndex">
2+
(keydown)="handleKeydown($event)">
53
<div class="md-tab-label" role="tab" md-tab-label-wrapper
64
*ngFor="let tab of _tabs; let i = index"
75
[id]="_getTabLabelId(i)"
@@ -16,10 +14,13 @@
1614
</div>
1715
<div class="md-tab-body-wrapper">
1816
<div class="md-tab-body"
19-
*ngFor="let tab of tabs; let i = index"
20-
[id]="getTabContentId(i)"
17+
role="tabpanel"
18+
*ngFor="let tab of _tabs; let i = index"
19+
[id]="_getTabContentId(i)"
2120
[class.md-active]="selectedIndex == i"
22-
[attr.aria-labelledby]="getTabLabelId(i)">
23-
<template role="tabpanel" [portalHost]="tab.content" [ngIf]="selectedIndex == i"></template>
21+
[attr.aria-labelledby]="_getTabLabelId(i)">
22+
<template [ngIf]="selectedIndex == i">
23+
<template [portalHost]="tab.content"></template>
24+
</template>
2425
</div>
2526
</div>

src/components/tabs/tabs.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ import {MdInkBar} from './ink-bar';
1919
import {Observable} from 'rxjs/Observable';
2020
import 'rxjs/add/operator/map';
2121

22+
// Due to a bug in the ChromeDriver, Angular 2 keyboard events are not triggered by `sendKeys`
23+
// during E2E tests when using dot notation such as `(keydown.rightArrow)`. To get around this,
24+
// we are temporarily using a single (keydown) handler.
25+
// See: https://github.com/angular/angular/issues/9419
26+
const RIGHT_ARROW = 39;
27+
const LEFT_ARROW = 37;
28+
const ENTER = 13;
29+
2230
/** Used to generate unique ID's for each tab component */
2331
let nextId = 0;
2432

@@ -159,6 +167,20 @@ export class MdTabGroup {
159167
return `md-tab-content-${this._groupId}-${i}`;
160168
}
161169

170+
handleKeydown(event: KeyboardEvent) {
171+
switch (event.keyCode) {
172+
case RIGHT_ARROW:
173+
this.focusNextTab();
174+
break;
175+
case LEFT_ARROW:
176+
this.focusPreviousTab();
177+
break;
178+
case ENTER:
179+
this.selectedIndex = this.focusIndex;
180+
break;
181+
}
182+
}
183+
162184
/** Increment the focus index by 1; prevent going over the number of tabs */
163185
focusNextTab(): void {
164186
if (this._labelWrappers && this.focusIndex < this._labelWrappers.length - 1) {

src/e2e-app/e2e-app/e2e-app.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<a md-list-item [routerLink]="['button']">Button</a>
2+
<a md-list-item [routerLink]="['tabs']">Tabs</a>
23

34
<router-outlet></router-outlet>

src/e2e-app/e2e-app/e2e-app.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import {Component} from '@angular/core';
22
import {ROUTER_DIRECTIVES} from '@angular/router';
33

4-
import {Dir} from '@angular2-material/core/rtl/dir';
5-
import {MdButton} from '@angular2-material/button/button';
6-
import {MD_SIDENAV_DIRECTIVES} from '@angular2-material/sidenav/sidenav';
7-
import {MD_LIST_DIRECTIVES} from '@angular2-material/list/list';
8-
import {MdIcon} from '@angular2-material/icon/icon';
9-
import {MdToolbar} from '@angular2-material/toolbar/toolbar';
10-
11-
124
@Component({
135
selector: 'home',
146
template: `
@@ -24,12 +16,6 @@ export class Home {}
2416
templateUrl: 'e2e-app.html',
2517
directives: [
2618
ROUTER_DIRECTIVES,
27-
Dir,
28-
MdButton,
29-
MdIcon,
30-
MD_SIDENAV_DIRECTIVES,
31-
MD_LIST_DIRECTIVES,
32-
MdToolbar
3319
],
3420
pipes: []
3521
})

src/e2e-app/e2e-app/routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {provideRouter, RouterConfig} from '@angular/router';
22
import {Home} from './e2e-app';
33
import {ButtonE2E} from '../button/button-e2e';
4+
import {BasicTabs} from '../tabs/tabs-e2e';
45

56

67
export const routes: RouterConfig = [
78
{path: '', component: Home},
89
{path: 'button', component: ButtonE2E},
10+
{path: 'tabs', component: BasicTabs},
911
];
1012

1113
export const E2E_APP_ROUTE_PROVIDER = provideRouter(routes);

src/e2e-app/tabs/tabs-e2e.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<section>
2+
<md-tab-group>
3+
<md-tab>
4+
<template md-tab-label>One</template>
5+
<template md-tab-content>First tab's content</template>
6+
</md-tab>
7+
<md-tab>
8+
<template md-tab-label>Two</template>
9+
<template md-tab-content>Second tab's content</template>
10+
</md-tab>
11+
<md-tab>
12+
<template md-tab-label>Three</template>
13+
<template md-tab-content>Third tab's content</template>
14+
</md-tab>
15+
</md-tab-group>
16+
</section>

src/e2e-app/tabs/tabs-e2e.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {Component} from '@angular/core';
2+
import {MD_TABS_DIRECTIVES} from '@angular2-material/tabs/tabs';
3+
4+
@Component({
5+
moduleId: module.id,
6+
selector: 'tabs-e2e',
7+
templateUrl: 'tabs-e2e.html',
8+
directives: [MD_TABS_DIRECTIVES]
9+
})
10+
export class BasicTabs {}

0 commit comments

Comments
 (0)