зеркало из https://github.com/nextcloud/spreed.git
Merge pull request #768 from nextcloud/add-acceptance-tests
Add acceptance tests
This commit is contained in:
Коммит
0c159d5b7f
26
.drone.yml
26
.drone.yml
|
@ -151,6 +151,21 @@ pipeline:
|
|||
when:
|
||||
matrix:
|
||||
TESTS: jsunit
|
||||
acceptance-conversation:
|
||||
image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2
|
||||
commands:
|
||||
# Pre-setup steps
|
||||
- git clone --depth 1 -b master https://github.com/nextcloud/server ../server
|
||||
- cp -R . ../server/apps/spreed
|
||||
- cd ../server
|
||||
- git submodule update --init
|
||||
- ln --symbolic `pwd` /var/www/html
|
||||
|
||||
# Run acceptance tests
|
||||
- tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-conversation --selenium-server selenium:4444 allow-git-repository-modifications features/conversation.feature
|
||||
when:
|
||||
matrix:
|
||||
TESTS-ACCEPTANCE: conversation
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
@ -171,6 +186,8 @@ matrix:
|
|||
DB: pgsql
|
||||
DATABASEHOST: postgres-10
|
||||
- TESTS: jsunit
|
||||
- TESTS: acceptance
|
||||
TESTS-ACCEPTANCE: conversation
|
||||
|
||||
services:
|
||||
mysql-5.7:
|
||||
|
@ -191,3 +208,12 @@ services:
|
|||
when:
|
||||
matrix:
|
||||
DATABASEHOST: postgres-10
|
||||
selenium:
|
||||
image: selenium/standalone-firefox:2.53.1-beryllium
|
||||
environment:
|
||||
# Reduce default log level for Selenium server (INFO) as it is too
|
||||
# verbose.
|
||||
- JAVA_OPTS=-Dselenium.LOGGER.level=WARNING
|
||||
when:
|
||||
matrix:
|
||||
TESTS: acceptance
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
default:
|
||||
autoload:
|
||||
'': %paths.base%/../features/bootstrap
|
||||
suites:
|
||||
default:
|
||||
paths:
|
||||
- %paths.base%/../features
|
||||
contexts:
|
||||
# Base contexts
|
||||
- ActorContext
|
||||
- NextcloudTestServerContext:
|
||||
nextcloudTestServerHelper: NextcloudTestServerLocalApacheHelper
|
||||
|
||||
- FeatureContext
|
||||
- FileListContext
|
||||
- FilesAppContext
|
||||
- LoginPageContext
|
||||
|
||||
# Talk app contexts
|
||||
- ChatContext
|
||||
- ConversationInfoContext
|
||||
- ConversationListContext
|
||||
- ParticipantListContext
|
||||
- TalkAppContext
|
||||
|
||||
extensions:
|
||||
Behat\MinkExtension:
|
||||
sessions:
|
||||
default:
|
||||
selenium2: ~
|
||||
John:
|
||||
selenium2: ~
|
||||
Jane:
|
||||
selenium2: ~
|
|
@ -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
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
|
||||
# @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/>.
|
||||
|
||||
# Helper script to install and configure the Nextcloud server as expected by the
|
||||
# acceptance tests.
|
||||
#
|
||||
# This script is not meant to be called manually; it is called when needed by
|
||||
# the acceptance tests launchers.
|
||||
#
|
||||
# It simply extends the default script by enabling the Talk app so it is already
|
||||
# available when the acceptance tests are run.
|
||||
|
||||
set -o errexit
|
||||
|
||||
tests/acceptance/installAndConfigureServer.sh "$@"
|
||||
|
||||
php occ app:enable spreed
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
|
||||
# @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/>.
|
||||
|
||||
# Helper script to run the acceptance tests, which test a running Nextcloud
|
||||
# Talk instance from the point of view of a real user.
|
||||
#
|
||||
# It simply calls the main "run.sh" script from the Nextcloud server setting a
|
||||
# specific acceptance tests directory (the one for the Talk app), and as such it
|
||||
# is expected that the grandparent directory of the root directory of the Talk
|
||||
# app is the root directory of the Nextcloud server.
|
||||
|
||||
set -o errexit
|
||||
|
||||
# Ensure working directory is script directory, as it is expected when the
|
||||
# script from the server is called.
|
||||
cd "$(dirname $0)"
|
||||
|
||||
../../../../tests/acceptance/run.sh --acceptance-tests-dir apps/spreed/tests/acceptance/ "$@"
|
Загрузка…
Ссылка в новой задаче