<?php

/**
 * @file
 * Functional tests for the commerce payment module user interface.
 */

/**
 * Test payment user interface.
 */
class CommercePaymentUITest extends CommerceBaseTestCase {
  /**
   * Order object.
   */
  protected $order;

  /**
   * Implementation of getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Payment user interface',
      'description' => 'Test the payment user and administrator interface.',
      'group' => 'Drupal Commerce',
    );
  }

  /**
   * Implementation of setUp().
   */
  function setUp() {
    $modules = parent::setUpHelper('all');
    parent::setUp($modules);

    // User creation for different operations.
    $this->store_admin = $this->createStoreAdmin();
    $this->store_customer = $this->createStoreCustomer();

    // The rule that sends a mail after checkout completion should be disabled
    //  as it returns an error caused by how mail messages are stored.
    $rules_config = rules_config_load('commerce_checkout_order_email');
    $rules_config->active = FALSE;
    $rules_config->save();
  }

  /**
   * Create a dummy order and go to checkout payment page.
   */
  protected function createOrderAndGoToPayment($user = NULL, $products = array()) {
    if (empty($user)) {
      $user = $this->store_customer;
    }

    // Log in as normal user.
    $this->drupalLogin($user);

    // Order creation, in cart status.
    $this->order = $this->createDummyOrder($user->uid, $products);

    // Go to checkout page.
    $this->drupalGet($this->getCommerceUrl('checkout'));

    // Check if the page resolves and if the default panes are present.
    $this->assertResponse(200, t('Store customer user is able to access the checkout page'));
    $this->assertTitle(t('Checkout') . ' | Drupal', t('Checkout page successfully loaded'));

    // Generate random information, as city, postal code, etc.
    $address_info = $this->generateAddressInformation();

    // Fill in the billing address information
    $billing_pane = $this->xpath("//select[starts-with(@name, 'customer_profile_billing[commerce_customer_address]')]");
    $this->drupalPostAJAX(NULL, array((string) $billing_pane[0]['name'] => 'US'), (string) $billing_pane[0]['name']);

    // Check if the country has been selected correctly, this uses XPath as the
    // ajax call replaces the element and the id may change.
    $this->assertFieldByXPath("//select[starts-with(@id, 'edit-customer-profile-billing-commerce-customer-address')]//option[@selected='selected']", 'US', t('Country selected'));

    // Fill in the required information for billing pane, with a random State.
    $info = array(
      'customer_profile_billing[commerce_customer_address][und][0][name_line]' => $address_info['name_line'],
      'customer_profile_billing[commerce_customer_address][und][0][thoroughfare]' => $address_info['thoroughfare'],
      'customer_profile_billing[commerce_customer_address][und][0][locality]' => $address_info['locality'],
      'customer_profile_billing[commerce_customer_address][und][0][administrative_area]' => $address_info['administrative_area'],
      'customer_profile_billing[commerce_customer_address][und][0][postal_code]' => $address_info['postal_code'],
    );
    $this->drupalPost(NULL, $info, t('Continue to next step'));

    // Check for default panes and information in this checkout phase.
    $this->assertTitle(t('Review order') . ' | Drupal', t('Review order page successfully loaded'));
  }

  /**
   * Test the payment on checkout process using an authenticated user.
   */
  public function testCommercePaymentCheckout() {
    $this->createOrderAndGoToPayment();

    $this->assertText('Example payment', t('Example payment method pane is present'));

    // Test validation messages not filling any information.
    $this->drupalPost(NULL, array(), t('Continue to next step'));

    // Get all panes from the system to get their forms.
    $panes = commerce_checkout_panes();

    // Check the validation message for the example payment pane.
    $callback = commerce_checkout_pane_callback($panes['commerce_payment'], 'checkout_form');
    $pane_form = drupal_get_form($callback, $panes['commerce_payment'], $this->order);
    foreach (element_children($pane_form['payment_details']) as $key) {
      if ($pane_form['payment_details'][$key]['#required']) {
        $this->assertText(t('!pane_message field is required', array('!pane_message' => $pane_form['payment_details'][$key]['#title'])), t('Check required payment pane message'));
      }
    }

    // Test payment information validation. Payment example module name field
    // will fail for single character value.
    $this->drupalPost(NULL, array('commerce_payment[payment_details][name]' => $this->randomName(1)), t('Continue to next step'));
    $this->assertTitle(t('Review order') . ' | Drupal', t('We should still be on review order page'));
    $this->assertText(t('You must enter a name two or more characters long.'), t('Validation error message found on the page'));

    // Finish checkout process.
    $this->drupalPost(NULL, array('commerce_payment[payment_details][name]' => $this->randomName()), t('Continue to next step'));

    // Load payment to check its status.
    $payment = commerce_payment_transaction_load_multiple(array(), array('order_id' => $this->order->order_id), TRUE);

    // Order status should be pending when completing checkout process.
    $this->assertEqual(reset($payment)->status, 'success', t('Payment was successfully processed'));

    // Check if the completion message has been displayed.
    $this->assertTitle(t('Checkout complete') . ' | Drupal', t('Checkout process completed successfully'));
  }

