Commit d8b419c6 by muhammad-ammar

Review changes

parent 6134e8b5
define ["jquery", "js/views/feedback", "js/views/feedback_notification", "js/views/feedback_alert",
"js/views/feedback_prompt", "sinon"],
($, SystemFeedback, NotificationView, AlertView, PromptView, sinon) ->
tpl = readFixtures('system-feedback.underscore')
beforeEach ->
setFixtures(sandbox({id: "page-alert"}))
appendSetFixtures(sandbox({id: "page-notification"}))
appendSetFixtures(sandbox({id: "page-prompt"}))
appendSetFixtures($("<script>", {id: "system-feedback-tpl", type: "text/template"}).text(tpl))
@addMatchers
toBeShown: ->
@actual.hasClass("is-shown") and not @actual.hasClass("is-hiding")
toBeHiding: ->
@actual.hasClass("is-hiding") and not @actual.hasClass("is-shown")
toContainText: (text) ->
# remove this when we upgrade jasmine-jquery
trimmedText = $.trim(@actual.text())
if text and $.isFunction(text.test)
return text.test(trimmedText)
else
return trimmedText.indexOf(text) != -1;
toHaveBeenPrevented: ->
# remove this when we upgrade jasmine-jquery
eventName = @actual.eventName
selector = @actual.selector
@message = ->
[
"Expected event #{eventName} to have been prevented on #{selector}",
"Expected event #{eventName} not to have been prevented on #{selector}"
]
return jasmine.JQuery.events.wasPrevented(selector, eventName)
describe "SystemFeedback", ->
beforeEach ->
@options =
title: "Portal"
message: "Welcome to the Aperture Science Computer-Aided Enrichment Center"
# it will be interesting to see when this.render is called, so lets spy on it
@renderSpy = spyOn(AlertView.Confirmation.prototype, 'render').andCallThrough()
@showSpy = spyOn(AlertView.Confirmation.prototype, 'show').andCallThrough()
@hideSpy = spyOn(AlertView.Confirmation.prototype, 'hide').andCallThrough()
@clock = sinon.useFakeTimers()
afterEach ->
@clock.restore()
it "requires a type and an intent", ->
neither = =>
new SystemFeedback(@options)
noType = =>
options = $.extend({}, @options)
options.intent = "confirmation"
new SystemFeedback(options)
noIntent = =>
options = $.extend({}, @options)
options.type = "alert"
new SystemFeedback(options)
both = =>
options = $.extend({}, @options)
options.type = "alert"
options.intent = "confirmation"
new SystemFeedback(options)
expect(neither).toThrow()
expect(noType).toThrow()
expect(noIntent).toThrow()
expect(both).not.toThrow()
# for simplicity, we'll use AlertView.Confirmation from here on,
# which extends and proxies to SystemFeedback
it "does not show on initalize", ->
view = new AlertView.Confirmation(@options)
expect(@renderSpy).not.toHaveBeenCalled()
expect(@showSpy).not.toHaveBeenCalled()
# Disabled flaky test TNL-559
xit "renders the template", ->
view = new AlertView.Confirmation(@options)
view.show()
expect(view.$(".action-close")).toBeDefined()
expect(view.$('.wrapper')).toBeShown()
expect(view.$el).toContainText(@options.title)
expect(view.$el).toContainText(@options.message)
# Disabled flaky test TNL-559
xit "close button sends a .hide() message", ->
view = new AlertView.Confirmation(@options).show()
view.$(".action-close").click()
expect(@hideSpy).toHaveBeenCalled()
@clock.tick(900)
expect(view.$('.wrapper')).toBeHiding()
describe "PromptView", ->
# for some reason, expect($("body")) blows up the test runner, so this test
# just exercises the Prompt rather than asserting on anything. Best I can
# do for now. :(
it "changes class on body", ->
# expect($("body")).not.toHaveClass("prompt-is-shown")
view = new PromptView.Confirmation({
title: "Portal"
message: "Welcome to the Aperture Science Computer-Aided Enrichment Center"
})
# expect($("body")).toHaveClass("prompt-is-shown")
view.hide()
# expect($("body")).not.toHaveClass("prompt-is-shown")
describe "NotificationView.Mini", ->
beforeEach ->
@view = new NotificationView.Mini()
it "should have minShown set to 1250 by default", ->
expect(@view.options.minShown).toEqual(1250)
it "should have closeIcon set to false by default", ->
expect(@view.options.closeIcon).toBeFalsy()
# Disabled flaky test TNL-559
xdescribe "SystemFeedback click events", ->
beforeEach ->
@primaryClickSpy = jasmine.createSpy('primaryClick')
@secondaryClickSpy = jasmine.createSpy('secondaryClick')
@view = new NotificationView.Warning(
title: "Unsaved",
message: "Your content is currently Unsaved.",
actions:
primary:
text: "Save",
class: "save-button",
click: @primaryClickSpy
secondary:
text: "Revert",
class: "cancel-button",
click: @secondaryClickSpy
)
@view.show()
it "should trigger the primary event on a primary click", ->
@view.$(".action-primary").click()
expect(@primaryClickSpy).toHaveBeenCalled()
expect(@secondaryClickSpy).not.toHaveBeenCalled()
it "should trigger the secondary event on a secondary click", ->
@view.$(".action-secondary").click()
expect(@secondaryClickSpy).toHaveBeenCalled()
expect(@primaryClickSpy).not.toHaveBeenCalled()
it "should apply class to primary action", ->
expect(@view.$(".action-primary")).toHaveClass("save-button")
it "should apply class to secondary action", ->
expect(@view.$(".action-secondary")).toHaveClass("cancel-button")
it "should preventDefault on primary action", ->
spyOnEvent(".action-primary", "click")
@view.$(".action-primary").click()
expect("click").toHaveBeenPreventedOn(".action-primary")
it "should preventDefault on secondary action", ->
spyOnEvent(".action-secondary", "click")
@view.$(".action-secondary").click()
expect("click").toHaveBeenPreventedOn(".action-secondary")
# Disabled flaky test TNL-559
xdescribe "SystemFeedback not preventing events", ->
beforeEach ->
@clickSpy = jasmine.createSpy('clickSpy')
@view = new AlertView.Confirmation(
title: "It's all good"
message: "No reason for this alert"
actions:
primary:
text: "Whatever"
click: @clickSpy
preventDefault: false
)
@view.show()
it "should not preventDefault", ->
spyOnEvent(".action-primary", "click")
@view.$(".action-primary").click()
expect("click").not.toHaveBeenPreventedOn(".action-primary")
expect(@clickSpy).toHaveBeenCalled()
# Disabled flaky test TNL-559
xdescribe "SystemFeedback multiple secondary actions", ->
beforeEach ->
@secondarySpyOne = jasmine.createSpy('secondarySpyOne')
@secondarySpyTwo = jasmine.createSpy('secondarySpyTwo')
@view = new NotificationView.Warning(
title: "No Primary",
message: "Pick a secondary action",
actions:
secondary: [
{
text: "Option One"
class: "option-one"
click: @secondarySpyOne
}, {
text: "Option Two"
class: "option-two"
click: @secondarySpyTwo
}
]
)
@view.show()
it "should render both", ->
expect(@view.el).toContain(".action-secondary.option-one")
expect(@view.el).toContain(".action-secondary.option-two")
expect(@view.el).not.toContain(".action-secondary.option-one.option-two")
expect(@view.$(".action-secondary.option-one")).toContainText("Option One")
expect(@view.$(".action-secondary.option-two")).toContainText("Option Two")
it "should differentiate clicks (1)", ->
@view.$(".option-one").click()
expect(@secondarySpyOne).toHaveBeenCalled()
expect(@secondarySpyTwo).not.toHaveBeenCalled()
it "should differentiate clicks (2)", ->
@view.$(".option-two").click()
expect(@secondarySpyOne).not.toHaveBeenCalled()
expect(@secondarySpyTwo).toHaveBeenCalled()
describe "NotificationView minShown and maxShown", ->
beforeEach ->
@showSpy = spyOn(NotificationView.Confirmation.prototype, 'show')
@showSpy.andCallThrough()
@hideSpy = spyOn(NotificationView.Confirmation.prototype, 'hide')
@hideSpy.andCallThrough()
@clock = sinon.useFakeTimers()
afterEach ->
@clock.restore()
# Disabled flaky test TNL-559
xit "should not have minShown or maxShown by default", ->
view = new NotificationView.Confirmation()
expect(view.options.minShown).toEqual(0)
expect(view.options.maxShown).toEqual(Infinity)
# Disabled flaky test TNL-559
xit "a minShown view should not hide too quickly", ->
view = new NotificationView.Confirmation({minShown: 1000})
view.show()
expect(view.$('.wrapper')).toBeShown()
# call hide() on it, but the minShown should prevent it from hiding right away
view.hide()
expect(view.$('.wrapper')).toBeShown()
# wait for the minShown timeout to expire, and check again
@clock.tick(1001)
expect(view.$('.wrapper')).toBeHiding()
# Disabled flaky test TNL-559
xit "a maxShown view should hide by itself", ->
view = new NotificationView.Confirmation({maxShown: 1000})
view.show()
expect(view.$('.wrapper')).toBeShown()
# wait for the maxShown timeout to expire, and check again
@clock.tick(1001)
expect(view.$('.wrapper')).toBeHiding()
# Disabled flaky test TNL-559
xit "a minShown view can stay visible longer", ->
view = new NotificationView.Confirmation({minShown: 1000})
view.show()
expect(view.$('.wrapper')).toBeShown()
# wait for the minShown timeout to expire, and check again
@clock.tick(1001)
expect(@hideSpy).not.toHaveBeenCalled()
expect(view.$('.wrapper')).toBeShown()
# can now hide immediately
view.hide()
expect(view.$('.wrapper')).toBeHiding()
# Disabled flaky test TNL-559
xit "a maxShown view can hide early", ->
view = new NotificationView.Confirmation({maxShown: 1000})
view.show()
expect(view.$('.wrapper')).toBeShown()
# wait 50 milliseconds, and hide it early
@clock.tick(50)
view.hide()
expect(view.$('.wrapper')).toBeHiding()
# wait for timeout to expire, make sure it doesn't do anything weird
@clock.tick(1000)
expect(view.$('.wrapper')).toBeHiding()
it "a view can have both maxShown and minShown", ->
view = new NotificationView.Confirmation({minShown: 1000, maxShown: 2000})
view.show()
# can't hide early
@clock.tick(50)
view.hide()
expect(view.$('.wrapper')).toBeShown()
@clock.tick(1000)
expect(view.$('.wrapper')).toBeHiding()
# show it again, and let it hide automatically
view.show()
@clock.tick(1050)
expect(view.$('.wrapper')).toBeShown()
@clock.tick(1000)
expect(view.$('.wrapper')).toBeHiding()
/**
* Provides useful utilities for views.
*/
define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt"],
;(function (define) {
'use strict';
define(["jquery", "underscore", "gettext", "common/js/components/views/feedback_notification",
"common/js/components/views/feedback_prompt"],
function ($, _, gettext, NotificationView, PromptView) {
var toggleExpandCollapse, showLoadingIndicator, hideLoadingIndicator, confirmThenRunOperation,
runOperationShowingMessage, disableElementWhileRunning, getScrollOffset, setScrollOffset,
......@@ -246,3 +249,4 @@ define(["jquery", "underscore", "gettext", "js/views/feedback_notification", "js
'checkTotalKeyLengthViolations': checkTotalKeyLengthViolations
};
});
}).call(this, define || RequireJS.define);
define(["jquery", "underscore", "underscore.string", "backbone", "js/utils/templates"],
function($, _, str, Backbone, TemplateUtils) {
var SystemFeedback = Backbone.View.extend({
options: {
title: "",
message: "",
intent: null, // "warning", "confirmation", "error", "announcement", "step-required", etc
type: null, // "alert", "notification", or "prompt": set by subclass
shown: true, // is this view currently being shown?
icon: true, // should we render an icon related to the message intent?
closeIcon: true, // should we render a close button in the top right corner?
minShown: 0, // length of time after this view has been shown before it can be hidden (milliseconds)
maxShown: Infinity // length of time after this view has been shown before it will be automatically hidden (milliseconds)
;(function (define) {
'use strict';
define(["jquery",
"underscore",
"underscore.string",
"backbone",
"text!common/templates/components/system-feedback.underscore"],
function($, _, str, Backbone, systemFeedbackTemplate) {
var SystemFeedback = Backbone.View.extend({
options: {
title: "",
message: "",
intent: null, // "warning", "confirmation", "error", "announcement", "step-required", etc
type: null, // "alert", "notification", or "prompt": set by subclass
shown: true, // is this view currently being shown?
icon: true, // should we render an icon related to the message intent?
closeIcon: true, // should we render a close button in the top right corner?
minShown: 0, // length of time after this view has been shown before it can be hidden (milliseconds)
maxShown: Infinity // length of time after this view has been shown before it will be automatically hidden (milliseconds)
/* Could also have an "actions" hash: here is an example demonstrating
the expected structure. For each action, by default the framework
will call preventDefault on the click event before the function is
run; to make it not do that, just pass `preventDefault: false` in
the action object.
/* Could also have an "actions" hash: here is an example demonstrating
the expected structure. For each action, by default the framework
will call preventDefault on the click event before the function is
run; to make it not do that, just pass `preventDefault: false` in
the action object.
actions: {
primary: {
"text": "Save",
"class": "action-save",
"click": function(view) {
// do something when Save is clicked
}
},
secondary: [
{
"text": "Cancel",
"class": "action-cancel",
"click": function(view) {}
}, {
"text": "Discard Changes",
"class": "action-discard",
"click": function(view) {}
}
]
}
*/
},
initialize: function() {
if (!this.options.type) {
throw "SystemFeedback: type required (given " +
JSON.stringify(this.options) + ")";
actions: {
primary: {
"text": "Save",
"class": "action-save",
"click": function(view) {
// do something when Save is clicked
}
},
secondary: [
{
"text": "Cancel",
"class": "action-cancel",
"click": function(view) {}
}, {
"text": "Discard Changes",
"class": "action-discard",
"click": function(view) {}
}
]
}
if (!this.options.intent) {
throw "SystemFeedback: intent required (given " +
JSON.stringify(this.options) + ")";
}
this.template = TemplateUtils.loadTemplate("system-feedback");
this.setElement($("#page-" + this.options.type));
// handle single "secondary" action
if (this.options.actions && this.options.actions.secondary &&
!_.isArray(this.options.actions.secondary)) {
this.options.actions.secondary = [this.options.actions.secondary];
}
return this;
},
*/
},
// public API: show() and hide()
show: function() {
clearTimeout(this.hideTimeout);
this.options.shown = true;
this.shownAt = new Date();
this.render();
if ($.isNumeric(this.options.maxShown)) {
this.hideTimeout = setTimeout(_.bind(this.hide, this),
this.options.maxShown);
}
return this;
},
initialize: function() {
if (!this.options.type) {
throw "SystemFeedback: type required (given " +
JSON.stringify(this.options) + ")";
}
if (!this.options.intent) {
throw "SystemFeedback: intent required (given " +
JSON.stringify(this.options) + ")";
}
this.setElement($("#page-" + this.options.type));
// handle single "secondary" action
if (this.options.actions && this.options.actions.secondary &&
!_.isArray(this.options.actions.secondary)) {
this.options.actions.secondary = [this.options.actions.secondary];
}
return this;
},
hide: function() {
if (this.shownAt && $.isNumeric(this.options.minShown) &&
this.options.minShown > new Date() - this.shownAt) {
// public API: show() and hide()
show: function() {
clearTimeout(this.hideTimeout);
this.hideTimeout = setTimeout(_.bind(this.hide, this),
this.options.minShown - (new Date() - this.shownAt));
} else {
this.options.shown = false;
delete this.shownAt;
this.options.shown = true;
this.shownAt = new Date();
this.render();
}
return this;
},
if ($.isNumeric(this.options.maxShown)) {
this.hideTimeout = setTimeout(_.bind(this.hide, this),
this.options.maxShown);
}
return this;
},
hide: function() {
if (this.shownAt && $.isNumeric(this.options.minShown) &&
this.options.minShown > new Date() - this.shownAt) {
clearTimeout(this.hideTimeout);
this.hideTimeout = setTimeout(_.bind(this.hide, this),
this.options.minShown - (new Date() - this.shownAt));
} else {
this.options.shown = false;
delete this.shownAt;
this.render();
}
return this;
},
// the rest of the API should be considered semi-private
events: {
"click .action-close": "hide",
"click .action-primary": "primaryClick",
"click .action-secondary": "secondaryClick"
},
// the rest of the API should be considered semi-private
events: {
"click .action-close": "hide",
"click .action-primary": "primaryClick",
"click .action-secondary": "secondaryClick"
},
render: function() {
// there can be only one active view of a given type at a time: only
// one alert, only one notification, only one prompt. Therefore, we'll
// use a singleton approach.
var singleton = SystemFeedback["active_" + this.options.type];
if (singleton && singleton !== this) {
singleton.stopListening();
singleton.undelegateEvents();
}
this.$el.html(this.template(this.options));
SystemFeedback["active_" + this.options.type] = this;
return this;
},
render: function() {
// there can be only one active view of a given type at a time: only
// one alert, only one notification, only one prompt. Therefore, we'll
// use a singleton approach.
var singleton = SystemFeedback["active_" + this.options.type];
if (singleton && singleton !== this) {
singleton.stopListening();
singleton.undelegateEvents();
}
this.$el.html(_.template(systemFeedbackTemplate)(this.options));
SystemFeedback["active_" + this.options.type] = this;
return this;
},
primaryClick: function(event) {
var actions, primary;
actions = this.options.actions;
if (!actions) { return; }
primary = actions.primary;
if (!primary) { return; }
if (primary.preventDefault !== false) {
event.preventDefault();
}
if (primary.click) {
primary.click.call(event.target, this, event);
}
},
primaryClick: function(event) {
var actions, primary;
actions = this.options.actions;
if (!actions) { return; }
primary = actions.primary;
if (!primary) { return; }
if (primary.preventDefault !== false) {
event.preventDefault();
}
if (primary.click) {
primary.click.call(event.target, this, event);
}
},
secondaryClick: function(event) {
var actions, secondaryList, secondary, i;
actions = this.options.actions;
if (!actions) { return; }
secondaryList = actions.secondary;
if (!secondaryList) { return; }
// which secondary action was clicked?
i = 0; // default to the first secondary action (easier for testing)
if (event && event.target) {
i = _.indexOf(this.$(".action-secondary"), event.target);
}
secondary = secondaryList[i];
if (secondary.preventDefault !== false) {
event.preventDefault();
}
if (secondary.click) {
secondary.click.call(event.target, this, event);
secondaryClick: function(event) {
var actions, secondaryList, secondary, i;
actions = this.options.actions;
if (!actions) { return; }
secondaryList = actions.secondary;
if (!secondaryList) { return; }
// which secondary action was clicked?
i = 0; // default to the first secondary action (easier for testing)
if (event && event.target) {
i = _.indexOf(this.$(".action-secondary"), event.target);
}
secondary = secondaryList[i];
if (secondary.preventDefault !== false) {
event.preventDefault();
}
if (secondary.click) {
secondary.click.call(event.target, this, event);
}
}
}
});
return SystemFeedback;
});
return SystemFeedback;
});
}).call(this, define || RequireJS.define);
define(["jquery", "underscore", "underscore.string", "js/views/feedback"], function($, _, str, SystemFeedbackView) {
var Alert = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "alert"
}),
slide_speed: 900,
show: function() {
SystemFeedbackView.prototype.show.apply(this, arguments);
this.$el.hide();
this.$el.slideDown(this.slide_speed);
return this;
},
hide: function () {
this.$el.slideUp({
duration: this.slide_speed
});
setTimeout(_.bind(SystemFeedbackView.prototype.hide, this, arguments),
this.slideSpeed);
}
});
;(function (define) {
'use strict';
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
function($, _, str, SystemFeedbackView) {
str = str || _.str;
var Alert = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "alert"
}),
slide_speed: 900,
show: function() {
SystemFeedbackView.prototype.show.apply(this, arguments);
this.$el.hide();
this.$el.slideDown(this.slide_speed);
return this;
},
hide: function () {
this.$el.slideUp({
duration: this.slide_speed
});
setTimeout(_.bind(SystemFeedbackView.prototype.hide, this, arguments),
this.slideSpeed);
}
});
// create Alert.Warning, Alert.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Alert.extend({
options: $.extend({}, Alert.prototype.options, {
intent: intent
})
// create Alert.Warning, Alert.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Alert.extend({
options: $.extend({}, Alert.prototype.options, {
intent: intent
})
});
Alert[capitalCamel(intent)] = subclass;
});
Alert[capitalCamel(intent)] = subclass;
});
return Alert;
});
return Alert;
});
}).call(this, define || RequireJS.define);
define(["jquery", "underscore", "underscore.string", "js/views/feedback"], function($, _, str, SystemFeedbackView) {
var Notification = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "notification",
closeIcon: false
})
});
// create Notification.Warning, Notification.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Notification.extend({
options: $.extend({}, Notification.prototype.options, {
intent: intent
;(function (define) {
'use strict';
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
function($, _, str, SystemFeedbackView) {
str = str || _.str;
var Notification = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "notification",
closeIcon: false
})
});
Notification[capitalCamel(intent)] = subclass;
});
// set more sensible defaults for Notification.Mini views
var miniOptions = Notification.Mini.prototype.options;
miniOptions.minShown = 1250;
miniOptions.closeIcon = false;
// create Notification.Warning, Notification.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Notification.extend({
options: $.extend({}, Notification.prototype.options, {
intent: intent
})
});
Notification[capitalCamel(intent)] = subclass;
});
// set more sensible defaults for Notification.Mini views
var miniOptions = Notification.Mini.prototype.options;
miniOptions.minShown = 1250;
miniOptions.closeIcon = false;
return Notification;
});
return Notification;
});
}).call(this, define || RequireJS.define);
define(["jquery", "underscore", "underscore.string", "js/views/feedback"], function($, _, str, SystemFeedbackView) {
var Prompt = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "prompt",
closeIcon: false,
icon: false
}),
render: function() {
if(!window.$body) { window.$body = $(document.body); }
if(this.options.shown) {
$body.addClass('prompt-is-shown');
} else {
$body.removeClass('prompt-is-shown');
}
// super() in Javascript has awkward syntax :(
return SystemFeedbackView.prototype.render.apply(this, arguments);
}
});
;(function (define) {
'use strict';
define(["jquery", "underscore", "underscore.string", "common/js/components/views/feedback"],
function($, _, str, SystemFeedbackView) {
str = str || _.str;
var Prompt = SystemFeedbackView.extend({
options: $.extend({}, SystemFeedbackView.prototype.options, {
type: "prompt",
closeIcon: false,
icon: false
}),
render: function() {
if(!window.$body) { window.$body = $(document.body); }
if(this.options.shown) {
$body.addClass('prompt-is-shown');
} else {
$body.removeClass('prompt-is-shown');
}
// super() in Javascript has awkward syntax :(
return SystemFeedbackView.prototype.render.apply(this, arguments);
}
});
// create Prompt.Warning, Prompt.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Prompt.extend({
options: $.extend({}, Prompt.prototype.options, {
intent: intent
})
});
Prompt[capitalCamel(intent)] = subclass;
});
// create Prompt.Warning, Prompt.Confirmation, etc
var capitalCamel, intents;
capitalCamel = _.compose(str.capitalize, str.camelize);
intents = ["warning", "error", "confirmation", "announcement", "step-required", "help", "mini"];
_.each(intents, function(intent) {
var subclass;
subclass = Prompt.extend({
options: $.extend({}, Prompt.prototype.options, {
intent: intent
})
});
Prompt[capitalCamel(intent)] = subclass;
});
return Prompt;
});
return Prompt;
});
}).call(this, define || RequireJS.define);
// Generated by CoffeeScript 1.6.1
(function() {
define(["jquery", "common/js/components/views/feedback", "common/js/components/views/feedback_notification", "common/js/components/views/feedback_alert", "common/js/components/views/feedback_prompt", "sinon"], function($, SystemFeedback, NotificationView, AlertView, PromptView, sinon) {
var tpl;
tpl = readFixtures('system-feedback.underscore');
beforeEach(function() {
setFixtures(sandbox({
id: "page-alert"
}));
appendSetFixtures(sandbox({
id: "page-notification"
}));
appendSetFixtures(sandbox({
id: "page-prompt"
}));
appendSetFixtures($("<script>", {
id: "system-feedback-tpl",
type: "text/template"
}).text(tpl));
return this.addMatchers({
toBeShown: function() {
return this.actual.hasClass("is-shown") && !this.actual.hasClass("is-hiding");
},
toBeHiding: function() {
return this.actual.hasClass("is-hiding") && !this.actual.hasClass("is-shown");
},
toContainText: function(text) {
var trimmedText;
trimmedText = $.trim(this.actual.text());
if (text && $.isFunction(text.test)) {
return text.test(trimmedText);
} else {
return trimmedText.indexOf(text) !== -1;
}
},
toHaveBeenPrevented: function() {
var eventName, selector;
eventName = this.actual.eventName;
selector = this.actual.selector;
this.message = function() {
return ["Expected event " + eventName + " to have been prevented on " + selector, "Expected event " + eventName + " not to have been prevented on " + selector];
};
return jasmine.JQuery.events.wasPrevented(selector, eventName);
}
});
});
describe("SystemFeedback", function() {
beforeEach(function() {
this.options = {
title: "Portal",
message: "Welcome to the Aperture Science Computer-Aided Enrichment Center"
};
this.renderSpy = spyOn(AlertView.Confirmation.prototype, 'render').andCallThrough();
this.showSpy = spyOn(AlertView.Confirmation.prototype, 'show').andCallThrough();
this.hideSpy = spyOn(AlertView.Confirmation.prototype, 'hide').andCallThrough();
return this.clock = sinon.useFakeTimers();
});
afterEach(function() {
return this.clock.restore();
});
it("requires a type and an intent", function() {
var both, neither, noIntent, noType,
_this = this;
neither = function() {
return new SystemFeedback(_this.options);
};
noType = function() {
var options;
options = $.extend({}, _this.options);
options.intent = "confirmation";
return new SystemFeedback(options);
};
noIntent = function() {
var options;
options = $.extend({}, _this.options);
options.type = "alert";
return new SystemFeedback(options);
};
both = function() {
var options;
options = $.extend({}, _this.options);
options.type = "alert";
options.intent = "confirmation";
return new SystemFeedback(options);
};
expect(neither).toThrow();
expect(noType).toThrow();
expect(noIntent).toThrow();
return expect(both).not.toThrow();
});
it("does not show on initalize", function() {
var view;
view = new AlertView.Confirmation(this.options);
expect(this.renderSpy).not.toHaveBeenCalled();
return expect(this.showSpy).not.toHaveBeenCalled();
});
xit("renders the template", function() {
var view;
view = new AlertView.Confirmation(this.options);
view.show();
expect(view.$(".action-close")).toBeDefined();
expect(view.$('.wrapper')).toBeShown();
expect(view.$el).toContainText(this.options.title);
return expect(view.$el).toContainText(this.options.message);
});
return xit("close button sends a .hide() message", function() {
var view;
view = new AlertView.Confirmation(this.options).show();
view.$(".action-close").click();
expect(this.hideSpy).toHaveBeenCalled();
this.clock.tick(900);
return expect(view.$('.wrapper')).toBeHiding();
});
});
describe("PromptView", function() {
return it("changes class on body", function() {
var view;
view = new PromptView.Confirmation({
title: "Portal",
message: "Welcome to the Aperture Science Computer-Aided Enrichment Center"
});
return view.hide();
});
});
describe("NotificationView.Mini", function() {
beforeEach(function() {
return this.view = new NotificationView.Mini();
});
it("should have minShown set to 1250 by default", function() {
return expect(this.view.options.minShown).toEqual(1250);
});
return it("should have closeIcon set to false by default", function() {
return expect(this.view.options.closeIcon).toBeFalsy();
});
});
xdescribe("SystemFeedback click events", function() {
beforeEach(function() {
this.primaryClickSpy = jasmine.createSpy('primaryClick');
this.secondaryClickSpy = jasmine.createSpy('secondaryClick');
this.view = new NotificationView.Warning({
title: "Unsaved",
message: "Your content is currently Unsaved.",
actions: {
primary: {
text: "Save",
"class": "save-button",
click: this.primaryClickSpy
},
secondary: {
text: "Revert",
"class": "cancel-button",
click: this.secondaryClickSpy
}
}
});
return this.view.show();
});
it("should trigger the primary event on a primary click", function() {
this.view.$(".action-primary").click();
expect(this.primaryClickSpy).toHaveBeenCalled();
return expect(this.secondaryClickSpy).not.toHaveBeenCalled();
});
it("should trigger the secondary event on a secondary click", function() {
this.view.$(".action-secondary").click();
expect(this.secondaryClickSpy).toHaveBeenCalled();
return expect(this.primaryClickSpy).not.toHaveBeenCalled();
});
it("should apply class to primary action", function() {
return expect(this.view.$(".action-primary")).toHaveClass("save-button");
});
it("should apply class to secondary action", function() {
return expect(this.view.$(".action-secondary")).toHaveClass("cancel-button");
});
it("should preventDefault on primary action", function() {
spyOnEvent(".action-primary", "click");
this.view.$(".action-primary").click();
return expect("click").toHaveBeenPreventedOn(".action-primary");
});
return it("should preventDefault on secondary action", function() {
spyOnEvent(".action-secondary", "click");
this.view.$(".action-secondary").click();
return expect("click").toHaveBeenPreventedOn(".action-secondary");
});
});
xdescribe("SystemFeedback not preventing events", function() {
beforeEach(function() {
this.clickSpy = jasmine.createSpy('clickSpy');
this.view = new AlertView.Confirmation({
title: "It's all good",
message: "No reason for this alert",
actions: {
primary: {
text: "Whatever",
click: this.clickSpy,
preventDefault: false
}
}
});
return this.view.show();
});
return it("should not preventDefault", function() {
spyOnEvent(".action-primary", "click");
this.view.$(".action-primary").click();
expect("click").not.toHaveBeenPreventedOn(".action-primary");
return expect(this.clickSpy).toHaveBeenCalled();
});
});
xdescribe("SystemFeedback multiple secondary actions", function() {
beforeEach(function() {
this.secondarySpyOne = jasmine.createSpy('secondarySpyOne');
this.secondarySpyTwo = jasmine.createSpy('secondarySpyTwo');
this.view = new NotificationView.Warning({
title: "No Primary",
message: "Pick a secondary action",
actions: {
secondary: [
{
text: "Option One",
"class": "option-one",
click: this.secondarySpyOne
}, {
text: "Option Two",
"class": "option-two",
click: this.secondarySpyTwo
}
]
}
});
return this.view.show();
});
it("should render both", function() {
expect(this.view.el).toContain(".action-secondary.option-one");
expect(this.view.el).toContain(".action-secondary.option-two");
expect(this.view.el).not.toContain(".action-secondary.option-one.option-two");
expect(this.view.$(".action-secondary.option-one")).toContainText("Option One");
return expect(this.view.$(".action-secondary.option-two")).toContainText("Option Two");
});
it("should differentiate clicks (1)", function() {
this.view.$(".option-one").click();
expect(this.secondarySpyOne).toHaveBeenCalled();
return expect(this.secondarySpyTwo).not.toHaveBeenCalled();
});
return it("should differentiate clicks (2)", function() {
this.view.$(".option-two").click();
expect(this.secondarySpyOne).not.toHaveBeenCalled();
return expect(this.secondarySpyTwo).toHaveBeenCalled();
});
});
return describe("NotificationView minShown and maxShown", function() {
beforeEach(function() {
this.showSpy = spyOn(NotificationView.Confirmation.prototype, 'show');
this.showSpy.andCallThrough();
this.hideSpy = spyOn(NotificationView.Confirmation.prototype, 'hide');
this.hideSpy.andCallThrough();
return this.clock = sinon.useFakeTimers();
});
afterEach(function() {
return this.clock.restore();
});
xit("should not have minShown or maxShown by default", function() {
var view;
view = new NotificationView.Confirmation();
expect(view.options.minShown).toEqual(0);
return expect(view.options.maxShown).toEqual(Infinity);
});
xit("a minShown view should not hide too quickly", function() {
var view;
view = new NotificationView.Confirmation({
minShown: 1000
});
view.show();
expect(view.$('.wrapper')).toBeShown();
view.hide();
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(1001);
return expect(view.$('.wrapper')).toBeHiding();
});
xit("a maxShown view should hide by itself", function() {
var view;
view = new NotificationView.Confirmation({
maxShown: 1000
});
view.show();
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(1001);
return expect(view.$('.wrapper')).toBeHiding();
});
xit("a minShown view can stay visible longer", function() {
var view;
view = new NotificationView.Confirmation({
minShown: 1000
});
view.show();
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(1001);
expect(this.hideSpy).not.toHaveBeenCalled();
expect(view.$('.wrapper')).toBeShown();
view.hide();
return expect(view.$('.wrapper')).toBeHiding();
});
xit("a maxShown view can hide early", function() {
var view;
view = new NotificationView.Confirmation({
maxShown: 1000
});
view.show();
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(50);
view.hide();
expect(view.$('.wrapper')).toBeHiding();
this.clock.tick(1000);
return expect(view.$('.wrapper')).toBeHiding();
});
return it("a view can have both maxShown and minShown", function() {
var view;
view = new NotificationView.Confirmation({
minShown: 1000,
maxShown: 2000
});
view.show();
this.clock.tick(50);
view.hide();
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(1000);
expect(view.$('.wrapper')).toBeHiding();
view.show();
this.clock.tick(1050);
expect(view.$('.wrapper')).toBeShown();
this.clock.tick(1000);
return expect(view.$('.wrapper')).toBeHiding();
});
});
});
}).call(this);
define(["jquery", "underscore", "js/views/baseview", "js/views/utils/view_utils", "js/spec_helpers/edit_helpers"],
function ($, _, BaseView, ViewUtils, ViewHelpers) {
;(function (define) {
'use strict';
define(["jquery", "underscore", "common/js/components/utils/view_utils", "common/js/spec_helpers/view_helpers", 'jasmine-stealth'],
function ($, _, ViewUtils, ViewHelpers) {
describe("ViewUtils", function() {
describe("disabled element while running", function() {
......@@ -90,3 +92,4 @@ define(["jquery", "underscore", "js/views/baseview", "js/views/utils/view_utils"
});
});
});
}).call(this, define || RequireJS.define);
\ No newline at end of file
/**
* Provides helper methods for invoking Studio modal windows in Jasmine tests.
*/
define(["jquery", "js/views/feedback_notification", "js/views/feedback_prompt", 'common/js/spec_helpers/ajax_helpers',
"common/js/spec_helpers/template_helpers"],
function($, NotificationView, Prompt, AjaxHelpers, TemplateHelpers) {
;(function (define) {
'use strict';
define(["jquery", "common/js/components/views/feedback_notification", "common/js/components/views/feedback_prompt",
'common/js/spec_helpers/ajax_helpers'],
function($, NotificationView, Prompt, AjaxHelpers) {
var installViewTemplates, createFeedbackSpy, verifyFeedbackShowing,
verifyFeedbackHidden, createNotificationSpy, verifyNotificationShowing,
verifyNotificationHidden, createPromptSpy, confirmPrompt, inlineEdit, verifyInlineEditChange,
installMockAnalytics, removeMockAnalytics, verifyPromptShowing, verifyPromptHidden;
installMockAnalytics, removeMockAnalytics, verifyPromptShowing, verifyPromptHidden,
clickDeleteItem, patchAndVerifyRequest, submitAndVerifyFormSuccess, submitAndVerifyFormError;
installViewTemplates = function(append) {
TemplateHelpers.installTemplate('system-feedback', !append);
installViewTemplates = function() {
appendSetFixtures('<div id="page-notification"></div>');
};
......@@ -144,3 +146,4 @@ define(["jquery", "js/views/feedback_notification", "js/views/feedback_prompt",
'submitAndVerifyFormError': submitAndVerifyFormError
};
});
}).call(this, define || RequireJS.define);
......@@ -21,17 +21,16 @@
<% if(obj.actions) { %>
<nav class="nav-actions">
<h3 class="sr"><%= type %> Actions</h3>
<ul>
<% if(actions.primary) { %>
<li class="nav-item">
<a href="#" class="button action-primary <%= actions.primary.class %>"><%= actions.primary.text %></a>
<button class="action-primary <%= actions.primary.class %>"><%= actions.primary.text %></button>
</li>
<% } %>
<% if(actions.secondary) {
_.each(actions.secondary, function(secondary) { %>
<li class="nav-item">
<a href="#" class="button action-secondary <%= secondary.class %>"><%= secondary.text %></a>
<button class="action-secondary <%= secondary.class %>"><%= secondary.text %></button>
</li>
<% });
} %>
......
......@@ -74,13 +74,13 @@ define([
return profileView;
};
clickLeaveTeam = function(requests, view, confirmLeave) {
clickLeaveTeam = function(requests, view, options) {
expect(view.$(leaveTeamLinkSelector).length).toBe(1);
// click on Leave Team link under Team Details
view.$(leaveTeamLinkSelector).click();
if (confirmLeave) {
if (!options.cancel) {
// click on Confirm button on dialog
$('.prompt.warning .action-primary').click();
......@@ -121,7 +121,7 @@ define([
view = createTeamProfileView(requests, {membership: DEFAULT_MEMBERSHIP});
expect(view.$('.new-post-btn').length).toEqual(1);
clickLeaveTeam(requests, view, true);
clickLeaveTeam(requests, view, {cancel: false});
expect(view.$('.new-post-btn').length).toEqual(0);
});
});
......@@ -188,7 +188,7 @@ define([
requests, {country: 'US', language: 'en', membership: DEFAULT_MEMBERSHIP}
);
assertTeamDetails(view, 1, true);
clickLeaveTeam(requests, view, true);
clickLeaveTeam(requests, view, {cancel: false});
assertTeamDetails(view, 0, false);
});
......@@ -199,7 +199,7 @@ define([
requests, {country: 'US', language: 'en', membership: DEFAULT_MEMBERSHIP}
);
assertTeamDetails(view, 1, true);
clickLeaveTeam(requests, view, false);
clickLeaveTeam(requests, view, {cancel: true});
assertTeamDetails(view, 1, true);
});
......
......@@ -81,4 +81,4 @@
// overrides
@import 'developer'; // used for any developer-created scss that needs further polish/refactoring
@import 'shame'; // used for any bad-form/orphaned scss
\ No newline at end of file
@import 'shame'; // used for any bad-form/orphaned scss
../../../common/static/sass/_mixins-inherited.scss
\ No newline at end of file
../../../common/static/sass/_mixins.scss
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment