Added scenario notes.

master
Pacman Ghost 6 years ago
parent 2b3222fdaa
commit 84ca6726b5
  1. 13
      vasl_templates/webapp/data/default-template-pack/scenario_note.j2
  2. 13
      vasl_templates/webapp/static/css/main.css
  3. 20
      vasl_templates/webapp/static/css/tabs-scenario.css
  4. 9
      vasl_templates/webapp/static/main.js
  5. 18
      vasl_templates/webapp/static/simple_notes.js
  6. 19
      vasl_templates/webapp/static/snippets.js
  7. 15
      vasl_templates/webapp/templates/index.html
  8. 1
      vasl_templates/webapp/tests/fixtures/data/default-template-pack/scenario_note.j2
  9. 1
      vasl_templates/webapp/tests/fixtures/template-packs/full/scenario_note.j2
  10. 1
      vasl_templates/webapp/tests/fixtures/template-packs/new-default/scenario_note.j2
  11. 5
      vasl_templates/webapp/tests/test_ob.py
  12. 6
      vasl_templates/webapp/tests/test_scenario_persistence.py
  13. 81
      vasl_templates/webapp/tests/test_snippets.py
  14. 17
      vasl_templates/webapp/tests/test_template_packs.py
  15. 16
      vasl_templates/webapp/tests/utils.py

@ -0,0 +1,13 @@
<html>
<table>
<tr>
<td style="
{%if SCENARIO_NOTE_WIDTH%} width: {{SCENARIO_NOTE_WIDTH}} ; {%endif%}
">
{{SCENARIO_NOTE}}
</table>
</html>

@ -30,14 +30,11 @@ body { height: 100% ; }
grid-template-rows: 16em 1fr ; -ms-grid-rows: 14em 1fr ;
grid-template-columns: 28em 1fr ; -ms-grid-columns: 28em 1fr ;
}
#tabs-scenario fieldset.tr {
-ms-grid-row: 1 ; -ms-grid-column: 2 ;
}
#tabs-scenario fieldset.br {
grid-column-start: 2 ;
-ms-grid-row: 2 ; -ms-grid-column: 2 ;
}
/* FUDGE! IE hackamathon follows... */
#tabs-scenario fieldset.tl { -ms-grid-row: 1 ; -ms-grid-column: 1 ; }
#tabs-scenario fieldset.tr { -ms-grid-row: 1 ; -ms-grid-column: 2 ; }
#tabs-scenario fieldset.bl { -ms-grid-row: 2 ; -ms-grid-column: 1 ; }
#tabs-scenario fieldset.br { -ms-grid-row: 2 ; -ms-grid-column: 2 ; }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

@ -61,6 +61,26 @@
/* -------------------------------------------------------------------- */
#panel-scenario_notes {
height: 100% ;
display: grid ; display: -ms-grid ;
grid-template-rows: 1fr 2em ; -ms-grid-rows: 1fr 2em ;
}
/* FUDGE! IE hackamathon follows (nb: <label> doesn't work, we use <div> for labels instead :-/) */
#panel-scenario_notes .content { -ms-grid-row: 1 ; -ms-grid-column: 1 ; }
#panel-scenario_notes .footer { -ms-grid-row: 2 ; -ms-grid-column: 1 ; }
#panel-scenario_notes ul.sortable li input[type="button"] { float: right ; }
#panel-scenario_notes .footer { text-align: right ; font-size: 75% ; }
#panel-scenario_notes .footer .l { float: left ; }
#panel-scenario_notes #scenario_notes-trash { margin-left: 5px ; height: 2em ; }
#scenario_notes-hint { width:100% ; height: calc(100% - 1.5em) ; font-size: 80% ; font-style: italic ; }
#scenario_notes-hint p { margin-bottom: 1em ; }
/* -------------------------------------------------------------------- */
#panel-ssr {
height: 100% ;
display: grid ; display: -ms-grid ;