  /**
   * Test the adding payments using administration pages.
   */
  public function testCommercePaymentAdministration() {
    // Order creation, in cart status.
    $this->order = $this->createDummyOrder($this->store_customer->uid);

    // Log in as administrator user.
    $this->drupalLogin($this->store_admin);

    // Go to payment administration page.
    $this->drupalGet('admin/commerce/orders/' . $this->order->order_id . '/payment');

    $this->assertText(t('Payment'), t('Payment text found on the page.'));

    // Check order balance.
    $balance = commerce_payment_order_balance($this->order);
    $this->assertRaw('<td class="label">' . t('Order balance') . '</td><td class="balance">' . commerce_currency_format($balance['amount'], $balance['currency_code']) . '</td>', t('Order balance is equal to order amount'));

    // Add a payment for half of balance.
    $this->drupalPostAJAX(NULL, array('payment_method' => 'commerce_payment_example|commerce_payment_commerce_payment_example'), array('op' => t('Add payment')));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-amount')]", NULL, t('Amount field is present'));
    $this->assertFieldByXPath("//select[starts-with(@id, 'edit-currency-code')]", NULL, t('Currency code field is present'));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-payment-details-name')]", NULL, t('Name text field from payment example module is present'));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-submit')]", NULL, t('Save button is present'));
    $payment_amount = intval($balance['amount'] / 2);
    $post_data = array(
      'amount' => (commerce_currency_amount_to_decimal($payment_amount, $balance['currency_code'])),
      'currency_code' => 'USD',
      'payment_details[name]' => $this->randomName(),
    );
    $this->drupalPost(NULL, $post_data, t('Save'));

    // Reload the order.
    $order = commerce_order_load_multiple(array($this->order->order_id), array(), TRUE);
    // Reset the cache as we don't want to keep the lock.
    entity_get_controller('commerce_order')->resetCache();

    // Check order balance, it should be half of total now.
    $new_balance = commerce_payment_order_balance(reset($order));
    $this->assertEqual($new_balance['amount'], $balance['amount'] - $payment_amount, t('After half payment order balance is correct'));
    $this->assertRaw('<td class="label">' . t('Total paid') . '</td><td class="total">' . commerce_currency_format($payment_amount, $post_data['currency_code']) . '</td>', t('Total paid reflects the payment'));
    $this->assertRaw('<td class="label">' . t('Order balance') . '</td><td class="balance">' . commerce_currency_format($new_balance['amount'], $new_balance['currency_code']) . '</td>', t('Order balance is displayed correctly'));

    // Add a payment for the remainder.
    $this->drupalPostAJAX(NULL, array('payment_method' => 'commerce_payment_example|commerce_payment_commerce_payment_example'), array('op' => t('Add payment')));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-amount')]", NULL, t('Amount field is present'));
    $this->assertFieldByXPath("//select[starts-with(@id, 'edit-currency-code')]", NULL, t('Currency code field is present'));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-payment-details-name')]", NULL, t('Name text field from payment example module is present'));
    $this->assertFieldByXPath("//input[starts-with(@id, 'edit-submit')]", NULL, t('Save button is present'));
    $post_data = array(
      'amount' => commerce_currency_amount_to_decimal($new_balance['amount'], $new_balance['currency_code']),
      'currency_code' => 'USD',
      'payment_details[name]' => $this->randomName(),
    );
    $this->drupalPost(NULL, $post_data, t('Save'));

    // Reload the order.
    $order = commerce_order_load_multiple(array($this->order->order_id), array(), TRUE);

    // Check order balance, it should be zero now.
    $new_balance = commerce_payment_order_balance(reset($order));
    $this->assertEqual($new_balance['amount'], 0, t('Order balance is now zero'));
    $this->assertRaw('<td class="label">' . t('Total paid') . '</td><td class="total">' . commerce_currency_format($balance['amount'], $balance['currency_code']) . '</td>', t('Total paid is now equal to order total'));
    $this->assertRaw('<td class="label">' . t('Order balance') . '</td><td class="balance">' . commerce_currency_format(0, $new_balance['currency_code']) . '</td>', t('Balance is displayed as zero'));
  }

  /**
   * Test payment method rules conditions.
   */
  public function testCommercePaymentMethodsAdministration() {
    // Log in as store administrator user.
    $this->drupalLogin($this->store_admin);

    // Go to payment methods page.
    $this->drupalGet('admin/commerce/config/payment-methods');

    $this->assertTitle(t('Payment methods') . ' | Drupal', t('We are now in the payment methods page'));
    $this->assertText(t('Example payment'), t('Example payment rule is present'));

    // Go to edit example payment rule.
    $this->clickLink(t('Example payment'));

    // Adding a new condition.
    $this->clickLink(t('Add condition'));

    // Create new data comparison condition for amount > $50.
    $this->drupalPost(NULL, array('element_name' => 'data_is'), t('Continue'));
    $this->assertText(t('Compare two data values of the same type with each other.'), t('Second step page for adding a condition was successfully loaded'));
    $this->drupalPost(NULL, array('parameter[data][settings][data:select]' => 'commerce-order:commerce-order-total:amount'), t('Continue'));
    $this->assertText(t('The data to be compared, specified by using a data selector, e.g. "node:author:name".'), t('Third step page for adding a condition was successfully loaded'));
    $this->drupalPost(NULL, array('parameter[op][settings][op]' => '>', 'parameter[value][settings][value]' => 50), t('Save'));
    $this->assertText(t('Your changes have been saved.'), t('New condition was successfully added'));

    // Adding a new action to enable the payment method if conditions are met.
    $this->clickLink(t('Add action'));
    $this->drupalPost(NULL, array('element_name' => 'commerce_payment_enable_commerce_payment_example'), t('Continue'));
    $this->drupalPost(NULL, array('parameter[commerce_order][settings][commerce_order:select]' => 'commerce-order'), t('Save'));
    $this->assertText(t('Your changes have been saved.'), t('New action was successfully added'));

    // Create a less than $50 order (20 products $2 each).
    $product = $this->createDummyProduct($this->randomName(), $this->randomName(), 2, 'USD', $this->store_admin->uid);
    $this->createOrderAndGoToPayment($this->store_customer, array($product->product_id => 20));
    // Check that the payment method example is *not* there.
    $this->assertNoText('Example payment', t('Example payment method panel is not present'));

    // Create a more than $50 order (40 products $2 each).
    $product = $this->createDummyProduct($this->randomName(), $this->randomName(), 2, 'USD', $this->store_admin->uid);
    $this->createOrderAndGoToPayment($this->store_customer, array($product->product_id => 40));
    // Check that the payment method example is there.
    $this->assertText('Example payment', t('Example payment method panel is present'));
  }

  /**
   * Test the access to the payment/payment methods administration pages.
   */
  public function testCommercePaymentAccessPaymentAdministration() {
    // Login with normal user.
    $this->drupalLogin($this->store_customer);

    // Order creation, in cart status.
    $this->order = $this->createDummyOrder($this->store_customer->uid);

    // Access payment administration page.
    $this->drupalGet('admin/commerce/orders/' . $this->order->order_id . '/payment');

    $this->assertResponse(403, t('Normal user is not able to access the payment administration page'));

    // Access to the payment methods administration page.
    $this->drupalGet('admin/commerce/config/payment-methods');

    $this->assertResponse(403, t('Normal user is not able to access the payment methods administration page'));

    // Login with store admin.
    $this->drupalLogin($this->store_admin);

    // Access payment administration page.
    $this->drupalGet('admin/commerce/orders/' . $this->order->order_id . '/payment');

    $this->assertResponse(200, t('Store admin user can access the payment administration page'));

    // Access to the payment methods administration page
    $this->drupalGet('admin/commerce/config/payment-methods');

    $this->assertResponse(200, t('Store admin user can access the payment methods administration page'));
  }

}

