测试一个复杂的AngularJS模块(自动展开菜单)
Testing a complex AngularJS module (auto-expanding menu)
我已经用AngularJS构造了一个相当复杂的菜单,我希望能得到一些关于如何测试它的指导。该项目使用了Jasmine和Karma。我在网上找到的所有示例和教程似乎都只演示了非常简单的指令和控制器。
它在Plunker上运行:http://plnkr.co/edit/6bgf5E6oP7v11I4i9SVz?p=preview
我甚至不知道从哪里开始,所以任何帮助都是非常感谢的。
代码:
HTML<jl-menu data-ng-class="menuState()" jl-scrollable-parent="#wrap" jl-fixed-offset-element="#wrap" jl-fixed-offset-y="{{menuOffset()}}" jl-scroll-offset="0" class="menu_wrap">
<jl-menu-range jl-from-element="#section1" jl-from-offset="0" jl-from-attr="top" jl-to-element="#section3" jl-to-offset="0" jl-to-attr="top"></jl-menu-range>
<jl-menu-range jl-from-element="#section4" jl-from-offset="0" jl-from-attr="top" jl-to-element="#section5" jl-to-offset="0" jl-to-attr="top"></jl-menu-range>
<jl-menu-range jl-from-element="#section6" jl-from-offset="0" jl-from-attr="top" jl-to-element="#section6" jl-to-offset="0" jl-to-attr="bottom"></jl-menu-range>
<ul class="menu">
<li data-ng-class="menuItemState('section1')">
<a href="#section1" jl-scroll-to="section1" jl-scrollable-parent="#wrap">Section 1</a>
<ul class="submenu" data-ng-class="submenuState('section1')">
<li data-ng-class="menuItemState('section1_1')">
<a href="#section1_1" jl-scroll-to="section1_1" jl-scrollable-parent="#wrap">Section1, 1</a>
</li>
<li data-ng-class="menuItemState('section1_2')">
<a href="#section1_2" jl-scroll-to="section1_2" jl-scrollable-parent="#wrap">Section1, 2</a>
</li>
<li data-ng-class="menuItemState('section1_3')">
<a href="#section1_3" jl-scroll-to="section1_3" jl-scrollable-parent="#wrap">Section1, 3</a>
</li>
</ul>
</li>
<li data-ng-class="menuItemState('section2')">
<a href="#section2" jl-scroll-to="section2" jl-scrollable-parent="#wrap">Section 2</a>
<ul class="submenu" data-ng-class="submenuState('section2')">
<li data-ng-class="menuItemState('section2_1')">
<a href="#section2_1" jl-scroll-to="section2_1" jl-scrollable-parent="#wrap">Section2, 1</a>
</li>
<li data-ng-class="menuItemState('section2_2')">
<a href="#section2_2" jl-scroll-to="section2_2" jl-scrollable-parent="#wrap">Section2, 2</a>
</li>
<li data-ng-class="menuItemState('section2_3')">
<a href="#section2_3" jl-scroll-to="section2_3" jl-scrollable-parent="#wrap">Section2, 3</a>
</li>
</ul>
</li>
<li data-ng-class="menuItemState('section3')">
<a href="#section3" jl-scroll-to="section3" jl-scrollable-parent="#wrap">Section 3</a>
<li data-ng-class="menuItemState('section4')">
<a href="#section4" jl-scroll-to="section4" jl-scrollable-parent="#wrap">Section 4</a>
<ul class="submenu" data-ng-class="submenuState('section4')">
<li data-ng-class="menuItemState('section4_1')">
<a href="#section4_1" jl-scroll-to="section4_1" jl-scrollable-parent="#wrap">Section4, 1</a>
</li>
<li data-ng-class="menuItemState('section4_2')">
<a href="#section4_2" jl-scroll-to="section4_2" jl-scrollable-parent="#wrap">Section4, 2</a>
</li>
<li data-ng-class="menuItemState('section4_3')">
<a href="#section4_3" jl-scroll-to="section4_3" jl-scrollable-parent="#wrap">Section4, 3</a>
</li>
</ul>
</li>
<li data-ng-class="menuItemState('section5')">
<a href="#section5" jl-scroll-to="section5" jl-scrollable-parent="#wrap">Section 5</a>
<li data-ng-class="menuItemState('section6')">
<a href="#section6" jl-scroll-to="section6" jl-scrollable-parent="#wrap">Section 6</a>
<ul class="submenu" data-ng-class="submenuState('section6')">
<li data-ng-class="menuItemState('section6_1')">
<a href="#section6_1" jl-scroll-to="section6_1" jl-scrollable-parent="#wrap">section6, 1</a>
</li>
<li data-ng-class="menuItemState('section6_2')">
<a href="#section6_2" jl-scroll-to="section6_2" jl-scrollable-parent="#wrap">section6, 2</a>
</li>
<li data-ng-class="menuItemState('section6_3')">
<a href="#section6_3" jl-scroll-to="section6_3" jl-scrollable-parent="#wrap">section6, 3</a>
</li>
</ul>
</li>
</ul>
</jl-menu>
Javascript angular.module("App", [
"Common",
"Menu"])
.config(["$logProvider", function($logProvider) {
$logProvider.debugEnabled(true);
}]);
angular.module("Common", [])
.config(["$logProvider", function($logProvider) {
$logProvider.debugEnabled(true);
}])
.directive("jlDocumentOffsetY", ["$log", function($log) {
function link(scope, element, attrs) {
attrs.$observe("jlDocumentOffsetY", function(value) {
var y = parseInt(value);
if (isNaN(y)) {
$log.error("Error parsing int expression in jlDocumentOffsetY directive");
return;
}
angular.element(element).offset({ top: y });
});
}
return {
restrict: "A",
link: link
};
}])
.directive("jlFixedOffsetY", ["$log", "$window", function($log, $window) {
function link(scope, element, attrs) {
var container = angular.element($window);
if (attrs.jlFixedOffsetElement && attrs.jlFixedOffsetElement != "window") {
container = angular.element(attrs.jlFixedOffsetElement);
if (container.length === 0) {
$log.warn("Could not find element '" + attrs.jlFixedOffsetElement + "'");
container = angular.element($window);
}
}
attrs.$observe("jlFixedOffsetY", function(value) {
var y = parseInt(value);
if (isNaN(y)) {
$log.error("Error parsing int expression in jlFixedOffsetY directive");
return;
}
angular.element(element).offset({ top: y + container.scrollTop() });
});
}
return {
restrict: "A",
link: link
};
}]);
angular.module("Menu", ["Common"])
.config(["$logProvider", function($logProvider) {
$logProvider.debugEnabled(true);
}])
.directive("jlMenu", ["$window", function($window) {
function link(scope, element, attrs) {
if (attrs.jlScrollOffset) {
scope.scrollOffset = parseInt(attrs.jlScrollOffset);
}
else {
scope.scrollOffset = 0;
}
scope.scrollable = angular.element($window);
scope.scrollableIsWindow = true;
if (attrs.jlScrollableParent && attrs.jlScrollableParent != "window") {
scope.scrollable = angular.element(attrs.jlScrollableParent);
if (scope.scrollable.length === 0) {
$log.warn("Could not find element '" + jlScrollableParent + "'");
scope.scrollable = angular.element($window);
}
else {
scope.scrollableIsWindow = false;
}
}
scope.scrollable.bind("scroll", function() {
scope.$apply();
});
}
return {
restrict: "E",
controller: [
"$scope", "$element", "$document", "$interval",
function($scope, $element, $document, $interval) {
var element = angular.element($element);
var ranges = [];
var offsetY;
var prevRange = -1;
var range = -1;
var nextRange = -1;
var pageH = angular.element($document).height();
var ST_DOCKED = 0; // Menu is in fixed position
var ST_HALT_ABOVE = 1; // Menu is positioned at the bottom of above range.
var ST_HALT_BELOW = 2; // Menu is positioned at top of below range.
var cssClasses = {
classes: [],
add: function(css) {
if (this.classes.indexOf(css) == -1) {
this.classes.push(css);
}
},
remove: function(css) {
var i = this.classes.indexOf(css);
if (i != -1) {
this.classes.splice(i, 1);
}
},
get: function() {
return this.classes.join(" ");
}
};
function getState() {
var scrollTop = $scope.scrollable.scrollTop();
var offset = $scope.scrollOffset;
prevRange = -1;
range = -1;
nextRange = -1;
for (var i = 0; i < ranges.length; ++i) {
if (scrollTop + offset > ranges[i].from) {
if (scrollTop + offset < ranges[i].to) {
range = i;
}
else {
prevRange = i;
}
}
else {
nextRange = i;
break;
}
}
var mnuH = element.height();
if (range != -1) {
if (scrollTop + offset >= ranges[range].from) {
if (scrollTop + offset + mnuH < ranges[range].to) {
return ST_DOCKED;
}
else {
return ST_HALT_ABOVE;
}
}
}
else {
if (nextRange != -1) {
return ST_HALT_BELOW;
}
}
}
var onScroll = (function() {
var lock = false;
var prevState = ST_HALT_BELOW;
var state = prevState;
return function() {
if (lock) return;
prevState = state;
state = getState();
if (ranges.length === 0) {
ranges.push({
from: 0,
to: pageH
});
}
var scrollTop = $scope.scrollable.scrollTop();
var offset = $scope.scrollOffset;
var mnuH = element.height();
cssClasses.remove("menu_docked");
cssClasses.remove("menu_halt_above");
cssClasses.remove("menu_halt_below");
switch (state) {
case ST_DOCKED:
offsetY = offset;
cssClasses.add("menu_docked");
break;
case ST_HALT_ABOVE:
cssClasses.add("menu_halt_above");
if (prevState == ST_HALT_BELOW) {
cssClasses.add("menu_fade_out");
$interval(function() {
offsetY = ranges[range].to - mnuH - scrollTop;
cssClasses.remove("menu_fade_out");
cssClasses.add("menu_fade_in");
$interval(function() {
cssClasses.remove("menu_fade_in");
}, 500, 1);
lock = false;
}, 300, 1);
lock = true;
}
else {
offsetY = ranges[range].to - mnuH - scrollTop;
}
break;
case ST_HALT_BELOW:
offsetY = ranges[nextRange].from - scrollTop;
cssClasses.add("menu_halt_below");
if (prevState == ST_HALT_ABOVE) {
cssClasses.add("menu_fade_in");
$interval(function() {
cssClasses.remove("menu_fade_in");
}, 500, 1);
}
break;
}
};
})();
this.addRange = function(range) {
ranges.push(range);
};
$scope.$watch(onScroll);
$scope.menuOffset = function() {
var x = 0;
if ($scope.scrollable && !$scope.scrollableIsWindow) {
x = $scope.scrollable.scrollTop() - $scope.scrollable.offset().top;
}
return offsetY - x;
};
$scope.menuState = function() {
return cssClasses.get();
};
$scope.menuItemState = function(sectionId) {
var x = 0;
if ($scope.scrollable && !$scope.scrollableIsWindow) {
x = $scope.scrollable.scrollTop() - $scope.scrollable.offset().top;
}
var section = angular.element("#" + sectionId);
if (section.length === 0) {
$log.error("No element with id '" + sectionId + "'");
return "item_inactive";
}
var sectionTop = section.offset().top + x;
var sectionBtm = sectionTop + section.height();
var scrollTop = $scope.scrollable.scrollTop() + $scope.scrollOffset;
return (scrollTop >= sectionTop && scrollTop < sectionBtm) ? "item_active" : "item_inactive";
};
$scope.submenuState = function(sectionId) {
var x = 0;
if ($scope.scrollable && !$scope.scrollableIsWindow) {
x = $scope.scrollable.scrollTop() - $scope.scrollable.offset().top;
}
var section = angular.element("#" + sectionId);
if (section.length === 0) {
$log.error("No element with id '" + sectionId + "'");
return "item_inactive";
}
var sectionTop = section.offset().top + x;
var sectionBtm = sectionTop + section.height();
var scrollTop = $scope.scrollable.scrollTop() + $scope.scrollOffset;
return (scrollTop >= sectionTop && scrollTop < sectionBtm) ? "submenu_active" : "submenu_inactive";
};
}],
replace: true,
transclude: true,
template: "<div ng-transclude='ng-transclude'></div>",
link: link
};
}])
.directive("jlMenuRange", ["$log", function($log) {
function link(scope, element, attrs, jlMenuCtrl) {
// Note that this assumes that the DOM doesn't change, that
// everything remains in the same place after page load.
var fromY = 0, fromOffset = 0;
var toY = 0, toOffset = 0;
if (attrs.jlFromElement) {
var e = angular.element(attrs.jlFromElement);
if (e.length > 0) {
if (attrs.jlFromAttr == "top") {
fromY = e.offset().top;
}
else if (attrs.jlFromAttr == "bottom") {
fromY = e.offset().top + e.height();
}
else { // Default to 'top'
fromY = e.offset().top;
}
}
else {
$log.error("Could not find element '" + attrs.jlFromElement + "'");
}
}
else {
$log.warn("jlMenuRange directive expects jlFromElement attribute");
}
if (attrs.jlFromOffset) {
fromOffset = parseInt(attrs.jlFromOffset);
}
else {
fromOffset = 0;
}
if (attrs.jlToElement) {
var e_ = angular.element(attrs.jlToElement);
if (e_.length > 0) {
if (attrs.jlToAttr == "top") {
toY = e_.offset().top;
}
else if (attrs.jlToAttr == "bottom") {
toY = e_.offset().top + e_.height();
}
else { // Default to 'top'
toY = e_.offset().top;
}
}
else {
$log.error("Could not find element '" + attrs.jlFromElement + "'");
}
}
else {
$log.warn("jlMenuRange directive expects jlToElement attribute");
toY = angular.element("body").height();
}
if (attrs.jlToOffset) {
toOffset = parseInt(attrs.jlToOffset);
}
else {
toOffset = 0;
}
jlMenuCtrl.addRange({
from: fromY + fromOffset,
to: toY + toOffset
});
}
return {
restrict: "E",
require: "^jlMenu",
replace: true,
template: "<span></span>",
link: link
};
}])
.directive("jlScrollTo", ["$log", function($log) {
function link(scope, element, attrs) {
if (!attrs.jlScrollTo) {
$log.error("jlScrollTo directive expects destination element id argument, e.g. 'sectionB'");
return;
}
var scrollable = angular.element("html, body");
if (attrs.jlScrollableParent && attrs.jlScrollableParent != "window") {
scrollable = angular.element(attrs.jlScrollableParent);
if (scrollable.length === 0) {
$log.warn("Could not find element '" + attrs.jlScrollableParent + "'");
scrollable = angular.element("html, body");
}
}
var elem = angular.element(element);
var destElem = angular.element("#" + attrs.jlScrollTo);
if (destElem.length === 0) {
$log.warn("Element with id '" + attrs.jlScrollTo + "' not found");
return;
}
var destY = destElem.offset().top;
elem.bind("click", function() {
var h = Math.abs(destY - scrollable.scrollTop());
var pps = 1600; // pixels per second
var t = h / pps;
scrollable.animate({
scrollTop: destY
}, t * 1000);
return false;
});
}
return {
restrict: "A",
link: link
};
}]);
我的解决方案如下:
"use strict";
describe("Menu", function() {
beforeEach(module("Menu"));
describe("jlScrollTo directive", function() {
var scope, $compile;
beforeEach(inject(function($rootScope, _$compile_) {
scope = $rootScope.$new();
$compile = _$compile_;
}));
it("should scroll to destination upon click", function() {
var html = "";
html += "<div id='wrap'>";
html += " <div id='scrollWin' style='position: absolute; top: 0; height: 100px; overflow-y: scroll'>";
html += " <div id='section1' style='height: 300px'>";
html += " <a id='buttonA' href='' jl-scrollable-parent='#scrollWin' jl-scroll-to='#section2'>Click me</a>";
html += " <a id='buttonB' href='' jl-scrollable-parent='#scrollWin' jl-scroll-to='#section5'>Click me</a>";
html += " </div>";
html += " <div id='section2' style='height: 300px'></div>";
html += " <div id='section3' style='height: 300px'></div>";
html += " <div id='section4' style='height: 300px'></div>";
html += " <div id='section5' style='height: 300px'></div>";
html += " </div>";
html += "</div>";
var element = angular.element(html);
element.appendTo(document.body)
var doc = $compile(element)(scope);
var eScrollWin = doc.find("#scrollWin");
var eButtonA = doc.find("#buttonA");
var eButtonB = doc.find("#buttonB");
$.fx.off = true;
expect(eScrollWin.scrollTop()).toEqual(0);
eButtonA.triggerHandler("click");
expect(eScrollWin.scrollTop()).toEqual(300);
eButtonB.triggerHandler("click");
expect(eScrollWin.scrollTop()).toEqual(1200);
eButtonA.triggerHandler("click");
expect(eScrollWin.scrollTop()).toEqual(300);
$.fx.off = false;
document.body.removeChild(element.get(0));
});
});
describe("jlMenuRange directive", function() {
var scope, $compile;
beforeEach(inject(function($rootScope, _$compile_) {
scope = $rootScope.$new();
$compile = _$compile_;
}));
it("should set ranges in jlMenu's controller", function() {
var html = "";
html += "<div id='wrap' style='position: absolute; padding: 0; top: 0'>";
html += "<jl-menu id='menu' jl-scrollable-parent='#wrap' jl-fixed-offset-element='window' jl-fixed-offset-y='{{0}}' jl-scroll-offset='0' style='position: absolute; top: 0; height: 0'>";
html += "</jl-menu>";
html += "<div id='section1' style='height: 300px'></div>";
html += "<div id='section2' style='height: 300px'></div>";
html += "<div id='section3' style='height: 300px'></div>";
html += "<div id='section4' style='height: 300px'></div>";
html += "<div id='section5' style='height: 300px'></div>";
html += "</div>";
var eWrap = angular.element(html);
eWrap.appendTo(document.body)
var eJlMenu = eWrap.find("#menu");
var cmpJlMenu = $compile(eJlMenu)(scope);
var jlMenuCtrl = cmpJlMenu.controller("jlMenu");
spyOn(jlMenuCtrl, "addRange");
var jlMenuRange = "";
jlMenuRange += "<jl-menu-range jl-from-element='#section1' jl-to-element='#section2'></jl-menu-range>";
jlMenuRange += "<jl-menu-range jl-from-element='#section3' jl-to-element='#section4' jl-to-attr='bottom'></jl-menu-range>";
var eJlMenuRange = angular.element(jlMenuRange);
cmpJlMenu.append(eJlMenuRange);
var cmpJlMenuRange = $compile(eJlMenuRange)(scope);
expect(jlMenuCtrl.addRange).toHaveBeenCalledWith({ from: 0, to: 300 });
expect(jlMenuCtrl.addRange).toHaveBeenCalledWith({ from: 600, to: 1200 });
document.body.removeChild(eWrap.get(0));
});
});
describe("jlMenu directive", function() {
var scope, element, doc, common;
var offset = 60;
beforeEach(inject(function($rootScope, $compile, _common_) {
scope = $rootScope.$new();
common = _common_;
var html = "";
html += "<div id='outer-wrap' style='position: absolute; top: 100px; left: 0; width: 100%; height: 100%; overflow: hidden;'>";
html += " <div id='wrap' style='top: 0; left: 0; width: 100%; height: 100%; overflow-y: scroll;'>";
html += " <div id='sidebar' style='position: absolute; width: 200px; left: 0; top: 0; height: 100%; padding: 0 0 0 10px; z-index: 1;'>";
html += " <jl-menu id='menu' jl-scrollable-parent='#wrap' jl-scroll-offset='" + offset + "' style='position: absolute; top: 0; margin: 0; padding: 0;'>";
html += " <jl-menu-range jl-container-element='#wrap' jl-from-element='#section1' jl-from-offset='0' jl-from-attr='top' jl-to-element='#section3' jl-to-offset='0' jl-to-attr='top'></jl-menu-range>";
html += " <jl-menu-range jl-container-element='#wrap' jl-from-element='#section4' jl-from-offset='0' jl-from-attr='top' jl-to-element='#section5' jl-to-offset='0' jl-to-attr='top'></jl-menu-range>";
html += " <jl-menu-range jl-container-element='#wrap' jl-from-element='#section6' jl-from-offset='0' jl-from-attr='top' jl-to-element='#section6' jl-to-offset='0' jl-to-attr='bottom'></jl-menu-range>";
html += " <ul class='menu'>";
html += " <li>";
html += " <a href='#section1'>Section 1</a>";
html += " <ul class='submenu'>";
html += " <li>";
html += " <a href='#section1_1'>Section1, 1</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section1_2'>Section1, 2</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section1_3'>Section1, 3</a>";
html += " </li>";
html += " </ul>";
html += " </li>";
html += " <li>";
html += " <a href='#section2'>Section 2</a>";
html += " <ul class='submenu'>";
html += " <li>";
html += " <a href='#section2_1'>Section2, 1</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section2_2'>Section2, 2</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section2_3'>Section2, 3</a>";
html += " </li>";
html += " </ul>";
html += " </li>";
html += " <li>";
html += " <a href='#section3'>Section 3</a>";
html += " <li>";
html += " <a href='#section4'>Section 4</a>";
html += " <ul class='submenu'>";
html += " <li>";
html += " <a href='#section4_1'>Section4, 1</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section4_2'>Section4, 2</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section4_3'>Section4, 3</a>";
html += " </li>";
html += " </ul>";
html += " </li>";
html += " <li>";
html += " <a href='#section5'>Section 5</a>";
html += " <li>";
html += " <a href='#section6'>Section 6</a>";
html += " <ul class='submenu'>";
html += " <li>";
html += " <a href='#section6_1'>Section6, 1</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section6_2'>Section6, 2</a>";
html += " </li>";
html += " <li>";
html += " <a href='#section6_3'>Section6, 3</a>";
html += " </li>";
html += " </ul>";
html += " </li>";
html += " </ul>";
html += " </jl-menu>";
html += " </div>";
html += " <div id='section1'>";
html += " <div class='section' id='section1_1' style='height: 300px;'></div>";
html += " <div class='section' id='section1_2' style='height: 300px;'></div>";
html += " <div class='section' id='section1_3' style='height: 300px;'></div>";
html += " </div>";
html += " <div id='section2'>";
html += " <div class='section' id='section2_1' style='height: 300px;'></div>";
html += " <div class='section' id='section2_2' style='height: 300px;'></div>";
html += " <div class='section' id='section2_3' style='height: 300px;'></div>";
html += " </div>";
html += " <div id='section3' style='position: relative; height: 500px;'></div>";
html += " <div id='section4'>";
html += " <div class='section' id='section4_1' style='height: 300px;'></div>";
html += " <div class='section' id='section4_2' style='height: 300px;'></div>";
html += " <div class='section' id='section4_3' style='height: 300px;'></div>";
html += " </div>";
html += " <div id='section5' style='position: relative; height: 500px;'></div>";
html += " <div id='section6'>";
html += " <div class='section' id='section6_1' style='height: 300px;'></div>";
html += " <div class='section' id='section6_2' style='height: 300px;'></div>";
html += " <div class='section' id='section6_3' style='height: 300px;'></div>";
html += " </div>";
html += " </div>";
html += "</div>";
element = angular.element(html);
element.appendTo(document.body)
doc = $compile(element)(scope);
}));
it("should remain docked when within range", function() {
var eWrap = doc.find("#wrap");
var eMenu = doc.find("#menu");
$.fx.off = true;
eWrap.scrollTop(310);
eWrap.triggerHandler("scroll");
scope.$digest();
expect(scope.menuState()).toEqual("menu_docked");
expect(scope.menuOffset()).toEqual(offset);
expect(scope.submenuState("#section1")).toEqual("submenu_active");
expect(scope.submenuState("#section2")).toEqual("submenu_inactive");
expect(scope.submenuState("#section4")).toEqual("submenu_inactive");
expect(scope.submenuState("#section6")).toEqual("submenu_inactive");
expect(scope.menuItemState("#section1")).toEqual("item_active");
expect(scope.menuItemState("#section1_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_2")).toEqual("item_active");
expect(scope.menuItemState("#section1_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section3")).toEqual("item_inactive");
expect(scope.menuItemState("#section4")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section5")).toEqual("item_inactive");
expect(scope.menuItemState("#section6")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_3")).toEqual("item_inactive");
$.fx.off = false;
document.body.removeChild(element.get(0));
});
it("should stick to bottom of range when approaching end of range", function() {
var eWrap = doc.find("#wrap");
var eMenu = doc.find("#menu");
var eSection3 = doc.find("#section3");
var rangeEnd = common.yPosWithinScrollable(eSection3, eWrap);
$.fx.off = true;
eWrap.scrollTop(rangeEnd - offset - 50);
eWrap.triggerHandler("scroll");
scope.$digest();
expect(scope.menuState()).toEqual("menu_halt_above");
expect(scope.menuOffset()).toEqual(common.fixedOffsetY(eSection3, eWrap) - eMenu.height());
expect(scope.submenuState("#section1")).toEqual("submenu_inactive");
expect(scope.submenuState("#section2")).toEqual("submenu_active");
expect(scope.submenuState("#section4")).toEqual("submenu_inactive");
expect(scope.submenuState("#section6")).toEqual("submenu_inactive");
expect(scope.menuItemState("#section1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section2")).toEqual("item_active");
expect(scope.menuItemState("#section2_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_3")).toEqual("item_active");
expect(scope.menuItemState("#section3")).toEqual("item_inactive");
expect(scope.menuItemState("#section4")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section5")).toEqual("item_inactive");
expect(scope.menuItemState("#section6")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_3")).toEqual("item_inactive");
$.fx.off = false;
document.body.removeChild(element.get(0));
});
it("should fade in at top of next range when exiting range", function() {
var eWrap = doc.find("#wrap");
var eMenu = doc.find("#menu");
var eSection3 = doc.find("#section3");
var eSection4 = doc.find("#section4");
var rangeEnd = common.yPosWithinScrollable(eSection3, eWrap);
$.fx.off = true;
eWrap.scrollTop(rangeEnd - offset - 1);
eWrap.triggerHandler("scroll");
scope.$digest();
eWrap.scrollTop(rangeEnd - offset);
eWrap.triggerHandler("scroll");
scope.$digest();
var nextRangeStart = common.fixedOffsetY(eSection4, eWrap);
expect(scope.menuState()).toEqual("menu_fade_in menu_halt_below");
expect(scope.menuOffset()).toEqual(nextRangeStart);
expect(scope.submenuState("#section1")).toEqual("submenu_inactive");
expect(scope.submenuState("#section2")).toEqual("submenu_inactive");
expect(scope.submenuState("#section4")).toEqual("submenu_inactive");
expect(scope.submenuState("#section6")).toEqual("submenu_inactive");
expect(scope.menuItemState("#section1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section3")).toEqual("item_active");
expect(scope.menuItemState("#section4")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section5")).toEqual("item_inactive");
expect(scope.menuItemState("#section6")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_3")).toEqual("item_inactive");
$.fx.off = false;
document.body.removeChild(element.get(0));
});
it("should fade out and reappear at bottom of above range when scrolling upward into range", function() {
var eWrap = doc.find("#wrap");
var eMenu = doc.find("#menu");
var eSection3 = doc.find("#section3");
var eSection4 = doc.find("#section4");
var rangeAEnd = common.yPosWithinScrollable(eSection3, eWrap);
$.fx.off = true;
eWrap.scrollTop(rangeAEnd - offset + 100);
eWrap.triggerHandler("scroll");
scope.$digest();
var rangeBStart_fixed = common.fixedOffsetY(eSection4, eWrap);
expect(scope.menuState()).toEqual("menu_halt_below");
expect(scope.menuOffset()).toEqual(rangeBStart_fixed);
expect(scope.submenuState("#section1")).toEqual("submenu_inactive");
expect(scope.submenuState("#section2")).toEqual("submenu_inactive");
expect(scope.submenuState("#section4")).toEqual("submenu_inactive");
expect(scope.submenuState("#section6")).toEqual("submenu_inactive");
expect(scope.menuItemState("#section1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section3")).toEqual("item_active");
expect(scope.menuItemState("#section4")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section5")).toEqual("item_inactive");
expect(scope.menuItemState("#section6")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_3")).toEqual("item_inactive");
eWrap.scrollTop(rangeAEnd - offset - 100);
eWrap.triggerHandler("scroll");
scope.$digest();
rangeBStart_fixed = common.fixedOffsetY(eSection4, eWrap);
expect(scope.menuState()).toEqual("menu_fade_out menu_halt_below");
expect(scope.menuOffset()).toEqual(rangeBStart_fixed);
expect(scope.submenuState("#section1")).toEqual("submenu_inactive");
expect(scope.submenuState("#section2")).toEqual("submenu_active");
expect(scope.submenuState("#section4")).toEqual("submenu_inactive");
expect(scope.submenuState("#section6")).toEqual("submenu_inactive");
expect(scope.menuItemState("#section1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section1_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section2")).toEqual("item_active");
expect(scope.menuItemState("#section2_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section2_3")).toEqual("item_active");
expect(scope.menuItemState("#section3")).toEqual("item_inactive");
expect(scope.menuItemState("#section4")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section4_3")).toEqual("item_inactive");
expect(scope.menuItemState("#section5")).toEqual("item_inactive");
expect(scope.menuItemState("#section6")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_1")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_2")).toEqual("item_inactive");
expect(scope.menuItemState("#section6_3")).toEqual("item_inactive");
$.fx.off = false;
document.body.removeChild(element.get(0));
});
});
});
相关文章:
- JavaScript下拉菜单-点击按钮并根据所选值重定向到url
- Javascript,访问一个主要对象模块模式中的每个对象
- Jquery菜单操作不稳定,定位不正确,存在一般错误
- 节点Js:How to catch a“;没有这样的文件或目录“;读取线模块出错
- 如何从模块链中调用函数.导出到节点中
- 尽管链接成功并已成功下载,但未找到NPM模块
- angular的下拉菜单
- 节点是否需要模块传递带有方括号的arg?这是个错误吗
- 使用JQuery的动态上下文菜单
- 创建带有和不带有JavaScript的Bootstrap下拉菜单
- 从模块内部访问Express装载路径
- 创建下拉菜单
- RequireJS向模块传递配置总是返回undefined
- Node.js正在更改应用程序以使用集群模块
- 测试一个复杂的AngularJS模块(自动展开菜单)
- Joomla下拉菜单在其他模块后面
- Drupal,如何创建带有弹出模块/面板的侧栏菜单
- 添加jquery事件到joomla主菜单模块
- 在左侧栏菜单中,一次只能打开一个模块
- 漂亮的菜单drupal模块移动到/all/modules后,在Firefox中无法工作