@ -88,6 +88,15 @@ $(document).ready( function () {
} ) ;
enable_ctrl_enter( $("#edit-ssr"), "OK" ) ;
// initialize the scenario notes
init_sortable( $("#scenario_notes-sortable"),
function() { add_scenario_note() ; },
edit_scenario_note
) ;
$("#panel-scenario_notes input[type='button'][data-id='scenario_note']").click( function() {
edit_template( "scenario_note" ) ;
} ) ;
// initialize the OB setups
init_sortable( $("#ob_setups-sortable_1"),
function() { add_ob_setup(1) ; },

@ -4,6 +4,10 @@
// --------------------------------------------------------------------
function add_scenario_note() { _do_edit_simple_note( $("#scenario_notes-sortable"), null ) ; }
function do_add_scenario_note( $sortable, data ) { _do_add_simple_note($sortable,data) ; }
function edit_scenario_note( $sortable, $entry ) { _do_edit_simple_note( $sortable, $entry ) ; }
function add_ob_setup( player_id ) { _do_edit_simple_note( $("#ob_setups-sortable_"+player_id), null ) ; }
function do_add_ob_setup( $sortable, data ) { _do_add_simple_note($sortable,data) ; }
function edit_ob_setup( $sortable, $entry ) { _do_edit_simple_note( $sortable, $entry ) ; }
@ -97,10 +101,16 @@ function _make_simple_note( note_type, caption )
// add a handler for the snippet button
$content.children("input[type='button']").click( function() {
var data = $(this).parent().parent().data( "sortable-data" ) ;
var prefix = (note_type === "ob_setups") ? "OB_SETUP" : "OB_NOTE" ;
var key ;
if ( note_type === "scenario_notes" )
key = "SCENARIO_NOTE" ;
else if ( note_type === "ob_setups" )
key = "OB_SETUP" ;
else if ( note_type == "ob_notes" )
key = "OB_NOTE" ;
var extra_params = {} ;
extra_params[prefix] = data.caption ;
extra_params[prefix+"_WIDTH"] = data.width ;
extra_params[key] = data.caption ;
extra_params[key+"_WIDTH"] = data.width ;
generate_snippet( $(this), extra_params ) ;
} ) ;
@ -113,6 +123,6 @@ function _get_note_type_for_sortable( $sortable )
{
// figure out what type of note the sortable has
var id = $sortable.prop( "id" ) ;
var match = /^(ob_(setups|notes))-sortable_\d$/.exec( id ) ;
var match = /^((scenario_notes|ob_setups|ob_notes))-sortable(_\d)?$/.exec( id ) ;
return match[1] ;
}

@ -504,6 +504,13 @@ function do_load_scenario( params )
continue ;
}
var player_id, $sortable ;
if ( key === "SCENARIO_NOTES" ) {
$sortable = $( "#scenario_notes-sortable" ) ;
for ( i=0 ; i < params[key].length ; ++i )
do_add_scenario_note( $sortable, params[key][i] ) ;
params_loaded[key] = true ;
continue ;
}
if ( key === "OB_SETUPS_1" || key === "OB_SETUPS_2" ) {
player_id = key.substring( key.length-1 ) ;
$sortable = $( "#ob_setups-sortable_" + player_id ) ;
@ -571,7 +578,7 @@ function do_load_scenario( params )
function on_save_scenario()
{
// unload the template parameters
function unload_ob_entries( $sortable ) {
function unload_sortable_entries( $sortable ) {
var entries = [] ;
$sortable.children("li").each( function() {
entries.push( $(this).data( "sortable-data" ) ) ;
@ -588,10 +595,11 @@ function on_save_scenario()
}
var params = {} ;
unload_params( params, false ) ;
params.OB_SETUPS_1 = unload_ob_entries( $("#ob_setups-sortable_1") ) ;
params.OB_SETUPS_2 = unload_ob_entries( $("#ob_setups-sortable_2") ) ;
params.OB_NOTES_1 = unload_ob_entries( $("#ob_notes-sortable_1") ) ;
params.OB_NOTES_2 = unload_ob_entries( $("#ob_notes-sortable_2") ) ;
params.SCENARIO_NOTES = unload_sortable_entries( $("#scenario_notes-sortable") ) ;
params.OB_SETUPS_1 = unload_sortable_entries( $("#ob_setups-sortable_1") ) ;
params.OB_SETUPS_2 = unload_sortable_entries( $("#ob_setups-sortable_2") ) ;
params.OB_NOTES_1 = unload_sortable_entries( $("#ob_notes-sortable_1") ) ;
params.OB_NOTES_2 = unload_sortable_entries( $("#ob_notes-sortable_2") ) ;
extract_vo_names( "VEHICLES_1" ) ;
extract_vo_names( "ORDNANCE_1" ) ;
extract_vo_names( "VEHICLES_2" ) ;
@ -639,6 +647,7 @@ function on_new_scenario( verbose )
update_ssr_hint() ;
// reset all the template parameters
delete_all_sortable_entries( $("#scenario_notes-sortable") ) ;
for ( var i=1 ; i <= 2 ; ++i ) {
delete_all_sortable_entries( $("#ob_setups-sortable_"+i) ) ;
delete_all_sortable_entries( $("#ob_notes-sortable_"+i) ) ;

@ -67,6 +67,21 @@
</div>
</div>
</fieldset>
<fieldset class="bl"> <legend>Notes</legend>
<div id="panel-scenario_notes">
<div class="content">
<div id="scenario_notes-hint"> <p>Click on the "+" below to add a new scenario note. <p>To re-order the scenario notes, use the mouse to drag them around. <p>Ctrl-click on a scenario note to delete it, or drag it into the trashcan below. </div>
<ul id="scenario_notes-sortable" class="sortable" style="display:none;"></ul>
</div>
<div class="footer">
<div class="l">
<input type="button" id="scenario_notes-add" value="+">
<img id="scenario_notes-trash" src="{{url_for('static',filename='images/trash.png')}}">
</div>
<input type="button" data-id="scenario_note" value="EDIT">
</div>
</div>
</fieldset>
<fieldset class="br"> <legend>SSR's</legend>
<div id="panel-ssr">
<div class="content">

@ -0,0 +1 @@
[{{SCENARIO_NOTE}}]{%if SCENARIO_NOTE_WIDTH%} (width=[{{SCENARIO_NOTE_WIDTH}}]){%endif%}

@ -65,9 +65,9 @@ def _do_test_ob_entries( webapp, webdriver, ob_type ):
return len(elems)
select_tab( "ob1" )
assert count_entries(1) == 2
elem = find_child( "#{}-sortable_1 li[2]".format( ob_type ) )
elems = find_children( "#{}-sortable_1 li".format( ob_type ) )
trash = find_child( "#{}-trash_1".format( ob_type ) )
ActionChains(webdriver).drag_and_drop( elem, trash ).perform()
ActionChains(webdriver).drag_and_drop( elems[1], trash ).perform()
assert count_entries(1) == 1
# delete an OB setup/note by emptying its caption
@ -214,6 +214,7 @@ def edit_ob_setup( webdriver, player_id, entry_no, caption, width ):
def _do_edit_ob_entry( webdriver, player_id, ob_type, entry_no, caption, width ):
"""Edit an OB setup/note."""
# locate the requested entry and start editing it
if entry_no is not None:
select_tab( "ob{}".format( player_id ) )

@ -30,6 +30,7 @@ def test_scenario_persistence( webapp, webdriver ):
"PLAYER_1": "russian", "PLAYER_1_ELR": "1", "PLAYER_1_SAN": "2",
"PLAYER_2": "german", "PLAYER_2_ELR": "3", "PLAYER_2_SAN": "4",
"VICTORY_CONDITIONS": "just do it!", "VICTORY_CONDITIONS_WIDTH": "102",
"SCENARIO_NOTES": [ { "caption": "note #1", "width": "" }, { "caption": "note #2", "width": "100px" } ],
"SSR": [ "This is an SSR.", "This is another SSR." ],
"SSR_WIDTH": "103",
},
@ -83,6 +84,8 @@ def test_scenario_persistence( webapp, webdriver ):
for tab_id in scenario_params:
select_tab( tab_id )
for field,val in scenario_params[tab_id].items():
if field == "SCENARIO_NOTES":
continue # nb: this requires special handling, we do it below
if field == "SSR":
continue # nb: this requires special handling, we do it below
if field in ("OB_SETUPS_1","OB_SETUPS_2"):
@ -99,6 +102,9 @@ def test_scenario_persistence( webapp, webdriver ):
assert Select(elem).first_selected_option.get_attribute("value") == val
else:
assert elem.get_attribute("value") == val
select_tab( "scenario" )
scenario_notes = [ c.text for c in find_children("#scenario_notes-sortable li") ]
assert scenario_notes == [ sn["caption"] for sn in scenario_params["scenario"]["SCENARIO_NOTES"] ]
ssrs = _get_ssrs()
assert ssrs == scenario_params["scenario"]["SSR"]
assert _get_ob_entries("ob_setups",1) == [ obs["caption"] for obs in scenario_params["ob1"]["OB_SETUPS_1"] ]

@ -1,11 +1,12 @@
""" Test HTML snippet generation. """
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from vasl_templates.webapp.tests.test_ob import add_ob_setup, add_ob_note
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
from vasl_templates.webapp.tests.utils import get_stored_msg, dismiss_notifications, find_child, find_children
from vasl_templates.webapp.tests.utils import for_each_template, wait_for, click_dialog_button
# ---------------------------------------------------------------------
@ -93,6 +94,68 @@ def test_vc_snippets( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def test_scenario_notes_snippets( webapp, webdriver ):
"""Test HTML snippet generation."""
# initialize
webdriver.get( webapp.url_for( "main", store_msgs=1 ) )
select_tab( "scenario" )
# add some scenario notes and check their snippets
def check_snippet( entry_no, expected ):
"""Check the snippet for a scenario note."""
elems = find_children( "#scenario_notes-sortable li input[type='button']" )
elems[entry_no].click()
assert get_clipboard() == expected
_add_scenario_note( webdriver, "scenario <i>note</i> #1", None )
_add_scenario_note( webdriver, "scenario note #2", "100px" )
check_snippet( 0, "[scenario <i>note</i> #1]" )
check_snippet( 1, "[scenario note #2] (width=[100px])" )
# delete a scenario note by dragging it into the trash
def count_entries():
"""Count the number of scenario notes."""
elems = find_children( "#scenario_notes-sortable li" )
return len(elems)
assert count_entries() == 2
elems = find_children( "#scenario_notes-sortable li" )
trash = find_child( "#scenario_notes-trash" )
ActionChains(webdriver).drag_and_drop( elems[0], trash ).perform()
assert count_entries() == 1
# delete scenario note by emptying its caption
_edit_scenario_note( webdriver, 0, "", None )
click_dialog_button( "OK" ) # nb: confirm the deletion
assert count_entries() == 0
def _add_scenario_note( webdriver, caption, width ): #FIXME! move to utils
"""Add a new scenario note."""
elem = find_child( "#scenario_notes-add" )
elem.click()
_edit_scenario_note( webdriver, None, caption, width )
def _edit_scenario_note( webdriver, entry_no, caption, width ): #FIXME! move to utils
"""Edit a scenario note."""
# locate the requested entry and start editing it
if entry_no is not None:
elems = find_children( "#scenario_notes-sortable li" )
elem = elems[ entry_no ]
ActionChains(webdriver).double_click( elem ).perform()
# edit the scenario note
if caption is not None:
elem = find_child( "#edit-simple_note textarea" )
elem.clear()
elem.send_keys( caption )
if width is not None:
elem = find_child( "#edit-simple_note input[type='text']" )
elem.clear()
elem.send_keys( width )
click_dialog_button( "OK" )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def test_players_snippets( webapp, webdriver ):
"""Test HTML snippet generation."""
@ -172,7 +235,7 @@ def test_edit_templates( webapp, webdriver ):
elem.send_keys( Keys.ESCAPE )
def test_template( template_id, orig_template_id ):
"""Test editing a template."""
if template_id in("ob_setup","ob_note"):
if template_id in("scenario_note","ob_setup","ob_note"):
return # nb: these require special handling (done below)
# edit the template
elem = find_child( "a.edit-template-link[data-id='{}']".format( template_id ) )
@ -187,6 +250,18 @@ def test_edit_templates( webapp, webdriver ):
)
for_each_template( test_template )
# customize the SCENARIO NOTE template
select_tab( "scenario" )
elem = find_child( "input[type='button'][data-id='scenario_note']" )
elem.click()
edit_template( "scenario_note" )
# check that the new template is being used
_add_scenario_note( webdriver, "scenario note (ignored)", None )
elem = find_child( "#scenario_notes-sortable li input[type='button']" )
elem.click()
assert get_clipboard() == "EDITED TEMPLATE: scenario_note"
# customize the OB SETUP template
select_tab( "ob1" )
elem = find_child( "#tabs-ob1 input[type='button'][data-id='ob_setup']" )

@ -26,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()
if template_id in ("ob_setup","ob_note"):
if template_id == "scenario_note":
from vasl_templates.webapp.tests.test_snippets import _add_scenario_note
select_tab( "scenario" )
_add_scenario_note( webdriver, "test scenario note", None )
elems = find_children( "#scenario_notes-sortable li input[type='button']" )
elem = elems[0]
elif template_id in ("ob_setup","ob_note"):
select_tab( "ob1" )
func = getattr( test_ob, "add_"+template_id )
func( webdriver, 1, "test {}".format(template_id), None )
@ -156,7 +162,14 @@ def _check_snippets( webdriver, expected ):
def test_template( template_id, orig_template_id ):
"""Test each template."""
dismiss_notifications()
if template_id in ("ob_setup","ob_note"):
# FIXME! this code is duplicated above
if template_id == "scenario_note":
from vasl_templates.webapp.tests.test_snippets import _add_scenario_note
select_tab( "scenario" )
_add_scenario_note( webdriver, "test scenario note", None )
elems = find_children( "#scenario_notes-sortable li input[type='button']" )
elem = elems[0]
elif template_id in ("ob_setup","ob_note"):
select_tab( "ob1" )
func = getattr( test_ob, "add_"+template_id )
func( webdriver, 1, "test {}".format(template_id), None )

@ -12,7 +12,7 @@ from selenium.common.exceptions import NoSuchElementException, StaleElementRefer
# standard templates
_STD_TEMPLATES = {
"scenario": [ "scenario", "players", "victory_conditions", "ssr" ],
"scenario": [ "scenario", "players", "victory_conditions", "scenario_notes", "ssr" ],
"ob1": [ "ob_setup_1", "ob_note_1", "vehicles_1", "ordnance_1" ],
"ob2": [ "ob_setup_2", "ob_note_2", "vehicles_2", "ordnance_2" ],
}
@ -29,7 +29,7 @@ _webdriver = None
# ---------------------------------------------------------------------
def for_each_template( func ):
def for_each_template( func ): #pylint: disable=too-many-branches
"""Test each template."""
# generate a list of all the templates we need to test
@ -45,7 +45,9 @@ def for_each_template( func ):
for tab_id,template_ids in _STD_TEMPLATES.items():
for template_id in template_ids:
select_tab( tab_id )
if template_id.startswith( "ob_setup_" ):
if template_id == "scenario_notes":
template_id2 = "scenario_note"
elif template_id.startswith( "ob_setup_" ):
template_id2 = "ob_setup"
elif template_id.startswith( "ob_note_" ):
template_id2 = "ob_note"
@ -95,6 +97,14 @@ def set_template_params( params ): #pylint: disable=too-many-branches
for key,val in params.items():
# check for scenario notes (these require special handling)
if key == "SCENARIO_NOTES":
# add them in (nb: we don't consider any existing scenario notes)
from vasl_templates.webapp.tests.test_snippets import _add_scenario_note #pylint: disable=cyclic-import
for entry in val:
_add_scenario_note( _webdriver, entry.get("caption",""), entry.get("width","") )
continue
# check for SSR's (these require special handling)
if key == "SSR":
# add them in (nb: we don't consider any existing SSR's)

Loading…
Cancel
Save