test($anchorScroll): add e2e tests

Fixes #9535
Closes #9583

Closes #14932
This commit is contained in:
Georgios Kalpakas
2016-07-19 00:33:02 +03:00
parent ce40d5cbda
commit 482a3ae9f9
5 changed files with 274 additions and 0 deletions
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html ng-app="test">
<body>
<div class="fixed-header" ng-controller="TestController">
<button ng-click="scrollTo('anchor-' + x)" ng-repeat="x in [1, 2, 3, 4, 5]">
Scroll to anchor-{{x}}
</button>
</div>
<div class="anchor" id="anchor-{{y}}" ng-repeat="y in [1, 2, 3, 4, 5]">
Anchor {{y}} of 5
</div>
<style type="text/css">
body {
height: 100%;
margin: 0;
padding-top: 50px;
}
.anchor {
border: 2px dashed darkorchid;
padding: 10px 10px 390px 10px;
}
.fixed-header {
background-color: rgba(0, 0, 0, 0.2);
height: 50px;
position: fixed;
top: 0; left: 0; right: 0;
}
.fixed-header > button {
display: inline-block;
margin: 5px 15px;
}
</style>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -0,0 +1,17 @@
angular.
module('test', []).
controller('TestController', function($anchorScroll, $location, $scope) {
$anchorScroll.yOffset = 50;
$scope.scrollTo = function(target) {
if ($location.hash() !== target) {
// Set `$location.hash()` to `target` and
// `$anchorScroll` will detect the change and scroll
$location.hash(target);
} else {
// The hash is the same, but `target` might be out of view -
// explicitly call `$anchorScroll`
$anchorScroll();
}
};
});
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html ng-app="test">
<body>
<div class="scroll-area" ng-controller="TestController">
<a id="top" ng-click="scrollTo('bottom')">Go to bottom</a>
<a id="bottom" ng-click="scrollTo('top')">Back to top</a>
</div>
<style type="text/css">
.scroll-area {
height: 200px;
overflow: auto;
}
#bottom {
display: block;
margin-top: 2000px;
}
</style>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -0,0 +1,9 @@
angular.
module('test', []).
controller('TestController', function($anchorScroll, $location, $scope) {
$scope.scrollTo = function(target) {
// Set `$location.hash()` to `target` and
// `$anchorScroll` will detect the change and scroll
$location.hash(target);
};
});
+187
View File
@@ -0,0 +1,187 @@
describe('$anchorScroll', function() {
beforeEach(function() {
jasmine.addMatchers({
toBeInViewport: function() {
return {
compare: function(id) {
var result = {
pass: browser.driver.
executeScript(_script_isInViewport, id).
then(function(isInViewport) {
result.message = 'Expected #' + id + (isInViewport ? ' not' : '') +
' to be in viewport';
return isInViewport;
})
};
return result;
}
};
},
toHaveTop: function() {
return {
compare: function(id, expectedTop) {
var result = {
pass: browser.driver.
executeScript(_script_getTop, id).
then(function(actualTop) {
var passed = actualTop === expectedTop;
result.message = 'Expected #' + id + '\'s top' + (passed ? ' not' : '') +
' to be ' + expectedTop + ', but it was ' + actualTop;
return passed;
})
};
return result;
}
};
}
});
});
describe('basic functionality', function() {
beforeEach(function() {
loadFixture('anchor-scroll');
});
it('should scroll to #bottom when clicking #top and vice versa', function() {
expect('top').toBeInViewport();
expect('bottom').not.toBeInViewport();
element(by.id('top')).click();
expect('top').not.toBeInViewport();
expect('bottom').toBeInViewport();
element(by.id('bottom')).click();
expect('top').toBeInViewport();
expect('bottom').not.toBeInViewport();
});
});
describe('with `yOffset`', function() {
var yOffset = 50;
var buttons = element.all(by.repeater('x in [1, 2, 3, 4, 5]'));
var anchors = element.all(by.repeater('y in [1, 2, 3, 4, 5]'));
beforeEach(function() {
loadFixture('anchor-scroll-y-offset');
});
it('should scroll to the correct anchor when clicking each button', function() {
var lastAnchor = anchors.last();
// Make sure there is enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height - 10;
execWithTempViewportHeight(tempHeight, function() {
buttons.each(function(button, idx) {
// For whatever reason, we need to run the assertions inside a callback :(
button.click().then(function() {
var anchorId = 'anchor-' + (idx + 1);
expect(anchorId).toBeInViewport();
expect(anchorId).toHaveTop(yOffset);
});
});
});
});
});
it('should automatically scroll when navigating to a URL with a hash', function() {
var lastAnchor = anchors.last();
var lastAnchorId = 'anchor-5';
// Make sure there is enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height - 10;
execWithTempViewportHeight(tempHeight, function() {
// Test updating `$location.url()` from within the app
expect(lastAnchorId).not.toBeInViewport();
browser.setLocation('#' + lastAnchorId);
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
// Test navigating to the URL directly
scrollToTop();
expect(lastAnchorId).not.toBeInViewport();
browser.refresh();
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
});
});
});
it('should not scroll "overzealously"', function() {
var lastButton = buttons.last();
var lastAnchor = anchors.last();
var lastAnchorId = 'anchor-5';
// Make sure there is not enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height + (yOffset / 2);
execWithTempViewportHeight(tempHeight, function() {
scrollIntoView(lastAnchorId);
expect(lastAnchorId).toHaveTop(yOffset / 2);
lastButton.click();
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
});
});
});
});
// Helpers
function _script_getTop(id) {
var elem = document.getElementById(id);
var rect = elem.getBoundingClientRect();
return rect.top;
}
function _script_isInViewport(id) {
var elem = document.getElementById(id);
var rect = elem.getBoundingClientRect();
var docElem = document.documentElement;
return (rect.top < docElem.clientHeight) &&
(rect.bottom > 0) &&
(rect.left < docElem.clientWidth) &&
(rect.right > 0);
}
function execWithTempViewportHeight(tempHeight, fn) {
setViewportHeight(tempHeight).then(function(oldHeight) {
fn();
setViewportHeight(oldHeight);
});
}
function scrollIntoView(id) {
browser.driver.executeScript('document.getElementById("' + id + '").scrollIntoView()');
}
function scrollToTop() {
browser.driver.executeScript('window.scrollTo(0, 0)');
}
function setViewportHeight(newHeight) {
return browser.driver.
executeScript('return document.documentElement.clientHeight').
then(function(oldHeight) {
var heightDiff = newHeight - oldHeight;
var win = browser.driver.manage().window();
return win.getSize().then(function(size) {
return win.
setSize(size.width, size.height + heightDiff).
then(function() { return oldHeight; });
});
});
}
});