diff --git a/codebender_testing/utils.py b/codebender_testing/utils.py index e132d1a..b83926a 100644 --- a/codebender_testing/utils.py +++ b/codebender_testing/utils.py @@ -27,7 +27,7 @@ from codebender_testing.config import get_path from codebender_testing.config import jsondump from codebender_testing.disqus import DisqusWrapper - +from selenium.webdriver.common.action_chains import ActionChains # Time to wait until we give up on a DOM property becoming available. DOM_PROPERTY_DEFINED_TIMEOUT = 30 @@ -916,11 +916,25 @@ def change_privacy(self, privacy): privateRadioButton = self.get_element(By.CSS_SELECTOR,'#create-sketch-modal-type-controls [value="private"]') privateRadioButton.click() + def change_privacy_editor(self, privacy): + driver=self.driver + if privacy == 'private': + privacy = self.get_element(By.CSS_SELECTOR,'#editor_heading_privacy_icon .cb-icon-globe-inv') + actions = ActionChains(driver) + actions.move_to_element(privacy) + actions.double_click(privacy) + actions.perform() + else: + privacy = self.get_element(By.CSS_SELECTOR,'#editor_heading_privacy_icon .icon-lock') + actions = ActionChains(driver) + actions.move_to_element(privacy) + actions.double_click(privacy) + actions.perform() + def change_name(self, name): nameField = self.get_element(By.CSS_SELECTOR,'#create-sketch-modal .modal-body [id="create-sketch-name"') nameField.clear() nameField.send_keys(name) - nameField.send_keys(Keys.ENTER) def change_name_editor(self, name): sketchHeading = self.get_element(By.ID, 'editor_heading_project_name') @@ -942,10 +956,9 @@ def change_name_editor(self, name): ) def change_short_description(self, description): - nameField = self.get_element(By.CSS_SELECTOR,'#create-sketch-modal-sort-description') + nameField = self.get_element(By.CSS_SELECTOR,'#create-sketch-modal-short-description') nameField.clear() nameField.send_keys(description) - nameField.send_keys(Keys.ENTER) def change_short_description_editor(self, description): editDescription = self.get_element(By.CSS_SELECTOR,'.short-description-edit') diff --git a/tests/common/homepage_filters_counters/test_homepage_filters_counters.py b/tests/common/homepage_filters_counters/test_homepage_filters_counters.py new file mode 100644 index 0000000..578f12c --- /dev/null +++ b/tests/common/homepage_filters_counters/test_homepage_filters_counters.py @@ -0,0 +1,220 @@ +from codebender_testing.utils import SeleniumTestCase +from selenium import webdriver +from selenium.webdriver.common.by import By +from codebender_testing import config +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions +import pytest + + +class TestSketchesCounters(SeleniumTestCase): + + @pytest.fixture(scope="class", autouse=True) + def open_user_home(self, tester_login): + """Makes sure we are logged in and are at the user home page + performing any of these tests.""" + pass + + def test_sketches_counters(self): + driver = self.driver + # Check that the counters: Public sketches (blue) and Private sketches + # (purple) have the correct value (number of sketches of each category). + + assert self.get_element(By.ID, "private-sketches-counter").text=="0" + assert self.get_element(By.ID,"public-sketches-counter").text=="0" + + # Check that the counter for private projects also appears at the Badges + # section and has the correct value. + privateProjects = self.get_element(By.ID, + "available-private-sketches-counter").text + assert self.get_element(By.ID, + "privateProjectAvailableNumber").text == \ + privateProjects.split('/')[0] + + # Check that the counter for available private sketches also appears at + # the upload sketch modal (ino, zip, multiple zip) and at the create and + # edit sketch modal. + + # Upload .ino. + self.get_element(By.ID, "sketch-upload-button").click() + self.get_element(By.ID, "upload-sketch-ino").click() + assert self.get_element(By.ID, + "upload-sketch-modal-available-private-sketches").text == \ + privateProjects + close_btn = self.get_element(By.CSS_SELECTOR, + "#home-upload-sketch-modal .btn-danger") + close_btn.click() + # Wait for the modal to close. + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, ".modal-backdrop fade") + ) + ) + + # Upload .zip. + self.get_element(By.ID, "sketch-upload-button").click() + self.get_element(By.ID, "upload-sketch-zip").click() + assert self.get_element(By.ID, + "upload-sketch-modal-available-private-sketches").text == \ + privateProjects + close_btn = self.get_element(By.CSS_SELECTOR, + "#home-upload-sketch-modal .btn-danger") + close_btn.click() + # Wait for the modal to close. + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, ".modal-backdrop fade") + ) + ) + + # Upload your sketch folder [.zip]. + self.get_element(By.ID, "sketch-upload-button").click() + self.get_element(By.ID, "upload-sketch-folder-zip").click() + assert self.get_element(By.ID, + "upload-sketch-modal-available-private-sketches").text == \ + privateProjects + close_btn = self.get_element(By.CSS_SELECTOR, + "#home-upload-sketch-modal .btn-danger") + close_btn.click() + # Wait for the modal to close. + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, ".modal-backdrop fade") + ) + ) + + # Create sketch modal. + self.get_element(By.ID, "create_sketch_btn").click() + assert self.get_element(By.ID, + "create-sketch-modal-available-private-sketches").text == \ + privateProjects + close_btn = self.get_element(By.CSS_SELECTOR, + "#create-sketch-modal .btn-danger") + close_btn.click() + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, ".modal-backdrop fade") + ) + ) + + # Create one public project. + self.create_sketch('public' , 'publicSketch1', 'short description') + self.open("/") + + # Check that public sketch number was increased from 0 to 1. + assert self.get_element(By.ID, "public-sketches-counter").text=="1" + + # Create one private project. + self.create_sketch('private' , 'privateSketch1', 'short description') + self.open("/") + + # Check that private sketch number was increased from 0 to 1. + assert self.get_element(By.ID, "private-sketches-counter").text=="1" + + # Check that the counter for available private projects that appears at + # the Badges section has the correct value. + + private_sketches = self.get_element(By.ID, + "available-private-sketches-counter").text + assert self.get_element(By.ID, + "privateProjectAvailableNumber").text == \ + private_sketches.split('/')[0] + + # Check that the number of private and public projects is updated + # each time that you make a change (change a sketch from public + # to private and vice versa). + editInfo = driver.find_element_by_css_selector( + '#project_list li[data-name="publicSketch1"] \ + .sketch-block-container .sketch-block-header \ + .sketch-block-edit-info') + hoverEditInfo = ActionChains(driver).move_to_element(editInfo).perform() + editInfoClick = driver.find_element_by_css_selector( + '#project_list li[data-name="publicSketch1"] \ + .sketch-block-container .sketch-block-header \ + .sketch-block-edit-info a') + editInfoClick.click() + # Change project from public to private. + privateRadioButton = self.get_element(By.CSS_SELECTOR, + '#edit-sketch-modal-type-controls [value="private"]') + privateRadioButton.click() + # Click the Save button. + save_btn = self.get_element(By.CSS_SELECTOR, + "#edit-sketch-modal .btn-success") + save_btn.click() + # Wait for the modal to close. + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, "#edit-sketch-modal") + ) + ) + # Check public and private projects counters. + assert self.get_element(By.ID, "private-sketches-counter").text=="2" + assert self.get_element(By.ID, "public-sketches-counter").text=="0" + + editInfo = driver.find_element_by_css_selector( + '#project_list li[data-name="publicSketch1"] \ + .sketch-block-container .sketch-block-header \ + .sketch-block-edit-info') + hoverEditInfo = ActionChains(driver).move_to_element(editInfo).perform() + editInfoClick = driver.find_element_by_css_selector( + '#project_list li[data-name="publicSketch1"] \ + .sketch-block-container .sketch-block-header \ + .sketch-block-edit-info a') + editInfoClick.click() + # Change project from private to public. + publicRadioButton = self.get_element(By.CSS_SELECTOR, + '#edit-sketch-modal-type-controls [value="public"]') + publicRadioButton.click() + # Click the Save button. + save_btn = self.get_element(By.CSS_SELECTOR, + "#edit-sketch-modal .btn-success") + save_btn.click() + # Wait for the modal to close. + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, "#edit-sketch-modal") + ) + ) + # Check public and private projects counters. + assert self.get_element(By.ID, "private-sketches-counter").text=="1" + assert self.get_element(By.ID, "public-sketches-counter").text=="1" + + + def test_sketch_filters(self): + # Check that All, Public and Private buttons work, by clicking on each + # of them and verifying hat the correct number of sketches appears. + + # Check "All sketches button". + self.get_element(By.CSS_SELECTOR, + '#filter-options label[data-filter="all"]').click() + sketches = self.find_all('#project_list >li') + assert len(sketches) == 2 + + # Check "Public button". + self.get_element(By.CSS_SELECTOR, + '#filter-options label[data-filter="public"]').click() + sketches = self.find_all('#project_list >li .cb-icon-globe-inv') + assert len(sketches) == 1 + + # Check "Private button". + self.get_element(By.CSS_SELECTOR, + '#filter-options label[data-filter="private"]').click() + sketches = self.find_all('#project_list >li .fa-lock') + assert len(sketches) == 1 + +class TestDeleteAllSketches(SeleniumTestCase): + + def test_delete(self, tester_login): + try: + sketches = self.find_all('#project_list > li \ + .sketch-block-title > a') + projects = [] + for sketch in sketches: + projects.append(sketch.text) + for project in projects: + self.delete_project(project) + except: + print 'No sketches found' + + self.logout() diff --git a/tests/common/modal_improvement/test_sketch_modal_improvement.py b/tests/common/modal_improvement/test_sketch_modal_improvement.py index c16e9c9..a47cfae 100644 --- a/tests/common/modal_improvement/test_sketch_modal_improvement.py +++ b/tests/common/modal_improvement/test_sketch_modal_improvement.py @@ -8,6 +8,7 @@ from codebender_testing.config import TIMEOUT from selenium.webdriver.support.ui import WebDriverWait import pytest +import time class TestSketchesCounters(SeleniumTestCase): @@ -37,11 +38,18 @@ def test_create_sketch_modal(self): assert self.get_element(By.ID, 'create-sketch-name') == \ self.driver.switch_to.active_element + # Check that sketch name is auto-generated as: + # Untitled Sketch CURRENT_DATE. + current_date_text= "Untitled Sketch " + time.strftime("%Y-%m-%d") + assert self.get_element(By.ID, + "create-sketch-name").get_attribute("value") == current_date_text + # Check that when the input has focus and you press Enter, # the create sketch action is executed. self.get_element(By.ID, 'create-sketch-name').send_keys(Keys.ENTER) - self.get_element(By.CSS_SELECTOR, '#create-sketch-modal-action-button .fa-spinner') + self.get_element(By.CSS_SELECTOR, + '#create-sketch-modal-action-button .fa-spinner') # Check that during the sketch creation, # the sketch privacy radio buttons are disabled. @@ -58,6 +66,104 @@ def test_create_sketch_modal(self): ) ) + # Check that when you create the sketch you are redirected + # into the editor. + self.get_element(By.ID, "cb_cf_flash_btn") + # Delete the created project. self.open("/") self.delete_project(createdProject) + + # Check that Create btn is disbaled when you try to create a sketch + # without a name. + createSketchBtn = self.get_element(By.ID, 'create_sketch_btn') + createSketchBtn.click() + WebDriverWait(self.driver, TIMEOUT['LOCATE_ELEMENT']).until( + expected_conditions.visibility_of_element_located( + (By.CSS_SELECTOR, "#create-sketch-modal") + ) + ) + self.get_element(By.ID, 'create-sketch-name').clear() + self.get_element(By.ID, "create-sketch-name").send_keys( + Keys.CONTROL + "a") + self.get_element(By.ID, "create-sketch-name").send_keys(Keys.DELETE) + assert self.get_element(By.ID, + 'create-sketch-modal-action-button').get_attribute('disabled') + + # Check thatwhen you create a sketch with invalid name error message + # "Invalid Project Name. Please enter a new one." appears. + self.get_element(By.ID, "create-sketch-name").send_keys('.') + self.get_element(By.ID, 'create-sketch-name').send_keys(Keys.ENTER) + assert self.get_element(By.ID, + 'create-sketch-modal-action-button').get_attribute('disabled') + WebDriverWait(self.driver, TIMEOUT['LOCATE_ELEMENT']).until( + expected_conditions.element_to_be_clickable( + (By.ID, 'create-sketch-modal-action-button') + ) + ) + errorMessage = self.get_element(By.ID, + 'create-sketch-modal-error-message') + assert errorMessage.text == \ + "Invalid Project Name. Please enter a new one." + + # Check the description character counter is always in sync with + # the length of the short description. + description = self.get_element(By.CSS_SELECTOR, + '#create-sketch-modal-short-description') + description.clear() + description.send_keys('short description') + print self.get_element(By.ID, + "create-sketch-modal-short-description-count").text + assert self.get_element(By.ID, + "create-sketch-modal-short-description-count").text == "123" + self.get_element(By.CSS_SELECTOR, + "#create-sketch-modal .btn-danger").click() + WebDriverWait(self.driver, TIMEOUT['LOCATE_ELEMENT']).until( + expected_conditions.invisibility_of_element_located( + (By.CSS_SELECTOR, "#create-sketch-modal") + ) + ) + createSketchBtn = self.get_element(By.ID, 'create_sketch_btn') + createSketchBtn.click() + WebDriverWait(self.driver, TIMEOUT['LOCATE_ELEMENT']).until( + expected_conditions.visibility_of_element_located( + (By.CSS_SELECTOR, "#create-sketch-modal") + ) + ) + + # Check that when you enter a short description longer than 140 chars + # the character counter becomes red. + description = self.get_element(By.CSS_SELECTOR, + '#create-sketch-modal-short-description') + description.clear() + description.send_keys( + "shortdescriptionshortdescriptionshortdescriptionshortdescription \ + shortdescriptionshortdescriptionshortdescriptionshortdescription") + assert self.get_element(By.ID, + "create-sketch-modal-short-description-count").text == "-1" + assert self.get_element(By.CSS_SELECTOR, + "#create-sketch-modal-short-description-count.overflow") + + # Check the description character counter is always in sync with + # the length of the short description. + description.clear() + description.send_keys( + "shortdescriptionshortdescriptionshortdescriptionshortdescription \ + shortdescriptionshortdescriptionshortdescriptionshortdescriptio") + assert self.get_element(By.ID, + "create-sketch-modal-short-description-count").text == "0" + self.get_element(By.CSS_SELECTOR, + "#create-sketch-modal-action-button").click() + self.get_element(By.ID, "cb_cf_flash_btn") + self.open("/") + + def test_delete(self, tester_login): + try: + sketches = self.find_all('#project_list > li .sketch-block-title > a') + projects = [] + for sketch in sketches: + projects.append(sketch.text) + for project in projects: + self.delete_project(project) + except: + print 'No sketches found' diff --git a/tests/common/sketch/test_sketch.py b/tests/common/sketch/test_sketch.py index 0c66f1b..9a9333d 100644 --- a/tests/common/sketch/test_sketch.py +++ b/tests/common/sketch/test_sketch.py @@ -26,17 +26,31 @@ class TestSketch(SeleniumTestCase): def create_test_project(self, tester_login): """Makes sure we are logged in and have a project open before performing any of these tests.""" - self.create_sketch('public' , TEST_PROJECT_NAME + '_initial', 'short description') + self.create_sketch('public' , TEST_PROJECT_NAME + '_initial', + 'short description') + + def test_change_privacy(self): + self.change_privacy_editor('private') + assert self.get_element(By.CSS_SELECTOR, + '#editor_heading_privacy_icon .icon-lock') + self.change_privacy_editor('public') + assert self.get_element(By.CSS_SELECTOR, + '#editor_heading_privacy_icon .cb-icon-globe-inv') def test_rename_project(self): self.change_name_editor(TEST_PROJECT_NAME) + sketchHeading = self.get_element(By.ID, 'editor_heading_project_name') + assert sketchHeading.text == TEST_PROJECT_NAME def test_change_short_description(self): self.change_short_description_editor('description') + description = self.get_element(By.ID,'short-description') + assert description.text == 'description' def test_verify_code(self): """Ensures that we can compile code and see the success message.""" - boards = ['Arduino Uno', 'Arduino Leonardo', 'Arduino Mega 2560 or Mega ADK'] + boards = ['Arduino Uno', 'Arduino Leonardo', + 'Arduino Mega 2560 or Mega ADK'] for board in boards: self.execute_script(SELECT_BOARD_SCRIPT(board)) compile_button = self.get_element(By.ID, "cb_cf_verify_btn") @@ -47,7 +61,8 @@ def test_verify_code(self): (By.ID, "progress")) ) - operation_output = self.driver.find_element_by_id('operation_output') + operation_output = \ + self.driver.find_element_by_id('operation_output') assert operation_output.text.strip() == 'Verification successful!' throttle_compile() @@ -85,7 +100,8 @@ def test_speeds_dropdown(self): def test_serial_monitor_disables_fields(self): """Tests that opening the serial monitor disables the port and baudrate fields.""" - open_serial_monitor_button = self.get_element(By.ID, 'cb_cf_serial_monitor_connect') + open_serial_monitor_button = self.get_element(By.ID, + 'cb_cf_serial_monitor_connect') open_serial_monitor_button.click() WebDriverWait(self.driver, FLASH_TIMEOUT).until( @@ -146,9 +162,40 @@ def test_add_projectfile_direct(self): ) assert 'test_file.txt' in self.driver.page_source + def test_rename_projectfile_direct(self): + # Tests that a file that was added to project can be renamed + rename_button = self.get_element(By.CSS_SELECTOR, + '#files_list .rename-file-button') + rename_button.click() + WebDriverWait(self.driver, VERIFY_TIMEOUT).until( + expected_conditions.visibility_of( + self.get_element(By.ID, "filenameModal") + ) + ) + filename = self.driver.find_element_by_id("newFilename") + filename.clear() + filename.send_keys('test.txt') + save_button = self.get_element(By.ID, 'renamebutton') + save_button.click() + operation_output = self.get_element(By.ID, "operation_output") + assert 'File successfully renamed.' in operation_output.text + WebDriverWait(self.driver, VERIFY_TIMEOUT).until( + expected_conditions.invisibility_of_element_located( + (By.ID, "filenameModal") + ) + ) + assert 'test.txt' in self.driver.page_source + def test_delete_file(self): """Tests file delete modal """ - delete_file_button = self.get_element(By.CLASS_NAME, 'delete-file-button') + WebDriverWait(self.driver, VERIFY_TIMEOUT).until( + expected_conditions.element_to_be_clickable( + (By.CSS_SELECTOR, + '#files_list .delete-file-button .icon-remove') + ) + ) + delete_file_button = self.get_element(By.CSS_SELECTOR, + '#files_list .delete-file-button .icon-remove') delete_file_button.click() WebDriverWait(self.driver, VERIFY_TIMEOUT).until( expected_conditions.visibility_of( diff --git a/tests/common/user_home/test_user_home.py b/tests/common/user_home/test_user_home.py index 1a19364..ad96f70 100644 --- a/tests/common/user_home/test_user_home.py +++ b/tests/common/user_home/test_user_home.py @@ -1,6 +1,6 @@ from selenium.webdriver.common.by import By import pytest - +from codebender_testing import config from codebender_testing.config import TEST_DATA_INO from codebender_testing.config import TEST_DATA_ZIP from codebender_testing.utils import SeleniumTestCase @@ -57,3 +57,90 @@ def test_upload_project_zip(self): sketch_upload_zip = self.get_element(By.CSS_SELECTOR, '#upload-sketch-zip') sketch_upload_zip.click() self._upload_test('#dropzoneForm', TEST_DATA_ZIP, sketch_name='upload_zip') + + def test_create(self): + self.create_sketch('public' , 'publicSketch1', 'short description') + self.open("/") + # Test that short description is present. + sketch = self.find('#project_list > li .sketch-block-title > a') + assert self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-creation-container \ + .sketch-block-creation') + assert self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-description \ + .sketch-block-short-description') + # Test that when a sketch is created, + # "created" appears next to its title. + sketch = self.find('#project_list > li .sketch-block-title > a') + assert self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-creation-container \ + .sketch-block-creation').text == \ + "created a few seconds ago" + + def test_modify(self): + driver = self.driver + sketch = self.find('#project_list > li .sketch-block-title > a').click() + self.change_short_description_editor('') + self.get_element(By.ID, "save").click() + self.open("/") + # Test that when a sketch is modified, + # "modified" appears next to its title. + sketch = self.find('#project_list > li .sketch-block-title > a') + assert self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-creation-container \ + .sketch-block-creation').text == \ + "modified a few seconds ago" + + def test_share(self): + # Test the Share button. + sketch = self.find('#project_list > li .sketch-block-title > a') + self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-controls \ + .fa-share-square-o').click() + # Check that share modal opens and you can click Embed tab. + self.get_element(By.CSS_SELECTOR, + '#share-modal .fa-code ').click() + assert self.get_element(By.CSS_SELECTOR, + '#share-modal .active > a ').text == "Embed" + # Check that you can click Share tab inside share modal. + self.get_element(By.CSS_SELECTOR, + '#share-modal .fa-share-square-o ').click() + assert self.get_element(By.CSS_SELECTOR, + '#share-modal .active > a ').text == "Share" + # Close share modal. + self.get_element(By.CSS_SELECTOR, + '#share-modal .modal-close-button').click() + WebDriverWait(self.driver, 30).until( + expected_conditions.invisibility_of_element_located( + (By.ID, "#share-modal") + ) + ) + + def test_clone(self): + # Test the Clone button. + sketch = self.find('#project_list > li .sketch-block-title > a') + self.get_element(By.CSS_SELECTOR, + '#project_list > li .sketch-block-controls .fa-clone').click() + self.get_element(By.ID, "save") + self.get_element(By.ID,"logo_small").click() + sketches = self.find_all('#project_list > li .sketch-block-title > a') + projects = [] + for sketch in sketches: + projects.append(sketch.text) + for project in projects: + if 'copy' in project: + assert self.get_element(By.CSS_SELECTOR, + '.sketch-block-cloned-from').text == \ + "Cloned from Sketch publicSketch1 by demo_user" + + def test_delete(self, tester_login): + try: + sketches = self.find_all('#project_list > li \ + .sketch-block-title > a') + projects = [] + for sketch in sketches: + projects.append(sketch.text) + for project in projects: + self.delete_project(project) + except: + print 'No sketches found'