test($anchorScroll): add e2e tests
Fixes #9535 Closes #9583 Closes #14932
This commit is contained in:
@@ -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);
|
||||
};
|
||||
});
|
||||
@@ -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; });
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user