import unittest

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
import os
import sys
import time

class TestphpLDAPadminWeb(unittest.TestCase):

    def wait_and_print_on_failure(self, by, value, timeout=10):
        try:
            return WebDriverWait(self.driver, timeout).until(lambda x: x.find_element(by, value))
        except TimeoutException as e:
            print("Page source on failure:")
            print(self.driver.page_source)
            raise e

    def setUp(self):

        options = webdriver.ChromeOptions()
        options.add_argument('--headless')
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-gpu')
        options.add_argument('--disable-dev-shm-usage')
        service = Service(executable_path=r'/usr/bin/chromedriver')
        self.driver = webdriver.Chrome(service=service, options=options)

    def tearDown(self):
        self.driver.close()

    def do_login(self):
        self.driver.get("http://localhost:8888/phpldapadmin/")
        self.assertIn("phpLDAPadmin", self.driver.title)

        login_btn = self.wait_and_print_on_failure(By.XPATH, "//a[@title='Login to My LDAP Server']")
        login_btn.click()

        # Anonymous checkbox on login page
        self.wait_and_print_on_failure(By.NAME, "anonymous_bind", timeout=60)

        title = self.wait_and_print_on_failure(By.CLASS_NAME, "title")
        self.assertIn("Authenticate to server My LDAP Server", title.text)

        user = self.wait_and_print_on_failure(By.ID, "login")
        user.send_keys("cn=admin,dc=testing,dc=local")

        password = self.wait_and_print_on_failure(By.ID, "password")
        password.send_keys("public")

        password.submit()

        tree_menu = self.wait_and_print_on_failure(By.CLASS_NAME, "treemenudiv", timeout=60)

    def do_expand(self, img_name):
        expander = self.wait_and_print_on_failure(By.XPATH, f"//img[@src='images/default/{img_name}']")
        expander.click()

    def test_title_and_footer(self):
        self.driver.get("http://localhost:8888/phpldapadmin/")
        self.assertIn("phpLDAPadmin", self.driver.title)

        version = self.wait_and_print_on_failure(By.ID, "ajFOOT")
        self.assertIn("1.2.", version.text)

        login_btn = self.wait_and_print_on_failure(By.CLASS_NAME, "logged_in")
        self.assertIn("login", login_btn.text)

    def test_login_and_export(self):
        self.do_login()

        tree_item = self.wait_and_print_on_failure(By.XPATH, "//a[@title='dc=testing,dc=local']")
        tree_item.click()

        export_btn = self.wait_and_print_on_failure(By.XPATH, "//a[@title='Save a dump of this object']")
        export_btn.click()

        export_form = self.wait_and_print_on_failure(By.ID, "export_form", timeout=60)
        export_form.submit()

        export_result = self.wait_and_print_on_failure(By.ID, "ajBODY", timeout=60)

        self.assertIn("# LDIF Export for dc=testing,dc=local", export_result.text)
        self.assertIn("# Server: My LDAP Server (127.0.0.1)", export_result.text)
        self.assertIn("# Search Scope: base", export_result.text)
        self.assertIn("# Search Filter: (objectClass=*)", export_result.text)
        self.assertIn("# Total Entries: 1", export_result.text)
        self.assertIn("# Generated by phpLDAPadmin", export_result.text)
        self.assertIn("# Version: 1.2.", export_result.text)
        self.assertIn("version: 1", export_result.text)
        self.assertIn("# Entry 1: dc=testing,dc=local", export_result.text)
        self.assertIn("dn: dc=testing,dc=local", export_result.text)
        self.assertIn("dc: testing", export_result.text)
        self.assertIn("o: Debian Testing", export_result.text)
        self.assertIn("objectclass: top", export_result.text)
        self.assertIn("objectclass: dcObject", export_result.text)
        self.assertIn("objectclass: organization", export_result.text)

    def test_login_and_import(self):
        Simpsons = open("/usr/share/doc/phpldapadmin/Simpsons.ldif", "r")
        Simpsons = Simpsons.read()
        self.assertIn("o=Simpsons", Simpsons)
        Simpsons = Simpsons.replace("o=Simpsons", "o=Simpsons,dc=testing,dc=local")

        self.do_login()

        import_btn = self.wait_and_print_on_failure(By.XPATH, "//a[@title='Import My LDAP Server']")
        import_btn.click()

        import_form = self.wait_and_print_on_failure(By.CLASS_NAME, "new_value", timeout=60)

        import_area = self.wait_and_print_on_failure(By.XPATH, "//textarea[@name='ldif']")

        for line in Simpsons.split('\n'):
            import_area.send_keys(line + "\n")

        import_form.submit()

        import_result = self.wait_and_print_on_failure(By.ID, "ajBODY", timeout=60)

        self.assertIn("STDIN 30,367 bytes (LDIF Import)", import_result.text)
        self.assertIn("Adding", import_result.text)
        self.assertIn("Success", import_result.text)
        self.assertIn("o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=simpsons-goun,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=simpsons-pg,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Bart Simpson,ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Homer Simpson,ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Lisa Simpson,ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Maggie Simpson,ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Marge Simpson,ou=People,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("ou=Pets,o=Simpsons,dc=testing,dc=local", import_result.text)
        self.assertIn("cn=Santas Little Helper,ou=Pets,o=Simpsons,dc=testing,dc=local", import_result.text)

        self.wait_and_print_on_failure(By.XPATH, "//a[@title='Refresh My LDAP Server']").click()

        # Expand dc=testing,dc=local
        self.do_expand("tree_expand_corner_first.png")
        # Expand o=Simpsons
        self.do_expand("tree_expand.png")
        # Expand ou=People
        self.do_expand("tree_expand.png")
        # Expand ou=Pets
        self.do_expand("tree_expand.png")

        self.wait_and_print_on_failure(By.XPATH, "//a[@title='cn=Santas Little Helper,ou=Pets,o=Simpsons,dc=testing,dc=local']").click()

        title = self.wait_and_print_on_failure(By.XPATH, "//h3[@class='title']")
        self.assertIn("cn=Santas Little Helper", title.text)

        object_class = self.wait_and_print_on_failure(By.ID, "new_values_objectclass_0")
        self.assertIn("inetOrgPerson", object_class.get_attribute("value"))

        self.wait_and_print_on_failure(By.XPATH, "//a[@title='cn=Marge Simpson,ou=People,o=Simpsons,dc=testing,dc=local']").click()

        title = self.wait_and_print_on_failure(By.XPATH, "//h3[@class='title']")
        self.assertIn("cn=Marge Simpson", title.text)

        object_class = self.wait_and_print_on_failure(By.ID, "new_values_objectclass_0")
        self.assertIn("inetOrgPerson", object_class.get_attribute("value"))

        object_class = self.wait_and_print_on_failure(By.ID, "new_values_objectclass_3")
        self.assertIn("shadowAccount", object_class.get_attribute("value"))

        # Check cn=simpsons-pg
        self.wait_and_print_on_failure(By.XPATH, "//a[@title='cn=simpsons-pg,o=Simpsons,dc=testing,dc=local']").click()

        object_class = self.wait_and_print_on_failure(By.ID, "new_values_memberuid_0")
        self.assertIn("maggie", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_memberuid_1")
        self.assertIn("marg", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_memberuid_2")
        self.assertIn("lisa", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_memberuid_3")
        self.assertIn("homer", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_memberuid_4")
        self.assertIn("bart", object_class.get_attribute("value"))

        # Check cn=simpsons-goun
        self.wait_and_print_on_failure(By.XPATH, "//a[@title='cn=simpsons-goun,o=Simpsons,dc=testing,dc=local']").click()

        object_class = self.wait_and_print_on_failure(By.ID, "new_values_uniquemember_0")
        self.assertIn("cn=Bart Simpson,ou=People,o=Simpsons,dc=testing,dc=local", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_uniquemember_1")
        self.assertIn("cn=Homer Simpson,ou=People,o=Simpsons,dc=testing,dc=local", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_uniquemember_2")
        self.assertIn("cn=Lisa Simpson,ou=People,o=Simpsons,dc=testing,dc=local", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_uniquemember_3")
        self.assertIn("cn=Maggie Simpson,ou=People,o=Simpsons,dc=testing,dc=local", object_class.get_attribute("value"))
        object_class = self.wait_and_print_on_failure(By.ID, "new_values_uniquemember_4")
        self.assertIn("cn=Marge Simpson,ou=People,o=Simpsons,dc=testing,dc=local", object_class.get_attribute("value"))

if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(TestphpLDAPadminWeb)
    result = unittest.TextTestRunner(verbosity=2).run(suite)

    if not result.wasSuccessful():
        sys.exit(1)
