# Tests WebGUI::Asset::Wobject::Survey # # use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../../../lib"; use Test::More; use Test::Deep; use Test::MockObject::Extends; use Test::Exception; use Data::Dumper; use List::Util qw/shuffle/; use WebGUI::Test; # Must use this before any other WebGUI modules use WebGUI::Session; use Tie::IxHash; #---------------------------------------------------------------------------- # Init my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests my $tests = 60; plan tests => $tests + 1; #---------------------------------------------------------------------------- # put your tests here my $usedOk = use_ok('WebGUI::Asset::Wobject::Survey::ExpressionEngine'); my $e = "WebGUI::Asset::Wobject::Survey::ExpressionEngine"; WebGUI::Test->originalConfig('enableSurveyExpressionEngine'); $session->config->set( 'enableSurveyExpressionEngine', 0 ); is( $e->run( $session, 'jump { 1 } target' ), undef, "Nothing happens unless we turn on enableSurveyExpressionEngine in config" ); $session->config->set( 'enableSurveyExpressionEngine', 1 ); cmp_deeply( $e->run( $session, 'jump { 1 } target' ), { jump => 'target', tags => {} }, "..now we're in business!" ); my %values = ( n => 5, s1 => 'my string', multi => [ 'answer1', 'answer2' ], ); my %scores = ( n1 => 1, n2 => 2, ); # These should all jump to 'target' my @should_jump = ( q{jump { 1 } target}, q{jump { return 1 } target}, q{jump { "string" } target}, q{jump { value(n) == 5 } target}, q{jump { value(n) > 0 } target}, q{jump { value(s1) eq "my string" } target}, q{jump { value(s1) =~ m/my/ } target}, q{jump { value(n) == 4 or value(n) == 5 } target}, q{jump { value(n) == 5 && value(n) > 0 } target}, q{jump { (value(n) > 1 ? 10 : 11) == 10 } target}, q{jump { $a=1; $a++; $a++; $a *= 2; $a == 6 } target}, q{jump { @a = (1..10); $a[0] == 1 && @a == 10 } target}, # arrays q{jump { if (value(n) == 5) { 1 } else { 0 } } target}, # if statement q{jump { $q2 = 3; $avg = (value(n) + $q2) / 2; $avg == 4 } target}, # look ma, averages! q{jump { $q2 = 3; avg(value(n), $q2) == 4 } target}, # look ma, built-in avg sub! q{jump { round(3.456) == 3 && round(3.456, 2) == 3.46 } target}, # rounding q{jump { value(n) == 5 } target; jump { value(n) == 5 } targetX}, # first jump wins q{jump { value(n) == 0 } targetX; jump { value(n) == 5 } target}, # false jumps ignored q{jump { min(3,5,2) == 2 } target}, # List::Util min q{jump { sum(value(n),1,1,1) == 8 } target}, # List::Util sum, etc.. q{jump { score(n1) == 1 && score(n2) == 2 } target}, # score() works q{jump { answered(n) && !answered(X) } target}, # answered() works q{jump { value(multi) eq 'answer1, answer2' } target}, # multi-answer question stringifies in scalar context q{jump { (value(multi))[1] eq 'answer2' } target}, # multi-answer question returns list in list context q{ sub mySub { return $_[0] + 2 } jump { mySub(1) == 3 } target }, # expressions can define and use subs q{ jump { (sort { $a <=> $b } ( 5, 4, 3, 2 ))[1] == 3 } target }, # sorting allowed ); my @should_not_jump = ( q{}, # empty q{ return }, # empty q{1}, # doesn't call jump q{blah-dee-blah-blah}, # rubbish expression q{jump {} target}, # empty anon sub to jump q{jump { 0 } target}, # false sub to jump q{jump { value(n) == 500 } target}, q{jump { value(s1) eq 'blah' } target}, ); my @should_fail = ( q|{|, # doesn't compile q{jump { time } target}, # time and other opcodes not allowed ); # These ones should have 'target' as the jump target for my $expr (@should_jump) { cmp_deeply( $e->run( $session, $expr, { values => \%values, scores => \%scores, tags => {} } ), { jump => 'target', tags => {} }, "\"$expr\" jumps as expected" ); } # These ones should come back with an undefined jump target for my $expr (@should_not_jump) { cmp_deeply( $e->run( $session, $expr, { values => \%values, scores => \%scores, tags => {} } ), { jump => undef, tags => {} }, "\"$expr\" does not jump" ); } # These ones should return undef (general failure to run) for my $expr (@should_fail) { is( $e->run( $session, $expr, { values => \%values, scores => \%scores } ), undef,, "\"$expr\" fails as expected" ); } $e->run( $session, q{jump {$x = value(s1); $x = 'X'} target}, { values => \%values } ); is( $values{s1}, 'my string', "Expression can't modify values" ); like( $e->run( $session, '{', { validate => 1 } ), qr/Missing right curly/, "Validation option works" ); # Check validTargets option cmp_deeply( $e->run( $session, q{jump {1} target}, { values => \%values, validTargets => { a => 1 } } ), { jump => undef, tags => {} }, 'target is not valid' ); cmp_deeply( $e->run( $session, q{jump {1} target}, { values => \%values, validTargets => { target => 1 } } ), { jump => 'target', tags => {} }, '..whereas now it is ok' ); # Try some tagging cmp_deeply( $e->run( $session, q{}, { values => \%values } ), { jump => undef, tags => {} }, 'returns empty hash for tags by default' ); cmp_deeply( $e->run( $session, q{}, { values => \%values, tags => { a => 1 } } ), { jump => undef, tags => { a => 1 } }, 'existing tag values survive' ); cmp_deeply( $e->run( $session, q{ tag(a,2) }, { values => \%values, tags => { a => 1 } } ), { jump => undef, tags => { a => 2 } }, '..but can be changed' ); cmp_deeply( $e->run( $session, q{ tag(b) }, { values => \%values, tags => { a => 1 } } ), { jump => undef, tags => { a => 1, b => 1 } }, '..and new values can be set (defaults to 1)' ); cmp_deeply( $e->run( $session, q{ jump{ tagged(a) } target }, { values => \%values, tags => { a => 1 } } ), { jump => 'target', tags => { a => 1 } }, '..flag can be checked with tagged()' ); cmp_deeply( $e->run( $session, q{ jump{ tagged(a) eq 'abc' } target }, { values => \%values, tags => { a => 'abc' } } ), { jump => 'target', tags => { a => 'abc' } }, '..and any sort of tagged data returned' ); cmp_deeply( $e->run( $session, q{ tag(a,xyz); jump{ tagged(a) eq 'xyz' } target }, { values => {a => 'def'}, tags => { a => 'abc' } } ), { jump => 'target', tags => { a => 'xyz' } }, '..overwritten tag data can be used too' ); # Try the exitUrl sub cmp_deeply( $e->run( $session, q{ exitUrl(blah)} ), { exitUrl => 'blah', tags => { } }, 'explicit exitUrl works' ); cmp_deeply( $e->run( $session, q{ exitUrl()} ), { exitUrl => undef, tags => { } }, '..as does unspecified exitUrl' ); # Try the restart sub cmp_deeply( $e->run( $session, q{ restart} ), { restart => 1, tags => { } }, 'restart works' ); # Create a test user my $user = WebGUI::User->new( $session, 'new' ); WebGUI::Test->addToCleanup($user); # Create a Survey my $versionTag = WebGUI::VersionTag->getWorking($session); my $survey = WebGUI::Asset->getImportNode($session)->addChild( { className => 'WebGUI::Asset::Wobject::Survey', }, ); isa_ok($survey, 'WebGUI::Asset::Wobject::Survey'); WebGUI::Test->addToCleanup($survey); WebGUI::Test->addToCleanup($versionTag); my $url = $survey->get('url'); my $id = $survey->getId; $survey->surveyJSON->newObject([]); # s0 $survey->surveyJSON->newObject([0]); # s0q0 $survey->surveyJSON->newObject([0,0]); # s0q0a0 $survey->surveyJSON->newObject([0]); # s0q1 $survey->surveyJSON->newObject([0,1]); # s0q1a0 $survey->surveyJSON->section([0])->{variable} = 'ext_s0'; $survey->surveyJSON->question([0,0])->{variable} = 'ext_s0q0'; $survey->surveyJSON->question([0,1])->{variable} = 'ext_s0q1'; $survey->surveyJSON->answer([0,0,0])->{recordedAnswer} = 'ext_s0q0a0'; $survey->surveyJSON->answer([0,0,0])->{value} = 150; # worth 150 points $survey->surveyJSON->answer([0,1,0])->{recordedAnswer} = 'ext_s0q1a0'; $survey->surveyJSON->answer([0,1,0])->{value} = 50; # worth 50 points my $responseId = $survey->responseId( { userId => $user->userId } ); my $rJSON = $survey->responseJSON(undef, $responseId); $rJSON->recordResponses({ '0-0-0' => 'My ext_s0q0a0 answer', '0-1-0' => 'My ext_s0q1a0 answer', }); $rJSON->processExpression(q{ tag(ext_tag, 199) }); # Remember to persist our changes.. $survey->persistSurveyJSON(); $survey->persistResponseJSON(); $survey->surveyEnd; cmp_deeply( $e->run( $session, qq{jump {valueX('$id', ext_s0q0) eq 'ext_s0q0a0'} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'external value resolves ok when id used' ); cmp_deeply( $e->run( $session, qq{jump {valueX('$url', ext_s0q0) eq 'ext_s0q0a0'} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'external value resolves ok when url used' ); cmp_deeply( $e->run( $session, qq{jump {scoreX('$url', ext_s0q0) == 150} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'external score resolves ok too' ); cmp_deeply( $e->run( $session, qq{jump {scoreX('$url', ext_s0) == 200} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'external score section totals work too' ); cmp_deeply( $e->run( $session, qq{jump {taggedX('$url', ext_tag) == 199} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'external tag lookups work too' ); # Test for nasty bugs caused by file-scoped lexicals not being properly initialised in L { # Create a second test user my $survey2 = WebGUI::Asset::Wobject::Survey->new($session, $survey->getId); WebGUI::Test->addToCleanup($survey2); my $user2 = WebGUI::User->new( $session, 'new' ); WebGUI::Test->addToCleanup($user2); $session->user({userId => $user2->userId}); my $responseId2 = $survey2->responseId( { userId => $user2->userId } ); my $rJSON2 = $survey2->responseJSON(undef, $responseId2); $rJSON2->recordResponses({ '0-0-0' => 'My ext_s0q0a0 answer', '0-1-0' => 'My ext_s0q1a0 answer', }); $rJSON2->processExpression(q{ tag(ext_tag, 299) }); # Remember to persist our changes.. $survey2->persistSurveyJSON(); $survey2->persistResponseJSON(); $survey2->surveyEnd; cmp_deeply( $e->run( $session, qq{jump {taggedX('$url', ext_tag) == 299} target}, {userId => $user2->userId} ), { jump => 'target', tags => {} }, 'external tag not cached' ); cmp_deeply( $e->run( $session, qq{jump {taggedX('$url', ext_tag) == 199} target}, {userId => $user->userId} ), { jump => 'target', tags => {} }, 'first external tag lookups still works' ); } #vim:ft=perl