text: //  Copyright (c) 2014 Readium Foundation and/or its licensees. All rights reserved.
  //  
  //  Redistribution and use in source and binary forms, with or without modification, 
  //  are permitted provided that the following conditions are met:
  //  1. Redistributions of source code must retain the above copyright notice, this 
  //  list of conditions and the following disclaimer.
  //  2. Redistributions in binary form must reproduce the above copyright notice, 
  //  this list of conditions and the following disclaimer in the documentation and/or 
  //  other materials provided with the distribution.
  //  3. Neither the name of the organization nor the names of its contributors may be 
  //  used to endorse or promote products derived from this software without specific 
  //  prior written permission.
  
  var webdriver = require('selenium-webdriver');
  var extensionUrl = 'chrome-extension://mgknhofkmkhdmjgjjbbcdcipolnmgipj/index.html';
  var driver;
  
  exports.setUp  = function(callback){
      var capabilities = webdriver.Capabilities.chrome();
      capabilities.set('chromeOptions', {'args' : 'load-extension=' + process.cwd() + '/build/chrome-app'});
      driver = new webdriver.Builder().
          withCapabilities(capabilities).
          build();
      callback();
  }
  exports.tearDown = function(callback){
      //driver.close();
      driver.quit();
      callback();
      
  }
  
  var testErrback = function(test, msg){
      debugger;
      var msg = msg || 'Required element is not present or visible'
      test.ok(false, msg);
      test.done();
  }
  var waitForPresent = function(test, locator, msg, timeout){
      
      return driver.wait(function(){
          return driver.isElementPresent(locator);
      }, timeout || 5000).addErrback(testErrback.bind(null, test, msg));
  }
  
  var waitForVisible = function(test, element, msg, timeout){
      return driver.wait(function(){
          return element.isDisplayed().then(function(displayed){
              return displayed;
          });    
      }, timeout || 5000).addErrback(testErrback.bind(null, test, msg));
  }
  
  var initAddToLibTests = function(test){
      driver.get(extensionUrl);
      driver.sleep(3000);
      driver.get(extensionUrl);
  
      var err = testErrback.bind(null, test);
      var btn = driver.findElement({className : 'icon-add-epub'});
      btn.addErrback(err);
      btn.click();
      
      var dlg = driver.findElement({id: 'add-epub-dialog'});
      waitForVisible(test, dlg, 'The add epub dialog didn\'t appear when the button was clicked');
  }
  
  exports.testLibraryNavbar = function(test){
      driver.get(extensionUrl);
      driver.sleep(3000);
      driver.get(extensionUrl);
  
      waitForPresent(test, {className : 'navbar'}, 'navbar not present');
  
      waitForPresent(test, {className : 'icon-add-epub'}, 'epub button not present').then(function(){
          //test.ok(present, 'Add epub button not present');
          test.done();
      });
  
  }
      
  var verifyProgressBar = function(test){
      var progressBar = driver.findElement({className: 'progress-bar'});
      progressBar.addErrback(testErrback.bind(null, test, 'The progress dialog didn\'t get created'));
      waitForVisible(test, progressBar, 'The progress bar was never made visible');
  };
  var verifyLibraryItem = function(test, expectedTitle, expectedAuthor){
      waitForPresent(test, {className:'library-item'}, 'The item was not imported into the library', 80000);
  
      var titleElement = driver.findElement({css: '.library-item .title'});
      titleElement.addErrback(testErrback.bind(null, test, 'The imported book has no title element'));
      titleElement.getText().then(function(text){
          test.equal(text, expectedTitle, 'Incorrect title');
      });
  
      var authorElement = driver.findElement({css: '.library-item .author'});
      authorElement.addErrback(testErrback.bind(null, test, 'The imported book has no author element'));
      authorElement.getText().then(function(text){
          test.equal(text, expectedAuthor);
      });
  
      return driver.executeScript("var naturalWidth = $('img.read').prop('naturalWidth');\
                           return $('img.read').prop('complete') && typeof naturalWidth != 'undefined' && naturalWidth > 0").then(function(goodImg){
                               test.ok(goodImg, "Cover image is broken");
                           });
  }
  exports.addToLibTests = 
  {
  
      // testAddEpubFromWeb : function(test){
      //     var err = testErrback.bind(null, test);
      //     // have to reload because it seems like some chrome.* APIs don't get initialized unless I do.
      //     initAddToLibTests(test);
  
      //     var urlUpload = driver.findElement({id: 'url-upload'});
      //     urlUpload.addErrback(err);
  
      //     var testUrl = 'http://www.gutenberg.org/ebooks/30971.epub.images';
      //     urlUpload.sendKeys(testUrl).then(function(){
      //         var dlgSubmit = driver.findElement({className: 'add-book'});
      //         dlgSubmit.addErrback(err);
      //         dlgSubmit.click();
      //     });
  
  
      //     verifyProgressBar(test);
      //     verifyLibraryItem(test, 'Industrial Revolution', 'Poul William Anderson').then(function(){
      //         test.done();
      //     })
      // },
  
      testAddEpubFromZipFile : function(test){
          initAddToLibTests(test);
  
          var  epubUpload = driver.findElement({id: 'epub-upload'});
          epubUpload.addErrback(testErrback.bind(null, test, 'file upload control does not exist'));
  
          var testFile = process.cwd() + '/chrome-app/tests/epubs/cc-shared-culture.epub';
  
          epubUpload.sendKeys(testFile);
  
          verifyProgressBar(test);
          verifyLibraryItem(test, 'Creative Commons - A Shared Culture', 'Jesse Dylan').then(function(){
              test.done();
          });
      },
  
      testAddEpubFromDirectory : function(test){
  
          // can't really test directory selects because of https://code.google.com/p/chromedriver/issues/detail?id=183
          // leaving it in but commented out because it may be fixed one day.
  
          test.done();
  
          // initAddToLibTests(test);
  
          // var dirUpload = driver.findElement({id: 'dir-upload'});
          // dirUpload.addErrback(testErrback.bind(null, test, 'directory upload control does not exist'));
  
          // var testDir = process.cwd() + '/chrome-app/tests/epubs/accessible_epub_3'
  
          // driver.sleep(3000);
          // dirUpload.sendKeys(testDir);
  
          // verifyProgressBar(test);
          // verifyLibraryItem(test, 'asdasd', 'adadad').then(function(){
          //     test.done();
          // })
      }
  
  }
  var uploadTestFile = function(test, path){
      var  epubUpload = driver.findElement({id: 'epub-upload'});
      epubUpload.addErrback(testErrback.bind(null, test, 'file upload control does not exist'));
  
      var testFile = process.cwd() + path;
  
      epubUpload.sendKeys(testFile);
  };
  
  var loadDetailsDialog = function(test){
      waitForPresent(test, {className:'details'}, 'The item was not imported into the library', 40000);
      driver.sleep(1000);
      var detailsBtn = driver.findElement({className: 'details'});
      detailsBtn.click();
  
      waitForPresent(test, {className: 'details-dialog'}, 'The details dialog did not appear', 2000);
      waitForPresent(test, {className: 'details-body'}, 'The details never loaded');
  };
  
  exports.libViewTests = {
      testViewDetails : function(test){
          initAddToLibTests(test);
          
          uploadTestFile(test, '/chrome-app/tests/epubs/accessible_epub_3.epub');
  
          loadDetailsDialog(test);
          
  
          var title = driver.findElement({css: '.modal-book-info .modal-title'});
          title.addErrback(testErrback.bind(null, test, 'The details title does not exist'));
  
          title.getText().then(function(text){
              test.equal(text, 'Accessible EPUB 3');
          });
  
          driver.findElements({css: '.modal-book-info .modal-detail'}).then(function(items){
              test.equal(items.length, 6, "There should be 6 detail items");
              
              items[0].getText().then(function(text){
                  test.equal(text, 'Author: Matt Garrish', 'author detail is wrong');
              });
              items[1].getText().then(function(text){
                  test.equal(text, 'Publisher: O’Reilly Media, Inc.', 'publisher detail is wrong');
              })
              items[2].getText().then(function(text){
                  test.equal(text, 'Pub Date: 2012-02-20', 'publish date is wrong');
              })
              items[3].getText().then(function(text){
                  test.equal(text, 'Modified Date: 2012-10-24T15:30:00Z', 'modified date is wrong');
              })
              items[4].getText().then(function(text){
                  test.equal(text, 'ID: urn:isbn:9781449328030', 'id detail is wrong');
              })
              items[5].getText().then(function(text){
                  test.equal(text, 'EPUB version: 3.0', 'EPUB version detail is wrong');
              })
  
          }).then(function(){
              test.done();
          });
      },
      testReadFromDetails : function(test){
          initAddToLibTests(test);
          
          uploadTestFile(test, '/chrome-app/tests/epubs/accessible_epub_3.epub');
          loadDetailsDialog(test);
  
          var readBtn = driver.findElement({css: '.details-body .btn.read'});
          readBtn.addErrback(testErrback.bind(null, test, 'The read button does not exist'));
          driver.sleep(500);
          readBtn.click();
  
          waitForPresent(test, {id: 'epubContentIframe'}, 'The reader never loaded after trying to open it from the details dialog');
          //driver.sleep(1000);
  
          driver.switchTo().frame('epubContentIframe').addErrback(testErrback.bind(null, test, 'The reader frame does not exist'));
  
          waitForPresent(test, {id: 'cover-image'}, 'The book was not loaded into the reader').then(function(){
              test.done();
          });
  
      },
  
      testSwitchViews : function(test){
          initAddToLibTests(test);
          
          uploadTestFile(test, '/chrome-app/tests/epubs/accessible_epub_3.epub');
          waitForPresent(test, {className:'details'}, 'The item was not imported into the library', 40000);
          driver.sleep(500);
  
          var thumbnailButton = driver.findElement({className: 'icon-thumbnails'});
          thumbnailButton.addErrback(testErrback.bind(null, test, 'The thumbnail button does not exist'));
  
          thumbnailButton.isDisplayed().then(function(displayed){
              test.ok(!displayed, 'The thumbnail button should be hidden');
          });
  
          var listViewButton = driver.findElement({className: 'icon-list-view'});
          thumbnailButton.addErrback(testErrback.bind(null, test, 'The list view button does not exist'));
  
          listViewButton.isDisplayed().then(function(displayed){
              test.ok(displayed, 'the list view button should be visible');
          });
  
          listViewButton.click();
  
          waitForPresent(test, {css: 'body.list-view'}, 'The list view was not enabled');
  
          listViewButton.isDisplayed().then(function(displayed){
              test.ok(!displayed, 'the list view button should not be visible');
          });
  
          thumbnailButton.isDisplayed().then(function(displayed){
              test.ok(displayed, 'The thumbnail button should be visible');
          });
  
          thumbnailButton.click();
  
          driver.isElementPresent({css: 'body.list-view'}).then(function(present){
              test.ok(!present, 'The thumbnail view was not enabled');
              test.done();
          });
      }
  
  }
  var switchToReaderFrame = function(test){
      driver.switchTo().frame('epubContentIframe').addErrback(testErrback.bind(null, test, 'The reader frame does not exist'));
  }
  var switchToDefaultFrame = function(){
      driver.switchTo().defaultContent();
  }
  var findElement = function(test, locator, msg){
      var element = driver.findElement(locator);
      element.addErrback(testErrback.bind(null, test, msg));
      return element;
  }
  var readerSetUp = function(callback){
      driver.get(extensionUrl);
      driver.sleep(2000);
      driver.get(extensionUrl);
  
      err = callback;
      var btn = driver.findElement({className : 'icon-add-epub'});
      btn.addErrback(err);
      btn.click();
      
      driver.sleep(1000);
  
      var  epubUpload = driver.findElement({id: 'epub-upload'});
      epubUpload.addErrback(callback);
  
      var testFile = process.cwd() + '/chrome-app/tests/epubs/accessible_epub_3.epub';
  
      epubUpload.sendKeys(testFile);
  
      driver.wait(function(){
          return driver.isElementPresent({className: 'read'});
      }, 40000).addErrback(callback);
      driver.sleep(500);
      var read = driver.findElement({className: 'read'}).click();
      driver.wait(function(){
          return driver.isElementPresent({id: 'reading-area'});
      }, 6000).addErrback(callback);
      callback();
  }
  exports.readerTests = {
      setUp : readerSetUp,
  
      testTurnPage : function(test){
          
          switchToReaderFrame(test);
          waitForPresent(test, {id: 'cover-image'}, 'The book was not loaded into the reader');
  
          //driver.sleep(2000);
          switchToDefaultFrame();
  
          var nextBtn = driver.findElement({id: 'next-page-btn'});
          nextBtn.addErrback(testErrback.bind(null, test, 'next button does not exist'));
          nextBtn.click();
  
          driver.sleep(2000);
          switchToReaderFrame(test);
          waitForPresent(test, {css: 'h1.title'}, 'The next page did not appear.');
          var title = driver.findElement({css: 'h1.title'});
  
          // getText does not seem to work in the frame
          driver.executeScript('return arguments[0].innerText;', title).then(function(text){
              test.equal(text, 'Accessible EPUB 3');
          });
          
  
          driver.sleep(2000);
          switchToDefaultFrame();
          var prevBtn = driver.findElement({id: 'previous-page-btn'});
          prevBtn.addErrback(testErrback.bind(null, test, 'prev button does not exist'));
          prevBtn.click();
  
          switchToReaderFrame(test);
          waitForPresent(test, {id: 'cover-image'}, 'The previous page did not appear').then(function(){
              test.done();
          });
  
  
      },
  
      testTableOfContents : function(test){
          var tocBtn = driver.findElement({className: 'icon-toc'});
          tocBtn.addErrback(testErrback.bind(null, test, 'toc button does not exist'));
  
          tocBtn.click();
  
          var tocBody = driver.findElement({id: 'readium-toc-body'});
          tocBody.addErrback(testErrback.bind(null, test, 'toc body does not exist'));
  
          waitForVisible(test, tocBody, 'table of contents did not appear after clicking on the button');
  
          // just test the preface and the last link
          var firstLink = driver.findElement({css: 'a[href="pr01.xhtml"]'});
          firstLink.addErrback(testErrback.bind(null, test, 'the first toc link did not exist'));
  
          firstLink.getText().then(function(text){
              test.equal(text, 'Preface');
          });
  
          firstLink.click();
  
          switchToReaderFrame(test);
  
          waitForPresent(test, {css: '#_preface > h2.title'}, 'The preface title header did not appear');
  
          var prefaceTitle = driver.findElement({css: '#_preface > h2.title'});
          driver.executeScript('return arguments[0].innerText;', prefaceTitle).then(function(text){
              test.equal(text, 'Preface');
          });
  
          switchToDefaultFrame();
  
          driver.executeScript('$("#readium-toc-body").scrollTop(10000)');
  
          var lastLink = driver.findElement({css: 'a[href="ch04.xhtml#_about_the_book"]'});
          lastLink.addErrback(testErrback.bind(null, test, 'the last toc link did not exist'));
  
          lastLink.isDisplayed().then(function(displayed){
              test.ok(displayed, 'The last link in the toc is not displayed after scrolling to the bottom');
          });
  
          lastLink.click();
  
          switchToReaderFrame(test);
  
          waitForPresent(test, {id: '_about_the_book'}, 'The last section title never appeared');
  
          var title = driver.findElement({id: '_about_the_book'});
          driver.executeScript('return arguments[0].innerText;', title).then(function(text){
              test.equal(text, 'About the Book');
              test.done();
          });
  
      },
      tearDown : exports.tearDown
  }
  
  var loadSettingsDialog = function(test){
      var settingsBtn = driver.findElement({className: 'icon-settings'});
      settingsBtn.addErrback(testErrback.bind(null, test, 'settings button does not exist'));
  
      settingsBtn.click();
  
      var settingsDlg = driver.findElement({id: 'settings-dialog'})
      settingsDlg.addErrback(testErrback.bind(null, test, 'settings dialog does not exist'));
  
      waitForVisible(test, settingsDlg, 'settings dialog did not appear');
  
  }
  var saveSettings = function(test){
      var saveBtn = findElement(test, {css: '#settings-dialog .btn-primary'}, 'save settings button not present');
      saveBtn.click();
  }
  
  exports.settingsTests = {
      setUp : readerSetUp,
      testFontSize : function(test){
          
          loadSettingsDialog(test);
          
          var fontSlider = driver.findElement({id: 'font-size-input'});
          fontSlider.isDisplayed().then(function(displayed){
              test.ok(displayed, 'font slider is not visible');
          });
  
          driver.executeScript('return $(arguments[0]).val()', fontSlider).then(function(val){
              test.equal(val, 100, "Font size default value is wrong")
          });
  
          driver.executeScript('$(arguments[0]).val(160); $(arguments[0]).change();', fontSlider);
  
          var previewText = findElement(test, {className: 'preview-text'}, 'preview text does not exist');
  
          driver.executeScript('return arguments[0].style.fontSize;',previewText).then(function(fontSize){
              test.equal(fontSize, '1.6em');
          });
  
          saveSettings(test);
  
          driver.sleep(2000);
  
          switchToReaderFrame(test);
  
          var htmlElement = findElement(test, {css: 'html'}, 'No content in reader frame');
          driver.executeScript('return arguments[0].style.fontSize;', htmlElement).then(function(fontSize){
              test.equal(fontSize, '160%');
              test.done();
          });
      },
      testLayoutOptions : function(test){
          loadSettingsDialog(test);
  
          var twoPageOption = findElement(test, {css: '#two-up-option input'}, 'two page option not present');
          var onePageOption = findElement(test, {css: '#one-up-option input'}, 'one page option not present');
  
          var checkChecked = function(ele, expect, msg){
              driver.executeScript('return $(arguments[0]).prop("checked");', ele).then(function(checked){
                  test.equal(checked, expect, msg);
              });
          }
          checkChecked(twoPageOption, true, "The two page options should be selected by default");
          checkChecked(onePageOption, false, "The one page option should be unselected by default");
  
          var selectRadio = function(ele){
              driver.executeScript('$(arguments[0]).prop("checked", true);', ele);
          }
  
          selectRadio(onePageOption);
          saveSettings(test);
  
          driver.sleep(2000);
  
          var iframeWidth;
          driver.executeScript('return $("iframe").css("width");').then(function(width){
              iframeWidth = width;
          });
  
  
  
          switchToReaderFrame(test);
  
          var htmlElement = findElement(test, {css: 'html'}, 'No content in reader frame');
          driver.executeScript('return arguments[0].style.width;', htmlElement).then(function(width){
              test.equal(width, iframeWidth, 'Did not switch to one column');        
          });
  
          switchToDefaultFrame();
  
          loadSettingsDialog(test);
  
          selectRadio(twoPageOption);
          driver.sleep(1000);
          saveSettings(test);
  
          driver.sleep(2000);
          switchToReaderFrame(test);
          driver.executeScript('return arguments[0].style.width;', htmlElement).then(function(width){
              test.notEqual(width, iframeWidth, 'Did not switch to two column');
              test.done();            
          });
      },
  
      testThemes : function(test){
          loadSettingsDialog(test);
  
          var previewText = findElement(test, {className: 'preview-text'}, 'preview text does not exist');
  
  
          driver.findElements({className: 'theme-option'}).then(function(themes){
              themes.forEach(function(theme){
                  var themeColor,
                      themeBg;
  
                  theme.getCssValue('color').then(function(color){
                      themeColor = color;
                  });
                  theme.getCssValue('backgroundColor').then(function(bgcolor){
                      themeBg = bgcolor;
                  });
                  
                  theme.click();
                  
                  previewText.getCssValue('color').then(function(color){
                      test.equal(color, themeColor);
                  });
                  previewText.getCssValue('backgroundColor').then(function(bgcolor){
                      test.equal(bgcolor, themeBg);
                  });
  
              });
          });
  
          var nightTheme = findElement(test, {css: '#theme-radio-group .night-theme'}, 'Cannot find night theme button');
          nightTheme.click();
          
          var nightThemeColor,
              nightThemeBg;
  
          nightTheme.getCssValue('color').then(function(color){
              nightThemeColor = color;
          });
          nightTheme.getCssValue('backgroundColor').then(function(bgcolor){
              nightThemeBg = bgcolor;
          });
  
          driver.sleep(1000);
  
          saveSettings(test);
  
          switchToReaderFrame(test);
  
          var body = findElement(test, {css: 'body'}, 'Cannot find reader body');
  
          body.getCssValue('color').then(function(color){
              test.equal(color, nightThemeColor);
          });
  
          body.getCssValue('backgroundColor').then(function(bg){
              test.equal(bg, nightThemeBg);
              test.done();
          });
      },
  
      testMargins : function(test){
          loadSettingsDialog(test);
  
          var marginSlider = driver.findElement({id: 'margin-size-input'});
          marginSlider.isDisplayed().then(function(displayed){
              test.ok(displayed, 'margin slider is not visible');
          });
  
          driver.executeScript('return $(arguments[0]).val()', marginSlider).then(function(val){
              test.equal(val, 20, "Font size default value is wrong")
          });
  
          driver.executeScript('$(arguments[0]).val(80);', marginSlider);
  
          saveSettings(test);
  
          driver.sleep(2000);
  
          switchToReaderFrame(test);
  
          var htmlElement = findElement(test, {css: 'html'}, 'No content in reader frame');
          driver.executeScript('return arguments[0].style.webkitColumnGap;', htmlElement).then(function(gap){
              test.equal(gap, '80px');
          });
  
          switchToDefaultFrame();
  
          var container = findElement(test, {id: "epub-reader-container"}, 'Cannot locate reader container');
  
          container.getCssValue('margin').then(function(margin){
              test.equal(margin, '80px');
              test.done();
          })
  
  
      }
  }
  