<! DOCTYPE html>
<!-- 
  All Rights Reserved. 

  @author nicksantos@google.com (Nick Santos)
  @author marcosalmeida@google.com (Marcos Almeida)
-->
<html>
<!-- 
Copyright 2008 The Closure Library Authors.  All Rights Reserved. 

Use of this source code is governed by the Apache License, Version 2.0. 
See the COPYING file for details. 
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.ui.editor. AbstractDialog</title>
<script src="../../base.js"></script>
<script>
  goog.require('goog.dom. DomHelper');
  goog.require('goog.dom.classlist');
  goog.require('goog.events. EventHandler');
  goog.require('goog.events. KeyCodes');
  goog.require('goog.testing. MockControl');
  goog.require('goog.testing.events');
  goog.require('goog.testing.jsunit');
  goog.require('goog.testing.mockmatchers. ArgumentMatcher');
  goog.require('goog.ui.editor. AbstractDialog');
  goog.require('goog.ui.editor. AbstractDialog. Builder');
  goog.require('goog.ui.editor. AbstractDialog. EventType');
  goog.require('goog.userAgent');
</script>
</head>
<body>
<script>

function shouldRunTests() {
  // Test disabled in IE7 due to flakiness.  See b/4269021. 
  return !( goog.userAgent. IE && goog.userAgent.isVersionOrHigher('7'));
}

var dialog;
var builder;

var mockCtrl;
var mockAfterHideHandler;
var mockOkHandler;
var mockCancelHandler;
var mockCustomButtonHandler;

var CUSTOM_EVENT = 'customEvent';
var CUSTOM_BUTTON_ID = 'customButton';


function setUp() {
  mockCtrl = new goog.testing. MockControl();
  mockAfterHideHandler = mockCtrl.createLooseMock(goog.events. EventHandler);
  mockOkHandler = mockCtrl.createLooseMock(goog.events. EventHandler);
  mockCancelHandler = mockCtrl.createLooseMock(goog.events. EventHandler);
  mockCustomButtonHandler = mockCtrl.createLooseMock(goog.events. EventHandler);
}

function tearDown() {
  if (dialog) {
    mockAfterHideHandler.$setIgnoreUnexpectedCalls(true);
    dialog.dispose();
  }
}

/**
 * Sets up the mock event handler to expect an AFTER_HIDE event. 
 */
function expectAfterHide() {
  mockAfterHideHandler.handleEvent(
      new goog.testing.mockmatchers. ArgumentMatcher(function(arg) {
        return arg.type ==
               goog.ui.editor. AbstractDialog. EventType. AFTER_HIDE;
      }));
}

/**
 * Sets up the mock event handler to expect an OK event. 
 */
function expectOk() {
  mockOkHandler.handleEvent(new goog.testing.mockmatchers. ArgumentMatcher(
      function(arg) {
        return arg.type == goog.ui.editor. AbstractDialog. EventType. OK;
      }));
}

/**
 * Sets up the mock event handler to expect an OK event and to call
 * preventDefault when handling it. 
 */
function expectOkPreventDefault() {
  expectOk();
  mockOkHandler.$does(function(e) {
    e.preventDefault();
  });
}

/**
 * Sets up the mock event handler to expect an OK event and to return false
 * when handling it. 
 */
function expectOkReturnFalse() {
  expectOk();
  mockOkHandler.$returns(false);
}

/**
 * Sets up the mock event handler to expect a CANCEL event. 
 */
function expectCancel() {
  mockCancelHandler.handleEvent(new goog.testing.mockmatchers. ArgumentMatcher(
      function(arg) {
        return arg.type == goog.ui.editor. AbstractDialog. EventType. CANCEL;
      }));
}

/**
 * Sets up the mock event handler to expect a custom button event. 
 */
function expectCustomButton() {
  mockCustomButtonHandler.handleEvent(
      new goog.testing.mockmatchers. ArgumentMatcher(function(arg) {
        return arg.type == CUSTOM_EVENT;
      }));
}

/**
 * Helper to create the dialog being tested in each test.  Since NewDialog is
 * abstract, needs to add a concrete version of any abstract methods.  Also
 * creates up the global builder variable which should be set up after the call
 * to this method. 
 * @return {goog.ui.editor. AbstractDialog} The dialog. 
 */
