Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
±àд¿É²âÊÔµÄJavascript´úÂë
 
À´Ô´£º²®ÀÖÔÚÏß ·¢²¼ÓÚ 2015-8-17
  3038  次浏览      27
 

±àд¿É²âÊÔµÄJavascript´úÂ루1£©£º·´Ä£Ê½¼°Æä½â¾ö·½°¸

<!DOCTYPE html>
<html>
<head>
<title>An Untestable Authentication Form</title>
</head>
<body>
<form id="authentication_form">
<label for="username">Username:</label>
<input type="text" id="username" name="username"></input>
<label for="password">Password:</label>
<input type="password" id="password" name="password"></input>
<button>Submit</button>

<p id="username_password_required" style="display: none;">
Both the username and password are required.
</p>

<p id="authentication_success" style="display: none;">
You have successfully authenticated!
</p>

<p id="authentication_failure" style="display: none;">
This username/password combination is not correct.
</p>

<p id="authentication_error" style="display: none;">
There was a problem authenticating the user, please try again later.
</p>
</form>
<script src="jquery.min.js"></script>

<!-- Inline Javascript is impossible to test from an external test harness -->
<script>
// Even if test harness was included in the HTML, Javascript is
// inaccessible to tests
$(function() {

// Pyramid of doom - A mixture of disparate concerns and
// very difficult to test individual parts
$("#authentication_form").on("submit", function(event) {
// Event handler logic is mixed with form handling logic
event.preventDefault();

var username = $("#username").val();
var password = $("#password").val();

if (username && password) {
// Without a mock, XHR requests require a functioning
// back end, adding extra dependencies and delay
$.ajax({
type: "POST",
url: "/authenticate_user",
data: {
username: username,
password: password
},
success: function(data, status, jqXHR) {
// Knowing when this completes requires some sort
// of notification
if (data.success) {
$("#authentication_success").show();
}
else {
$("#authentication_failure").show();
}
},
error: function(jqXHR, textStatus, errorThrown) {
$("#authentication_error").show();
}
});
}
else {
$("#username_password_required").show();
}
});
});
</script>
</body>
</html>

·´Ä£Ê½Ê¹µÃÓ¦ÓõĴúÂë±äµÄÄÑÒÔ²âÊÔ

1.ÄÚÁªJavascript ¨C ǶÈëÔÚHTMLÎļþÖеÄJavascript´úÂëÊÇÎÞ·¨°üº¬ÔÚÍⲿµ¥Ôª²âÊÔ¹¤¾ßÖеġ£

2.ÎÞ·¨¸´ÓõĴúÂë ¨C ¼´Ê¹Javascript´úÂëµ¥¶À·ÅÔÚÍâÃæ£¬Ò²Ã»ÓÐÌṩ¹«¹²µÄ½Ó¿Ú¹©ÆäËûÈ˵÷Óá£

3.ûÓй¹Ô캯Êý/Ô­ÐͶÔÏó ¨C ¸öÈ˵ĵ¥Ôª²âÊÔ¾ÍÒâζ×ŶÀÁ¢µÄ²Ù×÷¡£²âÊÔÒ»¸öµ¥ÀýÊǺÜÀ§Äѵģ¬ÒòΪһ¸ö²âÊԵĽá¹û¿ÉÄÜ»áÓ°Ïìµ½ÆäËû²âÊԵĽá¹û¡£

4.½ð×ÖËþ¶òÔË ¨C Éî²ãµÄǶÌ×ÔÚJavascript¿ª·¢Öзdz£¶à¼û£¬µ«ÊÇËûÃÇÊÇÈÃÈ˸÷ÖÖµ£ÓǵÄ×¥¿ñµÄ¶«Î÷¡£Éî²ãǶÌ×ÔÚÄÚ²¿µÄ´úÂëÂß¼­ÊǺÜÄѽøÐе¥¶À²âÊԵ쬲¢ÇÒËæ×Åʱ¼äµÄÍÆÒÆ£¬»áÓбäµÃÏñÒâ´óÀûÃæÌõʽµÄÄÑÒÔά»¤µÄÇãÏò¡£

5.×¾ÁÓµÄDOMʼþ´¦Àí³ÌÐò ¨C ʼþ´¦Àí³ÌÐòºÍ±íµ¥Ìá½»Âß¼­»ìÔÚÒ»Æð£¬´Ó¶øµ¼ÖÂÎÞ·¨±ÜÃâµÄµ£ÓÇ¡£

6.ÕæÕýµÄXHRÇëÇó ¨C ÕæÕýµÄXHRÇëÇóÐèÒªÒ»¸ö¿ÉÓõĺó¶Ë·þÎñ£¬Ç°¶ËºÍºó¶Ë¸ßËÙ²¢ÐеĿª·¢ÊǺÜÀ§Äѵģ¬ÒòΪXHRÇëÇóÐèÒªÒ»¸öÄܹ¤×÷µÄºó¶Ë²ÅÄÜ¿´µ½ÇëÇó½á¹û¡£

7.״̬֪ͨ ¨C ¸üÉÙµÄÒì²½Âß¼­ ¨C ûÓÐijÖÖÐÎʽµÄ֪ͨ£¬ÊÇÎÞ·¨ÖªµÀÒ»¸öÒì²½º¯ÊýÊÇʲôʱºòÖ´ÐÐÍê³ÉµÄ¡£

ÈçºÎ±àд¿É²âÊÔµÄJavascript UI´úÂë

ÉÏÃæÁгöµÄÿһ¸öÎÊÌâ¶¼ÊÇ¿ÉÒÔ½â¾öµÄ¡£ÉÔ΢¶¯ÏÂÄÔ×Ó˼¿¼Ò»Ï£¬Æäʵǰ¶ËµÄ´úÂëÊǺÜÈÝÒײâÊԵġ£

ÍâÁ´ËùÓеÄJavascript´úÂë

Ö±½ÓǶÈëµ½Ò»¸öHTMLÎļþÖеÄJavascript´úÂëÊÇÎÞ·¨±»ÁíÒ»¸öHTMLÎļþʹÓõġ£ÍâÁ´µÄJavascript´úÂëÊǿɸ´Óõ쬲¢ÇÒ¿ÉÒÔ±»²»Ö¹Ò»¸öµÄHTMLÎļþËùÒýÈë¡£

Ìṩһ¸ö¹«¹²½Ó¿Ú

