diff --git a/vasl_templates/tools/make_chapter_h_placeholders.py b/vasl_templates/tools/make_chapter_h_placeholders.py
index c501a19..d18f152 100755
--- a/vasl_templates/tools/make_chapter_h_placeholders.py
+++ b/vasl_templates/tools/make_chapter_h_placeholders.py
@@ -218,6 +218,11 @@ def load_vo_data_from_extension( fname ):
# get the extension ID
data = json.load( open( fname, "r" ) )
extn_id = data["extensionId"]
+ if extn_id == "08d":
+ # NOTE: All the vehicle/ordnance notes and multi-applicable notes in the Fight For Seoul extension
+ # actually reference those in K:FW (and there is code in the main application to handle this), so
+ # the user doesn't need to set anything up for FfS (other than what they already need to do for K:FW).
+ return results
# load the file
for nat in data:
diff --git a/vasl_templates/webapp/data/extensions/ffs.json b/vasl_templates/webapp/data/extensions/ffs.json
new file mode 100644
index 0000000..400bb1a
--- /dev/null
+++ b/vasl_templates/webapp/data/extensions/ffs.json
@@ -0,0 +1,44 @@
+{
+
+"extensionId": "08d",
+"version": "0.0",
+"displayName": "Fight For Seoul",
+"displayNameAbbrev": "FfS",
+
+"american": {
+
+"vehicles": [
+
+{ "name": "POA-CWS-H5",
+ "_comment_": "This was copied from kfw-uro/v:005. We can't use copy_from since K:FW is also an extension, and we don't control load order.",
+ "type": "MTv",
+ "CS#": 6,
+ "capabilities2": { "C": 5, "sM": 8 },
+ "note_number": "5\u2020",
+ "notes": [ "C", "M" ],
+ "comments": [ "TCA restrictions", "CE: MA, SA Fire NA", "Fire MA & SA NA" ],
+ "id": "ffs/v:000",
+ "gpid": "08d:15"
+}
+
+],
+
+"ordnance": [
+
+{ "name": "M20(L) 75mm Recoilless Rifle",
+ "_comment_": "This was copied from kfw-un-common/o:004. We can't use copy_from since K:FW is also an extension, and we don't control load order.",
+ "type": "RCL",
+ "capabilities": [ "H\u2020" ],
+ "capabilities2": { "WP": 7 },
+ "comments": [ "∞ H", "Crewed" ],
+ "note_number": "25\u2020",
+ "notes": [ "K", "M", "O", "P", "R" ],
+ "id": "ffs/o:000",
+ "gpid": "08d:75"
+}
+
+]
+
+}
+
+}
diff --git a/vasl_templates/webapp/data/vasl-overrides.json b/vasl_templates/webapp/data/vasl-overrides.json
index 9a51f1b..5a8fb21 100644
--- a/vasl_templates/webapp/data/vasl-overrides.json
+++ b/vasl_templates/webapp/data/vasl-overrides.json
@@ -142,6 +142,17 @@
"front_images": "us/veh/usSearchlight(KFW).png",
"back_images": null
}
+},
+
+"08d:75": {
+ "expected": {
+ "name": "RCL 75*",
+ "front_images": "amrcl75-malf.png",
+ "back_images": "dm-75rcl.gif"
+ },
+ "updated": {
+ "front_images": "amrcl75.png"
+ }
}
}
diff --git a/vasl_templates/webapp/static/snippets.js b/vasl_templates/webapp/static/snippets.js
index b97efff..0409c16 100644
--- a/vasl_templates/webapp/static/snippets.js
+++ b/vasl_templates/webapp/static/snippets.js
@@ -538,7 +538,11 @@ function get_vo_note_key( vo_entry )
return null ;
var key = match[0] ;
// NOTE: The K:FW counters appear in the main VASL module, but we handle them as if they were an extension.
- if ( vo_entry.extn_id )
+ if ( vo_entry.extn_id === "08d" ) {
+ // NOTE: All the FfS V/O and M/A notes actually reference K:FW (nb: there are only 2 American counters
+ // in this extension, so we can always map them to K:FW UN).
+ key = "kfw-un:" + key ;
+ } else if ( vo_entry.extn_id )
key = vo_entry.extn_id + ":" + key ;
else if ( vo_entry.id.match( /^kfw-(uro|bcfk|rok|ounc|un-common)\// ) )
key = "kfw-un:" + key ;
@@ -617,7 +621,9 @@ function get_ma_notes_keys( nat, vo_entries, vo_type )
for ( j=0 ; j < vo_entry.notes.length ; ++j ) {
// NOTE: The K:FW counters appear in the main VASL module, but we handle them as if they were an extension.
- var key = translate_kfw_key( vo_entry, j, /^kfw-(uro|bcfk|rok|ounc|un-common)\//, "kfw-un" ) ;
+ // NOTE: All the FfS V/O and M/A notes actually reference K:FW (nb: there are only 2 American counters
+ // in this extension, so we can always map them to K:FW UN).
+ var key = translate_kfw_key( vo_entry, j, /^(kfw-(uro|bcfk|rok|ounc|un-common)|ffs)\//, "kfw-un" ) ;
if ( key ) {
keys[0][ key ] = true ;
continue ;
diff --git a/vasl_templates/webapp/static/vassal.js b/vasl_templates/webapp/static/vassal.js
index e94858c..8cf4ea9 100644
--- a/vasl_templates/webapp/static/vassal.js
+++ b/vasl_templates/webapp/static/vassal.js
@@ -389,7 +389,8 @@ function _create_vo_entries_from_analysis( report )
return entries[0] ;
var entries2 = [] ;
for ( var i=0 ; i < entries.length ; ++i ) {
- var isKFW = entries[i].id.substr( 0, 4 ) === "kfw-" ;
+ var entry_id = entries[i].id ;
+ var isKFW = entry_id.substr(0,4) === "kfw-" || entry_id.substr(0,3) === "ffs" ;
if ( (theater == "Korea" && isKFW) || (theater != "Korea" && !isKFW) )
entries2.push( entries[i] ) ;
}
diff --git a/vasl_templates/webapp/tests/control_tests_servicer.py b/vasl_templates/webapp/tests/control_tests_servicer.py
index 02c2dc1..bb99c2c 100644
--- a/vasl_templates/webapp/tests/control_tests_servicer.py
+++ b/vasl_templates/webapp/tests/control_tests_servicer.py
@@ -194,7 +194,11 @@ class ControlTestsServicer( BaseControlTestsServicer ): #pylint: disable=too-man
self._log_request( request, context )
vassal_version = request.vassalVersion
# set the VASSAL engine
- if vassal_version:
+ if vassal_version == "random":
+ # NOTE: Some tests require VASSAL to be configured, and since they should all
+ # should behave in the same way, it doesn't matter which one we use.
+ dname = random.choice( list( self._vassal_engines.values() ) )
+ elif vassal_version:
dname = self._vassal_engines.get( vassal_version )
if not dname:
raise RuntimeError( "Unknown VASSAL version: {}".format( vassal_version ) )
diff --git a/vasl_templates/webapp/tests/fixtures/analyze-vsav/ffs.vsav b/vasl_templates/webapp/tests/fixtures/analyze-vsav/ffs.vsav
new file mode 100644
index 0000000..b26ffac
Binary files /dev/null and b/vasl_templates/webapp/tests/fixtures/analyze-vsav/ffs.vsav differ
diff --git a/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/ffs.vmdx b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/ffs.vmdx
new file mode 100644
index 0000000..1bee559
Binary files /dev/null and b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/ffs.vmdx differ
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1940.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1940.txt
index de97b7a..f3cf641 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1940.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1940.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1941.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1941.txt
index eef4657..c163d58 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1941.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1941.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1942.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1942.txt
index 2139d97..857ca52 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1942.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1942.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1943.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1943.txt
index 14b26dc..38ae09e 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1943.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1943.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1944.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1944.txt
index 111dbf0..fc3a527 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1944.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1944.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1945.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1945.txt
index 21a492b..a39847b 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1945.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/ordnance/american/1945.txt
@@ -30,6 +30,7 @@ M3 3-in. AA Gun
M1A1 90mm AA Gun 26† N No Move
M2 90mm AA Gun LF [90†, 1 ROF, B11] LF [90†, 1 ROF, B11] 27†[1] B†
20mm Oerlikon Mk4 1† US P 2 TK DR
+M20(L) 75mm Recoilless Rifle H† WP7 H† WP7 25† K M O P R ∞ H | Crewed
Type 89 Heavy Grenade Launcher 1† A P Range ≤ 2: [{ *:Air Bursts NA *:ROF 1 }] | Animal-Packed
M2 4.2-in. Mortar WP10 WP10 2† K M O P Y QSU | Area FP = 12
M3A1 37mm AT Gun C7 C7 3† C K P QSU
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1940.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1940.txt
index f859c29..8f0f444 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1940.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1940.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] CS 5[brewup]
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1941.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1941.txt
index c54a948..9f4bdbc 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1941.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1941.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] CS 5[brewup]
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1942.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1942.txt
index 7ec7c70..5c14dd8 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1942.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1942.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] CS 5[brewup]
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1943.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1943.txt
index 5810b58..b0c0428 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1943.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1943.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] CS 5[brewup]
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1944.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1944.txt
index cb3aa03..2513683 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1944.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1944.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] sM4 CS 5[brewu
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1945.txt b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1945.txt
index 84ee93a..234e143 100644
--- a/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1945.txt
+++ b/vasl_templates/webapp/tests/fixtures/vo-reports/vehicles/american/1945.txt
@@ -94,6 +94,7 @@ M4A3F WP7[J4+]†[3] s5[J4+] sM4[4+] CS 5[brewup] WP7†[3] s5 s
M4A3F(75)W WP7 s5 sM8 CS 6 WP7 s5 sM8 CS 6 13† US F†2 US G US R†1 US Y C Multiple Hits
M4A1F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 A5†[2] s5 sM8 CS 6 15† US A†2 US F†1 US G US P US Y C
M4A3F(76)W A4[A4]5[5]†[2] s5[5] sM8 CS 6 A5†[2] s5 sM8 CS 6 16† US A†2 US F†1 US G US P US Y C
+POA-CWS-H5 C5 sM8 CS 6 C5 sM8 CS 6 5† C M TCA restrictions | CE: MA, SA Fire NA | Fire MA & SA NA
M24 WP7 s5 sM8 CS 5 WP7 s5 sM8 CS 5 1† O Y Multiple Hits
M4A3E8 A†[1] s5 sM8 CS 6 A†[1] s5 sM8 CS 6 2† A†1 P ∞ A
M4A3E8(105) C7 H9 WP9 s7 sM8 CS 6 C7 H9 WP9 s7 sM8 CS 6 3† C M
diff --git a/vasl_templates/webapp/tests/test_vasl_extensions.py b/vasl_templates/webapp/tests/test_vasl_extensions.py
index 40d6b5b..86ae104 100644
--- a/vasl_templates/webapp/tests/test_vasl_extensions.py
+++ b/vasl_templates/webapp/tests/test_vasl_extensions.py
@@ -10,9 +10,10 @@ from selenium.webdriver.common.keys import Keys
from vasl_templates.webapp.utils import TempFile
from vasl_templates.webapp.tests.utils import init_webapp, set_player, select_tab, new_scenario, \
- find_child, find_children, wait_for_clipboard
+ find_child, find_children, wait_for_clipboard, generate_sortable_entry_snippet
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario
from vasl_templates.webapp.tests.test_vehicles_ordnance import add_vo
+from vasl_templates.webapp.tests.test_vassal import analyze_vsav
_TEST_VASL_EXTN_FNAME = "test-vasl-extension.zip"
@@ -339,6 +340,98 @@ def test_bfp_extensions2( webapp, webdriver ):
# ---------------------------------------------------------------------
+def test_ffs_extensions( webapp, webdriver ):
+ """Test the Fight For Seoul extension."""
+
+ # check if the remote webapp server supports this test
+ if not webapp.control_tests.has_capability( "chapter-h" ):
+ return
+
+ # analyze a VASL scenario that has the FfS counters
+ webapp.control_tests \
+ .set_vassal_version( "random" ) \
+ .set_vasl_version( "random", None ) # nb: we don't load the extension
+ init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
+ set_player( 1, "american" )
+ analyze_vsav( "ffs.vsav",
+ [ [], [] ],
+ [ [], [] ],
+ [ "No vehicles/ordnance were imported." ]
+ )
+
+ # analyze the same VASL scenario with the FfS extension loaded
+ webapp.control_tests \
+ .set_data_dir( "{REAL}" ) \
+ .set_vassal_version( "random" ) \
+ .set_vasl_version( "random", "{REAL}" ) \
+ .set_vo_notes_dir( "{REAL}" )
+ init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
+ set_player( 1, "american" )
+ analyze_vsav( "ffs.vsav",
+ [ [ "ffs/v:000" ], [ "ffs/o:000" ] ],
+ [ [], [] ],
+ [ "Imported 1 American vehicle and 1 ordnance." ]
+ )
+
+ # NOTE: All the vehicle/ordnance and multi-applicable notes in the FfS extension
+ # actually refer to K:FW, so we want to make sure we get the correct ones.
+ select_tab( "ob1" )
+
+ # check the vehicle's OB snippet
+ btn = find_child( "button.generate[data-id='ob_vehicles_1']" )
+ btn.click()
+ wait_for_clipboard( 2, re.compile(
+ 'POA-CWS-H5'
+ '.+