function createTestDialog() {
  var dialog = new goog.ui.editor. AbstractDialog(new goog.dom. DomHelper());
  builder = new goog.ui.editor. AbstractDialog. Builder(dialog);
  dialog.createDialogControl = function() {
    return builder.build();
  };
  dialog.createOkEvent = function(e) {
    return new goog.events. Event(goog.ui.editor. AbstractDialog. EventType. OK);
  };
  dialog.addEventListener(goog.ui.editor. AbstractDialog. EventType. AFTER_HIDE,
                          mockAfterHideHandler);
  dialog.addEventListener(goog.ui.editor. AbstractDialog. EventType. OK,
                          mockOkHandler);
  dialog.addEventListener(goog.ui.editor. AbstractDialog. EventType. CANCEL,
                          mockCancelHandler);
  dialog.addEventListener(CUSTOM_EVENT, mockCustomButtonHandler);
  return dialog;
}


/**
 * Asserts that the given dialog is open. 
 * @param {string} msg Message to be printed in case of failure. 
 * @param {goog.ui.editor. AbstractDialog} dialog Dialog to be tested. 
 */
function assertOpen(msg, dialog) {
  assertTrue(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
}

/**
 * Asserts that the given dialog is closed. 
 * @param {string} msg Message to be printed in case of failure. 
 * @param {goog.ui.editor. AbstractDialog} dialog Dialog to be tested. 
 */
function assertNotOpen(msg, dialog) {
  assertFalse(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
}


/**
 * Tests that if you create a dialog and hide it without having shown it, no
 * errors occur. 
 */
function testCreateAndHide() {
  dialog = createTestDialog();
  mockCtrl.$replayAll();

  assertNotOpen('Dialog should not be open after creation', dialog);
  dialog.hide();
  assertNotOpen('Dialog should not be open after hide()', dialog);

  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was not dispatched. 
}

/**
 * Tests that when you show and hide a dialog the flags indicating open are
 * correct and the AFTER_HIDE event is dispatched (and no errors happen). 
 */
function testShowAndHide() {
  dialog = createTestDialog();
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  assertNotOpen('Dialog should not be open before show()', dialog);
  dialog.show();
  assertOpen('Dialog should be open after show()', dialog);
  dialog.hide();
  assertNotOpen('Dialog should not be open after hide()', dialog);

  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched. 
}

/**
 * Tests that when you show and dispose a dialog (without hiding it first) the
 * flags indicating open are correct and the AFTER_HIDE event is dispatched (and
 * no errors happen). 
 */
function testShowAndDispose() {
  dialog = createTestDialog();
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  assertNotOpen('Dialog should not be open before show()', dialog);
  dialog.show();
  assertOpen('Dialog should be open after show()', dialog);
  dialog.dispose();
  assertNotOpen('Dialog should not be open after dispose()', dialog);

  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched. 
}

/**
 * Tests that when you dispose a dialog (without ever showing it first) the
 * flags indicating open are correct and the AFTER_HIDE event is never
 * dispatched (and no errors happen). 
 */
function testDisposeWithoutShow() {
  dialog = createTestDialog();
  mockCtrl.$replayAll();

  assertNotOpen('Dialog should not be open before dispose()', dialog);
  dialog.dispose();
  assertNotOpen('Dialog should not be open after dispose()', dialog);

  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was NOT dispatched. 
}

/**
 * Tests that labels set in the builder can be found in the resulting dialog's
 * HTML. 
 */
function testBasicLayout() {
  dialog = createTestDialog();
  mockCtrl.$replayAll();

  // create some dialog content
  var content = goog.dom.createDom('div', null, 'The Content');
  builder.setTitle('The Title')
      .setContent(content)
      .addOkButton('The OK Button')
      .addCancelButton()
      .addButton('The Apply Button', goog.nullFunction)
      .addClassName('myClassName');
  dialog.show();

  var dialogElem = dialog.dialogInternal_.getElement();
  var html = dialogElem.innerHTML;
  // TODO(user): This is really insufficient.  If the title and content
  // were swapped this test would still pass! 
  assertContains('Dialog html should contain title', '>The Title<', html);
  assertContains('Dialog html should contain content', '>The Content<', html);
  assertContains('Dialog html should contain custom OK button label',
                 '>The OK Button<',
                 html);
  assertContains('Dialog html should contain default Cancel button label',
                 '>Cancel<',
                 html);
  assertContains('Dialog html should contain custom button label',
                 '>The Apply Button<',
                 html);
  assertTrue('Dialog should have default Closure class',
             goog.dom.classlist.contains(dialogElem, 'modal-dialog'));
  assertTrue('Dialog should have our custom class',
             goog.dom.classlist.contains(dialogElem, 'myClassName'));

  mockCtrl.$verifyAll();
}


/**
 * Tests that clicking the OK button dispatches the OK event and closes the
 * dialog (dispatching the AFTER_HIDE event too). 
 */
function testOk() {
  dialog = createTestDialog();
  expectOk(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  assertNotOpen('Dialog should not be open after clicking OK', dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that hitting the enter key dispatches the OK event and closes the
 * dialog (dispatching the AFTER_HIDE event too). 
 */
function testEnter() {
  dialog = createTestDialog();
  expectOk(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireKeySequence(dialog.dialogInternal_.getElement(),
                                      goog.events. KeyCodes. ENTER);
  assertNotOpen('Dialog should not be open after hitting enter', dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that clicking the Cancel button dispatches the CANCEL event and closes
 * the dialog (dispatching the AFTER_HIDE event too). 
 */
function testCancel() {
  dialog = createTestDialog();
  expectCancel(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  builder.addCancelButton('My Cancel Button');

  dialog.show();
  goog.testing.events.fireClickSequence(dialog.getCancelButtonElement());
  assertNotOpen('Dialog should not be open after clicking Cancel', dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that hitting the escape key dispatches the CANCEL event and closes
 * the dialog (dispatching the AFTER_HIDE event too). 
 */
function testEscape() {
  dialog = createTestDialog();
  expectCancel(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireKeySequence(dialog.dialogInternal_.getElement(),
                                      goog.events. KeyCodes. ESC);
  assertNotOpen('Dialog should not be open after hitting escape', dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that clicking the custom button dispatches the custom event and closes
 * the dialog (dispatching the AFTER_HIDE event too). 
 */
function testCustomButton() {
  dialog = createTestDialog();
  expectCustomButton(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  builder.addButton('My Custom Button',
      function() {
        dialog.dispatchEvent(CUSTOM_EVENT)
      },
      CUSTOM_BUTTON_ID);

  dialog.show();
  goog.testing.events.fireClickSequence(
      dialog.getButtonElement(CUSTOM_BUTTON_ID));
  assertNotOpen('Dialog should not be open after clicking custom button',
                dialog);

  mockCtrl.$verifyAll();
}


/**
 * Tests that if the OK handler calls preventDefault, the dialog doesn't close. 
 */
function testOkPreventDefault() {
  dialog = createTestDialog();
  expectOkPreventDefault(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  assertOpen('Dialog should not be closed because preventDefault was called',
             dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that if the OK handler returns false, the dialog doesn't close. 
 */
function testOkReturnFalse() {
  dialog = createTestDialog();
  expectOkReturnFalse(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  assertOpen('Dialog should not be closed because handler returned false',
             dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that if creating the OK event fails, no event is dispatched and the
 * dialog doesn't close. 
 */
function testCreateOkEventFail() {
  dialog = createTestDialog();
  dialog.createOkEvent = function() { // Override our mock createOkEvent. 
    return null;
  };
  mockCtrl.$replayAll();

  dialog.show();
  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
  assertOpen('Dialog should not be closed because OK event creation failed',
             dialog);

  mockCtrl.$verifyAll(); // Verifies that no event was dispatched. 
}


/**
 * Tests that processOkAndClose() dispatches the OK event and closes the
 * dialog (dispatching the AFTER_HIDE event too). 
 */
function testProcessOkAndClose() {
  dialog = createTestDialog();
  expectOk(dialog);
  expectAfterHide(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  dialog.processOkAndClose();
  assertNotOpen('Dialog should not be open after processOkAndClose()', dialog);

  mockCtrl.$verifyAll();
}

/**
 * Tests that if the OK handler triggered by processOkAndClose calls
 * preventDefault, the dialog doesn't close (in the old implementation this
 * failed due to not great design, so this is sort of a regression test). 
 */
function testProcessOkAndClosePreventDefault() {
  dialog = createTestDialog();
  expectOkPreventDefault(dialog);
  mockCtrl.$replayAll();

  dialog.show();
  dialog.processOkAndClose();
  assertOpen('Dialog should not be closed because preventDefault was called',
              dialog);

  mockCtrl.$verifyAll();
}

</script>
</body>
</html>
