def test_add_creative_without_site(client, fill_db): ''' Checks if when we save advert without custom landing page, no Site object related to advert is saved to db. ''' utils.go_to_step(client, 'creatives', existing=True) client.click_on_button('add') container = client.find_elements_by_class_name('-t-creatives-widget')[-1] add_creative_btn = container.find_element_by_class_name('-t-button-creative-storage') client.click(add_creative_btn) client.wait_for_tray('trayCreatives') creatives = client.find_elements_by_css_selector('.-creatives-list li') # check random creative from tray creative = choice(creatives) creative.click() creative_name = creative.text # save strategy and return to the creatives utils.save_and_return_to_step(client) # check db if advert without Site object is created strategy = Strategy.objects.all()[0] adverts = strategy.advert_set.filter( is_deleted=False, creative__name=creative_name, landing_site__isnull=True ) assert adverts.count() == 1
def test_audiences_widget(client, fill_db): ''' Tests adding and removing audiences from targeting, both in UI and DB ''' def get_audiences_from_widget(client): return client.find_elements_by_css_selector('.-t-audience-list li') def get_audiences_from_tray(client): return client.find_elements_by_css_selector('.-t-audience-tray-list li') utils.go_to_step(client, 'targeting', existing=True) # Set default timeout for this test. client.implicitly_wait(1) # Check if widget is created empty display_widget(client, 'Audiences') widget_class = '-t-widget-%s-%s' % ('include', 'Audiences') audiences_included = get_audiences_from_widget(client) assert len(audiences_included) == 0 # Check if audiences in tray are displayed client.click_on_class('add-audience') client.wait_for_tray('trayAudiences') audiences_in_tray = get_audiences_from_tray(client) audiences_tray_names = [a.text for a in audiences_in_tray] audiences_from_db = Audience.objects.values_list('name', flat=True) assert sorted(audiences_tray_names) == sorted(audiences_from_db) # Check selecting an audience selected_audience = audiences_in_tray[0] selected_name = selected_audience.text client.click(selected_audience) audiences_included = get_audiences_from_widget(client) assert len(audiences_included) == 1 assert audiences_included[0].text == selected_name # Check saving audience utils.save_and_return_to_step(client) audiences_included = get_audiences_from_widget(client) assert len(audiences_included) == 1 assert audiences_included[0].text == selected_name # Check removing audience audiences_included = get_audiences_from_widget(client) remove_button = audiences_included[0].find_element_by_class_name('remove') client.click(remove_button) audiences_included = get_audiences_from_widget(client) assert len(audiences_included) == 0 utils.save_and_return_to_step(client) assert client.is_present_in_DOM(widget_class) is False
def test_creative_delete(client, fill_db): REMOVED_CREATIVE = {} utils.go_to_step(client, 'creatives', existing=True) client.wait_until_displayed('-t-creatives-container') # Store info about first add in strategy REMOVED_CREATIVE['name'] = client.get_input('creative-name').get_attribute('value') REMOVED_CREATIVE['landing_site'] = client.get_input('landing-site').get_attribute('value') REMOVED_CREATIVE['js-code'] = client.get_input('js-code').get_attribute('value') # Remove first ad client.click_on_class('remove-ad') # Save strategy utils.save_and_return_to_step(client) # Check UI and db adverts_data = get_adverts_data(client) assert REMOVED_CREATIVE not in adverts_data strategy = Strategy.objects.all()[0] removed_adverts = strategy.advert_set.filter( is_deleted=True, creative__name=REMOVED_CREATIVE['name'], landing_site__url=REMOVED_CREATIVE['landing_site'], js_code=REMOVED_CREATIVE['js-code'] ) assert removed_adverts.count() == 1
def test_landing_pages_correct(client, fill_db): ''' Inserts valid landing pages data, checks if they are saved and displayed correctly ''' correct_landing_pages = [ {'url': 'http://www.wykop.pl', 'weight': 10}, {'url': 'http://www.gazeta.pl', 'weight': 30}, ] utils.go_to_step(client, 'landing', existing=True) client.implicitly_wait(1) # Remove all lading pages and test if validation error is raised on save input_row_class = '-t-landing-page-row' input_rows = client.find_elements_by_class_name(input_row_class) for row in input_rows: client.click(client.find_element_by_class_name('remove')) for index in range(0, len(correct_landing_pages)): client.click_on_button('add') input_rows = client.find_elements_by_class_name(input_row_class) page_class = '-t-input-landing-page' weight_class = '-t-input-weight' for i, page in enumerate(correct_landing_pages): row = input_rows[i] url_input = row.find_element_by_class_name(page_class) weight_input = row.find_element_by_class_name(weight_class) url_input.clear() weight_input.clear() url_input.send_keys(page['url']) weight_input.send_keys(page['weight']) utils.save_and_return_to_step(client) strategy = Strategy.objects.all()[0] landing_pages = strategy.landing_sites.all() assert len(landing_pages) == len(correct_landing_pages) # Check sites in DB for page in correct_landing_pages: match_url_and_ratio = lambda el: (el.site.url == page['url']) and (el.ratio == page['weight']) matched_sites = filter(match_url_and_ratio, landing_pages) assert len(matched_sites) == 1 # Check sites in UI input_rows = client.find_elements_by_class_name(input_row_class) for row in input_rows: displayed_url = row.find_element_by_class_name(page_class).get_attribute('value') displayed_weight = row.find_element_by_class_name(weight_class).get_attribute('value') match_url_and_ratio = lambda el: (el['url'] == displayed_url) and (str(el['weight']) == displayed_weight) matched_sites = filter(match_url_and_ratio, correct_landing_pages) assert len(matched_sites) == 1
def test_creative_edit(client, fill_db): CREATIVE = { 'landing_site': 'http://test.site.com', 'JSCode': 'console.log("no code tehre")', } utils.go_to_step(client, 'creatives', existing=True) # Edit first of creatives (choose one from storage) client.click_on_button('creative-storage') client.wait_for_tray('trayCreatives') creatives = client.find_elements_by_css_selector('.-creatives-list li') # check random creative from tray creative = choice(creatives) creative.click() CREATIVE['name'] = creative.text # Try to type invalid site client.send_keys('landing-site', 'not a site in fact', clear=True) client.click_on_class('button-save-changes') client.wait_for_modal() assert SITE_ERROR in client.get_modal_errors() client.close_modal() # Change landing site and jscode for proper ones client.send_keys('landing-site', CREATIVE['landing_site'], clear=True) # Show custom tracker client.click_on_class('show-js-code-input') client.send_keys('js-code', CREATIVE['JSCode'], clear=True, focus_css_selector=False) # save strategy and return to the creatives utils.save_and_return_to_step(client) # check UI and DB assert CREATIVE['name'] == client.get_input('creative-name').get_attribute('value') assert CREATIVE['landing_site'] == client.get_input('landing-site').get_attribute('value') assert CREATIVE['JSCode'] == client.get_input('js-code').get_attribute('value') strategy = Strategy.objects.all()[0] adverts = strategy.advert_set.filter( is_deleted=False, creative__name=CREATIVE['name'], landing_site__url=CREATIVE['landing_site'], js_code=CREATIVE['JSCode'] ) assert adverts.count() == 1
def test_strategy_overall(client, fill_db): ''' Tries to save strategy without required fields, Check errors and fill inputs with proper data. Saves strategy and checks UI and DB. ''' EDITED_STRATEGY = { 'name': 'Strategy Edited', 'budget': '50', 'budget-daily': '20', } ERRORS = { 'name_required': 'Strategy name: This field is required.', 'budget_required': 'Total strategy budget: This field is required.', } INPUTS = ['name', 'budget', 'budget-daily'] utils.go_to_step(client, 'overall', existing=True) # Clear all inputs for input_name in INPUTS: client.get_input(input_name).clear() # Try to save and check if correct errors are displayed client.click_on_class('button-save-changes') client.wait_for_modal() modal_errors = client.get_modal_errors() assert len(modal_errors) == len(ERRORS) for key in ERRORS.keys(): assert ERRORS[key] in modal_errors client.close_modal() # Fill inputs with proper data for input_name in INPUTS: client.send_keys(input_name, EDITED_STRATEGY[input_name]) # Save again and check UI and DB utils.save_and_return_to_step(client) for input_name in INPUTS: assert client.get_input_val(input_name) == EDITED_STRATEGY[input_name] strategy = Strategy.objects.all()[0] assert strategy.name == EDITED_STRATEGY['name'] assert strategy.budget_total == Decimal(EDITED_STRATEGY['budget']) assert strategy.budget_daily == Decimal(EDITED_STRATEGY['budget-daily'])
def test_add_video_creative(client, video_db): """Checks saving strategy with video creative""" VIDEO_CREATIVE_NAME = 'creative_video_1' utils.go_to_step(client, 'creatives', existing=True) creative_edit = StrategyPage(client) # Check if both trackers are hidden creative_edit.assert_both_textareas_hidden() # Add a custom JS tracker creative_edit.show_js_code() creative_edit.js_code.send_keys('console.log("some tracker")') # Choose video creative from storage, check if its name is displayed in input creative_edit.remove_button.click() creative_edit.select_creative_from_tray(VIDEO_CREATIVE_NAME) creative_edit.check_name(VIDEO_CREATIVE_NAME) # Custom JS tracker should be cleared and disabled creative_edit.show_js_code(should_fail=True) assert creative_edit.js_code.get_attribute('value') == '' # Save and check if changes have been saved utils.save_and_return_to_step(client) creative_edit.refresh() creative_edit.check_name(VIDEO_CREATIVE_NAME) strategy_name = client.find_element_by_class_name('-t-name').text strategy = Strategy.objects.get(name=strategy_name) adverts = strategy.advert_set.filter( is_deleted=False, creative__name=VIDEO_CREATIVE_NAME ) assert adverts.count() == 1
def test_upload_creative(client, fill_db, creative_name, creative_type): """Upload creative from strategy edit""" CREATIVE_PATH = os.path.abspath(os.path.join( os.path.dirname(__file__), '..', 'uploads', creative_name )) utils.go_to_step(client, 'creatives', existing=True) strategy_name = client.find_element_by_class_name('-t-name').text strategy = Strategy.objects.get(name=strategy_name) # Creative with this name should not exist with pytest.raises(Advert.DoesNotExist): strategy.advert_set.get( is_deleted=False, creative__name=creative_name ) creative_edit = StrategyPage(client) creative_edit.remove_button.click() # Upload file and check if its name is displayed in form creative_edit.upload_file(CREATIVE_PATH) def file_name_updated(): return creative_edit.file_name.get_attribute('value') == creative_name client.wait_until(file_name_updated) # Save and check if creative has been created utils.save_and_return_to_step(client) creative_edit.refresh() creative_edit.check_name(creative_name) advert = strategy.advert_set.get( is_deleted=False, creative__name=creative_name ) assert advert.creative.type == creative_type
def test_automatic_bidding(client, bidding_db): """Check automatic bidding behaviour.""" utils.go_to_step(client, 'bidding', existing=True) # Check automatic bid price client.click_on_class('automatic-bid-price') utils.save_and_return_to_step(client) strategy = Strategy.objects.all()[0] strategy_pk = strategy.pk assert strategy.is_automatically_bidded is True assert strategy.optimized_metric == CPC.__name__ # move default metric to the end. metrics = list(OPTIMIZATION_METRICS) if metrics[0][0] == strategy.optimized_metric: cpc_metric = metrics.pop(0) metrics.append(cpc_metric) for metric, description in metrics: # let us check description first if correct element_description = client.get_content_elem('-metric-description-' + metric.lower()) assert client.get_by_tagname(element_description, 'span').text == description assert client.get_by_tagname(element_description, 'strong').text == metric # first metric will be set up, we'll go to others first to check, # and first should be checked last one control_element = client.get_content_elem('-controls-' + metric.lower()) client.click(client.get_by_tagname(control_element, 'span')) utils.save_and_return_to_step(client) strategy = Strategy.objects.get(pk=strategy_pk) assert strategy.is_automatically_bidded is True assert strategy.optimized_metric == metric
def test_audience_validation(client, fill_db): ''' Tests if Django validation rejects the same audience in includes and exludes section ''' def add_audience_to_widget(client, widget): add_button = widget.find_element_by_class_name('-t-add-audience') client.click(add_button) client.wait_for_tray('trayAudiences') audiences = client.find_elements_by_css_selector('.-t-audience-tray-list li') selected_audience = audiences[0] client.click(selected_audience) client.wait_for_tray('trayAudiences', hide=True) utils.go_to_step(client, 'targeting', existing=True) include_widget = display_widget(client, 'Audiences') exclude_widget = display_widget(client, 'Audiences', section="exclude") add_audience_to_widget(client, include_widget) add_audience_to_widget(client, exclude_widget) client.click_on_class('button-save-changes') modal = client.find_element_by_id('modal') assert client.has_class(modal, 'in') is True
def test_landing_pages_incorrect(client, fill_db): ''' Inserts invalid or none landing pages, checks if validation errors are raised ''' def get_row_count(): return len(client.find_elements_by_class_name('-t-landing-page-row')) ERRORS = { 'landing_page_invalid_format': 'Landing pages: some landing pages are in incorrect format.' } empty_page_count = 2 utils.go_to_step(client, 'landing', existing=True) client.implicitly_wait(1) # Remove all lading pages input_row_class = '-t-landing-page-row' input_rows = client.find_elements_by_class_name(input_row_class) for row in input_rows: row_count_before = get_row_count() client.click(client.find_element_by_class_name('remove')) row_count_after = get_row_count() # Add empty landing pages and check if they are saved correctly for index in range(0, empty_page_count): row_count_before = get_row_count() client.click_on_button('add') row_count_after = get_row_count() assert row_count_after == row_count_before + 1 # Check if validation rejects empty urls modal_errors = utils.save_with_errors(client) assert ERRORS['landing_page_invalid_format'] in modal_errors
def test_userprofile_widget(client, fill_db): ''' Tries to save data using checkboxes in User Profile widget. Checks if checkbox and tags are correctly saved in UI and if data are stored in DB. Then tries if when we remove widget data is also removed from DB. ''' # Representant values AGE_GROUPS = [t.value for t in TargetValue.objects.representants().filter(category=dimensions.age_group)] GENDERS = [t.value for t in TargetValue.objects.representants().filter(category=dimensions.gender)] utils.go_to_step(client, 'targeting', existing=True) widget = display_widget(client, 'UserProfile') # Checked values are stored here checked_age_groups = [] checked_genders = [] # Test both groups in widget for group_name, representants, checked in [ ('Age Group', AGE_GROUPS, checked_age_groups), ('Gender', GENDERS, checked_genders)]: group = get_group(widget, group_name) checkboxes = group.find_elements_by_class_name('-t-checkbox') # Check if labels text comes from representants for checkbox in checkboxes: checkbox.location_once_scrolled_into_view label = checkbox.find_element_by_css_selector('span') assert label.text in representants # Click random checkboxes labels_to_check = sample(representants, randrange(1, len(representants) + 1)) checked.extend(labels_to_check) for label in labels_to_check: click_checkbox(group, label) # Check if tags are correct all_checked = checked_age_groups + checked_genders tags = get_tags(widget) assert sorted(tags) == sorted(all_checked) utils.save_and_return_to_step(client) # Check if widget is displayed client.wait_until_displayed('-t-widget-include-UserProfile') widget = client.find_element_by_class_name('-t-widget-include-UserProfile') assert widget.is_displayed() is True strategy = Strategy.objects.all()[0] # Check displayed data and tags for group_name, category, checked in [ ('Age Group', dimensions.age_group, checked_age_groups), ('Gender', dimensions.gender, checked_genders)]: group = get_group(widget, group_name) for checkbox in group.find_elements_by_class_name('-t-checkbox'): checkbox.location_once_scrolled_into_view label = checkbox.find_element_by_css_selector('span') if label.text in checked: client.has_class(checkbox, 'checked') is True else: client.has_not_class(checkbox, 'checked') is True # Check database saved_tvalues = [tv.value for tv in strategy.targeting_values.filter(category=category)] assert sorted(checked) == sorted(saved_tvalues) tags = get_tags(widget) assert sorted(tags) == sorted(all_checked) client.click_on_class('widget-include-UserProfile-remove') utils.save_and_return_to_step(client) # Check if widget is not displayed and data is not in DB assert client.is_present_in_DOM('-t-widget-include-UserProfile') is False strategy = Strategy.objects.all()[0] assert strategy.targeting_values.representants().count() == 0
def test_bidding_step(client, bidding_db): OVERLAP_ERROR = "Bidding: bidding periods can't overlap each other." END_BEFORE_START_ERROR = "Bidding: period start can't be later than end." QUARTERS_ERROR = 'Bidding: Minutes must be multiples of quarter of an hour.' BID_REQUIRED_ERROR = 'Default CPM bid: This field is required.' BID_PRICE = '0.50' BID_PERIODS = ['08:00:00 - 12:00:00', '12:45:00 - 13:15:00', '13:15:00 - 13:30:00'] BID_MAPPING = { 'Creative Default': { 'type': 'default' }, 'Creative Custom': { 'type': 'custom', 'value': '2' }, 'Creative Parted': { 'type': 'day_parting', 'value': '1.50' } } utils.go_to_step(client, 'bidding', existing=True) client.wait_until_displayed('-t-bidding-container') client.get_input('bid').clear() client.click_on_class('enable-dayparting') add_bid_periods(client, [ ('08:00', '12:00'), ('12:45', '13:15'), ('13:00', '13:15'), # Overlapping periods ('13:15', '13:30'), ('14:00', '14:59'), # Minutes not multiple of 15 ('17:00', '00:00') # End before start ]) client.click_on_class('button-save') client.wait_for_modal() # Check if notification about default bid is shown assert BID_REQUIRED_ERROR in client.get_modal_errors() # Check if overlapping is detected assert OVERLAP_ERROR in client.get_modal_errors() # Check if end before start is detected assert END_BEFORE_START_ERROR in client.get_modal_errors() # Check if not quarter multiple minutes are detected assert QUARTERS_ERROR in client.get_modal_errors() client.close_modal() # Set default bid client.send_keys('bid', BID_PRICE) # Remove invalid periods remove_bid_period(client, '13:00 - 13:15') remove_bid_period(client, '14:00 - 14:59') remove_bid_period(client, '17:00 - 00:00') set_creative_bid_values(client, BID_MAPPING) # save strategy utils.save_and_return_to_step(client) check_creative_bid_values(client, BID_MAPPING) # check database strategy = Strategy.objects.all()[0] adverts = strategy.advert_set.all() check_strategy_adverts_in_database( strategy, adverts, BID_MAPPING, BID_PERIODS ) # check if creative prated value is zero # is should be zero. BID_MAPPING["Creative Parted"]["value"] = '0' set_creative_bid_values(client, BID_MAPPING) # save strategy utils.save_and_return_to_step(client) check_creative_bid_values(client, BID_MAPPING) # check database strategy = Strategy.objects.all()[0] adverts = strategy.advert_set.all() assert strategy.budget_bid_CPM == Decimal(BID_PRICE) assert strategy.is_automatically_bidded is False assert strategy.optimized_metric is None check_strategy_adverts_in_database( strategy, adverts, BID_MAPPING, BID_PERIODS )
def test_text_search_widgets(client, fill_db_mobile_strategies, representants): ''' Tries to select all available text search widgets, make manipulation with them (choosing/removing options) and saving strategy. Then it removes widgets and tests if they are not present in UI and if values are not stored in db. ''' def check_tags_boxes(widget, widget_data): tags = get_tags(widget) boxes = get_tags(widget, from_box=True) assert sorted(tags) == sorted(widget_data['checked']) assert sorted(boxes) == sorted(widget_data['checked']) SEARCH_WIDGETS = { # KANARY-2206 # 'Device': { # 'category': dimensions.g_device, # 'checked': [], # }, 'Location': { 'category': dimensions.g_location, 'checked': [], }, 'Os': { 'category': dimensions.g_os, 'checked': [], }, 'Carrier': { 'category': dimensions.carrier, 'checked': [], }, } utils.go_to_step(client, 'targeting', existing=True) # Set default timeout for this test. client.implicitly_wait(1) include_target_btn = client.find_element_by_class_name('-t-include-target') for widget_name, widget_data in SEARCH_WIDGETS.iteritems(): include_target_btn.click() client.click_on_class('-t-include-%s' % widget_name, bare=True) client.wait_until_displayed('-t-widget-include-%s' % widget_name) # Scroll down to the button to make sure suggestions are visible include_target_btn.location_once_scrolled_into_view widget = client.find_element_by_class_name( '-t-widget-include-%s' % widget_name ) target_values = TargetValue.objects.representants()\ .filter(category=widget_data['category'])\ .order_by('value')\ .distinct('value') values_count = target_values.count() # pick some random values from representants (we will type them in widget) random_values = sample(target_values, randrange(2, values_count)) input_field = client.find_element_by_class_name( '-t-input-include-%s' % widget_name ) for tv in random_values: # Scroll down to the button to make sure suggestions are visible include_target_btn.location_once_scrolled_into_view # type in input lowest_hierarchy = tv.value_list[-1] value_to_type = lowest_hierarchy[:3].lower() input_field.send_keys(value_to_type) # wait for dropdown appearance client.wait_until_displayed('tt-dataset-%s' % widget_name) # click in dropdown first suggestion client.click_on_class('value-%s' % widget_name) widget_data['checked'].append(lowest_hierarchy) check_tags_boxes(widget, widget_data) utils.save_and_return_to_step(client) # Check all widgets data and remove each widget for widget_name, widget_data in SEARCH_WIDGETS.iteritems(): client.wait_until_displayed('-t-widget-include-%s' % widget_name) widget = client.find_element_by_class_name( '-t-widget-include-%s' % widget_name ) assert widget.is_displayed() check_tags_boxes(widget, widget_data) # check database strategy = Strategy.objects.all()[0] saved_repr = [tv.value_list[-1] for tv in strategy.targeting_values.representants().filter( category=widget_data['category'])] assert sorted(saved_repr) == sorted(widget_data['checked']) # remove widget client.click_on_class('widget-include-%s-remove' % widget_name) utils.save_and_return_to_step(client) # for each widget check if db is empty and if it is not displayed for widget_name, widget_data in SEARCH_WIDGETS.iteritems(): assert client.is_present_in_DOM('-t-widget-include-%s' % widget_name) is False strategy = Strategy.objects.all()[0] assert strategy.targeting_values.representants().filter(category=widget_data['category']).count() == 0
def test_add_creative_custom_tracker_tracking_pixel(client, fill_db): ''' Test Custom Pixel and JS Code textareas' states interaction and validation of Custom Pixel URL. Both textareas are hidden by default. ''' JS_CODE = ' (function(){ }()); ' PIXEL_URL = ' http://pixel.example.com ' utils.go_to_step(client, 'creatives', existing=True) creative_edit = StrategyPage(client) # The labels should have default values for empty textareas. assert creative_edit.js_code_label.text == 'Add tracking' assert creative_edit.custom_pixel_label.text == 'Add tracking' creative_edit.assert_both_textareas_hidden() creative_edit.check_toggling() # No input - should toggle freely. # 1. Add whitespace to both inputs. It should be ignored. Test label text (Add/Hide). creative_edit.show_js_code() creative_edit.js_code.send_keys(' \n\n ') creative_edit.show_custom_pixel() # Close JS code textarea and open custom pixel. creative_edit.custom_pixel.send_keys(' \n\n ') creative_edit.hide_custom_pixel() creative_edit.check_toggling() # Whitespace-only input should be ignored and toggling should be possible. # 2. Add some non-whitespace chars to the custom pixel input. It should not be possible to # switch the textarea now. It should be possible to hide it. creative_edit.show_js_code() creative_edit.js_code.send_keys(JS_CODE) # Will also contain prevoiusly inserted whitespace - that's OK. # Now it should not be possible to open the other textarea. creative_edit.show_custom_pixel(should_fail=True) creative_edit.hide_js_code() # With JS code hidden try to open custom pixel textarea again: creative_edit.show_custom_pixel(should_fail=True) # 3. Save the campaign. JS code should be stripped and saved. utils.save_and_return_to_step(client) creative_edit.refresh() creative_edit.assert_both_textareas_hidden() creative_edit.show_js_code() assert creative_edit.js_code.get_attribute('value') == JS_CODE.strip() # 4. Entering custom pixel should fail because we have JS code inserted. creative_edit.show_custom_pixel(should_fail=True) creative_edit.js_code.clear() # Clear JS code textarea. assert creative_edit.js_code.get_attribute('value') == '' # 5. Now with JS code empty it should be possible to insert custom tracker. creative_edit.show_custom_pixel() creative_edit.custom_pixel.send_keys('My home page') # Invalid URL. creative_edit.show_js_code(should_fail=True) creative_edit.hide_custom_pixel() client.click_on_class('button-save-changes') # Error modal should appear. Handle the error. client.wait_for_modal() assert SITE_ERROR in client.get_modal_errors() client.close_modal() assert 'input-error' in creative_edit.custom_pixel.get_attribute('class') creative_edit.show_custom_pixel() creative_edit.custom_pixel.clear() creative_edit.custom_pixel.send_keys(PIXEL_URL) # Insert a valid URL. utils.save_and_return_to_step(client) creative_edit.refresh() creative_edit.assert_both_textareas_hidden() creative_edit.show_custom_pixel() assert creative_edit.custom_pixel.get_attribute('value') == PIXEL_URL.strip()
def test_brand_protection_widget(client, fill_db): ''' Tests adding and removing brand protection widget ''' strategy = fill_db.models['strategy']['Hello this is Citrus'] utils.go_to_step(client, 'targeting', existing=True) # checking additional data costs counter costs = client.find_element_by_class_name('-t-additional-data-costs') assert costs.text == '-' # adding BRAND PROTECTION widget to INCLUDE section widget_type = 'ProximicBrandProtection' widget = display_widget(client, widget_type) # additional data costs shouldn't be added client.wait_until(lambda: costs.text == '-') # checking if all segments are available and not selected segments = SegmentProximicMaturityRating.objects.all()\ .values_list('name', flat=True) for segment in segments: el = client.find_element_by_class_name('-t-protection-%s' % segment) assert el assert 'active' not in el.get_attribute('class').split() # checking segments in database assert strategy.segment_proximic_maturity_rating.all().count() == 0 # selecting PG-13 segment client.click_on_class('protection-PG13') # checking additional data costs counter client.wait_until(lambda: costs.text == '$%s' % constants.TARGETING_ADDITIONAL_DATA_COSTS['brand_protection']) # checking label in widget's header assert 'PG13' in get_tags(widget) # saving campaign utils.save_and_return_to_step(client) # check segment in database segments_in_db = strategy.segment_proximic_maturity_rating.all() client.wait_until(lambda: segments_in_db.count() == 1) # checking if PG-13 segment is already selected segment = client.find_element_by_class_name('-t-protection-PG13') assert client.has_class(segment, 'active') # checking label in widget's header widget_class = '-t-widget-include-%s' % widget_type widget = client.find_element_by_class_name(widget_class) assert 'PG13' in get_tags(widget) # removing widget client.click_on_class('widget-include-%s-remove' % widget_type) # checking if widget has been removed assert client.is_present_in_DOM(widget_class) is False # checking additional data costs counter costs = client.find_element_by_class_name('-t-additional-data-costs') client.wait_until(lambda: costs.text == '-') # saving campaign utils.save_and_return_to_step(client) # check if widget is removed after save assert client.is_present_in_DOM(widget_class) is False # checking segments in database client.wait_until(lambda: segments_in_db.count() == 0)
def test_tree_widget(client, fill_db, widget_name, get_category_tree, get_strategy_categories, section='include'): ''' Test editing content categories :param string widget_name: Name of widget, defined in strategy.js :param function get_category_tree: Function retrieving list of categories with subcategories :param function get_strategy_categories: A function retrieving saved categories from strategy :param string section: Name of targeting section widget will be added to. Can equal 'include' or 'exclude' ''' def scroll_to_bottom(): ''' Make sure that entire category list is visible by scrolling to a button under the widget ''' btn_class = '-t-%s-dropdown' % section include_btn = client.find_element_by_class_name(btn_class) include_btn.location_once_scrolled_into_view def find_by_text(elements, text): ''' Find first WebElement with a matching text :param list elements: List of WebElements :param string text: Text to match ''' def by_text(element): element.location_once_scrolled_into_view return element.text == text matches = filter(by_text, elements) return matches[0] def expand(node): ''' Expand tree node, return list of child nodes :param WebElement node: Element containing collapse handle and node name :returns Element containing list of child nodes :rtype WebElement ''' expand_btn = node.find_element_by_class_name('collapsed') subelements_selector = expand_btn.get_attribute('data-target') client.click(expand_btn) scroll_to_bottom() client.wait_until_displayed(subelements_selector, by="css_selector") return client.find_element_by_css_selector(subelements_selector) def collapse(node): ''' Collapse tree node :param WebElement node: Element containing collapse handle and node name ''' collapse_btn = node.find_element_by_class_name('collapse-handle') subelements_selector = collapse_btn.get_attribute('data-target') client.click(collapse_btn) client.wait_until_displayed(subelements_selector, by="css_selector", until=False) # Select 1 random main category, and 1 random subcategory that is # not included in the first category. category_tree = get_category_tree() category_tree_with_children = filter(lambda c: c.get('children'), category_tree) strategy = Strategy.objects.all().first() WIDGET_CLASS = '-t-widget-%s-%s' % (section, widget_name) random_main_categories = sample(category_tree_with_children, 2) first_category = random_main_categories[0] second_category = random_main_categories[1] SELECTED_CATEGORIES = [] SELECTED_MAIN_CATEGORY = first_category['name'] SELECTED_MAIN_CATEGORY_CHILDREN = [c['name'] for c in first_category['children']] SELECTED_SUBCATEGORY_PARENT = second_category['name'] SELECTED_SUBCATEGORY = choice(second_category['children'])['name'] # Store categories that should be saved to DB SELECTED_CATEGORIES.append(SELECTED_MAIN_CATEGORY) SELECTED_CATEGORIES.extend(SELECTED_MAIN_CATEGORY_CHILDREN) SELECTED_CATEGORIES.append(SELECTED_SUBCATEGORY) IS_ONLY_SUBCATEGORY = (len(second_category['children']) == 1) if IS_ONLY_SUBCATEGORY: SELECTED_CATEGORIES.append(SELECTED_SUBCATEGORY_PARENT) # Go to targeting, display widget and make sure it is fully visible utils.go_to_step(client, 'targeting', existing=True) widget = display_widget(client, widget_name, section=section) scroll_to_bottom() # Check if all main categories are displayed. main_tree_elements = [e for e in widget.find_elements_by_class_name('-t-tree-main') if e.text] ui_main_categories = [e.text for e in main_tree_elements if e.text] main_categories = [c['name'] for c in category_tree] assert ui_main_categories == main_categories # Check if all subcategories are displayed after expanding main category. # Check if they are hidden when category is collapsed. for index, category in enumerate(category_tree): subcategories = [c['name'] for c in category.get('children', [])] ui_main_category = main_tree_elements[index] collapse_handle = ui_main_category.find_element_by_class_name('tree-handle') ui_subcategories = [] if 'collapsed' in collapse_handle.get_attribute('class'): ui_main_category.location_once_scrolled_into_view subcategory_ul = expand(ui_main_category) subcategory_ul.location_once_scrolled_into_view for e in subcategory_ul.find_elements_by_class_name('-t-checkbox'): if not e.is_displayed(): e.location_once_scrolled_into_view # Filter undisplayed subelements if e.is_displayed() and e.text: ui_subcategories.append(e.text) collapse(ui_main_category) assert sorted(ui_subcategories) == sorted(subcategories) # Select a random main category. Check if main and subcategory # checkboxes state change. ui_main_category = find_by_text(main_tree_elements, SELECTED_MAIN_CATEGORY) subcategory_ul = expand(ui_main_category) main_checkbox = client.get_checkbox(ui_main_category) assert client.is_not_checked(main_checkbox) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') for checkbox in sub_checkboxes: assert client.is_not_checked(checkbox) main_checkbox.click() assert client.is_checked(main_checkbox) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') for checkbox in sub_checkboxes: assert client.is_checked(checkbox) collapse(ui_main_category) # Select a random subcategory. Check if checkbox state chenges. ui_subcategory_parent = find_by_text(main_tree_elements, SELECTED_SUBCATEGORY_PARENT) main_checkbox = client.get_checkbox(ui_subcategory_parent) subcategory_ul = expand(ui_subcategory_parent) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') sub_checkbox = find_by_text(sub_checkboxes, SELECTED_SUBCATEGORY) # If there is only one child category, parent should be automatically checked if IS_ONLY_SUBCATEGORY: assert client.is_not_checked(main_checkbox) assert client.is_not_checked(sub_checkbox) client.click(sub_checkbox) if IS_ONLY_SUBCATEGORY: assert client.is_checked(main_checkbox) assert client.is_checked(sub_checkbox) collapse(ui_subcategory_parent) # Save strategy assert len(get_strategy_categories(strategy)) == 0 utils.save_and_return_to_step(client) # Check if all categories were saved to DB db_categories = [c.name for c in get_strategy_categories(strategy)] assert sorted(SELECTED_CATEGORIES) == sorted(db_categories) # 5. Check if they are displayed correctly, uncheck them and save widget = client.find_element_by_class_name(WIDGET_CLASS) scroll_to_bottom() main_tree_elements = widget.find_elements_by_class_name('-t-tree-main') ui_main_category = find_by_text(main_tree_elements, SELECTED_MAIN_CATEGORY) subcategory_ul = expand(ui_main_category) main_checkbox = client.get_checkbox(ui_main_category) assert client.is_checked(main_checkbox) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') for checkbox in sub_checkboxes: assert client.is_checked(checkbox) main_checkbox.click() assert client.is_not_checked(main_checkbox) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') for checkbox in sub_checkboxes: assert client.is_not_checked(checkbox) collapse(ui_main_category) # Uncheck the subcategory ui_subcategory_parent = find_by_text(main_tree_elements, SELECTED_SUBCATEGORY_PARENT) subcategory_ul = expand(ui_subcategory_parent) sub_checkboxes = subcategory_ul.find_elements_by_class_name('-t-checkbox') sub_parent_checkbox = client.get_checkbox(ui_subcategory_parent) sub_checkbox = find_by_text(sub_checkboxes, SELECTED_SUBCATEGORY) if IS_ONLY_SUBCATEGORY: assert client.is_checked(sub_parent_checkbox) assert client.is_checked(sub_checkbox) client.click(sub_checkbox) if IS_ONLY_SUBCATEGORY: assert client.is_not_checked(sub_parent_checkbox) assert client.is_not_checked(sub_checkbox) collapse(ui_subcategory_parent) utils.save_and_return_to_step(client) # Check if categories were removed from UI and DB assert len(get_strategy_categories(strategy)) == 0 assert client.is_present_in_DOM(WIDGET_CLASS) is False
def test_targeting_widgets_behavior(client): ''' Tests if all targeting widgets are selectable from include/exclude dropdown Checks if they are visible and if can be removed. ''' utils.go_to_step(client, 'overall') type_selector = client.find_element_by_class_name('-t-type') for option in type_selector.find_elements_by_tag_name('option'): if option.text == 'Mobile': option.click() targeting_step_class = 'sidebar-targeting' client.click_on_class(targeting_step_class) # Set default timeout for this test. client.implicitly_wait(1) include_dropdown, exclude_dropdown = get_dropdowns(client) include_target_btn = client.find_element_by_class_name('-t-include-target') exclude_target_btn = client.find_element_by_class_name('-t-exclude-target') # Test both for include and exclude dropdown for dropdown in [include_dropdown, exclude_dropdown]: if dropdown is include_dropdown: button = include_target_btn section = 'include' else: button = exclude_target_btn section = 'exclude' # Display all available widgets for widget_name in WIDGET_NAMES: widget_class = '-t-widget-%s-%s' % (section, widget_name) widget_dropdown_class = '-t-%s-%s' % (section, widget_name) widget_collapse_class = '-t-widget-%s-%s-collapse' % (section, widget_name) widget_expand_class = '-t-widget-%s-%s-expand' % (section, widget_name) widget_content_id = 'targeting-%s-%s' % (section, widget_name) # Dropdown should be closed assert client.has_not_class(dropdown, 'open') is True # Open the dropdown client.click(button) assert client.has_class(dropdown, 'open') is True # Check if dropdown contains a picked widget assert client.is_present_in_DOM(widget_dropdown_class) is True # Select widget and check if it appears. client.click_on_class(widget_dropdown_class, bare=True) client.wait_until_displayed(widget_class) # Check if dropdown does not contain a picked widget assert client.is_present_in_DOM(widget_dropdown_class) is False # Collapse widget widget_content = client.find_element_by_id(widget_content_id) collapse_btn = client.find_element_by_class_name(widget_collapse_class) expand_btn = client.find_element_by_class_name(widget_expand_class) assert widget_content.is_displayed() is True client.click_on_class(widget_collapse_class, bare=True) client.wait_for_widget_action(widget_content, expanded=False) assert client.has_not_class(widget_content, 'in') is True assert collapse_btn.is_displayed() is False assert expand_btn.is_displayed() is True # Expand widget client.click_on_class(widget_expand_class, bare=True) client.wait_for_widget_action(widget_content, expanded=True) assert client.has_class(widget_content, 'in') is True assert collapse_btn.is_displayed() is True assert expand_btn.is_displayed() is False # Remove all displayed widgets for widget_name in WIDGET_NAMES: widget_class = '-t-widget-%s-%s' % (section, widget_name) widget_dropdown_class = '-t-%s-%s' % (section, widget_name) widget_remove_class = '-t-widget-%s-%s-remove' % (section, widget_name) client.click_on_class(widget_remove_class, bare=True, scroll=True) # Check if widget is not displayed assert client.is_present_in_DOM(widget_class) is False # Check if dropdown does contains a picked widget assert client.is_present_in_DOM(widget_dropdown_class) is True
def test_include_exclude_validation(client): """Test if the same values cannot be included and excluded at the same time""" utils.go_to_step(client, 'targeting', existing=True) targeting_page = TargetingPage(client) def check_errors(*args): targeting_page.save() client.wait_for_modal() errors = client.get_modal_errors() for field_name in args: message = '{field_name}: Cannot include and exlude the same values'.format(field_name=field_name) assert message in errors client.close_modal() # Checkbox widgets single_column_widgets = [ ('PeerContextual', 'Peer contextual'), ('PeerPageQuality', 'Peer page quality'), ('PeerBrandProtection', 'Peer brand protection'), ('PeerLanguage', 'Peer page language'), ('LotameDemographic', 'Lotame demographic'), ('LotameAdvancedDemographic', 'Lotame advanced demographic'), ('LotameBehavioralInterest', 'Lotame behavioral interest'), ('LotameInfluencer', 'Lotame influencers'), ('LotameOffline', 'Lotame offline'), ('ProximicLanguage', 'Proximic language'), ('ProximicPageQuality', 'Proximic page quality'), ('ProximicPageNoticeability', 'Proximic page noticeability'), ('ProximicPagePlacement', 'Proximic page placement'), ('ProximicContextual', 'Proximic contextual') ] for widget_name, field_name in single_column_widgets: for section in (targeting_page.include, targeting_page.exclude): widget = section.add(widget_name) widget.checkboxes[0].click() check_errors(field_name) multiple_column_widgets = [ ('UserProfile', ['Age group', 'Gender']), ] for widget_name, fields in multiple_column_widgets: for section in (targeting_page.include, targeting_page.exclude): widget = section.add(widget_name) for group in widget.checkbox_groups: group[0].click() check_errors(*fields) # Custom widgets for section in (targeting_page.include, targeting_page.exclude): widget = section.add('ProximicBrandProtection') maturity = widget.container.find_element_by_class_name('btn-radio') client.click(maturity) safety_level = widget.checkboxes[0] client.click(safety_level) check_errors('Proximic maturity rating', 'Proximic safety level') # Search widgets search_widgets = [ ('Device', 'sony', 'Device'), ('Location', 'ohio', 'Location'), ('Os', 'android', 'Operating system'), ('Carrier', 'plus', 'Carrier') ] for widget_name, text_to_type, field_name in search_widgets: for section in (targeting_page.include, targeting_page.exclude): widget = section.add(widget_name) # Scroll down to the button to make sure suggestions are visible section._dropdown.location_once_scrolled_into_view # type in input widget.search_input.send_keys(text_to_type) # wait for dropdown appearance client.wait_until_displayed(widget.search_dropdown) # click in dropdown first suggestion client.click(widget.search_suggestions[0]) check_errors(field_name)
def test_list_widget(client, fill_db, lotame_segments, widget_name, get_segment_list, get_strategy_included, get_strategy_excluded): """Test including and excluding segments through list widget""" utils.go_to_step(client, 'targeting', existing=True) strategy_name = client.find_element_by_class_name('-t-name').text strategy = Strategy.objects.get(name=strategy_name) # No segments should be selected assert get_strategy_included(strategy).count() == 0 assert get_strategy_excluded(strategy).count() == 0 # Choose more than 1 segment SAMPLE_SIZE = 6 segments = sample(get_segment_list(), SAMPLE_SIZE) # Include half of the sample, and exclude the other half included_segments = segments[:SAMPLE_SIZE/2] excluded_segments = segments[SAMPLE_SIZE/2:] targeting_page = TargetingPage(client) include_widget = targeting_page.include.add(widget_name) exclude_widget = targeting_page.exclude.add(widget_name) # Include and exclude some random segments for widget, segments in [ (include_widget, included_segments), (exclude_widget, excluded_segments) ]: checkboxes = widget.checkboxes for segment in segments: checkbox = client.find_by_text(checkboxes, segment.name) client.click(checkbox) assert client.is_checked(checkbox) assert sorted(widget.tags) == sorted(s.name for s in segments) # Save strategy and check if segments were saved utils.save_and_return_to_step(client) assert include_widget.is_displayed assert exclude_widget.is_displayed for widget, segments in [ (include_widget, included_segments), (exclude_widget, excluded_segments) ]: checkboxes = widget.checkboxes selected_names = [s.name for s in segments] assert sorted(widget.tags) == sorted(selected_names) for checkbox in checkboxes: checkbox.location_once_scrolled_into_view if checkbox.text in selected_names: assert client.is_checked(checkbox) else: assert client.is_not_checked(checkbox) # Check included and excluded segments in database included_segments_db = get_strategy_included(strategy).values_list('id', 'name') excluded_segments_db = get_strategy_excluded(strategy).values_list('id', 'name') def id_and_name(segment): return (segment.id, segment.name) assert sorted(map(id_and_name, included_segments)) == sorted(included_segments_db) assert sorted(map(id_and_name, excluded_segments)) == sorted(excluded_segments_db)