´úÂë±ØÐëÒªÌṩ¹«¹²½Ó¿Ú²ÅÄܱ»²âÊÔ¡£ÔÚÌṩһ¸ö¹«¹²½Ó¿ÚµÄʱºò£¬±»ÓÃÀ´·â×°Âß¼­µÄ×î¾­³£Ê¹ÓõÄģʽÊÇʹÓÃÄ£¿é¡£ÔÚAddy OsmaniµÄ·Ç³£ÓÅÐãµÄJavascriptÉè¼ÆÄ£Ê½±ØÖª±Ø»áÒ»ÊéÖУ¬ËûÖ¸³ö£ºÄ£¿éģʽ×î³õÔÚ´«Í³Èí¼þÐÐÒµÖÐ×÷ΪÀàµÄ˽Óк͹«¹²½Ó¿ÚµÄ·â×°±»Ìá³öµÄ¡£

Ô­À´µÄÑùÀýÓ¦ÓóÌÐòûÓй«¹²½Ó¿Ú£¬ËùÓеĴúÂë¶¼·â×°ÔÚÒ»¸ö×Ôµ÷ÓõÄ˽Óк¯ÊýÖС£Î¨Ò»¿ÉÒÔ½øÐвâÊԵĵط½¾ÍÊÇ±íµ¥Ìύʼþ¹¦Äܲ¿·Ö¡£ËäȻȷ¶¨ÊÇ¿ÉÒÔ£¨½øÐвâÊÔ£©µÄ£¬ÓýöÓеĻìºÏʼþ´¦Àí³ÌÐòÀ´±àд²âÊÔÓÃÀý»áÓв»±ØÒªµÄÂé·³¡£

Êʵ±µÄ·â×°Ä£¿é¿ÉÒÔÓÃÀ´ÏÞÖÆ¹¦ÄÜ·ÃÎÊ£¬¼õÉÙÈ«¾ÖÃüÃû¿Õ¼äµÄÎÛȾ£¬²¢ÇÒ¿ÉÒÔÌṩ¹«¹²½Ó¿Ú·½±ã²âÊÔ¡£

var PublicModule = (function() {
"use strict";

// This is the public interface of the Module.
var Module = {

// publicFunction can be called externally
publicFunction: function() {
return "publicFunction can be invoked externally but "
+ privateFunction();
}
};

// privateFunction is completely hidden from the outside.
function privateFunction() {
return "privateFunction cannot";
}

return Module;
}());

ÕýÈçAddyÖ¸³öµÄ£¬Ä£¿éģʽµÄÒ»¸ö±×¶ËÔÚÓÚ¡°ÎÞ·¨´´½¨¶Ô˽ÓгÉÔ±µÄ×Ô¶¯»¯µ¥Ôª²âÊÔ¡±¡£Ò»¸öº¯ÊýÈç¹û²»Äܱ»Ö±½Ó·ÃÎÊ£¬ÄÇôËü¾Í²»Äܱ»Ö±½Ó½øÐвâÊÔ¡£Ä£¿éÉè¼ÆµÄʱºò£¬ÔÚ±£³Ö³ÉԱ˽Óл¯ºÍÏò¹«ÖÚ¹«¿ª³ÉÔ±Ö®¼ä´æÔÚÒ»¶¨µÄÀ©Õ¹ÐÔ¡£

ÔÚMozilla Persona´úÂë¿âÖУ¬ÎÒÃǾ­³£ÔÚ²âÊÔ¹«¹²½Ó¿ÚµÄ˽Óк¯Êýʱ±©Â¶³öÀ§ÄÑ£¬»áºÜÃ÷ÏԵİѶîÍâµÄº¯Êý×÷Ϊ²âÊÔAPIµÄÒ»²¿·Ö¡£ËäÈ»ÆäËûµÄ¿ª·¢ÕßÈÔÈ»¿ÉÒÔµ÷ÓÃÕâЩ˽Óк¯Êý£¬µ«×÷ÕßµÄÒâͼÊǺÜÃ÷ÏԵġ£

...
publicFunction: function() {
return "publicFunction can be invoked externally but "
+ privateFunction();
}
// BEGIN TESTING API
,
privateFunction: privateFunction

// END TESTING API
};

