Browse Source
Add basic acceptance tests for conversations
Add basic acceptance tests for conversations
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>pull/768/head
8 changed files with 751 additions and 0 deletions
-
5tests/acceptance/config/behat.yml
-
66tests/acceptance/features/bootstrap/ChatAncestorSetter.php
-
97tests/acceptance/features/bootstrap/ChatContext.php
-
75tests/acceptance/features/bootstrap/ConversationInfoContext.php
-
197tests/acceptance/features/bootstrap/ConversationListContext.php
-
101tests/acceptance/features/bootstrap/ParticipantListContext.php
-
155tests/acceptance/features/bootstrap/TalkAppContext.php
-
55tests/acceptance/features/conversation.feature
@ -0,0 +1,66 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
use Behat\Behat\Hook\Scope\BeforeScenarioScope; |
|||
|
|||
/** |
|||
* Helper trait to set the ancestor of the chat. |
|||
* |
|||
* The ChatContext provides steps to interact with and check the behaviour of |
|||
* the chat view. However, the ChatContext does not know the right chat ancestor |
|||
* that has to be used by the chat steps; this has to be set from other |
|||
* contexts, for example, when the user joins or leaves a call. |
|||
* |
|||
* Contexts that "know" that certain chat ancestor has to be used by the |
|||
* ChatContext steps should use this trait and call "setChatAncestorForActor" |
|||
* when needed. |
|||
*/ |
|||
trait ChatAncestorSetter { |
|||
|
|||
/** |
|||
* @var ChatContext |
|||
*/ |
|||
private $chatContext; |
|||
|
|||
/** |
|||
* @BeforeScenario |
|||
*/ |
|||
public function getSiblingChatContext(BeforeScenarioScope $scope) { |
|||
$environment = $scope->getEnvironment(); |
|||
|
|||
$this->chatContext = $environment->getContext("ChatContext"); |
|||
} |
|||
|
|||
/** |
|||
* Sets the chat ancestor to be used in the chat steps performed by the |
|||
* given actor. |
|||
* |
|||
* @param null|Locator $chatAncestor the chat ancestor |
|||
* @param Actor $actor the actor |
|||
*/ |
|||
private function setChatAncestorForActor($chatAncestor, Actor $actor) { |
|||
$this->chatContext->setChatAncestorForActor($chatAncestor, $actor); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
|
|||
class ChatContext implements Context, ActorAwareInterface { |
|||
|
|||
/** |
|||
* @var Actor |
|||
*/ |
|||
private $actor; |
|||
|
|||
/** |
|||
* @var array |
|||
*/ |
|||
private $chatAncestorsByActor; |
|||
|
|||
/** |
|||
* @var Locator |
|||
*/ |
|||
private $chatAncestor; |
|||
|
|||
/** |
|||
* @BeforeScenario |
|||
*/ |
|||
public function initializeChatAncestors() { |
|||
$this->chatAncestorsByActor = array(); |
|||
$this->chatAncestor = null; |
|||
} |
|||
|
|||
/** |
|||
* @param Actor $actor |
|||
*/ |
|||
public function setCurrentActor(Actor $actor) { |
|||
$this->actor = $actor; |
|||
|
|||
if (array_key_exists($actor->getName(), $this->chatAncestorsByActor)) { |
|||
$this->chatAncestor = $this->chatAncestorsByActor[$actor->getName()]; |
|||
} else { |
|||
$this->chatAncestor = null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Sets the chat ancestor to be used in the steps performed by the given |
|||
* actor from that point on (until changed again). |
|||
* |
|||
* This is meant to be called from other contexts, for example, when the |
|||
* user joins or leaves a video call. |
|||
* |
|||
* The ChatAncestorSetter trait can be used to reduce the boilerplate needed |
|||
* to set the chat ancestor from other contexts. |
|||
* |
|||
* @param null|Locator $chatAncestor the chat ancestor |
|||
* @param Actor $actor the actor |
|||
*/ |
|||
public function setChatAncestorForActor($chatAncestor, Actor $actor) { |
|||
$this->chatAncestorsByActor[$actor->getName()] = $chatAncestor; |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function chatView($chatAncestor) { |
|||
return Locator::forThe()->css(".chat")-> |
|||
descendantOf($chatAncestor)-> |
|||
describedAs("Chat view in Talk app"); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the chat is shown in the main view |
|||
*/ |
|||
public function iSeeThatTheChatIsShownInTheMainView() { |
|||
PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::chatView($this->chatAncestor), 10)->isVisible()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
|
|||
class ConversationInfoContext implements Context, ActorAwareInterface { |
|||
|
|||
use ActorAware; |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationInfoContainer() { |
|||
return Locator::forThe()->css(".detailCallInfoContainer")-> |
|||
descendantOf(TalkAppContext::sidebar())-> |
|||
describedAs("Conversation info container in the sidebar"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationNameEditableTextLabel() { |
|||
return Locator::forThe()->css(".room-name")-> |
|||
descendantOf(self::conversationInfoContainer())-> |
|||
describedAs("Conversation name label in conversation info"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function editConversationNameButton() { |
|||
return Locator::forThe()->css(".edit-button")-> |
|||
descendantOf(self::conversationNameEditableTextLabel())-> |
|||
describedAs("Edit conversation name button in conversation info"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationNameTextInput() { |
|||
return Locator::forThe()->xpath("//input[@type = 'text']")-> |
|||
descendantOf(self::conversationNameEditableTextLabel())-> |
|||
describedAs("Conversation name text input in conversation info"); |
|||
} |
|||
|
|||
/** |
|||
* @Given I rename the conversation to :newConversationName |
|||
*/ |
|||
public function iRenameTheConversationTo($newConversationName) { |
|||
$this->actor->find(self::conversationNameEditableTextLabel(), 10)->click(); |
|||
$this->actor->find(self::editConversationNameButton(), 2)->click(); |
|||
$this->actor->find(self::conversationNameTextInput(), 2)->setValue($newConversationName . "\r"); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,197 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
|
|||
class ConversationListContext implements Context, ActorAwareInterface { |
|||
|
|||
use ActorAware; |
|||
use ChatAncestorSetter; |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function appNavigation() { |
|||
return Locator::forThe()->id("app-navigation")-> |
|||
describedAs("App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function showCreateConversationDropdownButton() { |
|||
return Locator::forThe()->css("#oca-spreedme-add-room .select2-choice")-> |
|||
descendantOf(self::appNavigation())-> |
|||
describedAs("Show create conversation dropdown button in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationList() { |
|||
return Locator::forThe()->id("spreedme-room-list")-> |
|||
descendantOf(self::appNavigation())-> |
|||
describedAs("Conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationListItemFor($conversation) { |
|||
return Locator::forThe()->xpath("//a[normalize-space() = '$conversation']/ancestor::li")-> |
|||
descendantOf(self::conversationList())-> |
|||
describedAs("$conversation item in conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function activeConversationListItemFor($conversation) { |
|||
return Locator::forThe()->xpath("//a[normalize-space() = '$conversation']/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' active ')]")-> |
|||
descendantOf(self::conversationList())-> |
|||
describedAs("$conversation item in conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationMenuButtonFor($conversation) { |
|||
return Locator::forThe()->css(".app-navigation-entry-utils-menu-button button")-> |
|||
descendantOf(self::conversationListItemFor($conversation))-> |
|||
describedAs("Menu button for $conversation in conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function conversationMenuFor($conversation) { |
|||
return Locator::forThe()->css(".app-navigation-entry-menu")-> |
|||
descendantOf(self::conversationListItemFor($conversation))-> |
|||
describedAs("Menu for $conversation in conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
private static function conversationMenuItemFor($conversation, $item) { |
|||
return Locator::forThe()->xpath("//button[normalize-space() = '$item']")-> |
|||
descendantOf(self::conversationMenuFor($conversation))-> |
|||
describedAs("$item item in menu for $conversation in conversation list in App navigation"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function removeConversationFromListMenuItemFor($conversation) { |
|||
return self::conversationMenuItemFor($conversation, "Remove conversation from list"); |
|||
} |
|||
|
|||
/** |
|||
* @Given I create a group conversation |
|||
*/ |
|||
public function iCreateAGroupConversation() { |
|||
// When the Talk app is opened and there are no conversations the
|
|||
// dropdown is automatically shown, and when the dropdown is shown
|
|||
// clicking on the button to open it fails because it is covered by the
|
|||
// search field of the dropdown. Due to that first it is assumed that
|
|||
// the dropdown is shown and the item is directly clicked; if it was not
|
|||
// shown, then it is explicitly shown and after that the item is
|
|||
// clicked.
|
|||
try { |
|||
$this->actor->find(TalkAppContext::itemInSelect2DropdownFor("New group conversation"), 2)->click(); |
|||
} catch (NoSuchElementException $exception) { |
|||
$this->actor->find(self::showCreateConversationDropdownButton(), 10)->click(); |
|||
$this->actor->find(TalkAppContext::itemInSelect2DropdownFor("New group conversation"), 2)->click(); |
|||
} |
|||
|
|||
$this->setChatAncestorForActor(TalkAppContext::mainView(), $this->actor); |
|||
} |
|||
|
|||
/** |
|||
* @Given I create a one-to-one conversation with :userName |
|||
*/ |
|||
public function iCreateAOneToOneConversationWith($userName) { |
|||
// When the Talk app is opened and there are no conversations the
|
|||
// dropdown is automatically shown, and when the dropdown is shown
|
|||
// clicking on the button to open it fails because it is covered by the
|
|||
// search field of the dropdown. Due to that first it is assumed that
|
|||
// the dropdown is shown and the item is directly clicked; if it was not
|
|||
// shown, then it is explicitly shown and after that the item is
|
|||
// clicked.
|
|||
try { |
|||
$this->actor->find(TalkAppContext::itemInSelect2DropdownFor($userName), 2)->click(); |
|||
} catch (NoSuchElementException $exception) { |
|||
$this->actor->find(self::showCreateConversationDropdownButton(), 10)->click(); |
|||
$this->actor->find(TalkAppContext::itemInSelect2DropdownFor($userName), 2)->click(); |
|||
} |
|||
|
|||
$this->setChatAncestorForActor(TalkAppContext::mainView(), $this->actor); |
|||
} |
|||
|
|||
/** |
|||
* @Given I open the :conversation conversation |
|||
*/ |
|||
public function iOpenTheConversation($conversation) { |
|||
$this->actor->find(self::conversationListItemFor($conversation), 10)->click(); |
|||
} |
|||
|
|||
/** |
|||
* @Given I remove the :conversation conversation from the list |
|||
*/ |
|||
public function iRemoveTheConversationFromTheList($conversation) { |
|||
$this->actor->find(self::conversationMenuButtonFor($conversation), 10)->click(); |
|||
$this->actor->find(self::removeConversationFromListMenuItemFor($conversation), 2)->click(); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the :conversation conversation is not shown in the list |
|||
*/ |
|||
public function iSeeThatTheConversationIsNotShownInTheList($conversation) { |
|||
if (!WaitFor::elementToBeEventuallyNotShown( |
|||
$this->actor, |
|||
self::conversationListItemFor($conversation), |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { |
|||
PHPUnit_Framework_Assert::fail("The $conversation conversation is still shown in the list after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the :conversation conversation is active |
|||
*/ |
|||
public function iSeeThatTheConversationIsActive($conversation) { |
|||
PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::activeConversationListItemFor($conversation), 10)->isVisible()); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the :conversation conversation is not active |
|||
*/ |
|||
public function iSeeThatTheConversationIsNotActive($conversation) { |
|||
if (!WaitFor::elementToBeEventuallyNotShown( |
|||
$this->actor, |
|||
self::activeConversationListItemFor($conversation), |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { |
|||
PHPUnit_Framework_Assert::fail("The $conversation conversation is still active after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
|
|||
class ParticipantListContext implements Context, ActorAwareInterface { |
|||
|
|||
use ActorAware; |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function participantsTabView() { |
|||
return Locator::forThe()->id("participantsTabView")-> |
|||
describedAs("Participants tab in the sidebar"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function participantsList() { |
|||
return Locator::forThe()->css(".participantWithList")-> |
|||
descendantOf(self::participantsTabView())-> |
|||
describedAs("Participants list in the sidebar"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function itemInParticipantsListFor($participantName) { |
|||
return Locator::forThe()->xpath("//a/text()[normalize-space() = '$participantName']/..")-> |
|||
descendantOf(self::participantsList())-> |
|||
describedAs("Item for $participantName in the participants list"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function moderatorIndicatorFor($participantName) { |
|||
return Locator::forThe()->css(".participant-moderator-indicator")-> |
|||
descendantOf(self::itemInParticipantsListFor($participantName))-> |
|||
describedAs("Moderator indicator for $participantName in the participants list"); |
|||
} |
|||
|
|||
/** |
|||
* @return array |
|||
*/ |
|||
public function participantsListItems() { |
|||
return $this->actor->find(self::participantsList(), 10) |
|||
->getWrappedElement()->findAll('xpath', '/li'); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the number of participants shown in the list is :numberOfParticipants |
|||
*/ |
|||
public function iSeeThatTheNumberOfParticipantsShownInTheListIs($numberOfParticipants) { |
|||
$numberOfParticipantsMatchCallback = function() use ($numberOfParticipants) { |
|||
try { |
|||
return count($this->participantsListItems()) === intval($numberOfParticipants); |
|||
} catch (NoSuchElementException $exception) { |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
if (!Utils::waitFor( |
|||
$numberOfParticipantsMatchCallback, |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier(), |
|||
$timeoutStep = 1)) { |
|||
PHPUnit_Framework_Assert::fail("The number of participants is still not $numberOfParticipants after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that :participantName is shown in the list of participants as a moderator |
|||
*/ |
|||
public function iSeeThatIsShownInTheListOfParticipantsAsAModerator($participantName) { |
|||
PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::itemInParticipantsListFor($participantName), 10)); |
|||
PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::moderatorIndicatorFor($participantName), 10)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,155 @@ |
|||
<?php |
|||
|
|||
/** |
|||
* |
|||
* @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com) |
|||
* |
|||
* @license GNU AGPL version 3 or any later version |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
* |
|||
*/ |
|||
|
|||
use Behat\Behat\Context\Context; |
|||
|
|||
class TalkAppContext implements Context, ActorAwareInterface { |
|||
|
|||
use ActorAware; |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
private static function appMenu() { |
|||
return Locator::forThe()->id("appmenu")-> |
|||
describedAs("App menu in header"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function talkItemInAppMenu() { |
|||
return Locator::forThe()->xpath("/li[@data-id = 'spreed']")-> |
|||
descendantOf(self::appMenu())-> |
|||
describedAs("Talk item in app menu in header"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function mainView() { |
|||
return Locator::forThe()->id("app-content-wrapper")-> |
|||
describedAs("Main view in Talk app"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function emptyContent() { |
|||
return Locator::forThe()->id("emptycontent")-> |
|||
descendantOf(self::mainView())-> |
|||
describedAs("Empty content in main view"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function sidebar() { |
|||
return Locator::forThe()->id("app-sidebar")-> |
|||
describedAs("Sidebar in Talk app"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
private static function select2Dropdown() { |
|||
return Locator::forThe()->css("#select2-drop")-> |
|||
describedAs("Select2 dropdown in Talk app"); |
|||
} |
|||
|
|||
/** |
|||
* @return Locator |
|||
*/ |
|||
public static function itemInSelect2DropdownFor($text) { |
|||
return Locator::forThe()->xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' select2-result-label ')]//span/text()[normalize-space() = '$text']/ancestor::li")-> |
|||
descendantOf(self::select2Dropdown())-> |
|||
describedAs("Item in select2 dropdown for $text in Talk app"); |
|||
} |
|||
|
|||
/** |
|||
* @Given I open the Talk app |
|||
*/ |
|||
public function iOpenTheTalkApp() { |
|||
$this->actor->find(self::talkItemInAppMenu(), 10)->click(); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the current page is the Talk app |
|||
*/ |
|||
public function iSeeThatTheCurrentPageIsTheTalkApp() { |
|||
PHPUnit_Framework_Assert::assertStringStartsWith( |
|||
$this->actor->locatePath("/apps/spreed/"), |
|||
$this->actor->getSession()->getCurrentUrl()); |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the empty content message is shown in the main view |
|||
*/ |
|||
public function iSeeThatTheEmptyContentMessageIsShownInTheMainView() { |
|||
// The empty content always exists in the DOM, so it has to be explictly
|
|||
// waited for it to be visible instead of relying on the implicit wait
|
|||
// made to find the element.
|
|||
if (!WaitFor::elementToBeEventuallyShown( |
|||
$this->actor, |
|||
self::emptyContent(), |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { |
|||
PHPUnit_Framework_Assert::fail("The empty content was not shown yet after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the sidebar is open |
|||
*/ |
|||
public function iSeeThatTheSidebarIsOpen() { |
|||
// The sidebar always exists in the DOM, so it has to be explicitly
|
|||
// waited for it to be visible instead of relying on the implicit wait
|
|||
// made to find the element.
|
|||
if (!WaitFor::elementToBeEventuallyShown( |
|||
$this->actor, |
|||
self::sidebar(), |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { |
|||
PHPUnit_Framework_Assert::fail("The sidebar was not shown yet after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @Then I see that the sidebar is closed |
|||
*/ |
|||
public function iSeeThatTheSidebarIsClosed() { |
|||
if (!WaitFor::elementToBeEventuallyNotShown( |
|||
$this->actor, |
|||
self::sidebar(), |
|||
$timeout = 10 * $this->actor->getFindTimeoutMultiplier())) { |
|||
PHPUnit_Framework_Assert::fail("The sidebar is still shown after $timeout seconds"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @Given I have opened the Talk app |
|||
*/ |
|||
public function iHaveOpenedTheTalkApp() { |
|||
$this->iOpenTheTalkApp(); |
|||
$this->iSeeThatTheCurrentPageIsTheTalkApp(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
Feature: conversation |
|||
|
|||
Scenario: create a group conversation |
|||
Given I am logged in |
|||
And I have opened the Talk app |
|||
When I create a group conversation |
|||
Then I see that the "You" conversation is active |
|||
And I see that the chat is shown in the main view |
|||
And I see that the sidebar is open |
|||
And I see that the number of participants shown in the list is "1" |
|||
And I see that "user0" is shown in the list of participants as a moderator |
|||
|
|||
Scenario: create a one-to-one conversation |
|||
Given I am logged in |
|||
And I have opened the Talk app |
|||
When I create a one-to-one conversation with "admin" |
|||
Then I see that the "admin" conversation is active |
|||
And I see that the chat is shown in the main view |
|||
And I see that the sidebar is open |
|||
And I see that the number of participants shown in the list is "2" |
|||
And I see that "user0" is shown in the list of participants as a moderator |
|||
And I see that "admin" is shown in the list of participants as a moderator |
|||
|
|||
Scenario: rename a conversation |
|||
Given I am logged in |
|||
And I have opened the Talk app |
|||
And I create a group conversation |
|||
And I see that the "You" conversation is active |
|||
When I rename the conversation to "Test conversation" |
|||
Then I see that the "Test conversation" conversation is active |
|||
|
|||
Scenario: change between conversations |
|||
Given I am logged in |
|||
And I have opened the Talk app |
|||
And I create a group conversation |
|||
And I see that the "You" conversation is active |
|||
And I see that the number of participants shown in the list is "1" |
|||
And I create a one-to-one conversation with "admin" |
|||
And I see that the "You" conversation is not active |
|||
And I see that the "admin" conversation is active |
|||
And I see that the number of participants shown in the list is "2" |
|||
When I open the "You" conversation |
|||
Then I see that the "You" conversation is active |
|||
And I see that the "admin" conversation is not active |
|||
And I see that the number of participants shown in the list is "1" |
|||
|
|||
Scenario: remove a conversation from the list |
|||
Given I am logged in |
|||
And I have opened the Talk app |
|||
And I create a group conversation |
|||
And I see that the "You" conversation is active |
|||
When I remove the "You" conversation from the list |
|||
Then I see that the "You" conversation is not shown in the list |
|||
And I see that the empty content message is shown in the main view |
|||
And I see that the sidebar is closed |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue