diff --git a/vasl_templates/webapp/static/vo.js b/vasl_templates/webapp/static/vo.js index 5a55727..f2db0b2 100644 --- a/vasl_templates/webapp/static/vo.js +++ b/vasl_templates/webapp/static/vo.js @@ -3,14 +3,6 @@ function add_vo( vo_type, player_no ) { - // get the vehicles/ordnance already added - var $sortable2 = $( "#ob_" + vo_type + "-sortable_" + player_no ) ; - var vo_present = []; - $sortable2.children("li").each( function() { - var vo_entry = $(this).data( "sortable2-data" ).vo_entry ; - vo_present.push( vo_entry.id ) ; - } ) ; - // load the available vehicles/ordnance var nat = $( "select[name='PLAYER_" + player_no + "']" ).val() ; var entries = gVehicleOrdnanceListings[vo_type][nat] ; @@ -19,11 +11,8 @@ function add_vo( vo_type, player_no ) return ; } var buf = [] ; - for ( var i=0 ; i < entries.length ; ++i ) { - if ( vo_present.indexOf( entries[i].id ) !== -1 ) - continue ; + for ( var i=0 ; i < entries.length ; ++i ) buf.push( "" ) ; - } function format_vo_entry( opt ) { if ( ! opt.id ) return opt.text ; @@ -70,10 +59,11 @@ function add_vo( vo_type, player_no ) } ) ; // let the user select a vehicle/ordnance + var $sortable2 = $( "#ob_" + vo_type + "-sortable_" + player_no ) ; function on_resize( $dlg ) { $( ".select2-results ul" ).height( $dlg.height() - 50 ) ; } - $("#select-vo").dialog( { + var $dlg = $("#select-vo").dialog( { title: "Add " + SORTABLE_DISPLAY_NAMES["ob_"+vo_type][0], dialogClass: "select-vo", modal: true, @@ -104,21 +94,35 @@ function add_vo( vo_type, player_no ) resize: function() { on_resize( $(this) ) ; }, buttons: { OK: function() { - // add the new vehicle/ordnance + // get the selected vehicle/ordnance // FUDGE! $sel.select("data") returns the wrong thing if the entries are filtered?!?! var $elem = $( "#select-vo .select2-results__option--highlighted" ) ; if ( $elem.length === 0 ) return ; var sel_index = $elem.children( ".vo-entry" ).data( "index" ) ; - var $img = $elem.find( "img[class='vasl-image']" ) ; - var vo_image_id = $img.data( "vo-image-id" ) ; - var usedIds = {}; + var sel_entry = entries[ sel_index ] ; + var usedVoIds=[], usedSeqIds={} ; $sortable2.find( "li" ).each( function() { - usedIds[ $(this).data( "sortable2-data" ).id ] = true ; + usedVoIds.push( $(this).data( "sortable2-data" ).vo_entry.id ) ; + usedSeqIds[ $(this).data( "sortable2-data" ).id ] = true ; } ) ; - var seq_id = auto_assign_id( usedIds, "seq_id" ) ; - do_add_vo( vo_type, player_no, entries[sel_index], vo_image_id, null, null, seq_id ) ; - $(this).dialog( "close" ) ; + // check for duplicates + function add_sel_entry() { + var $img = $elem.find( "img[class='vasl-image']" ) ; + var vo_image_id = $img.data( "vo-image-id" ) ; + var seq_id = auto_assign_id( usedSeqIds, "seq_id" ) ; + do_add_vo( vo_type, player_no, sel_entry, vo_image_id, null, null, seq_id ) ; + $dlg.dialog( "close" ) ; + } + if ( usedVoIds.indexOf( sel_entry.id ) !== -1 ) { + var vo_type2 = SORTABLE_DISPLAY_NAMES[ "ob_" + vo_type ][0] ; + ask( "Add "+vo_type2, "

This "+vo_type2+" is already in the OB

Do you want to add it again?", { + ok: add_sel_entry, + } ) ; + return ; + } + // add the new vehicle/ordnance + add_sel_entry() ; }, Cancel: function() { $(this).dialog( "close" ) ; }, }, diff --git a/vasl_templates/webapp/tests/test_vehicles_ordnance.py b/vasl_templates/webapp/tests/test_vehicles_ordnance.py index a11ec6e..6ed0179 100644 --- a/vasl_templates/webapp/tests/test_vehicles_ordnance.py +++ b/vasl_templates/webapp/tests/test_vehicles_ordnance.py @@ -263,9 +263,9 @@ def test_html_names( webapp, webdriver ): vehicles_sortable = find_child( "#ob_vehicles-sortable_1" ) assert get_sortable_vo_names( vehicles_sortable ) == [ "PzKpfw IVF2" ] - # start to add another vehicle - make sure only the PzKw IVF1 is present + # start to add another vehicle - make sure both PzKw IVF's are still available add_vehicle_btn.click() - assert get_available_ivfs() == [ "PzKpfw IVF1 (MT)" ] + assert get_available_ivfs() == [ "PzKpfw IVF1 (MT)", "PzKpfw IVF2 (MT)" ] # add the PzKw IVF1 elem = find_child( ".ui-dialog .select2-search__field" ) @@ -275,18 +275,78 @@ def test_html_names( webapp, webdriver ): # make sure it was added to the player's OB assert get_sortable_vo_names( vehicles_sortable ) == [ "PzKpfw IVF2", "PzKpfw IVF1" ] - # start to add another vehicle - make sure there are no PzKw IVF's present + # start to add another vehicle - make sure both PzKw IVF's are still available add_vehicle_btn.click() - assert not get_available_ivfs() + assert get_available_ivfs() == [ "PzKpfw IVF1 (MT)", "PzKpfw IVF2 (MT)" ] elem = find_child( ".ui-dialog .select2-search__field" ) elem.send_keys( Keys.ESCAPE ) # delete the PzKw IVF2 delete_vo( "vehicles", 1, "PzKpfw IVF2" , webdriver ) - # start to add another vehicle - make sure the PzKw IVF2 is available again + # start to add another vehicle - make sure both PzKw IVF's are still available add_vehicle_btn.click() - assert get_available_ivfs() == [ "PzKpfw IVF2 (MT)" ] + assert get_available_ivfs() == [ "PzKpfw IVF1 (MT)", "PzKpfw IVF2 (MT)" ] + +# --------------------------------------------------------------------- + +def test_duplicate_vo_entries( webapp, webdriver ): + """Test adding duplicate vehicles/ordnance.""" + + # initialize + init_webapp( webapp, webdriver ) + set_player( 1, "german" ) + select_tab( "ob1" ) + + def get_available_vo_entries(): + """Get the available vehicles/ordnance for selection.""" + entries = find_children( "#select-vo .select2-results li" ) + return [ e.text for e in entries ] + + def do_test( vo_type, vo_name ): #pylint: disable=missing-docstring + + # start to add a vehicle/ordnance + add_btn = find_child( "#ob_" + vo_type + "-add_1" ) + add_btn.click() + assert vo_name in get_available_vo_entries() + + # add the vehicle/ordnance + elem = find_child( ".ui-dialog .select2-search__field" ) + elem.send_keys( vo_name ) + elem.send_keys( Keys.RETURN ) + + # make sure it was added to the player's OB + sortable = find_child( "#ob_" + vo_type + "-sortable_1" ) + assert get_sortable_vo_names( sortable ) == [ vo_name ] + + # add the vehicle/ordnance, dismiss the warning + add_btn.click() + elem = find_child( ".ui-dialog .select2-search__field" ) + elem.send_keys( vo_name ) + elem.send_keys( Keys.RETURN ) + elem = find_child( "#ask" ) + assert "already in the OB" in elem.text + click_dialog_button( "Cancel", find_child(".ui-dialog.ask") ) + click_dialog_button( "Cancel" ) + + # make sure the player's OB is unchanged + assert get_sortable_vo_names( sortable ) == [ vo_name ] + + # add the vehicle/ordnance, accept the warning + add_btn.click() + elem = find_child( ".ui-dialog .select2-search__field" ) + elem.send_keys( vo_name ) + elem.send_keys( Keys.RETURN ) + elem = find_child( "#ask" ) + assert "already in the OB" in elem.text + click_dialog_button( "OK", find_child(".ui-dialog.ask") ) + + # make sure the vehicle/ordnance was added to the player's OB + assert get_sortable_vo_names( sortable ) == [ vo_name, vo_name ] + + # do the test + do_test( "vehicles", "a german vehicle" ) + do_test( "ordnance", "name only" ) # --------------------------------------------------------------------- diff --git a/vasl_templates/webapp/tests/utils.py b/vasl_templates/webapp/tests/utils.py index 96e13eb..ffcc153 100644 --- a/vasl_templates/webapp/tests/utils.py +++ b/vasl_templates/webapp/tests/utils.py @@ -447,13 +447,14 @@ def dismiss_notifications(): # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def click_dialog_button( caption, webdriver=None ): +def click_dialog_button( caption, parent=None ): """Click a dialog button.""" - btn = next( - elem for elem in find_children( ".ui-dialog button", webdriver ) + btns = [ + elem for elem in find_children( ".ui-dialog button", parent ) if elem.text == caption - ) - btn.click() + ] + assert len(btns) == 1 + btns[0].click() # ---------------------------------------------------------------------