// privateFunction is now accessible via the TESTING API
function privateFunction() {
...

ÔÚ×¢Êͱê¼Ç// BEGIN TESTING APIºÍ//END TESTING API Ö®¼äµÄ´úÂë¿ÉÒÔÔÚÏîÄ¿¹¹½¨µÄʱºòɾ³ýµô¡£

ʹÓÿÉʵÀý»¯µÄ¶ÔÏó

×î³õµÄÓ¦ÓóÌÐò²¢Ã»ÓÐʹÓÿÉʵÀý»¯µÄ¶ÔÏó£¬ËüµÄ´úÂë±»Éè¼ÆµÄÖ»Ö´ÐÐÒ»´Î¡£ÕâÒ»Ô¼ÊøÊ¹µÃÖØÖÃÓ¦ÓÃ״̬ÒÔ¼°¶ÀÁ¢µÄÖ´Ðе¥Ôª²âÊÔ±äµÃºÜÀ§ÄÑ¡£

²âÊÔ¿ÉÒÔ±»¶à´Î³õʼ»¯µÄÄ£¿éÏà¶ÔÀ´ËµÊǸüÈÝÒ׵ġ£ÔÚJavascriptÖУ¬´æÔÚÁ½¸öÏàËÆµÄ·½·¨£º¹¹Ô캯ÊýºÍObject.create.

Ç°ÃæÁ½¸öÀý×ÓÖеÄPublicModule±äÁ¿ÊǶÔÏó¶ø²»ÊǺ¯Êý£¬¶øObject.create¿ÉÒÔ±»ÓÃÀ´´´½¨¶ÔÏóµÄ¸±±¾¡£¿ÉÒÔÔÚÔ­ÐÍÖÐÔö¼ÓÒ»¸ö¿ÉÑ¡µÄ³õʼ»¯º¯ÊýÒÔ±ãÔÚ¹¹Ô캯ÊýÖÐÖ´Ðгõʼ»¯¡£

...

// the init function takes care of initialization traditionally done

// in a constructor
init: function(options) {
this.valueSetOnInit = options.valueSetOnInit;
},

publicFunction: function() {
...
1
2
3
4
5
// create an instance of the PublicModule.
var objInstance = Object.create(PublicModule);
objInstance.init({
valueSetOnInit: "value set during initialization"
});

¼õÉÙǶÌ׻ص÷

·Ç³£²»ÐÒµÄÊÇ£¬Ç¶Ì׻ص÷ÊÇǰ¶ËJavascript±à³ÌºÜÖØÒªµÄÒ»²¿·Ö¡£ÉÏÃæ²»¿É²âÊÔÑéÖ¤µÄForm±íµ¥µÄÀý×Ó¾øÃ»ÓжîÍâµÄ°üº¬Èý²ãǶÌ׵Ļص÷¡£Éî²ãµÄǶÌ׻ص÷´úÂëÊÇÕâÑùµÄ¨CËûÃǽ«¹¦ÄÜÔã¸âµÄ»ìÔÓÔÚÒ»Æð²¢ÇÒÈÃÈ˵£ÓÇÖØÖØ¡£

½«½ð×ÖËþʽµÄ´úÂë²ð·ÖΪ¸÷¹¦ÄÜ×é¼þÎÒÃÇ¿ÉÒԵõ½½Ï¡°Æ½Ì¹¡±µÄ´úÂ룬ÕâЩ´úÂë»áÓÉСµÄ£¬ÓÐÕ³×ÅÁ¦µÄÒÔ¼°¹¦ÄÜÒײâÊԵĴúÂë×é³É¡£

½«DOMʼþ´¦Àí³ÌÐòºÍËüµÄÐÐΪ·ÖÀë

²»¿É²âÊÔÑéÖ¤µÄFormÀý×ÓÓÃÁËÒ»¸öµ¥¶ÀµÄÌá½»´¦Àí³ÌÐòÀ´Í¬Ê±¹Ø×¢Ê¼þ´¦ÀíºÍ±íµ¥Ìá½»¡£²»½ö½öÊÇͬʱ¹Ø×¢ÁËÕâÁ½¼þÊ£¬¶øÇÒÕâ¸ö»ìºÏ½á¹ûµ¼ÖÂÈç¹û²»Ê¹ÓûìºÏµÄʼþ³ÌÐò½«ÎÞ·¨Ìá½»±íµ¥¡£

...
$("form").on("submit", function(event) {
event.preventDefault();


// this code is impossible to invoke programmatically

// without using a synthetic DOM event.
var name = $("#name").val();
doSomethingWithName(name);
});
...

½«±íµ¥´¦ÀíÂß¼­´Óʼþ´¦Àí³ÌÐòÖзÖÀë³öÀ´ÈÃÎÒÃÇ¿ÉÒÔ±à³ÌÌá½»±íµ¥¶ø²»ÓÃÇóÖúÓÚ»ìºÏµÄʼþ´¦Àí³ÌÐò¡£

...
$("form").on("submit", submitHandler);

function submitHandler(event) {
event.preventDefault();

submitForm();
});


// form submission can now be done programmatically

// by calling submitForm directly.
function submitForm() {
var name = $("#name").val();
doSomethingWithName(name);
}
...

µ¥Ôª²âÊÔ¿ÉÒÔʹÓÃsubmitForm¶ø²»±ØÊ¹ÓûìºÏµÄ±íµ¥Ìύʼþ´¦Àí³ÌÐò¡£

Ä£ÄâXHRÇëÇó

¼¸ºõËùÓеÄÏÖ´úÍøÕ¾¶¼ÊÇÓÃXHR£¨AJAX£©ÇëÇó¡£XHRÇëÇóÒÀÀµÓÚ·þÎñ¶Ë£»´Óǰ¶ËµÄÇëÇó±ØÐë±»·þÎñ¶ËÏìÓ¦£¬·ñÔòÓ¦ÓÃʲô¶¼×ö²»ÁË¡£Ö»ÓзþÎñ¶ËÒ²×¼±¸ºÃÁ˲ÅÄܲâÊÔÕæÕýµÄXHRÇëÇ󣬷ñÔò»áÑÏÖØÓ°Ïì²¢Ðпª·¢¡£

...

// This is an explicit dependency on the jQuery ajax functionality as well

// as a working back end.
$.ajax({
type: "POST",
url: "/authenticate_user",
data: {
username: username,
password: password
},
success: function(data, status, jqXHR) {
...

ÓëÆäÖ´ÐÐÕæÕýµÄXHRÇëÇ󣬲»ÈçÓÃÒ»ÖÖ¸ñʽ¶¨ÒåÁ¼ºÃµÄXHRÏìÓ¦À´Ä£Äâ¡£Mock¶ÔÏóÊÇÒ»ÖÖÒԿɿصķ½Ê½À´Ä£ÄâÕæÕý¶ÔÏóµÄÐÐΪµÄÄ£Äâ¶ÔÏó¡£Ä£Äâ¶ÔÏó¾­³£±»ÓÃÓÚÄÇЩÐèÒªÒÀÀµ²»¿É»ñµÃµÄ¡¢½ÏÂýµÄ¡¢²»¿É¿ØµÄ»òÕßȱÏÝÌ«¶à¶øÎÞ·¨ÐÅÈεŦÄÜÉÏÃæ¡£XHRÇëÇóÇ¡ºÃÊÇÒ»¸öºÜºÃµÄÀý×Ó¡£

ͬʱ²âÊÔǰ¶ËºÍºó¶ËÊǺÜÖØÒªµÄ£¬µ«ÊÇÕâ×îºÃÁô¸ø¹¦ÄܲâÊÔ¡£µ¥Ôª²âÊÔÒâζ×ŲâÊÔµ¥¶ÀµÄÌõÄ¿¡£

Ò»¸ö·¢ÆðXHRÇëÇóµÄÄ£¿éÓ¦¸Ã½ÓÊÜÒ»¸ö°üº¬ÔÚÆä¹¹Ô캯Êý»òÕß³õʼ»¯º¯ÊýÖеÄXHRÄ£Äâ¶ÔÏó¡£È»ºóÕâ¸öÄ£¿éʹÓÃÕâ¸ö±»°üº¬µÄÄ£Äâ¶ÔÏó¶ø²»ÊÇÈ¥Ö±½Óµ÷ÓÃ$.ajax¡£Ä£¿éÔÚÖ´Ðе¥Ôª²âÊÔµÄʱºòʹÓÃÄ£Äâ¶ÔÏ󣬵«ÊÇÔÚÉú²úÖÐʹÓÃ$.ajax¡£

ºÏÀíµÄĬÈÏÖµ¿ÉÒÔ¼õÉÙÉú²úÌåϵÖгõʼ»¯´úÂëµÄÊýÁ¿¡£

...
init: function(options) {

// Use the injected ajax function if available, otherwise

// use $.ajax by default.
this.ajax = options.ajax || $.ajax;
},

submitForm: function() {
...

// This can call either an XHR mock or a production XHR resource

// depending on how the object is initialized.
this.ajax({
type: "POST",
url: "/authenticate_user",
data: {
username: username,
password: password
},
...
});
}
...

Òì²½±à³ÌÐèҪ֪ͨ»úÖÆ

ÉÏÃæ²»¿É²âÊÔÑéÖ¤µÄForm±íµ¥µÄÀý×ÓȱÉÙ֪ͨµÄ»úÖÆÀ´±íÃ÷ʲôʱºòËùÓеĽø³ÌÒѽáÊø¡£ÕâÔÚÒì²½º¯ÊýÔËÐнáÊøºóµÄÐèÒªÖ´Ðеĵ¥Ôª²âÊÔÖлáÊÇÒ»¸öÎÊÌâ¡£
JavascriptÖдæÔںܶàµÄ֪ͨ»úÖÆ£¬»Øµ÷£¬¹Û²ìÕßģʽÒÔ¼°Ê¼þÊǼ¸¸ö¡£¼òµ¥µÄ»Øµ÷º¯ÊýÊÇĿǰ×î³£Óõġ£

...
submitForm: function(done) {
...
this.ajax({
...

// an ajax call is asynchronous. When it successfully completes,

// it calls the done function.
success: done
});
}
...

µ¥Ôª²âÊÔºóÇå³ý²»±ØÒªµÄ´úÂë

µ¥Ôª²âÊÔÓ¦¸Ãµ¥¶ÀµÄ½øÐУ»Ò»µ©Ò»¸öµ¥Ôª²âÊÔ½áÊø£¬ËùÓеIJâÊÔ״̬Ӧ¸Ã±»Çå³ý£¬°üÀ¨DOMʼþ´¦Àí¡£µ¼Ö¶ÔÏó½«DOMʼþ´¦Àí³ÌÐò°ó¶¨µ½ÏàͬµÄDOMÔªËØµÄÁ½¸ö²âÊÔÓÃÀýÈÝÒ×Ï໥ӰÏ죬¶øÕâÈÝÒ×±»¿ª·¢ÕßÊèºö¡£ÎªÁËÅųýÕâÖÖÓ°Ï죬һ¸öûÓõĶÔÏóÓ¦¸Ã´ÓËüµÄDOMʼþ´¦Àí³ÌÐòÖÐÒÆ³ý¡£¶îÍâµÄ¹¤×÷»áÌṩһЩ¶îÍâµÄºÃ´¦£»ÔÚÓ¦ÓÃÖд´½¨ºÍÏú»Ù¶ÔÏó¿ÉÒÔ´ó´óµÄ¼õÉÙÄÚ´æÒç³ö¡£

...
teardown: teardown() {
$("form").off("submit", submitHandler);
}
...

×ܽá

¾ÍÊÇÕâÑù¡£Êµ¼ÊÉÏûÓбàд¶àÉÙǰ¶ËJavascript´úÂë,ÕâÑù¾Í¿ÉÒÔ½øÐе¥Ôª²âÊÔ¡£¹«¹²½Ó¿Ú£¬³õʼ»¯¶ÔÏó£¬ÉÙǶÌ׵ĴúÂë½á¹¹£¬×éÖ¯Á¼ºÃµÄʼþ´¦Àí³ÌÐòÒÔ¼°²âÊÔÖ®ºó²»±ØÒª´úÂëµÄÇå³ý¡£

±àд¿É²âÊÔµÄJavascript´úÂ루2£©£º´Ó·´Ä£Ê½½øÐÐÖØ¹¹

ÕâÊǽéÉÜ¡°ÈçºÎ±àд¿É²âÊÔµÄJavascript UI´úÂ롱Á½ÆªÎÄÕÂÖеĵڶþƪ¡£

ÔÚµÚһƪÎÄÕ·´Ä£Ê½¼°ÆäËüÃǵĽâ¾ö·½°¸ÖÐÓÃÒ»¸öʾÀýÓ¦ÓóÌÐòÒýÈëÁ˼¸¸ö³£¼ûµÄ£¬¿É±ÜÃâµÄ£¬Ô¼ÊøÁ˿ɲâÊÔÐԵķ´Ä£Ê½£¬²¢ÇÒ½âÊÍÁËΪʲôÕâЩ³£¼ûµÄ×ö·¨ÊÇ·´Ä£Ê½µÄÒÔ¼°ÈçºÎÐÞ¸´ËûÃÇ¡£

ÕâÆªÎÄÕÂÀ´¼ÌÐøÖØ¹¹Ô­À´µÄÓ¦Óã¬ÒÔʹµÃËüµÄ´úÂë¸üÈÝÒ×ÔĶÁ£¬¸üÈÝÒ×±»¸´ÓÃÒÔ¼°¸üÈÝÒ×½øÐвâÊÔ¡£Ò»µ©Öع¹Íê³É£¬²âÊÔ¾ÍÒª¿ªÊ¼£º´´½¨²âÊÔ¹¤¾ß£¬¿ª·¢XHRÄ£Ä⣬×îºó£¬Ìí¼ÓÒ»¸öÍêÕûµÄµ¥Ôª²âÊÔÓÃÀý¡£

ʹÓÃ×î¼Ñʵ¼ùÀ´±àд¿É²âÊÔµÄUI´úÂë

ÔÚµÚһƪÎÄÕ¡°·´Ä£Ê½¼°Æä½â¾ö·½°¸¡±ÖУ¬ÁгöÁ˼¸¸öʹµÃUI´úÂë¿É²âÊÔµÄ×î¼Ñʵ¼ù£º

1.ÍâÁ´ËùÓеÄJavascript£»

2.Ìṩ¹«¹²½Ó¿Ú£»

3.ʹÓÿÉʵÀý»¯µÄ¶ÔÏó£»

4.¼õÉÙǶÌ׻ص÷£»

5.½«DOMʼþ´¦Àí³ÌÐòÓëʼþ´¦Àíº¯ÊýÏà·ÖÀ룻

6.µ±Òì²½º¯ÊýÍê³ÉµÄʱºò֪ͨ¶©ÔÄÕߣ»

7.²âÊÔÍê³ÉºóÇå³ýûÓõĶÔÏó£»

8.ÔÚXHRÇëÇóÖÐÌí¼ÓÄ£Äâ¶ÔÏó£»

9.°ÑÓ¦Óóõʼ»¯·ÖÀë³É×Ô¼ºµÄÄ£¿é¡£

ÓÃÕâ¸öÇåµ¥×÷ΪָÒý£¬Ô­À´µÄÓ¦ÓóÌÐò±»»áÍêÈ«ÖØ¹¹£¬²¢ÄÜ´ïµ½ÎÒÃǶԴúÂë½øÐе¥Ôª²âÊÔµÄÄ¿µÄ¡£

´ÓHTML¿ªÊ¼¨CÍâÁ´ËùÓеĽű¾

Ô­À´ÄÚÁªÔÚHTMLÎļþÖеÄJavascript´úÂëÒѾ­±»·ÅÔÚÁËÍⲿ²¢ÇÒ±»·ÅÖÃÓÚÁ½¸öÎļþÖУºauthentication-form.js ºÍstart.js¡£Ô­À´µÄ´ó²¿·ÖµÄÂß¼­·ÅÓÚauthentication-form.jsÄ£¿éÖУ¬Ó¦Óõijõʼ»¯ÔòÔÚstart.jsÖнøÐС£

ÒýÓÃ×Ôindex.html

<!DOCTYPE html>
<html>
<head>
<title>A Testable Authentication Form</title>
</head>
<body>
...

<script src="jquery.min.js"></script>
<!-- Both the authentication form and the initialization code are split into
their own files. Javascript resources can be combined for production
use. -->
<script src="authentication-form.js"></script>
<script src="start.js"></script>
</body>
</html>

ÓµÓпÉÑ¡µÄ¹«¹²½Ó¿ÚµÄÂß¼­·â×°Ä£¿é

AuthenticationFormÊÇÒ»¸ö¹«¹²µÄ¿É»ñÈ¡µÄÄ£¿é£¬¸ÃÄ£¿é±È½Ï¼ò½àµØ·â×°Á˴󲿷ֵÄԭʼÂß¼­¡£AuthenticationFormÌṩÁËÒ»¸ö¹«¹²µÄ½Ó¿Ú£¬Í¨¹ý¸Ã½Ó¿ÚÆä¹¦ÄÜ¿ÉÒÔ±»²âÊÔ¡£

Ò»¸ö¹«¹²½Ó¿Ú¨CÒýÓÃ×Ôauthentication-form.js

// The Module pattern is used to encapsulate logic. AuthenticationForm is the 
// public interface.
var AuthenticationForm = (function() {
"use strict";
...

ʹÓÿɳõʼ»¯µÄ¶ÔÏó

Ô­À´µÄform±íµ¥Àý×ÓûÓпÉʵÀý»¯µÄ²¿·Ö£¬ÕâÒ²¾ÍÒâζ×ÅËüµÄ´úÂëÖ»ÄÜÔËÐÐÒ»´Î¡£ÕâÑùµÄ»°ÓÐЧµÄµ¥Ôª²âÊÔ¼¸ºõÊDz»¿ÉÄܵġ£Öع¹Á˵ÄAuthenticationFormÊÇÒ»¸öÔ­ÐͶÔÏó£¬Ê¹ÓÃObject.createÀ´´´½¨ÐµÄʵÀý¡£

¿ÉʵÀý»¯µÄ¶ÔÏó-ÒýÓÃ×Ôauthentication-form.js

var AuthenticationForm = (function() {
"use strict";


// Module is the prototype object that is assigned to

// AuthenticationForm. New instances of AuthenticationForm

// are created using:

//

// var authForm = Object.create(AuthenticationForm)

//
var Module = {
init: function(options) {
...
};

return Module;
...
}());

¼õÉÙǶÌ׻ص÷µÄʹÓÃ

ÖØ¹¹µÄAuthenticationForm´ÓÔ­À´µÄµÄÉî²ãǶÌ׻ص÷£¨µ¼Ö´úÂë³É½ð×ÖËþ×´£©³éÈ¡Âß¼­ÐγÉËĸö¹«¹²µÄ¿ÉÒÔ»ñÈ¡µÃµ½µÄº¯Êý¡£ÕâЩº¯ÊýÖеÄÁ½¸ö±»ÓÃÀ´Ìṩ¸ø¶ÔÏó³õʼ»¯ºÍÏú»Ù£¬ÆäÓàµÄÁ½¸öÓÃÓÚ²âÊÔ½Ó¿Ú¡£

È¥³ý½ð×ÖËþ¨CÒýÓÃ×Ôauthentication-form.js

...
var Module = {
init: ...
teardown: ...


// BEGIN TESTING API
submitForm: submitForm,
checkAuthentication: checkAuthentication

// END TESTING API
};
...

½«DOMʼþ´¦Àí³ÌÐò´ÓʼþÐÐΪÖзÖÀë³öÀ´

½«DOMʼþ´¦Àí³ÌÐò´ÓʼþÐÐΪÖзÖÀë³öÀ´ÓÐÖúÓÚ´úÂëµÄÖØÓúͿɲâÊÔ¡£

½«DOMʼþ´¦Àí³ÌÐò´ÓʼþÐÐΪÖзÖÀë³öÀ´¨CÒýÓÃ×Ôauthentication-form.js

...
init: function(options) {
...

// A little bit of setup is needed for teardown. This will be

// explained shortly.
this.submitHandler = onFormSubmit.bind(this);
$("#authentication_form").on("submit", this.submitHandler);
},
...
};

...


// Separate the submit handler from the actual action. This allows

// onFormSubmit takes care of the event then calls submitForm like any

// other function would.
function onFormSubmit(event) {
event.preventDefault();

submitForm.call(this);
}


// submitForm to be called programatically without worrying about

// handling the event.
function submitForm(done) {
...
}

ÔÚÒì²½º¯ÊýÖÐʹÓûص÷£¨»òÕ߯äËûµÄ֪ͨ»úÖÆ£©

AuthenticationFormµÄ²âÊÔ½Ó¿ÚÖеÄÁ½¸öº¯Êý£¬submitFormºÍcheckAuthenticationÊÇÒì²½µÄ¡£µ±ËùÓд¦Àí³ÌÐò¶¼Íê³ÉµÄʱºòËûÃǶ¼½ÓÊÜÒ»¸öº¯Êý½øÐлص÷¡£

Óлص÷º¯ÊýµÄÒì²½»Øµ÷¨CÒýÓÃ×Ôauthentication-form.js

};
...


// checkAuthentication is asynchronous but the unit tests need to

// perform their checks after all actions are complete. "done" is an optional

// callback that is called once all other actions complete.
function submitForm(done) {
...
}


// checkAuthentication makes use of the ajax mock for unit testing.
function checkAuthentication(username, password, done) {
...
}
...

½«Ã»ÓõĶÔÏó´¦Àíµô

µ¥Ôª²âÊÔÓ¦¸Ã¶ÀÁ¢µÄ½øÐС£ÈκεÄ״̬£¬°üÀ¨¸½ÊôµÄDOMʼþ´¦ÀíÆ÷£¬ÔÚ²âÊÔµÄʱºò±ØÐë±»ÖØÖá£

ÒÆ³ý¸½¼ÓµÄDOMʼþ´¦Àí³ÌÐò¨CÒýÓÃ×Ôauthentication-form.js

...
init: function(options) {
...


// If unit tests are run multiple times, it is important to be able to

// detach events so that one test run does not interfere with another.
this.submitHandler = onFormSubmit.bind(this);
$("#authentication_form").on("submit", this.submitHandler);
},

teardown: function() {

// detach event handlers so that subsequent test runs do not interfere

// with each other.
$("#authentication_form").off("submit", this.submitHandler);
},
...

½«Ó¦Óõijõʼ»¯Âß¼­·ÖÀë³öÒ»¸öµ¥¶ÀµÄ£¨³õʼ»¯£©Ä£¿é

start.jsÊÇÒ»¸ö×Ôµ÷Óõĺ¯Êý£¬ËüÔÚ¶àÓеÄjsÎļþ¶¼ÏÂÔØÍê³ÉºóÖ´ÐС£ÒòΪÎÒÃǵÄÓ¦Óúܼòµ¥£¬Ö»ÐèÒªºÜÉٵijõʼ»¯´úÂë¨CÒ»¸öAuthenticationFormʵÀý±»´´½¨²¢³õʼ»¯¡£

start.js

(function() {
"use strict";

var authenticationForm = Object.create(AuthenticationForm);
authenticationForm.init();
}());

ÔÚÕâÒ»µãÉÏ£¬Ô­À´µÄÕû¸öÓ¦ÓóÌÐò±»Öع¹²¢ÇÒÖØÐÂʵÏÖÁË¡£Óû§Ó¦¸ÃÄÜ¿´µ½ÔÚ¹¦ÄÜÉϲ¢Ã»ÓÐ×ö¸Ä±ä£¬´¿´âÊÇ´úÂë½á¹¹µÄÐ޸ġ£

ÈçºÎ½øÐе¥Ôª²âÊÔ£¿

¾¡¹Üµ±Ç°ÎÒÃǵĴúÂëÊǿɲâÊԵģ¬Ò»Æª¹ØÓÚµ¥Ôª²âÊÔµÄÎÄÕÂȴûÓÐдÈκÎÏà¹ØµÄ²âÊÔ´úÂ룡Óм¸¸ö¸ßÖÊÁ¿µÄ²âÊÔ¿ò¼Ü£¬ÔÚÕâ¸öÀý×ÓÖÐÎÒÃÇʹÓÃQUnit¡£

Ê×ÏÈ£¬ÎÒÃÇÐèÒªÒ»¸ö²âÊÔ¹¤¾ß¡£Ò»¸ö²âÊÔ¹¤¾ßÓÉÒ»¸öÄ£ÄâDOMºÍJavascript´úÂë×é³É¡£Ä£ÄâDOMÓɲâÊÔÖÐҪʹÓõÄÔªËØ×é³É£¬Í¨³£ÊÇÀàËÆÓÚform»òÕßÄãÒª¼ì²â¿É¼ûÐÔµÄÔªËØÕâЩ¶«Î÷¡£ÎªÁ˱ÜÃâ²âÊÔ½»²æÎÛȾ£¬ÔÚÿһ¸öµ¥Ôª²âÊÔÖ®ºó¶¼½«DOMÔªËØ½øÐÐÖØÖá£QUnitÆÚÍûÄ£ÄâÔªËØ°üº¬ÔÚidΪ#qunit-fixtureµÄÔªËØÖС£

Javascript´úÂë°üº¬Ò»¸öµ¥Ôª²âÊÔÔËÐÐÆ÷£¬Òª±»²âÊԵĴúÂ룬¶ÀÁ¢µÄÄ£ÄâÒÔ¼°¶ÔËûÃÇ×Ô¼ºµÄһЩ²âÊÔ¡£

²âÊÔ¹¤¾ß¨CÒýÓÃ×Ôtests/index.html

...
<h1 id="qunit-header">Authentication Form Test Suite</h1>

...

<!-- A slimmed down form mock is used so there are

form elements to attach event handlers to -->
<div id="qunit-fixture">
<form>
<input type="text" id="username" name="username"></input>
<input type="password" id="password" name="password"></input>
</form>

<p id="username_password_required" style="display: none;">
Both the username and password are required.
</p>
...
</div>


<!-- Javascript used for testing -->
<script src="qunit.js"></script>


<!-- Include the ajax mock so no XHR requests are actually made -->
<script src="ajax-mock.js"></script>


<!-- Include the module to test -->
<script src="../authentication-form.js"></script>


<!-- The tests -->
<script src="authentication-form.js"></script>
...

ÊéдXHRÄ£Äâ

XHRÇëÇóÐèÒªÒÀÀµÓÚ·þÎñ¶Ë£¬´Óǰ¶Ë·¢ÆðµÄÇëÇó±ØÐë±»·þÎñ¶ËÏìÓ¦·ñÔòÓ¦ÓÃʲô¶¼¸É²»ÁË¡£ÓÃÕæÕýµÄXHRÇëÇó½øÐвâÊÔÒâζ×Å·þÎñ¶Ë±ØÐë×öºÃ×¼±¸£¬Õâ»áÑÏÖØ×谭ǰºó¶Ë²¢Ðпª·¢¡£

ÓëÆä·¢ÆðÕæÕýµÄXHRÇëÇ󣬲»ÈçʹÓÃÒ»¸öÄ£ÄâµÄÇëÇóÀ´×ö¡£Ä£Äâ¶ÔÏóÊÇÒ»Ð©Ìæ´ú¶ÔÏó¨C¿ÉÒÔÔÚ²âÊÔÖнøÐо«È·µØ¿ØÖÆ¡£Ò»¸öÄ£Äâ¶ÔÏó±ØÐëʵÏÖÓû§ÒªÊ¹ÓõÄËùÓй¦ÄÜ¡£ÐÒÔ˵ÄÊÇ£¬XHRÄ£Ä⣨Ҳ½ÐAjaxMock£©Ö»ÐèҪʵÏÖËùÓÐjQuery.ajax¹¦ÄܵĺÜСµÄÒ»²¿·Ö¼´¿É¡£Õâ¸öµ¥¶ÀµÄÄ£Ä⹦ÄÜÌṩÁËÕûºÏËùÓзþÎñ¶ËÏìÓ¦µÄÄÜÁ¦¡£¼¸¸ö¶îÍâµÄº¯Êý±»¼Ó½øÀ´¸¨Öúµ¥Ôª²âÊÔ¡£

AjaxMock½Ó¿Ú

AjaxMock = (function() {
...


/*

* AjaxMock mimicks portions of the $.ajax functionality.

* See http://api.jquery.com/jQuery.ajax/

*/
var AjaxMock = {

// The only jQuery function that is needed by the consumer
ajax: function(options) {
...
},


// What follows are non standard functions used for testing.
setSuccess: ...

setError: ...

getLastType: ...

getLastURL: ...

getLastData: ...
};

return AjaxMock;
}());

Íê³ÉһЩ²âÊÔ!

ÏÖÔÚ£¬²âÊÔ¹¤¾ßºÍXHRÄ£Äâ¶¼ÒѾ­×¼±¸ºÃÁË£¬ÎÒÃÇ¿ÉÒÔдһЩµ¥Ôª²âÊÔÁË£¡²âÊÔ°üº¬6¸ö¶ÀÁ¢µÄ²âÊÔ¡£Ã¿¸ö²âÊÔ¶¼»áʵÀý»¯Ò»¸öеÄAuthenticationForm¶ÔÏóºÍXHRÄ£Äâ¡£XHRÄ£Äâ¿ÉÒÔΪÿһ¸ö¿ÉÄܵĺó¶ËÏìÓ¦±àд²âÊÔ¡£

(function() {
"use strict";

var ajaxMock,
authenticationForm;

module("testable-authentication-form", {
setup: function() {

// create a mock XHR object to inject into the authenticationForm for

// testing.
ajaxMock = Object.create(AjaxMock);
authenticationForm = Object.create(AuthenticationForm);
authenticationForm.init({

// Inject the ajax mock for unit testing.
ajax: ajaxMock.ajax.bind(ajaxMock)
});
},
teardown: function() {

// tear down the authenticationForm so that subsequent test runs do not

// interfere with each other.
authenticationForm.teardown();
authenticationForm = null;
}
});

asyncTest("submitForm with valid username and password", function() {
$("#username").val("testuser");
$("#password").val("password");

ajaxMock.setSuccess({
success: true,
username: "testuser",
userid: "userid"
});

authenticationForm.submitForm(function(error) {
equal(error, null);

ok($("#authentication_success").is(":visible"));

start();
});
});

...
}());

×ܽá

»¨ÁËһЩʱ¼ä£¬µ«ÊÇÎÒÃÇ´ïµ½ÁËÎÒÃǵÄÄ¿µÄ¡£ÎÒÃǵĴúÂëÒ×ÓÚÔĶÁ,Ò×ÓÚÖØÓÃ,²¢ÇÒÓÐÒ»¸öÍêÕûµÄ²âÊÔÌ×¼þ¡£

±àд¿É²âÊԵĴúÂëͨ³£ÊÇÒ»¸öÌôÕ½£¬µ«ÊÇÄãÒ»µ©ÊÊÓ¦£¬»ù´¡µÄ²¿·Ö»¹ÊǺÜÈÝÒ׵ġ£ÔÚ¿ªÊ¼Ò»ÐдúÂë֮ǰ£¬ÄãÒªÎÊÄã×Ô¼º¡°ÎÒÒªÈçºÎÀ´¶Ô´úÂë½øÐвâÊÔ£¿¡±¡£Õâ¸ö¼òµ¥µÄÎÊÌâ×îÖÕ½«½ÚÊ¡´óÁ¿Ê±¼äºÍ²¢ÔÚÄãÖØ¹¹»òÌí¼Óй¦ÄܵÄʱºò¸øÄãÐÅÐÄ¡£

×îÖÕ²úÆ·

index.html

<!DOCTYPE html>
<!--
/* This Source Code Form is subject to the terms of the Mozilla Public

* License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-->
<html>
<head>
<title>A Testable Authentication Form</title>
</head>
<body>
<form id="authentication_form">
<label for="username">Username:</label>
<input type="text" id="username" name="username"></input>
<label for="password">Password:</label>
<input type="password" id="password" name="password"></input>
<button>Submit</button>

<p id="username_password_required" style="display: none;">
Both the username and password are required.
</p>

<p id="authentication_success" style="display: none;">
You have successfully authenticated!
</p>

<p id="authentication_failure" style="display: none;">
This username/password combination is not correct.
</p>

<p id="authentication_error" style="display: none;">
There was a problem authenticating the user, please try again later.
</p>
</form>

<script src="jquery.min.js"></script>
<!-- Both the authentication form and the initialization code are split into
their own files. They can be combined for production use. -->

<script src="authentication-form.js"></script>
<script src="start.js"></script>
</body>
</html>

authentication-form.js

// The Module pattern is used to encapsulate logic. AuthenticationForm is the 
// public interface.
var AuthenticationForm = (function() {
"use strict";

var Module = {
init: function(options) {
options = options || {};


// Use an injected request function for testing, use jQuery's xhr

// function as a default.
this.ajax = options.ajax || $.ajax;


// If unit tests are run multiple times, it is important to be able to

// detach events so that one test run does not interfere with another.
this.submitHandler = onFormSubmit.bind(this);
$("#authentication_form").on("submit", this.submitHandler);
},

teardown: function() {

// detach event handlers so that subsequent test runs do not interfere

// with each other.
$("#authentication_form").off("submit", this.submitHandler);
},


// BEGIN TESTING API

// A build script could strip this out to save bytes.
submitForm: submitForm,
checkAuthentication: checkAuthentication

// END TESTING API
};

return Module;


// Separate the submit handler from the actual action. This allows

// submitForm to be called programatically without worrying about

// handling the event.
function onFormSubmit(event) {
event.preventDefault();

submitForm.call(this);
}


// checkAuthentication is asynchronous but the unit tests need to

// perform their checks after all actions are complete. "done" is an

// optional callback that is called once all other actions complete.
function submitForm(done) {
var username = $("#username").val();
var password = $("#password").val();

if (username && password) {
checkAuthentication.call(this, username, password,
function(error, user) {
if (error) {
$("#authentication_error").show();
} else {
updateAuthenticationStatus(user);
}


// surface any errors so tests can be done.
done && done(error);
});
}
else {
$("#username_password_required").show();


// pass back an error message that can be used for testing.
done && done("username_password_required");
}
}


// checkAuthentication makes use of the ajax mock for unit testing.
function checkAuthentication(username, password, done) {
this.ajax({
type: "POST",
url: "/authenticate_user",
data: {
username: username,
password: password
},
success: function(resp) {
var user = null;
if (resp.success) {
user = {
username: resp.username,
userid: resp.userid
};
}

done && done(null, user);
},
error: function(jqXHR, textStatus, errorThrown) {
done && done(errorThrown);
}
});
}

function updateAuthenticationStatus(user) {
if (user) {
$("#authentication_success").show();
}
else {
$("#authentication_failure").show();
}
}
}());

start.js

(function() {
"use strict";

var authenticationForm = Object.create(AuthenticationForm);
authenticationForm.init();
}());

tests/index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link type="text/css" rel="stylesheet" href="qunit.css" />
</head>
<body>
<h1 id="qunit-header">Authentication Form Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-test-area"></div>

<div id="qunit-fixture">

<!-- A slimmed down form mock is used so there are

form elements to attach event handlers to -->
<form>
<input type="text" id="username" name="username"></input>
<input type="password" id="password" name="password"></input>
</form>

<p id="username_password_required" style="display: none;">
Both the username and password are required.
</p>

<p id="authentication_success" style="display: none;">
You have successfully authenticated!
</p>

<p id="authentication_failure" style="display: none;">
This username/password combination is not correct.
</p>

<p id="authentication_error" style="display: none;">
There was a problem authenticating the user, please try again later.
</p>
</div>

<script src="../jquery.min.js"></script>


<!-- QUnit is used for this example. Jasmine and Mocha are two other

popular test suites -->
<script src="qunit.js"></script>


<!-- Include the ajax mock so no XHR requests are actually made -->
<script src="ajax-mock.js"></script>


<!-- Include the module to test -->
<script src="../authentication-form.js"></script>


<!-- The tests -->
<script src="authentication-form.js"></script>
</body>
</html>

tests/ajax-mock.js

AjaxMock = (function() {
"use strict";


/*

* The AjaxMock object type is a controllable XHR module used for unit

* testing. It is injected into the AuthenticationForm so that real XHR

* requests are not made. Instead, the mock can be controlled to return

* expected values.

*

* AjaxMock mimicks the portions of the $.ajax functionality.

* See http://api.jquery.com/jQuery.ajax/

*/
var AjaxMock = {

// The only jQuery function used for ajax requests
ajax: function(options) {
this.type = options.type;
this.url = options.url;
this.data = options.data;

if ("successValue" in this) {

// Neither our code nor our tests make use of jqXHR or textStatus
if (options.success) options.success(this.successValue);
}
else if ("errorValue" in this) {

// Neither our code nor our tests make use of jqXHR or textStatus
if (options.error) options.error(null, 500, this.errorValue);
}
else {
throw new Error("setSuccess or setError must be called before ajax");
}
},


// What follows are non standard functions used for testing.
setSuccess: function(successValue) {
this.successValue = successValue;
},

setError: function(errorValue) {
this.errorValue = errorValue;
},

getLastType: function() {
return this.type;
},

getLastURL: function() {
return this.url;
},

getLastData: function() {
return this.data;
}
};

return AjaxMock;

}());

tests/authentication-form.js

(function() {
"use strict";

var ajaxMock,
authenticationForm;

module("testable-authentication-form", {
setup: function() {

// create a mock XHR object to inject into the authenticationForm for

// testing.
ajaxMock = Object.create(AjaxMock);
authenticationForm = Object.create(AuthenticationForm);
authenticationForm.init({

// Inject the ajax mock for unit testing.
ajax: ajaxMock.ajax.bind(ajaxMock)
});
},
teardown: function() {

// tear down the authenticationForm so that subsequent test runs do not

// interfere with each other.
authenticationForm.teardown();
authenticationForm = null;
}
});

asyncTest("submitForm with valid username and password", function() {
$("#username").val("testuser");
$("#password").val("password");

ajaxMock.setSuccess({
success: true,
username: "testuser",
userid: "userid"
});

authenticationForm.submitForm(function(error) {
equal(error, null);

ok($("#authentication_success").is(":visible"));

start();
});
});

asyncTest("submitForm with invalid username and password", function() {
$("#username").val("testuser");
$("#password").val("invalidpassword");

ajaxMock.setSuccess({
success: false
});

authenticationForm.submitForm(function(error) {
equal(error, null);

ok($("#authentication_failure").is(":visible"));

start();
});
});

asyncTest("submitForm with missing username and password", function() {
$("#username").val("");
$("#password").val("");

authenticationForm.submitForm(function(error) {
equal(error, "username_password_required");

ok($("#username_password_required").is(":visible"));

start();
});
});

asyncTest("submitForm with XHR error", function() {
$("#username").val("testuser");
$("#password").val("password");

ajaxMock.setError("could not complete");

authenticationForm.submitForm(function(error) {
equal(error, "could not complete");

ok($("#authentication_error").is(":visible"));

start();
});
});

asyncTest("checkAuthentication with valid user", function() {
ajaxMock.setSuccess({
success: true,
username: "testuser",
userid: "userid"
});

authenticationForm.checkAuthentication("testuser", "password",
function(error, user) {
equal(error, null);

equal(ajaxMock.getLastType(), "POST");
equal(ajaxMock.getLastURL(), "/authenticate_user");

var data = ajaxMock.getLastData();
equal(data.username, "testuser");
equal(data.password, "password");

equal(user.username, "testuser");
equal(user.userid, "userid");

start();
});
});

asyncTest("checkAuthentication with missing XHR error", function() {
ajaxMock.setError("could not complete");
authenticationForm.checkAuthentication("testuser", "password",
function(error) {
equal(error, "could not complete");

start();
});
});

}());
   
3038 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕ Ïà¹ØÎĵµ Ïà¹Ø¿Î³Ì



Éî¶È½âÎö£ºÇåÀíÀôúÂë
ÈçºÎ±àд³öÓµ±§±ä»¯µÄ´úÂë
ÖØ¹¹-ʹ´úÂë¸ü¼ò½àÓÅÃÀ
ÍŶÓÏîÄ¿¿ª·¢"±àÂë¹æ·¶"ϵÁÐÎÄÕÂ
ÖØ¹¹-¸ÄÉÆ¼ÈÓдúÂëµÄÉè¼Æ
Èí¼þÖØ¹¹v2
´úÂëÕû½àÖ®µÀ
¸ßÖÊÁ¿±à³Ì¹æ·¶
»ùÓÚHTML5¿Í»§¶Ë¡¢Web¶ËµÄÓ¦Óÿª·¢
HTML 5+CSS ¿ª·¢
ǶÈëʽC¸ßÖÊÁ¿±à³Ì
C++¸ß¼¶±à³Ì
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

AndroidÊÖ»ú¿ª·¢£¨Ò»£©
Àí½âJavascript
·ÇµäÐÍajaxʵ¼ù
³¹µ×µÄAjax
javascript ʹÓÃCookies
ʹÓà jQuery ¼ò»¯ Ajax ¿ª·¢

Struts+Spring+Hibernate
»ùÓÚJ2EEµÄWeb 2.0Ó¦Óÿª·¢
J2EEÉè¼ÆÄ£Ê½ºÍÐÔÄܵ÷ÓÅ
Java EE 5ÆóÒµ¼¶¼Ü¹¹Éè¼Æ
Javaµ¥Ôª²âÊÔ·½·¨Óë¼¼Êõ
Java±à³Ì·½·¨Óë¼¼Êõ

ijº½¿Õ¹«Ë¾IT²¿ JavaScriptʵ¼ù
ijµçÊÓÈí¼þ HTML5ºÍJavaScript
Öк½ÐÅ JavaScript¸ß¼¶Ó¦Óÿª·¢
´óÇìÓÍÌï web½çÃæAjax¿ª·¢¼¼Êõ
ºÍÀûʱ ʹÓÃAJAX½øÐÐWEBÓ¦Óÿª·¢