From 13ae3074cc8c088bf2bad5094e76bc0dfe1ff3dd Mon Sep 17 00:00:00 2001 From: Taka Date: Wed, 8 Aug 2018 01:49:13 +0000 Subject: [PATCH] Allow multiple OB setup's --- vasl_templates/webapp/static/css/main.css | 24 ++-- vasl_templates/webapp/static/css/tabs-ob.css | 34 ++++-- vasl_templates/webapp/static/main.js | 27 +++-- vasl_templates/webapp/static/ob_setup.js | 87 ++++++++++++++ vasl_templates/webapp/static/snippets.js | 48 +++++--- vasl_templates/webapp/static/sortable.js | 110 +++++++++++++++++ vasl_templates/webapp/static/utils.js | 17 +++ vasl_templates/webapp/templates/index.html | 49 +++++--- vasl_templates/webapp/tests/test_ob_setup.py | 113 +++++++++++------- vasl_templates/webapp/tests/test_players.py | 3 +- .../webapp/tests/test_scenario_persistence.py | 16 ++- vasl_templates/webapp/tests/test_snippets.py | 32 ++++- vasl_templates/webapp/tests/test_ssr.py | 3 +- .../webapp/tests/test_template_packs.py | 31 +++-- vasl_templates/webapp/tests/utils.py | 11 +- 15 files changed, 486 insertions(+), 119 deletions(-) create mode 100644 vasl_templates/webapp/static/ob_setup.js create mode 100644 vasl_templates/webapp/static/sortable.js diff --git a/vasl_templates/webapp/static/css/main.css b/vasl_templates/webapp/static/css/main.css index c807825..254e865 100644 --- a/vasl_templates/webapp/static/css/main.css +++ b/vasl_templates/webapp/static/css/main.css @@ -43,21 +43,25 @@ body { height: 100% ; } #tabs-ob1, #tabs-ob2 { display: grid ; display: -ms-grid ; - grid-template-rows: 10em 1fr ; -ms-grid-rows: 10em 1fr ; + grid-template-rows: 1fr 1fr ; -ms-grid-rows: 1fr 1fr ; grid-template-columns: 1fr 1fr ; -ms-grid-columns: 1fr 1fr ; } -#tabs-ob1 fieldset.tl, #tab-ob2 fieldset.tl { +#tabs-ob1 fieldset.tl, #tabs-ob2 fieldset.tl { grid-row-start: 1 ; -ms-grid-row: 1 ; - grid-column-start: 1 ; -ms-grid-row: 1 ; + grid-column-start: 1 ; -ms-grid-column: 1 ; } -#tabs-ob1 fieldset.r, #tabs-ob2 fieldset.r { +#tabs-ob1 fieldset.bl, #tabs-ob2 fieldset.bl { + grid-row-start: 2 ; -ms-grid-row: 2 ; + grid-column-start: 1 ; -ms-grid-column: 1 ; +} +#tabs-ob1 fieldset.tr, #tabs-ob2 fieldset.tr { grid-row-start: 1 ; -ms-grid-row: 1 ; - grid-row-end: 3 ; -ms-grid-row-span: 2 ; grid-column-start: 2 ; -ms-grid-column: 2 ; } -#tabs-ob1 fieldset.bl, #tabs-ob2 fieldset.bl { - -ms-grid-row: 2 ; -ms-grid-column: 1 ; +#tabs-ob1 fieldset.br, #tabs-ob2 fieldset.br { + grid-row-start: 2 ; -ms-grid-row: 2 ; + grid-column-start: 2 ; -ms-grid-column: 2 ; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -107,6 +111,12 @@ input[type="text"] { margin-bottom: 0.25em ; } .ui-dialog.edit-ssr textarea { resize: none ; width: calc(100% - 4px) ; height: calc(100% - 1.25em) ; } .ui-dialog.edit-ssr button { margin: 0 0 0 5px ; padding: 0.1em 0.2em ; } +.ui-dialog.edit-ob_setup .ui-dialog-titlebar { display: none ; } +.ui-dialog.edit-ob_setup .ui-dialog-buttonpane { border: none ; padding: 0 ; font-size: 75% ; } +#edit-ob_setup { padding: 2px ; } +.ui-dialog.edit-ob_setup textarea { resize: none ; width: calc(100% - 4px) ; height: calc(100% - 3em) ; } +.ui-dialog.edit-ob_setup button { margin: 0 0 0 5px ; padding: 0.1em 0.2em ; } + #select-vo { overflow: hidden ; } #select-vo .header { height: 2em ; } #select-vo select { width: 100% ; top: 2em ; height: calc(100% - 2em) ; } diff --git a/vasl_templates/webapp/static/css/tabs-ob.css b/vasl_templates/webapp/static/css/tabs-ob.css index 153e18e..34bf172 100644 --- a/vasl_templates/webapp/static/css/tabs-ob.css +++ b/vasl_templates/webapp/static/css/tabs-ob.css @@ -1,19 +1,37 @@ /* -------------------------------------------------------------------- */ -.panel-obsetup { +.panel-ob_setup { height: 100% ; display: grid ; display: -ms-grid ; - grid-template-rows: 1fr 2.5em ; -ms-grid-rows: 1fr 2.5em ; + grid-template-rows: 1fr 2em ; -ms-grid-rows: 1fr 2em ; + grid-template-columns: 1fr ; -ms-grid-columns: 1fr ; +} +/* FUDGE! IE hackamathon follows... */ +.panel-ob_setup .content { -ms-grid-row: 1 ; -ms-grid-column: 1 ; } +.panel-ob_setup .footer { -ms-grid-row: 2 ; -ms-grid-column: 1 ; } + +.panel-ob_setup ul.sortable li input[type="button"] { float: right ; } +.panel-ob_setup .footer { text-align: right ; font-size: 75% ; } +.panel-ob_setup .footer .l { float: left ; } +.ob_setup-trash { margin-left: 5px ; height: 2em ; } + +.ob_setup-hint { width:100% ; height: calc(100% - 1.5em) ; font-size: 80% ; font-style: italic ; } +.ob_setup-hint p { margin-bottom: 1em ; } + +/* -------------------------------------------------------------------- */ + +.panel-ob_notes { + height: 100% ; + display: grid ; display: -ms-grid ; + grid-template-rows: 1fr 2em ; -ms-grid-rows: 1fr 2em ; grid-template-columns: 1fr ; -ms-grid-columns: 1fr ; } /* FUDGE! IE hackamathon follows... */ -.panel-obsetup .footer { -ms-grid-row: 2 ; -ms-grid-column: 1 ; } +.panel-ob_notes .content { -ms-grid-row: 1 ; -ms-grid-column: 1 ; } +.panel-ob_notes .footer { -ms-grid-row: 2 ; -ms-grid-column: 1 ; } -.panel-obsetup textarea { width: 100% ; height: 100% ; resize: none ; } -.panel-obsetup .footer { font-size: 75% ; } -.panel-obsetup div.snippet-control { float: left ; margin: 0.25em 0.25em 0 0 ; } -.panel-obsetup .footer .r { display: block-inline ; float: right ; } -.panel-obsetup .footer .r div.snippet-control { float: none ; margin-right: 0 ; } +.panel-ob_notes .footer { font-size: 75% ; } +.panel-ob_notes div.snippet-control { float: left ; margin: 0.25em 0.25em 0 0 ; } /* -------------------------------------------------------------------- */ diff --git a/vasl_templates/webapp/static/main.js b/vasl_templates/webapp/static/main.js index caf07f9..f5b361a 100644 --- a/vasl_templates/webapp/static/main.js +++ b/vasl_templates/webapp/static/main.js @@ -86,12 +86,24 @@ $(document).ready( function () { $("#ssr-trash").sortable( { receive: function( evt, ui ) { ui.item.remove() ; update_ssr_hint() ; } } ) ; - $("#edit-ssr textarea").keydown( function(evt) { - if ( evt.keyCode == 13 && evt.ctrlKey ) { - $(".ui-dialog.edit-ssr button:contains('OK')").click() ; - evt.preventDefault() ; - } + enable_ctrl_enter( $("#edit-ssr"), "OK" ) ; + + // initialize OB setup controls + init_sortable( $("#ob_setup-sortable_1"), + function() { add_ob_setup(1) ; }, + edit_ob_setup + ) ; + $("#panel-ob_setup1 input[type='button'][data-id='ob_setup']").click( function() { + edit_template( "ob_setup" ) ; + } ) ; + init_sortable( $("#ob_setup-sortable_2"), + function() { add_ob_setup(2) ; }, + edit_ob_setup + ) ; + $("#panel-ob_setup2 input[type='button'][data-id='ob_setup']").click( function() { + edit_template( "ob_setup" ) ; } ) ; + enable_ctrl_enter( $("#edit-ob_setup"), "OK" ) ; // initialize vehicle controls (1) $("#vehicle-sortable_1").sortable( { connectWith: "#vehicle-trash_1", cursor: "move" } ) ; @@ -241,11 +253,12 @@ $(document).ready( function () { // handle requests to generate/edit HTML snippets $("input[type='button'].generate").click( function() { - generate_snippet( $(this) ) ; + generate_snippet( $(this), null ) ; } ) ; $("div.snippet-control select").on( "selectmenuselect", function() { edit_template( $(this).attr("data-id") ) ; } ) ; + enable_ctrl_enter( $("#edit-template"), "Close" ) ; // initialize hotkeys init_hotkeys() ; @@ -347,7 +360,7 @@ function on_player_change( $select ) for ( var nat in _NATIONALITY_SPECIFIC_BUTTONS ) { for ( var i=0 ; i < _NATIONALITY_SPECIFIC_BUTTONS[nat].length ; ++i ) { var button_id = _NATIONALITY_SPECIFIC_BUTTONS[nat][i] ; - $elem = $( "#panel-obsetup" + player_id + " div.snippet-control[data-id='" + button_id + "']" ) ; + $elem = $( "#panel-ob_notes" + player_id + " div.snippet-control[data-id='" + button_id + "']" ) ; $elem.css( "display", nat == player_nat ? "block" : "none" ) ; } } diff --git a/vasl_templates/webapp/static/ob_setup.js b/vasl_templates/webapp/static/ob_setup.js new file mode 100644 index 0000000..fbdd804 --- /dev/null +++ b/vasl_templates/webapp/static/ob_setup.js @@ -0,0 +1,87 @@ + +// -------------------------------------------------------------------- + +function add_ob_setup( player_id ) +{ + // add a new OB setup + edit_ob_setup( $("#ob_setup-sortable_"+player_id), null ) ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function edit_ob_setup( $sortable, $entry ) +{ + var $caption, $width ; + + // let the user edit the OB setup + $("#edit-ob_setup").dialog( { + dialogClass: "edit-ob_setup", + modal: true, + minWidth: 400, + minHeight: 150, + open: function() { + $caption = $(this).children( "textarea" ) ; + $width = $(this).children( "input[type='text']" ) ; + if ( $entry ) { + var data = $entry.data( "sortable-data" ) ; + $caption.val( data.caption ) ; + $width.val( data.width ) ; + } + else { + $caption.val( "" ) ; + $width.val( "" ) ; + } + $(this).height( $(this).height() ) ; // fudge: force the textarea to resize + }, + buttons: { + OK: function() { + var caption = $caption.val().trim() ; + var width = $width.val().trim() ; + if ( $entry ) { + // update the existing OB setup + if ( caption === "" ) + delete_sortable_entry( $entry ) ; + else { + $entry.data("sortable-data").caption = caption ; + $entry.data("sortable-data").width = width ; + $entry.empty().append( _make_sortable_entry( caption ) ) ; + } + } + else { + // create a new OB setup + if ( caption !== "" ) { + data = { caption: caption, width: width } ; + do_add_ob_setup( $sortable, data ) ; + } + } + $(this).dialog( "close" ) ; + }, + Cancel: function() { $(this).dialog( "close" ) ; }, + }, + } ) ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function do_add_ob_setup( $sortable, data ) +{ + // add a new sortable entry + add_sortable( $sortable, _make_sortable_entry(data.caption), data ) ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function _make_sortable_entry( caption ) +{ + // generate the sortable entry + var $content = $( "
" + caption + "
" ) ; + + // add a handler for the snippet button + $content.children("input[type='button']").click( function() { + var data = $(this).parent().parent().data( "sortable-data" ) ; + var extra_params = { OB_SETUP: data.caption, OB_SETUP_WIDTH: data.width } ; + generate_snippet( $(this), extra_params ) ; + } ) ; + + return $content ; +} diff --git a/vasl_templates/webapp/static/snippets.js b/vasl_templates/webapp/static/snippets.js index 6ee40d3..62a0539 100644 --- a/vasl_templates/webapp/static/snippets.js +++ b/vasl_templates/webapp/static/snippets.js @@ -16,7 +16,7 @@ var _DAY_OF_MONTH_POSTFIXES = { // nb: we assume English :-/ // -------------------------------------------------------------------- -function generate_snippet( $btn ) +function generate_snippet( $btn, extra_params ) { // initialize storeMsgForTestSuite( "_last-info_", "" ) ; @@ -44,18 +44,6 @@ function generate_snippet( $btn ) unload_params( params, true ) ; // set player-specific parameters - // NOTE: We used to delete the player-specific parameters (e.g. OB_SETUP_1/2) - // and just return a generic player-independent one (e.g. OB_SETUP), but now, - // we just leave them in place, in case a user-defined template wants them both. - if ( template_id === "ob_setup_1" ) { - template_id = "ob_setup" ; - params.OB_SETUP = params.OB_SETUP_1 ; - params.OB_SETUP_WIDTH = params.OB_SETUP_WIDTH_1 ; - } else if ( template_id === "ob_setup_2" ) { - template_id = "ob_setup" ; - params.OB_SETUP = params.OB_SETUP_2 ; - params.OB_SETUP_WIDTH = params.OB_SETUP_WIDTH_2 ; - } var nationalities = gTemplatePack.nationalities ; var curr_tab = $("#tabs .ui-tabs-active a").attr( "href" ) ; if ( curr_tab === "#tabs-ob1" ) { @@ -154,6 +142,10 @@ function generate_snippet( $btn ) showWarningMsg( "ATMM are only available from 1944." ) ; } + // add in any extra parameters + if ( extra_params ) + $.extend( true, params, extra_params ) ; + // check that the players have different nationalities if ( params.PLAYER_1 === params.PLAYER_2 ) showWarningMsg( "Both players have the same nationality!" ) ; @@ -511,8 +503,16 @@ function do_load_scenario( params ) params_loaded[key] = true ; continue ; } + var player_id ; + if ( key === "OB_SETUP_1" || key === "OB_SETUP_2" ) { + player_id = key.substring( key.length-1 ) ; + var $sortable = $( "#ob_setup-sortable_" + player_id ) ; + for ( i=0 ; i < params[key].length ; ++i ) + do_add_ob_setup( $sortable, params[key][i] ) ; + params_loaded[key] = true ; + } if ( key === "VEHICLES_1" || key === "ORDNANCE_1" || key === "VEHICLES_2" || key === "ORDNANCE_2" ) { - var player_id = key.substring( key.length-1 ) ; + player_id = key.substring( key.length-1 ) ; var nat = params[ "PLAYER_" + player_id ] ; var vo_type = key.substring(0,9) === "VEHICLES_" ? "vehicle" : "ordnance" ; for ( i=0 ; i < params[key].length ; ++i ) { @@ -562,6 +562,13 @@ function do_load_scenario( params ) function on_save_scenario() { // unload the template parameters + function unload_ob_setups( $sortable ) { + var entries = [] ; + $sortable.children("li").each( function() { + entries.push( $(this).data( "sortable-data" ) ) ; + } ) ; + return entries ; + } function extract_vo_names( key ) { // nb: we only need to save the vehicle/ordnance name if ( !(key in params) ) return ; @@ -570,8 +577,10 @@ function on_save_scenario() names.push( params[key][i].name ) ; params[key] = names ; } - var params = {}; + var params = {} ; unload_params( params, false ) ; + params.OB_SETUP_1 = unload_ob_setups( $("#ob_setup-sortable_1") ) ; + params.OB_SETUP_2 = unload_ob_setups( $("#ob_setup-sortable_2") ) ; extract_vo_names( "VEHICLES_1" ) ; extract_vo_names( "ORDNANCE_1" ) ; extract_vo_names( "VEHICLES_2" ) ; @@ -619,10 +628,11 @@ function on_new_scenario( verbose ) update_ssr_hint() ; // reset all the template parameters - delete_all_vo( "vehicle", 1 ) ; - delete_all_vo( "ordnance", 1 ) ; - delete_all_vo( "vehicle", 2 ) ; - delete_all_vo( "ordnance", 2 ) ; + for ( var i=1 ; i <= 2 ; ++i ) { + delete_all_sortable_entries( $("#ob_setup-sortable_"+i) ) ; + delete_all_vo( "vehicle", i ) ; + delete_all_vo( "ordnance", i ) ; + } // provide some feedback to the user if ( verbose ) diff --git a/vasl_templates/webapp/static/sortable.js b/vasl_templates/webapp/static/sortable.js new file mode 100644 index 0000000..3f08e8a --- /dev/null +++ b/vasl_templates/webapp/static/sortable.js @@ -0,0 +1,110 @@ + +// -------------------------------------------------------------------- + +function init_sortable( $sortable, on_add, on_edit ) +{ + // initialize the support elements + var $add = _find_sortable_helper( $sortable, "add" ) ; + $add.click( on_add ) ; + $sortable.data( "on_edit", on_edit ) ; + + // handle dragging entries to the trash + var $trash = _find_sortable_helper( $sortable, "trash" ) ; + $sortable.sortable( { connectWith: $trash, cursor: "move" } ) ; + $trash.sortable( { + receive: function( evt, ui ) { + ui.item.remove() ; + update_sortable_hint($sortable) ; + } + } ) ; +} + +// -------------------------------------------------------------------- + +function add_sortable( $sortable, $content, sortable_data ) +{ + // add a new entry to the sortable + var $entry = $( "
  • " ) ; + $entry.append( $content ) ; + $entry.data( "sortable-data", sortable_data ) ; + $sortable.append( $entry ) ; + init_sortable_entry( $entry ) ; + + // update the hint + update_sortable_hint( $sortable ) ; + + return $entry ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function init_sortable_entry( $entry ) +{ + // initialize the sortable entry + var $sortable = $entry.parent() ; + $entry.dblclick( function() { + $sortable.data("on_edit")( $sortable, $entry ) ; + } ) ; + $entry.click( function( evt ) { + if ( evt.ctrlKey ) + delete_sortable_entry( $(this) ) ; + } ) ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function update_sortable_hint( $sortable ) +{ + // show/hide the hint + var $hint = _find_sortable_helper( $sortable, "hint" ) ; + if ( $sortable.children("li").length === 0 ) { + $sortable.hide() ; + $hint.show() ; + } else { + $sortable.show() ; + $hint.hide() ; + } +} + +// -------------------------------------------------------------------- + +function delete_sortable_entry( $entry ) +{ + // initialize + var $sortable = $entry.parent() ; + + // ask if it's OK to delete the entry + $entry.addClass( "highlighted" ) ; + var caption = $entry.data("sortable-data").caption ; + if ( ! caption ) + caption = $entry.html() ; + ask( "OK to delete?", escapeHTML(caption), { + "ok": function() { + // yup - make it so + $entry.remove() ; + update_sortable_hint( $sortable ) ; + }, + "close": function() { $entry.removeClass("highlighted") ; }, + } ) ; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +function delete_all_sortable_entries( $sortable ) +{ + // delete all entries from the sortable + $sortable.children("li").each( function() { + $(this).remove() ; + } ) ; + update_sortable_hint( $sortable ) ; +} + +// -------------------------------------------------------------------- + +function _find_sortable_helper( $sortable, type ) +{ + // find a support element for the specified sortable + var id = $sortable.prop( "id" ) ; + var pos = id.indexOf( "sortable" ) ; + return $( "#" + id.substring(0,pos) + type+ id.substring(pos+8) ) ; +} diff --git a/vasl_templates/webapp/static/utils.js b/vasl_templates/webapp/static/utils.js index 1e2d1bc..d82b603 100644 --- a/vasl_templates/webapp/static/utils.js +++ b/vasl_templates/webapp/static/utils.js @@ -77,6 +77,23 @@ jQuery.fn.filterByText = function( $textbox ) { // -------------------------------------------------------------------- +function enable_ctrl_enter( $dlg, btn_text ) +{ + // allow Ctrl-Enter to dismiss a dialog + var dismiss_dialog = function( evt ) { + if ( evt.keyCode == 13 && evt.ctrlKey ) { + // locate the OK button (nb: we assume the dialog was created with a class the same as its ID) + var id = $(this).parent().prop( "id" ) ; + $( ".ui-dialog" + "."+id + " button:contains('"+btn_text+"')" ).click() ; + evt.preventDefault() ; + } + } ; + $dlg.find("input[type='text']").keydown( dismiss_dialog ) ; + $dlg.find("textarea").keydown( dismiss_dialog ) ; +} + +// -------------------------------------------------------------------- + function ask( title, msg, args ) { // ask a question diff --git a/vasl_templates/webapp/templates/index.html b/vasl_templates/webapp/templates/index.html index 8ee19f2..22a3303 100644 --- a/vasl_templates/webapp/templates/index.html +++ b/vasl_templates/webapp/templates/index.html @@ -88,25 +88,36 @@
    OB setup -
    - +
    +
    +

    Click on the "+" below to add a new setup note.

    To re-order the setup notes, use the mouse to drag them around.

    Ctrl-click on a setup note to delete it, or drag it into the trashcan below.

    + +
    -
    Vehicles +
    Notes +
    +
    +
    + +
    +
    +
    Vehicles

    Click on the "+" below to add a new Vehicle.

    To re-order the Vehicles, use the mouse to drag them around.

    Ctrl-click on an Vehicle to delete it, or drag it into the trashcan below.

    @@ -123,7 +134,7 @@
    -
    Ordnance +
    Ordnance

    Click on the "+" below to add a new Gun.

    To re-order the Gun's, use the mouse to drag them around.

    Ctrl-click on an Gun to delete it, or drag it into the trashcan below.

    @@ -154,8 +165,12 @@ +
    @@ -180,7 +195,9 @@ gOrdnanceListingsUrl = "{{url_for('get_ordnance_listings')}}" ; + + diff --git a/vasl_templates/webapp/tests/test_ob_setup.py b/vasl_templates/webapp/tests/test_ob_setup.py index 835081f..6ff9b9b 100644 --- a/vasl_templates/webapp/tests/test_ob_setup.py +++ b/vasl_templates/webapp/tests/test_ob_setup.py @@ -3,8 +3,10 @@ import types from selenium.webdriver.support.ui import Select +from selenium.webdriver.common.action_chains import ActionChains -from vasl_templates.webapp.tests.utils import select_tab, get_nationalities, get_clipboard, get_stored_msg, find_child +from vasl_templates.webapp.tests.utils import get_nationalities, get_clipboard, get_stored_msg +from vasl_templates.webapp.tests.utils import select_tab, find_child, find_children, click_dialog_button # --------------------------------------------------------------------- @@ -15,50 +17,43 @@ def test_ob_setup( webapp, webdriver ): webdriver.get( webapp.url_for( "main" ) ) # generate OB SETUP snippets for both players + def check_snippet( player_id, entry_no, expected ): + """Generate the snippet for an OB setup.""" + select_tab( "ob{}".format( player_id ) ) + elems = find_children( "#ob_setup-sortable_{} li input[type='button']".format( player_id ) ) + elems[entry_no].click() + assert get_clipboard() == expected + add_ob_setup( webdriver, 1, "ob setup #1" ) + add_ob_setup( webdriver, 1, "ob setup #2", "2px" ) + add_ob_setup( webdriver, 2, "ob setup #3", "3px" ) + check_snippet( 1, 0, "[German] [ob setup #1] (col=[OBCOL:german/OBCOL2:german])" ) + check_snippet( 1, 1, "[German] [ob setup #2] (col=[OBCOL:german/OBCOL2:german]) (width=[2px])" ) + check_snippet( 2, 0, "[Russian] [ob setup #3] (col=[OBCOL:russian/OBCOL2:russian]) (width=[3px])" ) + + # make some changes and check the snippets again + edit_ob_setup( webdriver, 2, 0, "updated ob setup #3", "" ) + edit_ob_setup( webdriver, 1, 1, "updated ob setup #2", "200px" ) + edit_ob_setup( webdriver, 1, 0, None, "100px" ) + check_snippet( 2, 0, "[Russian] [updated ob setup #3] (col=[OBCOL:russian/OBCOL2:russian])" ) + check_snippet( 1, 1, "[German] [updated ob setup #2] (col=[OBCOL:german/OBCOL2:german]) (width=[200px])" ) + check_snippet( 1, 0, "[German] [ob setup #1] (col=[OBCOL:german/OBCOL2:german]) (width=[100px])" ) + + # delete an OB setup by dragging it into the trash + def count_entries( player_id ): + """Count the number of OB setup's.""" + elems = find_children( "#ob_setup-sortable_{} li".format( player_id ) ) + return len(elems) select_tab( "ob1" ) - textarea1 = find_child( "textarea[name='OB_SETUP_1']" ) - textarea1.clear() - textarea1.send_keys( "setup here." ) - btn1 = find_child( "input[type='button'][data-id='ob_setup_1']" ) - select_tab( "ob2" ) - textarea2 = find_child( "textarea[name='OB_SETUP_2']" ) - textarea2.clear() - textarea2.send_keys( "setup there." ) - btn2 = find_child( "input[type='button'][data-id='ob_setup_2']" ) - btn2.click() - assert get_clipboard() == "[Russian] [setup there.] (col=[OBCOL:russian/OBCOL2:russian])" - select_tab( "ob1" ) - btn1.click() - assert get_clipboard() == "[German] [setup here.] (col=[OBCOL:german/OBCOL2:german])" - - # change the player nationalities and generate the OB SETUP snippets again - select_tab( "scenario" ) - sel = Select( - find_child( "select[name='PLAYER_1']" ) - ) - sel.select_by_value( "british" ) - sel = Select( - find_child( "select[name='PLAYER_2']" ) - ) - sel.select_by_value( "french" ) - select_tab( "ob1" ) - btn1.click() - assert get_clipboard() == "[British] [] (col=[OBCOL:british/OBCOL2:british])" - select_tab( "ob2" ) - btn2.click() - assert get_clipboard() == "[French] [] (col=[OBCOL:french/OBCOL2:french])" + assert count_entries(1) == 2 + elem = find_child( "#ob_setup-sortable_1 li[2]" ) + trash = find_child( "#ob_setup-trash_1" ) + ActionChains(webdriver).drag_and_drop( elem, trash ).perform() + assert count_entries(1) == 1 - # set the snippet widths and generate the snippets again - select_tab( "ob1" ) - elem = find_child( "input[name='OB_SETUP_WIDTH_1']" ) - elem.send_keys( "100px" ) - btn1.click() - assert get_clipboard() == "[British] [] (col=[OBCOL:british/OBCOL2:british]) (width=[100px])" - select_tab( "ob2" ) - elem = find_child( "input[name='OB_SETUP_WIDTH_2']" ) - elem.send_keys( "200px" ) - btn2.click() - assert get_clipboard() == "[French] [] (col=[OBCOL:french/OBCOL2:french]) (width=[200px])" + # delete an OB setup by emptying its caption + edit_ob_setup( webdriver, 1, 0, "", None ) + click_dialog_button( "OK" ) # nb: confirm the deletion + assert count_entries(1) == 0 # --------------------------------------------------------------------- @@ -169,3 +164,35 @@ def test_nationality_specific( webapp, webdriver ): else: # it should be hidden for all other nationalities assert not elem.is_displayed() + +# --------------------------------------------------------------------- + +def add_ob_setup( webdriver, player_id, caption, width=None ): + """Add a new OB setup.""" + select_tab( "ob{}".format( player_id ) ) + elem = find_child( "#ob_setup-add_{}".format( player_id ) ) + elem.click() + edit_ob_setup( webdriver, player_id, None, caption, width ) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +def edit_ob_setup( webdriver, player_id, entry_no, caption, width ): + """Edit an OB setup.""" + + # locate the requested entry and start editing it + if entry_no is not None: + select_tab( "ob{}".format( player_id ) ) + elems = find_children( "#ob_setup-sortable_{} li".format( player_id ) ) + elem = elems[ entry_no ] + ActionChains(webdriver).double_click( elem ).perform() + + # edit the OB setup + if caption is not None: + elem = find_child( "#edit-ob_setup textarea" ) + elem.clear() + elem.send_keys( caption ) + if width is not None: + elem = find_child( "#edit-ob_setup input[type='text']" ) + elem.clear() + elem.send_keys( width ) + click_dialog_button( "OK" ) diff --git a/vasl_templates/webapp/tests/test_players.py b/vasl_templates/webapp/tests/test_players.py index d7cdf58..fa7c6ae 100644 --- a/vasl_templates/webapp/tests/test_players.py +++ b/vasl_templates/webapp/tests/test_players.py @@ -2,7 +2,7 @@ from selenium.webdriver.support.ui import Select -from vasl_templates.webapp.tests.utils import get_nationalities, find_child +from vasl_templates.webapp.tests.utils import get_nationalities, select_tab, find_child # --------------------------------------------------------------------- @@ -20,6 +20,7 @@ def test_player_change( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main" ) ) + select_tab( "scenario" ) nationalities = get_nationalities( webapp ) # make sure that the UI was updated correctly for the initial players diff --git a/vasl_templates/webapp/tests/test_scenario_persistence.py b/vasl_templates/webapp/tests/test_scenario_persistence.py index b675bf3..01f895e 100644 --- a/vasl_templates/webapp/tests/test_scenario_persistence.py +++ b/vasl_templates/webapp/tests/test_scenario_persistence.py @@ -34,14 +34,14 @@ def test_scenario_persistence( webapp, webdriver ): "SSR_WIDTH": "103", }, "ob1": { - "OB_SETUP_1": "Player 1's OB", "OB_SETUP_WIDTH_1": "201", + "OB_SETUP_1": [ { "caption": "ob setup 1a", "width": "" }, { "caption": "ob setup 1b", "width": "200px" } ], "VEHICLES_1": [ "a russian vehicle", "another russian vehicle" ], "VEHICLES_WIDTH_1": "202", "ORDNANCE_1": [ "a russian ordnance", "another russian ordnance" ], "ORDNANCE_WIDTH_1": "203", }, "ob2": { - "OB_SETUP_2": "Player 2's OB", "OB_SETUP_WIDTH_2": "301", + "OB_SETUP_2": [ { "caption": "ob setup 2", "width": "" } ], "VEHICLES_2": [ "a german vehicle" ], "VEHICLES_WIDTH_2": "302", "ORDNANCE_2": [ "a german ordnance" ], @@ -77,6 +77,8 @@ def test_scenario_persistence( webapp, webdriver ): for field,val in scenario_params[tab_id].items(): if field == "SSR": continue # nb: this requires special handling, we do it below + if field in ("OB_SETUP_1","OB_SETUP_2"): + continue # nb: this requires special handling, we do it below if field in ("VEHICLES_1","ORDNANCE_1","VEHICLES_2","ORDNANCE_2"): continue # nb: this requires special handling, we do it below elem = next( c for c in ( \ @@ -89,6 +91,8 @@ def test_scenario_persistence( webapp, webdriver ): assert elem.get_attribute("value") == val ssrs = _get_ssrs() assert ssrs == scenario_params["scenario"]["SSR"] + assert _get_ob_setups(1) == [ obs["caption"] for obs in scenario_params["ob1"]["OB_SETUP_1"] ] + assert _get_ob_setups(2) == [ obs["caption"] for obs in scenario_params["ob2"]["OB_SETUP_2"] ] assert _get_vo("vehicle",1) == scenario_params["ob1"]["VEHICLES_1"] assert _get_vo("ordnance",1) == scenario_params["ob1"]["ORDNANCE_1"] assert _get_vo("vehicle",2) == scenario_params["ob2"]["VEHICLES_2"] @@ -160,6 +164,14 @@ def _get_ssrs(): select_tab( "scenario" ) return [ c.text for c in find_children("#ssr-sortable li") ] +def _get_ob_setups( player_id ): + """Get the OB setup's from the UI.""" + select_tab( "ob{}".format( player_id ) ) + return [ + c.text + for c in find_children( "#ob_setup-sortable_{} li".format( player_id ) ) + ] + def _get_vo( vo_type, player_id ): """Get the vehicles/ordnance from the UI.""" select_tab( "ob{}".format( player_id ) ) diff --git a/vasl_templates/webapp/tests/test_snippets.py b/vasl_templates/webapp/tests/test_snippets.py index ecd66d6..4bdfd44 100644 --- a/vasl_templates/webapp/tests/test_snippets.py +++ b/vasl_templates/webapp/tests/test_snippets.py @@ -2,7 +2,7 @@ from selenium.webdriver.common.keys import Keys -from vasl_templates.webapp.tests.utils import set_template_params, get_clipboard +from vasl_templates.webapp.tests.utils import select_tab, set_template_params, get_clipboard from vasl_templates.webapp.tests.utils import get_stored_msg, dismiss_notifications, find_child from vasl_templates.webapp.tests.utils import for_each_template, wait_for @@ -13,6 +13,7 @@ def test_scenario_snippets( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main", store_msgs=1 ) ) + select_tab( "scenario" ) # generate a SCENARIO snippet _test_snippet( webdriver, "scenario", { @@ -62,6 +63,7 @@ def test_vc_snippets( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main", store_msgs=1 ) ) + select_tab( "scenario" ) # generate a VC snippet _test_snippet( webdriver, "victory_conditions", { @@ -95,6 +97,7 @@ def test_players_snippets( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main", store_msgs=1 ) ) + select_tab( "scenario" ) # generate a PLAYERS snippet _test_snippet( webdriver, "players", { @@ -160,15 +163,20 @@ def test_edit_templates( webapp, webdriver ): webdriver.get( webapp.url_for( "main", edit_template_links=1 ) ) # try uploading a customized version of each template + def edit_template( template_id ): + """Edit a template.""" + elem = find_child( "#edit-template textarea" ) + elem.clear() + elem.send_keys( "EDITED TEMPLATE: {}".format( template_id ) ) + elem.send_keys( Keys.ESCAPE ) def test_template( template_id, orig_template_id ): """Test editing a template.""" + if template_id == "ob_setup": + return # nb: this requires special handling (done below) # edit the template elem = find_child( "a.edit-template-link[data-id='{}']".format( template_id ) ) webdriver.execute_script( "$(arguments[0]).click();", elem ) - elem = find_child( "#edit-template textarea" ) - elem.clear() - elem.send_keys( "EDITED TEMPLATE: {}".format( orig_template_id ) ) - elem.send_keys( Keys.ESCAPE ) + edit_template( orig_template_id ) # check that the new template is being used dismiss_notifications() elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) @@ -177,3 +185,17 @@ def test_edit_templates( webapp, webdriver ): lambda: get_clipboard() == "EDITED TEMPLATE: {}".format( orig_template_id ) ) for_each_template( test_template ) + + # customize the OB SETUP template + select_tab( "ob2" ) + elem = find_child( "#tabs-ob2 input[type='button'][data-id='ob_setup']" ) + elem.click() + edit_template( "ob_setup" ) + + # check that the new template is being used + from vasl_templates.webapp.tests.test_ob_setup import add_ob_setup + for player_id in range(1,2+1): + add_ob_setup( webdriver, player_id, "ob setup (ignored)" ) + elem = find_child( "#ob_setup-sortable_{} li input[type='button']".format( player_id ) ) + elem.click() + assert get_clipboard() == "EDITED TEMPLATE: ob_setup" diff --git a/vasl_templates/webapp/tests/test_ssr.py b/vasl_templates/webapp/tests/test_ssr.py index c76ca2a..1118d24 100644 --- a/vasl_templates/webapp/tests/test_ssr.py +++ b/vasl_templates/webapp/tests/test_ssr.py @@ -4,7 +4,7 @@ import html from selenium.webdriver.common.action_chains import ActionChains -from vasl_templates.webapp.tests.utils import find_child, find_children +from vasl_templates.webapp.tests.utils import select_tab, find_child, find_children from vasl_templates.webapp.tests.utils import get_clipboard, dismiss_notifications, click_dialog_button # --------------------------------------------------------------------- @@ -14,6 +14,7 @@ def test_ssr( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main" ) ) + select_tab( "scenario" ) # initialize expected = [] diff --git a/vasl_templates/webapp/tests/test_template_packs.py b/vasl_templates/webapp/tests/test_template_packs.py index 7288c76..ebd2edb 100644 --- a/vasl_templates/webapp/tests/test_template_packs.py +++ b/vasl_templates/webapp/tests/test_template_packs.py @@ -8,9 +8,10 @@ import base64 from selenium.webdriver.support.ui import Select from vasl_templates.webapp import snippets -from vasl_templates.webapp.tests.utils import select_menu_option, get_clipboard -from vasl_templates.webapp.tests.utils import get_stored_msg, set_stored_msg, dismiss_notifications, find_child -from vasl_templates.webapp.tests.utils import for_each_template +from vasl_templates.webapp.tests.test_ob_setup import add_ob_setup +from vasl_templates.webapp.tests.utils import select_tab, select_menu_option, dismiss_notifications +from vasl_templates.webapp.tests.utils import get_clipboard, get_stored_msg, set_stored_msg +from vasl_templates.webapp.tests.utils import for_each_template, find_child, find_children # --------------------------------------------------------------------- @@ -25,7 +26,13 @@ def test_individual_files( webapp, webdriver ): """Test uploading a customized version of the template.""" # make sure generating a snippet returns something dismiss_notifications() - elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) + if template_id == "ob_setup": + select_tab( "ob1" ) + add_ob_setup( webdriver, 1, "test ob setup" ) + elems = find_children( "#ob_setup-sortable_1 li input[type='button']" ) + elem = elems[0] + else: + elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) elem.click() assert get_clipboard() != "" # upload a new template @@ -36,7 +43,6 @@ def test_individual_files( webapp, webdriver ): select_menu_option( "template_pack" ) # make sure generating a snippet returns the new version dismiss_notifications() - elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) elem.click() assert get_clipboard() == "UPLOADED TEMPLATE" for_each_template( test_template ) @@ -71,7 +77,7 @@ def test_zip_files( webapp, webdriver ): assert get_stored_msg("_last-error_") is None # check that the uploaded templates are being used - _check_snippets( + _check_snippets( webdriver, lambda tid: "Customized {}.".format( tid.upper() ) ) @@ -100,7 +106,7 @@ def test_new_default_template_pack( webapp, webdriver, monkeypatch ): webdriver.get( webapp.url_for( "main" ) ) # check that the new templates are being used - _check_snippets( + _check_snippets( webdriver, lambda tid: "New default {}.".format( tid.upper() ) ) @@ -111,6 +117,7 @@ def test_nationality_data( webapp, webdriver ): # initialize webdriver.get( webapp.url_for( "main", store_msgs=1, template_pack_persistence=1 ) ) + select_tab( "scenario" ) # select the British as player 1 sel = Select( @@ -142,13 +149,19 @@ def test_nationality_data( webapp, webdriver ): # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def _check_snippets( func ): +def _check_snippets( webdriver, func ): """Check that snippets are being generated as expected.""" def test_template( template_id, orig_template_id ): """Test each template.""" dismiss_notifications() - elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) + if template_id == "ob_setup": + select_tab( "ob1" ) + add_ob_setup( webdriver, 1, "test ob setup" ) + elems = find_children( "#ob_setup-sortable_1 li input[type='button']" ) + elem = elems[0] + else: + elem = find_child( "input.generate[data-id='{}']".format( orig_template_id ) ) elem.click() assert get_clipboard() == func( template_id ) for_each_template( test_template ) diff --git a/vasl_templates/webapp/tests/utils.py b/vasl_templates/webapp/tests/utils.py index 90449b1..894846c 100644 --- a/vasl_templates/webapp/tests/utils.py +++ b/vasl_templates/webapp/tests/utils.py @@ -43,8 +43,8 @@ def for_each_template( func ): # test the standard templates for tab_id,template_ids in _STD_TEMPLATES.items(): - select_tab( tab_id ) for template_id in template_ids: + select_tab( tab_id ) if template_id.startswith( "ob_setup_" ): template_id2 = "ob_setup" elif template_id.startswith( "vehicles_" ): @@ -101,6 +101,15 @@ def set_template_params( params ): add_ssr( _webdriver, ssr ) continue + # check for OB set up (these require special handling) + if key in ("OB_SETUP_1","OB_SETUP_2"): + # add them in (nb: we don't consider any existing OB setup's) + from vasl_templates.webapp.tests.test_ob_setup import add_ob_setup #pylint: disable=cyclic-import + player_id = int( key[-1] ) + for entry in val: + add_ob_setup( _webdriver, player_id, entry.get("caption",""), entry.get("width","") ) + continue + # check for vehicles/ordnance (these require special handling) if key in ("VEHICLES_1","ORDNANCE_1","VEHICLES_2","ORDNANCE_2"): # add them in (nb: we don't consider any existing vehicles/ordnance)