Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
@ -0,0 +1,93 @@ |
||||
<html> <!-- vasl-templates:id {{SNIPPET_ID}} --> |
||||
|
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<style> {{CSS:common}} </style> |
||||
|
||||
<style> |
||||
|
||||
td { |
||||
width: 50px ; min-width: 50px ; |
||||
height: {%if TURN_TRACK_PREVIEW_MODE%} 50px {%else%} 43px {%endif%} ; |
||||
padding: 2px ; |
||||
border: 1px solid black ; |
||||
} |
||||
{% set RESET_TD = "min-width: unset ; height: unset ; padding: 0 ; border: none" %} |
||||
td.turn-no { |
||||
{{RESET_TD}} ; width: unset ; |
||||
text-align: center ; vertical-align: center ; font-size: 18px ; font-weight: bold ; |
||||
} |
||||
td.no-reinforce { {{RESET_TD}} ; width: 13px ; } |
||||
{# NOTE: We do the reinforcement flags as CSS backgrounds, since VASSAL is incredibly slow downloading normal images. #} |
||||
td.reinforce1 { {{RESET_TD}} ; width: 13px ; background: url("{{TURN_TRACK_FLAG_1}}") top left no-repeat ; vertical-align: top ; } |
||||
td.reinforce2 { {{RESET_TD}} ; width: 13px ; background: url("{{TURN_TRACK_FLAG_2}}") bottom right no-repeat ; vertical-align: bottom ; } |
||||
td.half-turn { |
||||
background: url("{{IMAGES_BASE_URL}}/turn-track-half-turn.png") bottom right no-repeat ; |
||||
background-size: contain ; {# nb: doesn't work in VASSAL, the image file needs to be the correct size :-/ #} |
||||
} |
||||
|
||||
{% if TURN_TRACK_PREVIEW_MODE %} |
||||
body { margin: 0 ; } |
||||
.reinforce1, .reinforce2 { opacity: 0 ; } |
||||
.click { width: 13px ; height: 13px ; cursor: pointer ; } |
||||
{%endif%} |
||||
|
||||
</style> |
||||
|
||||
</head> |
||||
|
||||
{% if TURN_TRACK_PREVIEW_MODE %} |
||||
<script> |
||||
// notify the parent window of a click on a reinforcement flag |
||||
function onFlagClick( turnNo, playerNo ) { |
||||
window.parent.postMessage( { |
||||
type: "FlagClick", |
||||
turnNo: turnNo, uiPlayerNo: playerNo |
||||
}, "*" ) ; |
||||
} |
||||
</script> |
||||
{%endif%} |
||||
|
||||
<table class="turn-track"> |
||||
|
||||
{% for row in TURN_TRACK_SQUARES %} |
||||
<tr> |
||||
|
||||
{% for turnSquare in row %} |
||||
<td {%if turnSquare[0] == TURN_TRACK_HALF_TURN%} class="half-turn" {%endif%} > |
||||
|
||||
<table style="width:100%;height:100%;"> <tr> |
||||
|
||||
<td id="flag-{{turnSquare[0]}}_1" width="100%" |
||||
class = {% if turnSquare[1] %} "reinforce1" {%else%} "no-reinforce" {%endif%} |
||||
> |
||||
{% if TURN_TRACK_PREVIEW_MODE %} |
||||
<div class="click" |
||||
onclick = "onFlagClick( {{turnSquare[0]}}, 1 )" |
||||
> </div> |
||||
{%endif%} |
||||
</td> |
||||
|
||||
<td class="turn-no"> {{turnSquare[0]}} </td> |
||||
|
||||
<td id="flag-{{turnSquare[0]}}_2" width="100%" |
||||
class = {% if turnSquare[2] and turnSquare[0] != TURN_TRACK_HALF_TURN %} "reinforce2" {%else%} "no-reinforce" {%endif%} |
||||
> |
||||
{% if TURN_TRACK_PREVIEW_MODE and turnSquare[0] != TURN_TRACK_HALF_TURN %} |
||||
<div class="click" |
||||
onclick = "onFlagClick( {{turnSquare[0]}}, 2 )" |
||||
> </div> |
||||
{%endif%} |
||||
</td> |
||||
|
||||
</tr> </table> |
||||
|
||||
</td> |
||||
{%endfor%} |
||||
|
||||
</tr> |
||||
{%endfor%} |
||||
|
||||
</table> |
||||
|
||||
</html> |
@ -0,0 +1,27 @@ |
||||
#turn-track { display: flex ; overflow: hidden ; } |
||||
.ui-dialog.turn-track .ui-dialog-titlebar { background: #80d0ff ; } |
||||
.ui-dialog.turn-track .ui-dialog-buttonpane { border: none ; margin-top: 0 !important ; padding-top: 0 !important ; } |
||||
|
||||
.ui-dialog.turn-track .controls { display: flex ; } |
||||
.ui-dialog.turn-track .controls .row { display: flex ; align-items: center ; } |
||||
.ui-dialog.turn-track .controls .select2 { margin: -1px 0 0 0.5em ; } |
||||
.ui-dialog.turn-track .controls input { margin-top: 0.25em ; } |
||||
.ui-dialog.turn-track .controls button.reset { |
||||
height: 26px ; width: 4.5em ; |
||||
margin-top: 1em ; padding: 2px 10px 2px 5px ; |
||||
display: none ; |
||||
} |
||||
|
||||
.ui-dialog.turn-track iframe { margin: -4px ; border: 0 ; overflow: scroll ; } |
||||
|
||||
/* special layout settings for a horizontal turn track */ |
||||
#turn-track.horz { flex-direction: column ; } |
||||
#turn-track.horz .controls { flex-direction: row ; } |
||||
#turn-track.horz .controls>div { margin: 0 1em 0 0 ; } |
||||
#turn-track.horz .preview { height: auto ; width: 100% ; margin: 10px 0 0 0 ; padding-bottom: 15px ; } |
||||
|
||||
/* special layout settings for a vertical turn track */ |
||||
#turn-track.vert { flex-direction: row ; } |
||||
#turn-track.vert .controls { flex-direction: column ; } |
||||
#turn-track.vert .controls>div { margin: 0 0 1em 0 ; } |
||||
#turn-track.vert .preview { height: 100% ; width: auto ; margin: 0 0 0 10px ; padding-right: 15px ; } |
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,309 @@ |
||||
DEFAULT_TURN_TRACK_TURNS_MIN = 6 ; |
||||
DEFAULT_TURN_TRACK_TURNS_MAX = 10 ; |
||||
|
||||
// NOTE: Reinforcement flags get clipped on turn 100, but this is unlikely to be an issue :-/
|
||||
_MAX_TURN_TRACK_TURNS = 100 ; |
||||
|
||||
gTurnTrackReinforcements = null ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
function editTurnTrackSettings() |
||||
{ |
||||
// initialize
|
||||
var $dlg, $iframe, iframeSeqNo=0 ; |
||||
// FUDGE! This should work as a local variable, but causes a weird problem where it doesn't get reset properly :-/
|
||||
gTurnTrackReinforcements = null ; |
||||
|
||||
function loadControls() { |
||||
// load the dialog controls
|
||||
$dlg.find( "select[name='nturns']" ).val( |
||||
$panel.find( "select[name='TURN_TRACK_NTURNS'].param" ).val() |
||||
).trigger("change" ) ; |
||||
var width = $panel.find( "input[name='TURN_TRACK_WIDTH']" ).val() ; |
||||
$dlg.find( "select[name='width']" ).val( |
||||
isNaN( parseInt( width ) ) ? "" : width |
||||
).trigger( "change" ) ; |
||||
$dlg.find( "input[name='vertical']" ).prop( "checked", |
||||
$panel.find( "input[name='TURN_TRACK_VERTICAL']" ).prop( "checked" ) |
||||
) ; |
||||
$dlg.find( "input[name='swap-players']" ).prop( "checked", |
||||
$panel.find( "input[name='TURN_TRACK_SWAP_PLAYERS']" ).prop( "checked" ) |
||||
) ; |
||||
// load the reinforcements
|
||||
var params = updatePreview( false ) ; |
||||
var args = parseTurnTrackParams( params ) ; |
||||
gTurnTrackReinforcements = { 1: args.reinforce1, 2: args.reinforce2 } ; |
||||
// update the UI
|
||||
updateUI() ; |
||||
} |
||||
|
||||
function onResetControls() { |
||||
// reset all the controls
|
||||
ask( "Reset turn track", "Do you want to reset the turn track?", { |
||||
ok: function() { |
||||
setTurnTrackNTurns( DEFAULT_TURN_TRACK_TURNS_MIN ) ; |
||||
$panel.find( "input[name='TURN_TRACK_WIDTH']" ).val( "" ) ; |
||||
$panel.find( "input[name='TURN_TRACK_VERTICAL']" ).prop( "checked", false ) ; |
||||
$panel.find( "input[name='TURN_TRACK_SWAP_PLAYERS']" ).prop( "checked", false ) ; |
||||
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_1']" ).val( "" ) ; |
||||
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_2']" ).val( "" ) ; |
||||
gTurnTrackReinforcements = null ; |
||||
loadControls() ; |
||||
} |
||||
} ) ; |
||||
} |
||||
|
||||
function initTurnCountSelect2( $sel ) { |
||||
// initialize the TURN COUNT droplist
|
||||
init_select2( |
||||
$sel, "4em", false, formatTurnTrackOption |
||||
).on( "select2:open", function() { |
||||
restrict_droplist_height( $(this) ) ; |
||||
} ).on( "change", function() { |
||||
setTurnTrackNTurns( $(this).val() ) ; |
||||
if ( $dlg ) |
||||
updateUI() ; |
||||
} ) ; |
||||
for ( var nTurns=1 ; nTurns <= _MAX_TURN_TRACK_TURNS ; nTurns += 0.5 ) |
||||
$sel.append( $( "<option value='" + nTurns + "'>" + nTurns + "</option>" ) ) ; |
||||
} |
||||
function initWidthSelect2( $sel ) { |
||||
// initialize the WIDTH droplist
|
||||
init_select2( |
||||
$sel, "3.5em", false, null |
||||
).on( "select2:open", function() { |
||||
restrict_droplist_height( $(this) ) ; |
||||
} ).on( "change", function() { |
||||
$panel.find( "input[name='TURN_TRACK_WIDTH']" ).val( $(this).val() ) ; |
||||
if ( $dlg ) |
||||
updateUI() ; |
||||
} ) ; |
||||
$sel.append( $( "<option value=''>-</option>" ) ) ; |
||||
for ( var i=1 ; i <= 30 ; ++i ) |
||||
$sel.append( $( "<option value='" + i + "'>" + i + "</option>" ) ) ; |
||||
} |
||||
|
||||
function syncCheckbox( $elem, $target ) { |
||||
// sync the target checkbox in the SCENARIO panel with the one in this dialog
|
||||
$target.prop( "checked", $elem.prop("checked") ) ; |
||||
updateUI() ; |
||||
} |
||||
|
||||
function updatePreview( showAllFlags ) { |
||||
|
||||
// generate the turn track snippet
|
||||
var params = unload_snippet_params( true, "turn_track" ) ; |
||||
if ( showAllFlags ) |
||||
params.TURN_TRACK.REINFORCEMENTS_1 = params.TURN_TRACK.REINFORCEMENTS_2 = makeCommaList( _MAX_TURN_TRACK_TURNS ) ; |
||||
params.TURN_TRACK_PREVIEW_MODE = true ; |
||||
var $btn = $( "button.generate[data-id='turn_track']" ) ; |
||||
var snippet = make_snippet( $btn, params, {}, false ).content ; |
||||
|
||||
// update the preview
|
||||
// NOTE: To minimize flickering, we load the snippet into a hidden <iframe>,
|
||||
// then replace the preview <iframe> with it.
|
||||
// FUDGE! We should be able to wait until the new iframe has finished loading before removing the old one,
|
||||
// but for some inexplicable reason, the remove doesn't work on Windows, and the iframe's just build up :-/
|
||||
// Instead, we remove the old iframe here, but by fiddling with opacity, we can avoid flicker. Sigh...
|
||||
var style = $iframe.attr( "style" ) ; |
||||
style.opacity = 0 ; |
||||
var $newFrame = $( "<iframe></iframe>", { style: style, "data-seqno": ++iframeSeqNo } ) ; |
||||
$iframe.after( $newFrame ) ; |
||||
$iframe.remove() ; |
||||
$iframe = $newFrame ; |
||||
$newFrame.attr( "srcdoc", snippet ).on( "load", function() { |
||||
// update the state of each reinforcement flag
|
||||
for ( var turnNo=1 ; turnNo <= _MAX_TURN_TRACK_TURNS ; ++turnNo ) { |
||||
for ( var playerNo=1 ; playerNo <= 2 ; ++playerNo ) |
||||
updateFlag( turnNo, playerNo ) ; |
||||
} |
||||
// install the new <iframe>
|
||||
$newFrame.attr( "id", "turn-track-preview" ).css( "opacity", 1 ) ; |
||||
// FUDGE! This works around a weird problem when we load a scenario with a vertical turn track
|
||||
// and show it in a turn track dialog that was previously showing a horizontal turn track :-/
|
||||
updateLayout() ; |
||||
} ) ; |
||||
|
||||
return params ; |
||||
} |
||||
|
||||
function onFlagClick( turnNo, playerNo ) { |
||||
// NOTE: This method gets called by a click handler in the snippet HTML.
|
||||
// toggle the player turn reinforcements
|
||||
if ( gTurnTrackReinforcements[playerNo][turnNo] ) |
||||
delete gTurnTrackReinforcements[playerNo][turnNo] ; |
||||
else |
||||
gTurnTrackReinforcements[playerNo][turnNo] = true ; |
||||
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_" + playerNo + "']" ).val( |
||||
Object.keys( gTurnTrackReinforcements[playerNo] ).join( "," ) |
||||
) ; |
||||
updateFlag( turnNo, playerNo ) ; |
||||
} |
||||
|
||||
function updateFlag( turnNo, playerNo ) { |
||||
// update the specified reinforcement flag
|
||||
$iframe.contents().find( "#flag-" + turnNo + "_" + flipPlayerNo2(playerNo) ).css( { |
||||
opacity: gTurnTrackReinforcements && gTurnTrackReinforcements[playerNo][turnNo] ? 1 : 0.4, |
||||
} ) ; |
||||
} |
||||
|
||||
function updateUI() { |
||||
// update the UI
|
||||
updateLayout() ; |
||||
updatePreview( true ) ; |
||||
} |
||||
|
||||
function updateLayout() { |
||||
// update the layout based on the direction of the turn track
|
||||
if ( $dlg.find( "input[name='vertical']" ).prop( "checked" ) ) { |
||||
// vertical layout
|
||||
$iframe.css( { position: "absolute", |
||||
top: "10px", height: "calc(100% - 20px)", left: "150px", width: "calc(100% - 160px)" |
||||
} ) ; |
||||
$dlg.addClass( "vert" ).removeClass( "horz" ) ; |
||||
$dlg.find( ".controls" ).addClass( "vert" ).removeClass( "horz" ) ; |
||||
$dlg.find( ".reset1" ).hide() ; |
||||
$dlg.find( ".reset2" ).show() ; |
||||
} else { |
||||
// horizontal layout
|
||||
$iframe.css( { position: "absolute", |
||||
top: "95px", height: "calc(100% - 105px)", left: "10px", width: "calc(100% - 20px)" |
||||
} ) ; |
||||
$dlg.addClass( "horz" ).removeClass( "vert" ) ; |
||||
$dlg.find( ".controls" ).addClass( "horz" ).removeClass( "vert" ) ; |
||||
$dlg.find( ".reset1" ).show() ; |
||||
$dlg.find( ".reset2" ).hide() ; |
||||
} |
||||
} |
||||
|
||||
// NOTE: Since Player 1 in the UI is Player 2 in the Turn Track template (by default),
|
||||
// and vice versa, we often need to flip the player numbers.
|
||||
function flipPlayerNo( playerNo ) { return parseInt(playerNo) === 1 ? 2 : 1 ; } |
||||
function flipPlayerNo2( playerNo ) { |
||||
if ( ! $panel.find("input[name='TURN_TRACK_SWAP_PLAYERS']").prop( "checked" ) ) |
||||
playerNo = flipPlayerNo( playerNo ) ; |
||||
return playerNo ; |
||||
} |
||||
|
||||
function makeCommaList( nVals ) { |
||||
// generate a comma-separated list of values
|
||||
var vals = [] ; |
||||
for ( var i=1 ; i <= nVals ; ++i ) |
||||
vals.push( i ) ; |
||||
return vals.join( "," ) ; |
||||
} |
||||
|
||||
// show the TURN TRACK dialog
|
||||
var $panel = $( "#panel-scenario" ) ; |
||||
$( "#turn-track" ).dialog( { |
||||
"title": "Turn track", |
||||
dialogClass: "turn-track", |
||||
modal: true, |
||||
minWidth: 500, minHeight: 250, |
||||
resizable: true, |
||||
create: function() { |
||||
// initialize the dialog
|
||||
init_dialog( $(this), "OK", true ) ; |
||||
initTurnCountSelect2( $(this).find( "select[name='nturns']" ) ) ; |
||||
initWidthSelect2( $(this).find( "select[name='width']" ) ) ; |
||||
$(this).find( "button.reset" ).button().on( |
||||
"click", onResetControls |
||||
) ; |
||||
// keep the settings in the SCENARIO panel in sync with the dialog
|
||||
$(this).find( "input[name='vertical']" ).on( "change", function() { |
||||
syncCheckbox( $(this), $panel.find("input[name='TURN_TRACK_VERTICAL']") ) ; |
||||
} ) ; |
||||
$(this).find( "input[name='swap-players']" ).on( "change", function() { |
||||
syncCheckbox( $(this), $panel.find("input[name='TURN_TRACK_SWAP_PLAYERS']") ) ; |
||||
} ) ; |
||||
// update the UI when the direction of the turn track is changed
|
||||
$(this).find( "input[name='vertical']" ).on( "change", function() { |
||||
updateUI() ; |
||||
} ) ; |
||||
// handle clicks on reinforcement flags in the turn track preview
|
||||
window.addEventListener( "message", function( evt ) { |
||||
if ( evt.data.type === "FlagClick" ) |
||||
onFlagClick( evt.data.turnNo, flipPlayerNo2(evt.data.uiPlayerNo) ) ; |
||||
} ) ; |
||||
|
||||
}, |
||||
open: function() { |
||||
// initialize the dialog
|
||||
var $btnPane = $( ".ui-dialog.turn-track .ui-dialog-buttonpane" ) ; |
||||
var $btn = $btnPane.find( "button.snippet" ) ; |
||||
$btn.prepend( |
||||
$( "<img src='" + gImagesBaseUrl+"/snippet.png" + "' style='height:0.9em;margin:0 0.35em -1px 0;'>" ) |
||||
) ; |
||||
$btn.css( { position: "absolute", left: 5 } ) ; |
||||
// load the dialog
|
||||
$dlg = $(this) ; |
||||
$iframe = $dlg.find( "iframe#turn-track-preview" ) ; |
||||
loadControls() ; |
||||
}, |
||||
buttons: { |
||||
Snippet: { text:" Snippet", class: "snippet", click: function( evt ) { |
||||
var $btn = $( "button.generate[data-id='turn_track']" ) ; |
||||
generate_snippet( $btn, evt.shiftKey, null ) ; |
||||
} }, |
||||
Close: function() { $(this).dialog( "close" ) ; }, |
||||
}, |
||||
} ) ; |
||||
} |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
function setTurnTrackNTurns( nTurns ) |
||||
{ |
||||
// select the specified number of turns
|
||||
updateTurnTrackNTurns( nTurns ) ; |
||||
$( "select[name='TURN_TRACK_NTURNS']" ).val( |
||||
nTurns |
||||
).trigger( "change" ) ; |
||||
} |
||||
|
||||
function updateTurnTrackNTurns( nTurns ) |
||||
{ |
||||
function makeExtraOption( val, caption ) { |
||||
return $( "<option class='extra' value='" + val + "'>" + caption + "</option>" ) ; |
||||
} |
||||
|
||||
// initialize
|
||||
var $sel = $( "select[name='TURN_TRACK_NTURNS']" ) ; |
||||
var $extra = $sel.find( "option.extra" ) ; |
||||
|
||||
// check if the specified number of turns is already in the droplist
|
||||
var $opt = $sel.find( "option[value='" + nTurns + "']" ) ; |
||||
if ( $opt.length > 0 ) { |
||||
// yup - check if it's the special extra entry
|
||||
if ( ! $opt.hasClass( "extra" ) ) { |
||||
// nope - we don't need it any more
|
||||
$extra.remove() ; |
||||
} |
||||
// check if the turn track has been disabled
|
||||
if ( nTurns === "" ) { |
||||
// yup - add a special extra entry to open the turn track dialog
|
||||
$sel.append( makeExtraOption( "(show-dialog)", "(more)" ) ) ; |
||||
} |
||||
} else { |
||||
// nope - add it as a special extra entry
|
||||
if ( $extra.length > 0 ) { |
||||
// FUDGE! If the special entry is already there, we delete and re-create it to get the select2 to work :-/
|
||||
$extra.remove() ; |
||||
} |
||||
var $opt2 = makeExtraOption( nTurns, nTurns ) ; |
||||
if ( nTurns < DEFAULT_TURN_TRACK_TURNS_MIN ) |
||||
$sel.find( "option[value='']" ).after( $opt2 ) ; |
||||
else |
||||
$sel.append( $opt2 ) ; |
||||
} |
||||
} |
||||
|
||||
function formatTurnTrackOption( opt ) { |
||||
// format the turn track <option> element
|
||||
if ( opt.id === "(show-dialog)" ) |
||||
return $( "<span style='font-size:80%;font-style:italic;color:#666;'>" + opt.text + "</span>" ) ; |
||||
if ( opt.text.substr( opt.text.length-2 ) === ".5" ) |
||||
return $( "<span>" + opt.text.substr( 0, opt.text.length-2 ) + "½" + "</span>" ) ; |
||||
return opt.text ; |
||||
} |
@ -0,0 +1,21 @@ |
||||
<div id="turn-track" style="display:none;"> |
||||
|
||||
<div class="controls"> |
||||
|
||||
<div style="display:flex;flex-direction:column;"> |
||||
<div class="row"> Turns: <select name="nturns"></select> </div> |
||||
<button class="reset reset1"> Reset </button> |
||||
</div> |
||||
|
||||
<div style="display:flex;flex-direction:column;"> |
||||
<div class="row"> Width: <select name="width"></select> </div> |
||||
<div> <input type="checkbox" name="vertical"> Vertical </input> </div> |
||||
<div> <input type="checkbox" name="swap-players"> Swap players </input> </div> |
||||
<button class="reset reset2"> Reset </button> |
||||
</div> |
||||
|
||||
</div> |
||||
|
||||
<iframe id="turn-track-preview"></iframe> |
||||
|
||||
</div> |
@ -0,0 +1,4 @@ |
||||
TURN TRACK: |
||||
{% for row in TURN_TRACK_SQUARES %} |
||||
{% for turnSquare in row %}[{{turnSquare}}] {%endfor%} |
||||
{%endfor%} |
@ -0,0 +1 @@ |
||||
Customized TURN_TRACK. |
@ -0,0 +1 @@ |
||||
New default TURN_TRACK. |
@ -0,0 +1,343 @@ |
||||
""" Test the turn track functionality. """ |
||||
|
||||
from selenium.webdriver.support.ui import Select |
||||
from selenium.webdriver.common.keys import Keys |
||||
|
||||
from vasl_templates.webapp.tests.utils import \ |
||||
init_webapp, get_turn_track_nturns, set_turn_track_nturns, select_droplist_val, get_droplist_vals, \ |
||||
SwitchFrame, unload_table, wait_for, wait_for_elem, wait_for_clipboard, find_child |
||||
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, save_scenario |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_turn_track_basic( webapp, webdriver ): |
||||
"""Test basic turn track functionality.""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||
init_webapp( webapp, webdriver ) |
||||
|
||||
# check the initial state of the UI |
||||
assert get_turn_track_nturns() == "" |
||||
assert not find_child( "button#turn-track-settings" ).is_displayed() |
||||
assert not find_child( ".snippet-control[data-id='turn_track']" ).is_displayed() |
||||
|
||||
# configure a number of turns |
||||
set_turn_track_nturns( 6 ) |
||||
assert find_child( "button#turn-track-settings" ).is_displayed() |
||||
assert find_child( ".snippet-control[data-id='turn_track']" ).is_displayed() |
||||
|
||||
# generate a snippet |
||||
assert _generate_turn_track_snippet( None ) == [ |
||||
[ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ] |
||||
] |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_turn_track_controls( webapp, webdriver ): |
||||
"""Test the basic controls for configuring the turn track.""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||
init_webapp( webapp, webdriver ) |
||||
|
||||
# show the turn track dialog |
||||
dlg = _show_turn_track_dialog( 6 ) |
||||
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||
_click_reinf_flag( 1, 1 ) |
||||
_click_reinf_flag( 2, 1 ) |
||||
_click_reinf_flag( 2, 2 ) |
||||
_click_reinf_flag( 3, 2 ) |
||||
|
||||
# change the width |
||||
_change_turn_track_width( dlg, 3 ) |
||||
def check_for_width(): |
||||
return _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,"player1",None), (2,"player1","player2"), (3,None,"player2") ], |
||||
[ (4,None,None), (5,None,None), (6,None,None) ] |
||||
] |
||||
wait_for( 2, check_for_width ) |
||||
|
||||
# swap the players |
||||
_swap_turn_track_players( dlg ) |
||||
def check_for_swap_players(): |
||||
return _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,None,"player2"), (2,"player1","player2"), (3,"player1",None) ], |
||||
[ (4,None,None), (5,None,None), (6,None,None) ] |
||||
] |
||||
wait_for( 2, check_for_swap_players ) |
||||
|
||||
# make the turn track vertical |
||||
_change_turn_track_direction( dlg ) |
||||
def check_for_vertical(): |
||||
return _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,None,"player2"), (3,"player1",None), (5,None,None) ], |
||||
[ (2,"player1","player2"), (4,None,None), (6,None,None) ] |
||||
] |
||||
wait_for( 2, check_for_vertical ) |
||||
|
||||
# reset the controls |
||||
find_child( "button.reset2" ).click() |
||||
ask = wait_for_elem( 2, ".ui-dialog.ask" ) |
||||
find_child( "button.ok", ask ).click() |
||||
def check_for_reset(): |
||||
return _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ] |
||||
] |
||||
wait_for( 2, check_for_reset ) |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_turn_track_reinforcements( webapp, webdriver ): |
||||
"""Test configuring reinforcements on the turn track.""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||
init_webapp( webapp, webdriver ) |
||||
|
||||
# show the turn track dialog |
||||
dlg = _show_turn_track_dialog( 6.5 ) |
||||
|
||||
# turn on some reinforcements, then check the snippet |
||||
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||
_click_reinf_flag( 2, 1 ) |
||||
_click_reinf_flag( 3, 1 ) |
||||
_click_reinf_flag( 3, 2 ) |
||||
_click_reinf_flag( 7, 1 ) |
||||
assert _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,None,None), (2,"player1",None), (3,"player1","player2"), |
||||
(4,None,None), (5,None,None) , (6,None,None), (7,"player1",None) |
||||
] |
||||
] |
||||
|
||||
# turn off some reinforcements, turn some on, then check the snippet |
||||
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||
_click_reinf_flag( 2, 2 ) |
||||
_click_reinf_flag( 3, 1 ) |
||||
_click_reinf_flag( 3, 2 ) |
||||
_click_reinf_flag( 5, 2 ) |
||||
_click_reinf_flag( 7, 1 ) |
||||
assert _generate_turn_track_snippet( dlg ) == [ |
||||
[ (1,None,None), (2,"player1","player2"), (3,None,None), |
||||
(4,None,None), (5,None,"player2") , (6,None,None), (7,None,None) |
||||
] |
||||
] |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_turn_track_persistence( webapp, webdriver ): |
||||
"""Test saving and loading turn track settings.""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||
init_webapp( webapp, webdriver, scenario_persistence=1 ) |
||||
|
||||
# show the turn track dialog |
||||
load_scenario( { |
||||
"PLAYER_1": "japanese", "PLAYER_2": "american", |
||||
"TURN_TRACK": { "NTURNS": 6.5 }, |
||||
} ) |
||||
dlg = _show_turn_track_dialog( None ) |
||||
|
||||
# configure the turn track |
||||
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||
_click_reinf_flag( 1, 1 ) |
||||
_click_reinf_flag( 2, 2 ) |
||||
_click_reinf_flag( 3, 1 ) |
||||
_click_reinf_flag( 3, 2 ) |
||||
_change_turn_track_width( dlg, 4 ) |
||||
_swap_turn_track_players( dlg ) |
||||
_change_turn_track_direction( dlg ) |
||||
|
||||
# check the snippet |
||||
expected = [ |
||||
[ (1,None,"player2"), (3,"player1","player2"), (5,None,None), (7,None,None) ], |
||||
[ (2,"player1",None) , (4,None,None), (6,None,None) ] |
||||
] |
||||
wait_for( 2, |
||||
lambda: _generate_turn_track_snippet( dlg ) == expected |
||||
) |
||||
|
||||
# save the scenario |
||||
dlg.send_keys( Keys.ESCAPE ) |
||||
saved_scenario = save_scenario() |
||||
assert saved_scenario["TURN_TRACK"] == { |
||||
"NTURNS": "6.5", |
||||
"WIDTH": "4", "VERTICAL": True, "SWAP_PLAYERS": True, |
||||
"REINFORCEMENTS_1": "2,3", "REINFORCEMENTS_2": "1,3", |
||||
} |
||||
assert _generate_turn_track_snippet( None ) == expected |
||||
|
||||
# reset the scenario |
||||
webdriver.refresh() |
||||
assert not find_child( "button.generate[data-id='turn_track']" ).is_displayed() |
||||
|
||||
# load the scenario and generate the snippet |
||||
load_scenario( saved_scenario ) |
||||
assert _generate_turn_track_snippet( None ) == expected |
||||
|
||||
# open the turn track dialog and check that the controls were loaded correctly |
||||
dlg = _show_turn_track_dialog( None ) |
||||
assert get_turn_track_nturns() == "6.5" |
||||
sel = Select( find_child( "select[name='width']", dlg ) ) |
||||
assert sel.first_selected_option.get_attribute( "value" ) == "4" |
||||
assert find_child( "input[name='vertical']" ).is_selected() |
||||
assert find_child( "input[name='swap-players']" ).is_selected() |
||||
assert _generate_turn_track_snippet( dlg ) == expected |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_turn_track_droplist( webapp, webdriver ): |
||||
"""Test updating entries in the turn track droplist (#turns).""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||
init_webapp( webapp, webdriver, scenario_persistence=1 ) |
||||
|
||||
# check the initial state of the droplist |
||||
assert _unload_turn_track_droplist() == [ |
||||
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)" |
||||
] |
||||
|
||||
# configure a number of turns that is not in the default list (small) |
||||
sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) ) |
||||
select_droplist_val( sel, "(show-dialog)" ) |
||||
sel2 = Select( find_child( "#turn-track select[name='nturns']" ) ) |
||||
select_droplist_val( sel2, "2" ) |
||||
assert _unload_turn_track_droplist() == [ |
||||
"", "2", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10" |
||||
] |
||||
|
||||
# configure a number of turns that is not in the default list (large) |
||||
select_droplist_val( sel2, "14.5" ) |
||||
expected = [ "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "14.5" ] |
||||
assert _unload_turn_track_droplist() == expected |
||||
|
||||
# save and reload the scenario |
||||
find_child( ".ui-dialog.turn-track" ).send_keys( Keys.ESCAPE ) |
||||
saved_scenario = save_scenario() |
||||
webdriver.refresh() |
||||
load_scenario( saved_scenario ) |
||||
assert _unload_turn_track_droplist() == expected |
||||
|
||||
# configure a number of turns that is in the default list |
||||
sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) ) |
||||
select_droplist_val( sel, "8" ) |
||||
assert _unload_turn_track_droplist() == [ |
||||
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10" |
||||
] |
||||
|
||||
# disable the turn track |
||||
select_droplist_val( sel, "" ) |
||||
assert _unload_turn_track_droplist() == [ |
||||
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)" |
||||
] |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def _show_turn_track_dialog( nturns ): |
||||
"""Show the TURN TRACK dialog.""" |
||||
if nturns: |
||||
set_turn_track_nturns( nturns ) |
||||
btn = wait_for_elem( 2, "button#turn-track-settings" ) |
||||
btn.click() |
||||
dlg = wait_for_elem( 2, ".ui-dialog.turn-track" ) |
||||
return dlg |
||||
|
||||
def _generate_turn_track_snippet( dlg ): |
||||
"""Generate a turn track snippet.""" |
||||
|
||||
# generate the snippet |
||||
btn = find_child( "button.snippet", dlg ) if dlg else find_child( "button.generate[data-id='turn_track']" ) |
||||
assert btn.is_displayed() |
||||
btn.click() |
||||
clipboard = wait_for_clipboard( 2, "<!-- vasl-templates:id", contains=True ) |
||||
|
||||
def get_reinforce_class( cell ): |
||||
cell_class = str( cell.xpath( ".//@class" )[0] ) |
||||
if cell_class == "reinforce1": |
||||
return "player1" |
||||
elif cell_class == "reinforce2": |
||||
return "player2" |
||||
else: |
||||
assert cell_class == "no-reinforce" |
||||
return None |
||||
|
||||
def unload_square( square ): |
||||
cells = square.xpath( ".//td" ) |
||||
assert len(cells) == 3 |
||||
return ( |
||||
int( cells[1].text ), |
||||
get_reinforce_class( cells[0] ), |
||||
get_reinforce_class( cells[2] ), |
||||
) |
||||
|
||||
# unload the snippet contents |
||||
squares = unload_table( "//table[@class='turn-track']", html=clipboard, unload=False ) |
||||
for row_no, row in enumerate(squares): |
||||
for col_no, square in enumerate(row): |
||||
squares[row_no][col_no] = unload_square( square ) |
||||
|
||||
return squares |
||||
|
||||
def _unload_turn_track_droplist(): |
||||
"""Get the available options in the turn track droplist.""" |
||||
keys = [] |
||||
prev_key = None |
||||
options = get_droplist_vals( Select( |
||||
find_child( "select[name='TURN_TRACK_NTURNS']" ) |
||||
) ) |
||||
for key, caption in options: |
||||
if key == "": |
||||
assert caption == "-" |
||||
elif key == "(show-dialog)": |
||||
assert caption == "(more)" |
||||
else: |
||||
assert key == caption |
||||
key2 = float( key ) |
||||
if prev_key: |
||||
assert key2 > prev_key |
||||
assert int( 10 * key2 ) % 10 in (0,5) |
||||
prev_key = key2 |
||||
keys.append( key ) |
||||
return keys |
||||
|
||||
def _click_reinf_flag( turn_no, player_no ): |
||||
"""Click on a reinforcement flag.""" |
||||
find_child( |
||||
"#flag-{}_{} .click".format( turn_no, player_no ) |
||||
).click() |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def _change_turn_track_width( dlg, width ): |
||||
"""Change the turn track width.""" |
||||
sel = Select( find_child( "select[name='width']", dlg ) ) |
||||
_wait_for_preview( dlg, |
||||
lambda: select_droplist_val( sel, width ) |
||||
) |
||||
|
||||
def _change_turn_track_direction( dlg ): |
||||
"""Toggle the direction of the turn track.""" |
||||
_wait_for_preview( dlg, |
||||
lambda: find_child( "input[name='vertical']", dlg ).click() |
||||
) |
||||
|
||||
def _swap_turn_track_players( dlg ): |
||||
"""Swap the turn track players.""" |
||||
_wait_for_preview( dlg, |
||||
lambda: find_child( "input[name='swap-players']", dlg ).click() |
||||
) |
||||
|
||||
def _wait_for_preview( dlg, func ): |
||||
"""Make a change to the preview, and wait for the UI to update.""" |
||||
# NOTE: The preview <iframe> is replaced with a new one, so we need to be ready for the old one to disappear. |
||||
iframe = find_child( "iframe", dlg ) |
||||
seqno = iframe.get_attribute( "data-seqno" ) |
||||
func() |
||||
def check_seqno(): |
||||
iframe = find_child( "iframe", dlg ) |
||||
if not iframe: |
||||
return False |
||||
return iframe.get_attribute( "data-seqno" ) != seqno |
||||
wait_for( 20, check_seqno ) |