diff --git a/src/PollVoteStorage.php b/src/PollVoteStorage.php index 93c7235f3..ae8dc10a4 100644 --- a/src/PollVoteStorage.php +++ b/src/PollVoteStorage.php @@ -25,6 +25,13 @@ class PollVoteStorage implements PollVoteStorageInterface { */ protected $cacheTagsInvalidator; + /** + * The poll vote for the current user keyed by Poll ID and User ID. + * + * @var array[] + */ + protected $currentUserVote = []; + /** * Constructs a new PollVoteStorage. * @@ -57,6 +64,8 @@ class PollVoteStorage implements PollVoteStorageInterface { // Deleting a vote means that any cached vote might not be updated in the // UI, so we need to invalidate them all. $this->cacheTagsInvalidator->invalidateTags(['poll-votes:' . $poll->id()]); + // Invalidate the static cache of votes. + $this->currentUserVote = []; } /** @@ -80,6 +89,8 @@ class PollVoteStorage implements PollVoteStorageInterface { // Deleting a vote means that any cached vote might not be updated in the // UI, so we need to invalidate them all. $this->cacheTagsInvalidator->invalidateTags(['poll-votes:' . $poll->id()]); + // Invalidate the static cache of votes. + $this->currentUserVote = []; } /** @@ -94,6 +105,8 @@ class PollVoteStorage implements PollVoteStorageInterface { // Deleting a vote means that any cached vote might not be updated in the // UI, so we need to invalidate them all. $this->cacheTagsInvalidator->invalidateTags(['poll-votes:' . $options['pid']]); + // Invalidate the static cache of votes. + $this->currentUserVote = []; } /** @@ -121,6 +134,11 @@ class PollVoteStorage implements PollVoteStorageInterface { */ public function getUserVote(PollInterface $poll) { $uid = \Drupal::currentUser()->id(); + $key = $poll->id() . ':' . $uid; + if (isset($this->currentUserVote[$key])) { + return $this->currentUserVote[$key]; + } + $this->currentUserVote[$key] = FALSE; if ($uid || $poll->getAnonymousVoteAllow()) { if ($uid) { $query = $this->connection->query("SELECT * FROM {poll_vote} WHERE pid = :pid AND uid = :uid", array( @@ -134,9 +152,9 @@ class PollVoteStorage implements PollVoteStorageInterface { ':hostname' => \Drupal::request()->getClientIp() )); } - return $query->fetchAssoc(); + $this->currentUserVote[$key] = $query->fetchAssoc(); } - return FALSE; + return $this->currentUserVote[$key]; } /** diff --git a/tests/src/FunctionalJavascript/PollVoteJavascriptTest.php b/tests/src/FunctionalJavascript/PollVoteJavascriptTest.php index d80908a01..7270098a7 100644 --- a/tests/src/FunctionalJavascript/PollVoteJavascriptTest.php +++ b/tests/src/FunctionalJavascript/PollVoteJavascriptTest.php @@ -208,6 +208,12 @@ class PollVoteJavascriptTest extends WebDriverTestBase { $this->assertCount(1, $this->cssSelect('.choice-title.is-current-selection')); $this->assertCount(4, $this->cssSelect('.choice-title.not-current-selection')); $this->assertTrue($page->hasButton('Cancel vote'), "'Cancel your vote' button appears."); + // Reload the page so that the messages are reset. + $this->drupalGet('poll/' . $this->poll->id()); + $page->pressButton('Cancel vote'); + $session->wait(1000, 'jQuery(".messages--status").length > 0'); + $this->assertTrue($page->hasContent('Your vote was cancelled.')); + $this->assertTrue($page->hasButton('Vote'), "Vote button appears."); } } diff --git a/tests/src/Kernel/PollKernelTestBase.php b/tests/src/Kernel/PollKernelTestBase.php new file mode 100644 index 000000000..99aef9d22 --- /dev/null +++ b/tests/src/Kernel/PollKernelTestBase.php @@ -0,0 +1,75 @@ +installSchema('poll', 'poll_vote'); + $this->installEntitySchema('poll'); + $this->installEntitySchema('poll_choice'); + } + + /** + * Creates and saves a poll. + * + * @param int $choice_count + * (optional) The number of choices to generate. Defaults to 7. + * + * @return \Drupal\poll\PollInterface + * A poll. + */ + public function createPoll($choice_count = 7) { + /** @var \Drupal\poll\PollInterface $poll */ + $poll = Poll::create([ + 'question' => $this->randomMachineName(), + ]); + $poll_choice_ids = []; + for ($i = 1; $i <= $choice_count; $i++) { + $poll_choice = PollChoice::create([ + 'choice' => $this->randomMachineName(), + ]); + $poll_choice->save(); + $poll_choice_ids[] = $poll_choice->id(); + } + $poll + ->set('anonymous_vote_allow', TRUE) + ->set('choice', $poll_choice_ids) + ->save(); + + return $poll; + } + + /** + * Saves a vote for a given poll. + */ + public function saveVote(PollInterface $poll, $choice_id) { + $options = []; + $options['chid'] = $choice_id; + $options['uid'] = \Drupal::currentUser()->id(); + $options['pid'] = $poll->id(); + $options['hostname'] = \Drupal::request()->getClientIp(); + $options['timestamp'] = \Drupal::time()->getRequestTime(); + /** @var \Drupal\poll\PollVoteStorage $vote_storage */ + $vote_storage = \Drupal::service('poll_vote.storage'); + $vote_storage->saveVote($options); + } + +} diff --git a/tests/src/Kernel/PollVoteStorageTest.php b/tests/src/Kernel/PollVoteStorageTest.php new file mode 100644 index 000000000..ae77811f2 --- /dev/null +++ b/tests/src/Kernel/PollVoteStorageTest.php @@ -0,0 +1,62 @@ +createPoll(); + $choice_id = $poll->get('choice')->first()->getValue()['target_id']; + $this->saveVote($poll, $choice_id); + + /** @var \Drupal\poll\PollVoteStorageInterface $poll_vote_storage */ + $poll_vote_storage = $this->container->get('poll_vote.storage'); + + $this->assertEquals($choice_id, $poll_vote_storage->getUserVote($poll)['chid']); + + // Test that a second poll returns the vote for that poll and not the first. + $second_poll = $this->createPoll(); + $another_choice_id = $second_poll->get('choice')->first()->getValue()['target_id']; + $this->saveVote($second_poll, $another_choice_id); + $this->assertEquals($another_choice_id, $poll_vote_storage->getUserVote($second_poll)['chid']); + + // Test that anonymous users with non-anonymous poll return FALSE. + $not_anonymous_poll = $this->createPoll(); + $not_anonymous_poll->setAnonymousVoteAllow(FALSE); + $poll->save(); + $this->assertFalse($poll_vote_storage->getUserVote($not_anonymous_poll)); + + // Test with an authenticated user. + $this->setUpCurrentUser(['uid' => 2]); + $choice_id = $poll->get('choice')->get(2)->target_id; + $this->saveVote($poll, $choice_id); + $this->assertEquals($choice_id, $poll_vote_storage->getUserVote($poll)['chid']); + + // Make sure we are using the stored value by removing the votes from the + // database and testing that we still get a value from our service. + /** @var \Drupal\Core\Database\Connection $connection */ + $connection = $this->container->get('database'); + $connection->delete('poll_vote')->execute(); + $this->assertEquals($choice_id, $poll_vote_storage->getUserVote($poll)['chid']); + } + +}