/*
 * nodeunit_suite.js
  * Zestaw testów jednostkowych dla aplikacji SPA.
 *
 * Zestaw należy uruchomić za pomocą polecenia /nodeunit <nazwa_tego_pliku>/.
*/

/*jslint         node    : true, continue : true,
  devel  : true, indent  : 2,    maxerr   : 50,
  newcap : true, nomen   : true, plusplus : true,
  regexp : true, sloppy  : true, vars     : false,
  white  : true
*/
/*global $, spa */

// Moduły zewnętrzne i zmienne globalne.
global.jQuery = require( 'jquery' );
global.TAFFY  = require( './js/jq/taffydb-2.6.2.js' ).taffy;
global.$      = global.jQuery;
require( './js/jq/jquery.event.gevent-0.1.9.js' );

// Nasze moduły i zmienne globalne.
global.spa = null;
require( './js/spa.js'       );
require( './js/spa.util.js'  );
require( './js/spa.fake.js'  );
require( './js/spa.data.js'  );
require( './js/spa.model.js' );

var
  // Narzędzia i procedury obsługi.
  makePeopleStr, onLogin, onListchange,
  onSetchatee,  onUpdatechat,

  // Funkcje testowe.
  testInitialState,    loginAsFred,    testUserAndPeople,
  testWilmaMsg,        sendPebblesMsg, testMsgToPebbles,
  testPebblesResponse,

  // Procedury obsługi zdarzeń.
  loginEvent, changeEvent, chateeEvent, msgEvent,
  loginData, changeData, msgData, chateeData,

  // Indeksy.
  changeIdx = 0, chateeIdx = 0, msgIdx = 0,

  // Obiekty deferred.
  $deferLogin      = $.Deferred(),
  $deferChangeList = [ $.Deferred() ],
  $deferChateeList = [ $.Deferred() ],
  $deferMsgList    = [ $.Deferred() ];

// Narzędzie do tworzenia łańcucha znaków z nazw osób online.
makePeopleStr = function ( people_db ) {
  var people_list = [];
  people_db().each(function( person, idx ) {
    people_list.push( person.name );
  });
  return people_list.sort().join( ',' );
};

// Procedura obsługi dla zdarzenia 'spa-login'.
onLogin = function ( event, arg ) {
  loginEvent = event;
  loginData  = arg;
  $deferLogin.resolve();
};

// Procedura obsługi dla zdarzenia 'spa-listchange'.
onListchange = function ( event, arg ) {
  changeEvent = event;
  changeData  = arg;
  $deferChangeList[ changeIdx ].resolve();
  changeIdx++;
  $deferChangeList[ changeIdx ] = $.Deferred();
};

// Procedura obsługi dla zdarzenia 'spa-updatechat'
onUpdatechat = function ( event, arg ) {
  msgEvent = event;
  msgData  = arg;
  $deferMsgList[ msgIdx ].resolve();
  msgIdx++;
  $deferMsgList[ msgIdx ] = $.Deferred();
};

// Procedura obsługi dla zdarzenia 'spa-setchatee'
onSetchatee = function ( event, arg ) {
  chateeEvent = event;
  chateeData  = arg;
  $deferChateeList[ chateeIdx ].resolve();
  chateeIdx++;
  $deferChateeList[ chateeIdx ] = $.Deferred();
};

// Rozpoczęcie testu /testInitialState/.
testInitialState = function ( test ) {
  var $t, user, people_db, people_str, test_str;
  test.expect( 2 );

  // Inicjowanie aplikacji SPA.
  spa.initModule( null );
  spa.model.setDataMode( 'fake' );

  // Utworzenie obiektu jQuery.
  $t = $('<div/>');

  // Ustanowienie dla funkcji subskrypcji globalnych zdarzeń niestandardowych.
  $.gevent.subscribe( $t, 'spa-login',      onLogin      );
  $.gevent.subscribe( $t, 'spa-listchange', onListchange );
  $.gevent.subscribe( $t, 'spa-setchatee',  onSetchatee  );
  $.gevent.subscribe( $t, 'spa-updatechat', onUpdatechat );

  // Testowanie użytkownika w stanie początkowym.
  user     = spa.model.people.get_user();
  test_str = 'Użytkownik jest anonimowy';
  test.ok( user.get_is_anon(), test_str );

  // Testowanie listy osób online.
  test_str = 'Spodziewana lista zawiera tylko użytkownika „anonimowy”';
  people_db  = spa.model.people.get_db();
  people_str = makePeopleStr( people_db );
  test.ok( people_str === 'anonimowy', test_str );

  // Przejście do następnego testu bez blokowania.
  test.done();
};
// Przejście do następnego testu bez blokowania.

// Rozpoczęcie testu /loginAsFred/.
loginAsFred = function ( test ) {
  var user, people_db, people_str, test_str;
  test.expect( 6 );

  // Zalogowanie się jako użytkownik 'Fred'.
  spa.model.people.login( 'Fred' );
  test_str = 'zalogowany jako Fred';
  test.ok( true, test_str );

  // Testowanie atrybutów użytkownika przed zakończeniem procesu logowania.
  user     = spa.model.people.get_user();
  test_str = 'Użytkownik nie jest już anonimowy';
  test.ok( ! user.get_is_anon(), test_str );

  test_str = 'Nazwa użytkownika to "Fred"';
  test.ok( user.name === 'Fred', test_str );

  test_str = 'Identyfikator użytkownika jest niezdefiniowany, ponieważ logowanie nie jest zakończone';
  test.ok( ! user.id, test_str );

  test_str = 'cid użytkownika to c0';
  test.ok( user.cid === 'c0', test_str );

  test_str   = 'Lista użytkowników jest zgodna z oczekiwaniami';
  people_db  = spa.model.people.get_db();
  people_str = makePeopleStr( people_db );
  test.ok( people_str === 'Fred, anonimowy', test_str );

  // Przejście do następnego testu, gdy spełnione zostaną oba warunki:
  //   + logowanie zostało zakończone (zdarzenie spa-login);
  //   + lista osób online została zaktualizowana
  //     (zdarzenie spa-listchange).
  $.when( $deferLogin, $deferChangeList[ 0 ] )
    .then( test.done );
};
// Zakończenie testu /loginAsFred/.

// Rozpoczęcie testu /testUserAndPeople/.
testUserAndPeople = function ( test ) {
  var
    user, cloned_user,
    people_db, people_str,
    user_str, test_str;
  test.expect( 4 );

  // Testowanie atrybutów użytkownika.
  test_str = 'Logowanie jako Fred zakończone';
  test.ok( true, test_str );

  user        = spa.model.people.get_user();
  test_str    = 'Fred ma oczekiwane atrybuty';
  cloned_user = $.extend( true, {}, user );

  delete cloned_user.___id;
  delete cloned_user.___s;
  delete cloned_user.get_is_anon;
  delete cloned_user.get_is_user;

  test.deepEqual(
    cloned_user,
    { cid     : 'id_5',
      css_map : { top: 25, left: 25, 'background-color': '#8f8' },
      id      : 'id_5',
      name    : 'Fred'
    },
    test_str
  );

  // Testowanie listy osób online.
  test_str = 'Odbiór listchange zakończony';
  test.ok( true, test_str );

  people_db  = spa.model.people.get_db();
  people_str = makePeopleStr( people_db );
  user_str = 'Betty,Fred,Mike,Pebbles,Wilma';
  test_str = 'Dostarczona lista użytkowników jest zgodna z oczekiwaniami - ' + user_str;

  test.ok( people_str === user_str, test_str );

  // Przejście do następnego testu, gdy spełnione zostaną oba warunki:
  //   + odebrana została pierwsza wiadomość (zdarzenie spa-updatechat)
  //     (jest to przykładowa wiadomość od użytkownika 'Wilma');
  //   + nastąpiła zmiana rozmówcy (zdarzenie spa-setchatee).
  $.when($deferMsgList[ 0 ], $deferChateeList[ 0 ] )
    .then( test.done );
};
// Zakończenie testu /testUserAndPeople/.

// Rozpoczęcie testu /testWilmaMsg/.
testWilmaMsg = function ( test ) {
  var test_str;
  test.expect( 4 );

  // Testowanie wiadomości otrzymanej od użytkownika 'Wilma'.
  test_str = 'Wiadomość jest zgodna z oczekiwaniami';
  test.deepEqual(
    msgData,
    { dest_id: 'id_5',
      dest_name: 'Fred',
      sender_id: 'id_04',
      msg_text: 'Cześć, Fred! Tu Wilma.'
    },
    test_str
  );

  // Testowanie atrybutów rozmówcy.
  test.ok( chateeData.new_chatee.cid  === 'id_04' );
  test.ok( chateeData.new_chatee.id   === 'id_04' );
  test.ok( chateeData.new_chatee.name === 'Wilma' );

  // Przejście do następnego testu bez blokowania.
  test.done();
};
// Zakończenie testu /testWilmaMsg/.

// Rozpoczęcie testu /sendPebblesMsg/.
sendPebblesMsg = function ( test ) {
  var test_str, chatee;
  test.expect( 1 );

  // Użycie metody set_chatee do ustawienia jako rozmówcy użytkownika 'Pebbles'.
  spa.model.chat.set_chatee( 'id_03' );

  // Użycie metody send_msg do wysłania wiadomości do użytkownika 'Pebbles'.
  spa.model.chat.send_msg( 'Co tam słychać?' );

  // Testowanie rezultatów wywołania metody get_chatee().
  chatee = spa.model.chat.get_chatee();
  test_str = 'Rozmówca jest zgodny z oczekiwaniami';
  test.ok( chatee.name === 'Pebbles', test_str );

  // Przejście do następnego testu, gdy spełnione zostaną oba warunki:
  //   + rozmówca został ustawiony (zdarzenie spa-setchatee);
  //   + wiadomość została wysłana (zdarzenie spa-updatechat).
  $.when( $deferMsgList[ 1 ], $deferChateeList[ 1] )
    .then( test.done );
};
// Zakończenie testu /sendPebblesMsg/.

// Rozpoczęcie testu /testMsgToPebbles/.
testMsgToPebbles = function ( test ) {
  var test_str;
  test.expect( 2 );

  // Testowanie atrybutów rozmówcy.
  test_str = 'Pebbles to nazwa rozmówcy';
  test.ok(
    chateeData.new_chatee.name === 'Pebbles',
    test_str
  );

  // Testowanie wysłanej wiadomości.
  test_str = 'Zmiana wiadomości jest zgodna z oczekiwaniami';
  test.ok( msgData.msg_text === 'Co tam słychać?', test_str );

  // Przejście do następnego testu, gdy spełniony zostanie warunek:
  //   + odpowiedź została odebrana od użytkownika 'Pebbles'
  //    (zdarzenie spa-updatechat).
  $deferMsgList[ 2 ].done( test.done );
};
// Zakończenie testu /testMsgToPebbles/.

// Rozpoczęcie testu /testPebblesResponse/.
testPebblesResponse = function ( test ) {
  var test_str;
  test.expect( 1 );

  // Testowanie wiadomości otrzymanej od użytkownika 'Pebbles'.
  test_str = 'Wiadomość jest zgodna z oczekiwaniami';
  test.deepEqual(
    msgData,
    { dest_id: 'id_5',
      dest_name: 'Fred',
      sender_id: 'id_03',
      msg_text: 'Dzięki za uwagi, Fred'
    },
    test_str
  );

  // Przejście do następnego testu bez blokowania.
  test.done();
};
// Zakończenie testu /testPebblesResponse/.

module.exports = {
  testInitialState     : testInitialState,
  loginAsFred          : loginAsFred,
  testUserAndPeople    : testUserAndPeople,
  testWilmaMsg         : testWilmaMsg,
  sendPebblesMsg       : sendPebblesMsg,
  testMsgToPebbles     : testMsgToPebbles,
  testPebblesResponse  : testPebblesResponse
};
// Zakończenie zestawu testów.