/**
 * Test payment user interface.
 */
class CommercePaymentOffsiteTest extends CommerceBaseTestCase {
  /**
   * Order object.
   */
  protected $order;

  /**
   * Implementation of getInfo().
   */
  public static function getInfo() {
    return array(
      'name' => 'Payment Offsite',
      'description' => 'Test the payment offsite process.',
      'group' => 'Drupal Commerce',
    );
  }

  /**
   * Implementation of setUp().
   */
  function setUp() {
    $modules = parent::setUpHelper('all');
    $modules[] = 'commerce_payment_dummy_offsite';
    parent::setUp($modules);

    // User creation for different operations.
    $this->store_admin = $this->createStoreAdmin();
    $this->store_customer = $this->createStoreCustomer();
  }

  /**
   * Test an Offsite payment method.
   */
  public function testCommercePaymentOffsitePayment() {
    // Create a new customer profile.
    $profile = $this->createDummyCustomerProfile('billing', $this->store_customer->uid);
    $profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $profile);
    // Create an order for store customer.
    $order = $this->createDummyOrder($this->store_customer->uid, array(), 'cart', $profile->profile_id);

    // Login with store admin.
    $this->drupalLogin($this->store_admin);

    // Access to the payment methods administration page.
    $this->drupalGet('admin/commerce/config/payment-methods');

    // Check if the payment method exists and it's listed.
    $this->assertText(t('Dummy Payment Method Offsite'), t('Offsite example payment method is listed in the payment methods administration page'));

    // Login with store customer and access to checkout.
    $this->drupalLogin($this->store_customer);
    $this->drupalGet($this->getCommerceUrl('checkout'));

    // Process the order and check if the offsite payment is working.
    $this->drupalPost(NULL, array(), t('Continue to next step'));
    $this->assertText(t('Dummy Payment Method Offsite'), t('Offsite example payment method is listed in the checkout process form'));
    $this->drupalPostAJAX(NULL, array('commerce_payment[payment_method]' => 'commerce_payment_dummy_offsite|commerce_payment_commerce_payment_dummy_offsite'), 'commerce_payment[payment_method]');
    $this->drupalPost(NULL, array(), t('Continue to next step'));

    $this->assertFieldById('edit-submit', t('Redirect to Offsite platform'), t('Redirection button to offsite payment platform is present'));
    // We can't really test further than this.
  }

}
