PageRenderTime 49ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/QnA/tests/QnAEventsTest.php

http://github.com/vanillaforums/Garden
PHP | 248 lines | 182 code | 27 blank | 39 comment | 0 complexity | 9632a7b24f2241a9cbd3f6436f051d76 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * @author David Barbier <david.barbier@vanillaforums.com>
  4. * @copyright 2009-2020 Vanilla Forums Inc.
  5. * @license GPLv2
  6. */
  7. namespace VanillaTests\QnA;
  8. use Garden\Events\ResourceEvent;
  9. use Vanilla\Community\Events\DiscussionEvent;
  10. use Vanilla\Community\Events\DiscussionStatusEvent;
  11. use Vanilla\Dashboard\Models\RecordStatusModel;
  12. use Vanilla\QnA\Events\AnswerEvent;
  13. use Vanilla\QnA\Models\AnswerModel;
  14. use VanillaTests\APIv2\QnaApiTestTrait;
  15. use VanillaTests\SetupTraitsTrait;
  16. use VanillaTests\SiteTestTrait;
  17. use VanillaTests\EventSpyTestTrait;
  18. use VanillaTests\VanillaTestCase;
  19. /**
  20. * Test QnA events are working.
  21. */
  22. class QnAEventsTest extends VanillaTestCase {
  23. use SiteTestTrait, SetupTraitsTrait, EventSpyTestTrait, QnaApiTestTrait;
  24. /** @var AnswerModel */
  25. private $answerModel;
  26. /** @var int */
  27. private $categoryID;
  28. /** @var \DiscussionModel */
  29. private $discussionModel;
  30. /** @var \QnAPlugin */
  31. private $plugin;
  32. /**
  33. * Get the names of addons to install.
  34. *
  35. * @return string[] Returns an array of addon names.
  36. */
  37. protected static function getAddons(): array {
  38. return ["vanilla", "qna"];
  39. }
  40. /**
  41. * Instantiate fixtures.
  42. */
  43. public function setUp(): void {
  44. parent::setUp();
  45. $this->setUpTestTraits();
  46. $this->enableCaching();
  47. $this->categoryID = $this->createCategory()["categoryID"];
  48. $this->container()->call(function (\QnAPlugin $plugin, AnswerModel $answerModel, \DiscussionModel $discussionModel) {
  49. $this->answerModel = $answerModel;
  50. $this->discussionModel = $discussionModel;
  51. $this->plugin = $plugin;
  52. });
  53. }
  54. /**
  55. * This method is called before the first test of this test class is run.
  56. */
  57. public static function setUpBeforeClass(): void {
  58. parent::setUpBeforeClass();
  59. self::setUpBeforeClassTestTraits();
  60. }
  61. /**
  62. * Tests events dispatched when submitting and updating answers to a question:
  63. * - Submitting a question should show an inserted discussion as "unanswered"
  64. * - First answer submitted should show the discussion as "answered"
  65. * - Rejecting first answer should show the discussion is "rejected"
  66. * - Second answer should show the discussion as "answered"
  67. * - Once second answer is accepted, should show the discussion is "accepted"
  68. */
  69. public function testQnaChosenAnswerEvent() {
  70. // Ask a question, verify discussion insert event dispatched
  71. $question = $this->createQuestion([
  72. 'categoryID' => $this->categoryID,
  73. 'name' => 'Question 1',
  74. 'body' => 'Question 1'
  75. ]);
  76. $this->assertEventDispatched(
  77. $this->expectedResourceEvent(
  78. DiscussionEvent::class,
  79. ResourceEvent::ACTION_INSERT,
  80. [
  81. 'discussionID' => $question['discussionID'],
  82. 'type' => 'question',
  83. 'name' => 'Question 1',
  84. 'categoryID' => $this->categoryID,
  85. 'statusID' => RecordStatusModel::DISCUSSION_STATUS_UNANSWERED
  86. ]
  87. )
  88. );
  89. // First answer submitted - discussion is "answered"
  90. $answer1 = $this->createAnswer([
  91. 'discussionID' => $question['discussionID'],
  92. 'name' => 'Answer 1',
  93. 'body' => 'Answer 1'
  94. ]);
  95. $matchingDispatchedEvent = $this->assertEventDispatched(
  96. $this->expectedResourceEvent(
  97. DiscussionEvent::class,
  98. DiscussionStatusEvent::ACTION_DISCUSSION_STATUS,
  99. [
  100. 'discussionID' => $question['discussionID'],
  101. 'type' => 'question',
  102. 'name' => 'Question 1',
  103. 'categoryID' => $this->categoryID,
  104. 'statusID' => RecordStatusModel::DISCUSSION_STATUS_ANSWERED
  105. ]
  106. )
  107. );
  108. $assertStatusPayload =
  109. function (ResourceEvent $event, int $expectedStatusID, string $expectedStatusName) {
  110. $payload = $event->getPayload();
  111. $this->assertArrayHasKey('status', $payload);
  112. $this->assertThat(
  113. $payload['status'],
  114. $this->logicalAnd(
  115. $this->arrayHasKey('statusID'),
  116. $this->arrayHasKey('name'),
  117. $this->arrayHasKey('recordType'),
  118. $this->arrayHasKey('recordSubtype')
  119. )
  120. );
  121. $this->assertEquals($expectedStatusID, $payload['status']['statusID']);
  122. $this->assertEquals($expectedStatusName, $payload['status']['name']);
  123. $this->assertEquals('discussion', $payload['status']['recordType']);
  124. $this->assertEquals('question', $payload['status']['recordSubtype']);
  125. };
  126. $assertStatusPayload($matchingDispatchedEvent, RecordStatusModel::DISCUSSION_STATUS_ANSWERED, "Answered");
  127. // First answer is rejected - both answer and discussion "rejected"
  128. $patchResponse = $this->api()->patch("/comments/{$answer1['commentID']}/answer", ['status' => 'rejected']);
  129. $this->assertTrue($patchResponse->isSuccessful());
  130. $this->assertEventDispatched(
  131. $this->expectedResourceEvent(
  132. AnswerEvent::class,
  133. ResourceEvent::ACTION_UPDATE,
  134. [
  135. 'commentID' => $answer1['commentID'],
  136. 'discussionID' => $question['discussionID'],
  137. 'qnA' => "Rejected"
  138. ]
  139. )
  140. );
  141. $matchingDispatchedEvent = $this->assertEventDispatched(
  142. $this->expectedResourceEvent(
  143. DiscussionEvent::class,
  144. DiscussionStatusEvent::ACTION_DISCUSSION_STATUS,
  145. [
  146. 'discussionID' => $question['discussionID'],
  147. 'type' => 'question',
  148. 'name' => 'Question 1',
  149. 'categoryID' => $this->categoryID,
  150. 'statusID' => RecordStatusModel::DISCUSSION_STATUS_REJECTED
  151. ]
  152. )
  153. );
  154. $assertStatusPayload($matchingDispatchedEvent, RecordStatusModel::DISCUSSION_STATUS_REJECTED, "Rejected");
  155. // Second answer submitted - discussion status back to answered
  156. $answer2 = $this->createAnswer([
  157. 'discussionID' => $question['discussionID'],
  158. 'name' => 'Answer 2',
  159. 'body' => 'Answer 2'
  160. ]);
  161. $matchingDispatchedEvent = $this->assertEventDispatched(
  162. $this->expectedResourceEvent(
  163. DiscussionEvent::class,
  164. DiscussionStatusEvent::ACTION_DISCUSSION_STATUS,
  165. [
  166. 'discussionID' => $question['discussionID'],
  167. 'type' => 'question',
  168. 'name' => 'Question 1',
  169. 'categoryID' => $this->categoryID,
  170. 'statusID' => RecordStatusModel::DISCUSSION_STATUS_ANSWERED
  171. ]
  172. )
  173. );
  174. $assertStatusPayload($matchingDispatchedEvent, RecordStatusModel::DISCUSSION_STATUS_ANSWERED, "Answered");
  175. // Accept second answer - both answer and discussion updated as "accepted"
  176. $patchResponse = $this->api()->patch("/comments/{$answer2['commentID']}/answer", ['status' => 'accepted']);
  177. $this->assertTrue($patchResponse->isSuccessful());
  178. $this->assertEventDispatched(
  179. $this->expectedResourceEvent(
  180. AnswerEvent::class,
  181. ResourceEvent::ACTION_UPDATE,
  182. [
  183. 'commentID' => $answer2['commentID'],
  184. 'discussionID' => $question['discussionID'],
  185. 'qnA' => "Accepted"
  186. ]
  187. )
  188. );
  189. $matchingDispatchedEvent = $this->assertEventDispatched(
  190. $this->expectedResourceEvent(
  191. DiscussionEvent::class,
  192. DiscussionStatusEvent::ACTION_DISCUSSION_STATUS,
  193. [
  194. 'discussionID' => $question['discussionID'],
  195. 'type' => 'question',
  196. 'name' => 'Question 1',
  197. 'categoryID' => $this->categoryID,
  198. 'statusID' => RecordStatusModel::DISCUSSION_STATUS_ACCEPTED
  199. ]
  200. )
  201. );
  202. $assertStatusPayload($matchingDispatchedEvent, RecordStatusModel::DISCUSSION_STATUS_ACCEPTED, "Accepted");
  203. }
  204. /**
  205. * Verify basic functionality of count limiting for unanswered questions.
  206. */
  207. public function testUnansweredLimit(): void {
  208. $previousLimit = $this->plugin->getUnansweredCountLimit();
  209. try {
  210. $limit = 3;
  211. $this->plugin->setUnansweredCountLimit($limit);
  212. $this->createQuestion();
  213. $this->createQuestion();
  214. $this->createQuestion();
  215. $result = $this->bessy()->getJsonData("/discussions/unansweredcount");
  216. $this->assertSame("{$limit}+", $result["UnansweredCount"]);
  217. } finally {
  218. $this->plugin->setUnansweredCountLimit($previousLimit);
  219. }
  220. }
  221. }