{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} module Spec where import TestPrelude test_Test = testGroup "Actions" [ testGroup "cast" [ prove "casts from hand to stack" $ do withLocation (Active, Hand) $ addCreature (1, 1) "Zombie" addMana "B" castFromLocation (Active, Hand) "B" "Zombie" validate "Zombie" $ matchLocation (Active, Stack) validateBoardEquals (manaPoolFor Active) mempty , prove "can cast as opponent" $ do as Opponent $ do withLocation (Opponent, Hand) $ addCreature (1, 1) "Zombie" addMana "B" castFromLocation (Opponent, Hand) "B" "Zombie" validate "Zombie" $ matchLocation (Opponent, Stack) validateBoardEquals (manaPoolFor Opponent) mempty , refute "requires card to be in hand" "Zombie does not match requirements: in location (Active,Hand)" $ do withLocation (Active, Play) $ addCreature (1, 1) "Zombie" cast "" "Zombie" ] , testGroup "castFromLocation" [ prove "places card on top of stack, spending mana" $ do withLocation (Active, Graveyard) $ addCreature (1, 1) "Zombie" addMana "B" castFromLocation (Active, Graveyard) "B" "Zombie" validate "Zombie" $ matchLocation (Active, Stack) validateBoardEquals (manaPoolFor Active) mempty , refute "requires mana be available" "Mana pool () does not contain (B)" $ do withLocation (Active, Hand) $ addCreature (1, 1) "Zombie" castFromLocation (Active, Hand) "B" "Zombie" , prove "can cast non-instant in second main" $ do withLocation (Active, Hand) $ addSorcery "Lava Spike" transitionTo SecondMain castFromLocation (Active, Hand) "" "Lava Spike" , refute "requires main phase for non-instant" "not in a main phase" $ do withLocation (Active, Hand) $ addSorcery "Lava Spike" transitionTo BeginCombat castFromLocation (Active, Hand) "" "Lava Spike" , refute "requires stack to be empty for non-instant" "stack is not empty" $ do withLocation (Active, Hand) $ addInstant "Shock" withLocation (Active, Hand) $ addSorcery "Lava Spike" castFromLocation (Active, Hand) "" "Shock" castFromLocation (Active, Hand) "" "Lava Spike" , prove "increases storm count if instant" $ do withLocation (Active, Hand) $ addInstant "Shock" castFromLocation (Active, Hand) "" "Shock" validateBoardEquals (counters . at "storm" . non 0) 1 , prove "increases storm count if sorcery" $ do withLocation (Active, Hand) $ addSorcery "Lava Spike" castFromLocation (Active, Hand) "" "Lava Spike" , prove "does not increase storm count otherwise" $ do withLocation (Active, Hand) $ addArtifact "Mox Amber" castFromLocation (Active, Hand) "" "Mox Amber" validateBoardEquals (counters . at "storm" . non 0) 0 ] , testGroup "spendMana" [ prove "removes colored mana from pool" $ do addMana "BBRRRWW" spendMana "RB" validateBoardEquals (manaPoolFor Active) "BRRWW" , prove "removes colorless mana from pool" $ do addMana "RWB" spendMana "3" validateBoardEquals (manaPoolFor Active) mempty , prove "removes multiple digits for colorless mana from pool" $ do addMana "9" addMana "9" spendMana "15" validateBoardEquals (manaPoolFor Active) "XXX" , prove "removes colored mana before colorless" $ do addMana "RWB" spendMana "1R" validateBoardEquals (manaPoolFor Active) "W" , refute "requires sufficient mana (colorless)" "Mana pool () does not contain (X)" $ do spendMana "1" , refute "requires sufficient mana (colored)" "Mana pool () does not contain (X)" $ do addMana "R" spendMana "2" , refute "requires right color mana" "Mana pool (R) does not contain (W)" $ do addMana "R" spendMana "W" ] , testGroup "addMana" [ prove "adds mana to pool" $ do addMana "2RG" validateBoardEquals (manaPool . at Active . _Just) "GRXX" , prove "adds mana with multiple digits to pool" $ do addMana "10" validateBoardEquals (manaPool . at Active . _Just) (replicate 10 'X') ] , testGroup "tap" [ prove "taps card in play" $ do withLocation (Active, Play) $ addLand "Forest" tap "Forest" validate "Forest" $ matchAttribute tapped , prove "taps card in opponent's play" $ do withLocation (Active, Play) $ addLand "Forest" tap "Forest" validate "Forest" $ matchAttribute tapped , refute "requires card exists" "Card does not exist: Forest" $ do tap "Forest" , refute "requires untapped" "not has attribute tapped" $ do withAttribute tapped $ withLocation (Active, Play) $ addLand "Forest" tap "Forest" , refute "requires in play" "in play" $ do withLocation (Active, Graveyard) $ addLand "Forest" tap "Forest" ] , testGroup "tapForMana" [ prove "taps card and adds mana to pool" $ do withLocation (Active, Play) $ addLand "Forest" tapForMana "G" "Forest" validate "Forest" $ matchAttribute tapped validateBoardEquals (manaPoolFor Active) "G" ] , testGroup "transitionTo" [ prove "moves to new state" $ do transitionTo BeginCombat validatePhase BeginCombat , prove "empties mana pool" $ do addMana "B" transitionTo BeginCombat validateBoardEquals manaPool mempty , refute "requires later phase" "FirstMain does not occur after BeginCombat" $ do transitionTo BeginCombat transitionTo FirstMain ] , testGroup "transitionToForced" [ prove "can transition backwards" $ do transitionTo BeginCombat transitionToForced FirstMain validatePhase FirstMain ] , testGroup "untap" [ prove "untaps card in play" $ do withLocation (Active, Play) $ withAttribute tapped $ addLand "Forest" untap "Forest" validate "Forest" $ missingAttribute tapped , prove "untaps card in opponent's play" $ do withLocation (Opponent, Play) $ withAttribute tapped $ addLand "Forest" untap "Forest" validate "Forest" $ missingAttribute tapped , refute "requires card exists" "Card does not exist: Forest" $ do untap "Forest" , refute "requires tapped" "has attribute tapped" $ do withLocation (Active, Play) $ addLand "Forest" untap "Forest" , refute "requires in play" "in play" $ do withLocation (Active, Graveyard) $ addLand "Forest" untap "Forest" ] , testGroup "splice" [ prove "splices on to an arcane spell" $ do withLocation (Active, Hand) $ do addInstant "Glacial Ray" withAttribute arcane $ addInstant "Lava Spike" addMana "RRR" cast "R" "Lava Spike" splice "Lava Spike" "1R" "Glacial Ray" resolveTop validate "Glacial Ray" $ matchLocation (Active, Hand) validateBoardEquals stack mempty validateBoardEquals (manaPoolFor Active) mempty , prove "can splice as opponent" $ do withLocation (Opponent, Hand) $ do addInstant "Glacial Ray" withAttribute arcane $ addInstant "Lava Spike" as Opponent $ do addMana "RRR" cast "R" "Lava Spike" splice "Lava Spike" "1R" "Glacial Ray" resolveTop validate "Glacial Ray" $ matchLocation (Opponent, Hand) validateBoardEquals stack mempty validateBoardEquals (manaPoolFor Opponent) mempty , refute "requires arcane" "has attribute arcane" $ do withLocation (Active, Hand) $ do addInstant "Glacial Ray" addInstant "Lava Spike" cast "" "Lava Spike" splice "Lava Spike" "" "Glacial Ray" , refute "requires spliced spell on stack" "Lava Spike not on stack" $ do withLocation (Active, Hand) $ do addInstant "Glacial Ray" withAttribute arcane $ addInstant "Lava Spike" splice "Lava Spike" "" "Glacial Ray" , refute "requires spell in hand" "Glacial Ray not in hand" $ do withLocation (Active, Graveyard) $ addInstant "Glacial Ray" withLocation (Active, Hand) $ withAttribute arcane $ addInstant "Lava Spike" cast "" "Lava Spike" splice "Lava Spike" "" "Glacial Ray" ] , testGroup "attackWith" [ prove "attacks with listed creatures" $ do withLocation (Active, Play) $ do addCreature (1, 1) "Bat 1" addCreature (1, 1) "Bat 2" addCreature (1, 1) "Bat 3" attackWith ["Bat 1", "Bat 2"] validatePhase DeclareAttackers validate "Bat 1" $ matchAttributes [attacking, tapped] validate "Bat 2" $ matchAttributes [attacking, tapped] validate "Bat 3" $ missingAttribute attacking validate "Bat 3" $ missingAttribute tapped , prove "does not tap vigilant attackers" $ do withLocation (Active, Play) $ do withAttribute vigilance $ addCreature (1, 1) "Bat" attackWith ["Bat"] validate "Bat" $ matchAttribute attacking validate "Bat" $ missingAttribute tapped , refute "prevents summoned creatures from attacking" "does not have summoning sickness" $ do withLocation (Active, Play) $ do withAttributes [summoned] $ addCreature (1, 1) "Bat" attackWith ["Bat"] , prove "allows hasty creatures to attack" $ do withLocation (Active, Play) $ do withAttributes [haste, summoned] $ addCreature (1, 1) "Bat" attackWith ["Bat"] validate "Bat" $ matchAttributes [attacking, tapped] ] ]