FilesAppSharingContext.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. /**
  3. *
  4. * @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com)
  5. *
  6. * @license GNU AGPL version 3 or any later version
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License as
  10. * published by the Free Software Foundation, either version 3 of the
  11. * License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. use Behat\Behat\Context\Context;
  23. class FilesAppSharingContext implements Context, ActorAwareInterface {
  24. use ActorAware;
  25. /**
  26. * @return Locator
  27. */
  28. public static function sharedByLabel() {
  29. return Locator::forThe()->css(".reshare")->
  30. descendantOf(FilesAppContext::detailsView())->
  31. describedAs("Shared by label in the details view in Files app");
  32. }
  33. /**
  34. * @return Locator
  35. */
  36. public static function shareWithInput() {
  37. return Locator::forThe()->css(".shareWithField")->
  38. descendantOf(FilesAppContext::detailsView())->
  39. describedAs("Share with input in the details view in Files app");
  40. }
  41. /**
  42. * @return Locator
  43. */
  44. public static function shareeList() {
  45. return Locator::forThe()->css(".shareeListView")->
  46. descendantOf(FilesAppContext::detailsView())->
  47. describedAs("Sharee list in the details view in Files app");
  48. }
  49. /**
  50. * @return Locator
  51. */
  52. public static function sharedWithRow($sharedWithName) {
  53. // "username" class is used for any type of share, not only for shares
  54. // with users.
  55. return Locator::forThe()->xpath("//span[contains(concat(' ', normalize-space(@class), ' '), ' username ') and normalize-space() = '$sharedWithName']/ancestor::li")->
  56. descendantOf(self::shareeList())->
  57. describedAs("Shared with $sharedWithName row in the details view in Files app");
  58. }
  59. /**
  60. * @return Locator
  61. */
  62. public static function shareWithMenuButton($sharedWithName) {
  63. return Locator::forThe()->css(".share-menu > .icon")->
  64. descendantOf(self::sharedWithRow($sharedWithName))->
  65. describedAs("Share with $sharedWithName menu button in the details view in Files app");
  66. }
  67. /**
  68. * @return Locator
  69. */
  70. public static function shareWithMenu($sharedWithName) {
  71. return Locator::forThe()->css(".share-menu > .menu")->
  72. descendantOf(self::sharedWithRow($sharedWithName))->
  73. describedAs("Share with $sharedWithName menu in the details view in Files app");
  74. }
  75. /**
  76. * @return Locator
  77. */
  78. public static function canReshareCheckbox($sharedWithName) {
  79. // forThe()->checkbox("Can reshare") can not be used here; that would
  80. // return the checkbox itself, but the element that the user interacts
  81. // with is the label.
  82. return Locator::forThe()->xpath("//label[normalize-space() = 'Can reshare']")->
  83. descendantOf(self::shareWithMenu($sharedWithName))->
  84. describedAs("Can reshare checkbox in the share with $sharedWithName menu in the details view in Files app");
  85. }
  86. /**
  87. * @return Locator
  88. */
  89. public static function canReshareCheckboxInput($sharedWithName) {
  90. return Locator::forThe()->checkbox("Can reshare")->
  91. descendantOf(self::shareWithMenu($sharedWithName))->
  92. describedAs("Can reshare checkbox input in the share with $sharedWithName menu in the details view in Files app");
  93. }
  94. /**
  95. * @return Locator
  96. */
  97. public static function shareLinkRow() {
  98. return Locator::forThe()->css(".linkShareView .shareWithList:first-child")->
  99. descendantOf(FilesAppContext::detailsView())->
  100. describedAs("Share link row in the details view in Files app");
  101. }
  102. /**
  103. * @return Locator
  104. */
  105. public static function shareLinkAddNewButton() {
  106. // When there is no link share the "Add new share" item is shown instead
  107. // of the menu button as a direct child of ".share-menu".
  108. return Locator::forThe()->css(".share-menu > .new-share")->
  109. descendantOf(self::shareLinkRow())->
  110. describedAs("Add new share link button in the details view in Files app");
  111. }
  112. /**
  113. * @return Locator
  114. */
  115. public static function copyLinkButton() {
  116. return Locator::forThe()->css("a.clipboard-button")->
  117. descendantOf(self::shareLinkRow())->
  118. describedAs("Copy link button in the details view in Files app");
  119. }
  120. /**
  121. * @return Locator
  122. */
  123. public static function shareLinkMenuButton() {
  124. return Locator::forThe()->css(".share-menu > .icon")->
  125. descendantOf(self::shareLinkRow())->
  126. describedAs("Share link menu button in the details view in Files app");
  127. }
  128. /**
  129. * @return Locator
  130. */
  131. public static function shareLinkMenu() {
  132. return Locator::forThe()->css(".share-menu > .menu")->
  133. descendantOf(self::shareLinkRow())->
  134. describedAs("Share link menu in the details view in Files app");
  135. }
  136. /**
  137. * @return Locator
  138. */
  139. public static function hideDownloadCheckbox() {
  140. // forThe()->checkbox("Hide download") can not be used here; that would
  141. // return the checkbox itself, but the element that the user interacts
  142. // with is the label.
  143. return Locator::forThe()->xpath("//label[normalize-space() = 'Hide download']")->
  144. descendantOf(self::shareLinkMenu())->
  145. describedAs("Hide download checkbox in the details view in Files app");
  146. }
  147. /**
  148. * @return Locator
  149. */
  150. public static function hideDownloadCheckboxInput() {
  151. return Locator::forThe()->checkbox("Hide download")->
  152. descendantOf(self::shareLinkMenu())->
  153. describedAs("Hide download checkbox input in the details view in Files app");
  154. }
  155. /**
  156. * @return Locator
  157. */
  158. public static function allowUploadAndEditingRadioButton() {
  159. // forThe()->radio("Allow upload and editing") can not be used here;
  160. // that would return the radio button itself, but the element that the
  161. // user interacts with is the label.
  162. return Locator::forThe()->xpath("//label[normalize-space() = 'Allow upload and editing']")->
  163. descendantOf(self::shareLinkMenu())->
  164. describedAs("Allow upload and editing radio button in the details view in Files app");
  165. }
  166. /**
  167. * @return Locator
  168. */
  169. public static function passwordProtectCheckbox() {
  170. // forThe()->checkbox("Password protect") can not be used here; that
  171. // would return the checkbox itself, but the element that the user
  172. // interacts with is the label.
  173. return Locator::forThe()->xpath("//label[normalize-space() = 'Password protect']")->
  174. descendantOf(self::shareLinkMenu())->
  175. describedAs("Password protect checkbox in the details view in Files app");
  176. }
  177. /**
  178. * @return Locator
  179. */
  180. public static function passwordProtectCheckboxInput() {
  181. return Locator::forThe()->checkbox("Password protect")->
  182. descendantOf(self::shareLinkMenu())->
  183. describedAs("Password protect checkbox input in the details view in Files app");
  184. }
  185. /**
  186. * @return Locator
  187. */
  188. public static function passwordProtectField() {
  189. return Locator::forThe()->css(".linkPassText")->descendantOf(self::shareLinkMenu())->
  190. describedAs("Password protect field in the details view in Files app");
  191. }
  192. /**
  193. * @return Locator
  194. */
  195. public static function passwordProtectWorkingIcon() {
  196. return Locator::forThe()->css(".linkPassMenu .icon-loading-small")->descendantOf(self::shareLinkMenu())->
  197. describedAs("Password protect working icon in the details view in Files app");
  198. }
  199. /**
  200. * @return Locator
  201. */
  202. public static function passwordProtectByTalkCheckbox() {
  203. // forThe()->checkbox("Password protect by Talk") can not be used here;
  204. // that would return the checkbox itself, but the element that the user
  205. // interacts with is the label.
  206. return Locator::forThe()->xpath("//label[normalize-space() = 'Password protect by Talk']")->
  207. descendantOf(self::shareLinkMenu())->
  208. describedAs("Password protect by Talk checkbox in the details view in Files app");
  209. }
  210. /**
  211. * @return Locator
  212. */
  213. public static function passwordProtectByTalkCheckboxInput() {
  214. return Locator::forThe()->checkbox("Password protect by Talk")->
  215. descendantOf(self::shareLinkMenu())->
  216. describedAs("Password protect by Talk checkbox input in the details view in Files app");
  217. }
  218. /**
  219. * @Given I share the link for :fileName
  220. */
  221. public function iShareTheLinkFor($fileName) {
  222. $this->actor->find(FileListContext::shareActionForFile(FilesAppContext::currentSectionMainView(), $fileName), 10)->click();
  223. $this->actor->find(self::shareLinkAddNewButton(), 5)->click();
  224. // Wait until the menu was opened after the share creation to continue.
  225. if (!WaitFor::elementToBeEventuallyShown(
  226. $this->actor,
  227. self::shareLinkMenu(),
  228. $timeout = 5 * $this->actor->getFindTimeoutMultiplier())) {
  229. PHPUnit_Framework_Assert::fail("The share link menu is not open yet after $timeout seconds");
  230. }
  231. }
  232. /**
  233. * @Given I share :fileName with :shareWithName
  234. */
  235. public function iShareWith($fileName, $shareWithName) {
  236. $this->actor->find(FileListContext::shareActionForFile(FilesAppContext::currentSectionMainView(), $fileName), 10)->click();
  237. $this->actor->find(self::shareWithInput(), 5)->setValue($shareWithName . "\r");
  238. }
  239. /**
  240. * @Given I write down the shared link
  241. */
  242. public function iWriteDownTheSharedLink() {
  243. $this->actor->find(self::copyLinkButton(), 10)->click();
  244. // Clicking on the menu item copies the link to the clipboard, but it is
  245. // not possible to access that value from the acceptance tests. Due to
  246. // this the value of the attribute that holds the URL is used instead.
  247. $this->actor->getSharedNotebook()["shared link"] = $this->actor->find(self::copyLinkButton(), 2)->getWrappedElement()->getAttribute("data-clipboard-text");
  248. }
  249. /**
  250. * @When I set the download of the shared link as hidden
  251. */
  252. public function iSetTheDownloadOfTheSharedLinkAsHidden() {
  253. $this->showShareLinkMenuIfNeeded();
  254. $this->iSeeThatTheDownloadOfTheLinkShareIsShown();
  255. $this->actor->find(self::hideDownloadCheckbox(), 2)->click();
  256. }
  257. /**
  258. * @When I set the download of the shared link as shown
  259. */
  260. public function iSetTheDownloadOfTheSharedLinkAsShown() {
  261. $this->showShareLinkMenuIfNeeded();
  262. $this->iSeeThatTheDownloadOfTheLinkShareIsHidden();
  263. $this->actor->find(self::hideDownloadCheckbox(), 2)->click();
  264. }
  265. /**
  266. * @When I set the shared link as editable
  267. */
  268. public function iSetTheSharedLinkAsEditable() {
  269. $this->showShareLinkMenuIfNeeded();
  270. $this->actor->find(self::allowUploadAndEditingRadioButton(), 2)->click();
  271. }
  272. /**
  273. * @When I protect the shared link with the password :password
  274. */
  275. public function iProtectTheSharedLinkWithThePassword($password) {
  276. $this->showShareLinkMenuIfNeeded();
  277. $this->actor->find(self::passwordProtectCheckbox(), 2)->click();
  278. $this->actor->find(self::passwordProtectField(), 2)->setValue($password . "\r");
  279. }
  280. /**
  281. * @When I set the password of the shared link as protected by Talk
  282. */
  283. public function iSetThePasswordOfTheSharedLinkAsProtectedByTalk() {
  284. $this->showShareLinkMenuIfNeeded();
  285. $this->iSeeThatThePasswordOfTheLinkShareIsNotProtectedByTalk();
  286. $this->actor->find(self::passwordProtectByTalkCheckbox(), 2)->click();
  287. }
  288. /**
  289. * @When I set the password of the shared link as not protected by Talk
  290. */
  291. public function iSetThePasswordOfTheSharedLinkAsNotProtectedByTalk() {
  292. $this->showShareLinkMenuIfNeeded();
  293. $this->iSeeThatThePasswordOfTheLinkShareIsProtectedByTalk();
  294. $this->actor->find(self::passwordProtectByTalkCheckbox(), 2)->click();
  295. }
  296. /**
  297. * @When I set the share with :shareWithName as not reshareable
  298. */
  299. public function iSetTheShareWithAsNotReshareable($shareWithName) {
  300. $this->showShareWithMenuIfNeeded($shareWithName);
  301. $this->iSeeThatCanReshareTheShare($shareWithName);
  302. $this->actor->find(self::canReshareCheckbox($shareWithName), 2)->click();
  303. }
  304. /**
  305. * @Then I see that the file is shared with me by :sharedByName
  306. */
  307. public function iSeeThatTheFileIsSharedWithMeBy($sharedByName) {
  308. PHPUnit_Framework_Assert::assertEquals(
  309. $this->actor->find(self::sharedByLabel(), 10)->getText(), "Shared with you by $sharedByName");
  310. }
  311. /**
  312. * @Then I see that the file is shared with :sharedWithName
  313. */
  314. public function iSeeThatTheFileIsSharedWith($sharedWithName) {
  315. PHPUnit_Framework_Assert::assertTrue(
  316. $this->actor->find(self::sharedWithRow($sharedWithName), 10)->isVisible());
  317. }
  318. /**
  319. * @Then I see that resharing the file is not allowed
  320. */
  321. public function iSeeThatResharingTheFileIsNotAllowed() {
  322. PHPUnit_Framework_Assert::assertEquals(
  323. $this->actor->find(self::shareWithInput(), 10)->getWrappedElement()->getAttribute("disabled"), "disabled");
  324. PHPUnit_Framework_Assert::assertEquals(
  325. $this->actor->find(self::shareWithInput(), 10)->getWrappedElement()->getAttribute("placeholder"), "Resharing is not allowed");
  326. }
  327. /**
  328. * @Then I see that :sharedWithName can reshare the share
  329. */
  330. public function iSeeThatCanReshareTheShare($sharedWithName) {
  331. $this->showShareWithMenuIfNeeded($sharedWithName);
  332. PHPUnit_Framework_Assert::assertTrue(
  333. $this->actor->find(self::canReshareCheckboxInput($sharedWithName), 10)->isChecked());
  334. }
  335. /**
  336. * @Then I see that :sharedWithName can not reshare the share
  337. */
  338. public function iSeeThatCanNotReshareTheShare($sharedWithName) {
  339. $this->showShareWithMenuIfNeeded($sharedWithName);
  340. PHPUnit_Framework_Assert::assertFalse(
  341. $this->actor->find(self::canReshareCheckboxInput($sharedWithName), 10)->isChecked());
  342. }
  343. /**
  344. * @Then I see that the download of the link share is hidden
  345. */
  346. public function iSeeThatTheDownloadOfTheLinkShareIsHidden() {
  347. $this->showShareLinkMenuIfNeeded();
  348. PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::hideDownloadCheckboxInput(), 10)->isChecked());
  349. }
  350. /**
  351. * @Then I see that the download of the link share is shown
  352. */
  353. public function iSeeThatTheDownloadOfTheLinkShareIsShown() {
  354. $this->showShareLinkMenuIfNeeded();
  355. PHPUnit_Framework_Assert::assertFalse($this->actor->find(self::hideDownloadCheckboxInput(), 10)->isChecked());
  356. }
  357. /**
  358. * @Then I see that the working icon for password protect is shown
  359. */
  360. public function iSeeThatTheWorkingIconForPasswordProtectIsShown() {
  361. PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::passwordProtectWorkingIcon(), 10));
  362. }
  363. /**
  364. * @Then I see that the working icon for password protect is eventually not shown
  365. */
  366. public function iSeeThatTheWorkingIconForPasswordProtectIsEventuallyNotShown() {
  367. if (!WaitFor::elementToBeEventuallyNotShown(
  368. $this->actor,
  369. self::passwordProtectWorkingIcon(),
  370. $timeout = 10 * $this->actor->getFindTimeoutMultiplier())) {
  371. PHPUnit_Framework_Assert::fail("The working icon for password protect is still shown after $timeout seconds");
  372. }
  373. }
  374. /**
  375. * @Then I see that the link share is password protected
  376. */
  377. public function iSeeThatTheLinkShareIsPasswordProtected() {
  378. $this->showShareLinkMenuIfNeeded();
  379. PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::passwordProtectCheckboxInput(), 10)->isChecked(), "Password protect checkbox is checked");
  380. PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::passwordProtectField(), 10)->isVisible(), "Password protect field is visible");
  381. }
  382. /**
  383. * @Then I see that the password of the link share is protected by Talk
  384. */
  385. public function iSeeThatThePasswordOfTheLinkShareIsProtectedByTalk() {
  386. $this->showShareLinkMenuIfNeeded();
  387. PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::passwordProtectByTalkCheckboxInput(), 10)->isChecked());
  388. }
  389. /**
  390. * @Then I see that the password of the link share is not protected by Talk
  391. */
  392. public function iSeeThatThePasswordOfTheLinkShareIsNotProtectedByTalk() {
  393. $this->showShareLinkMenuIfNeeded();
  394. PHPUnit_Framework_Assert::assertFalse($this->actor->find(self::passwordProtectByTalkCheckboxInput(), 10)->isChecked());
  395. }
  396. /**
  397. * @Then I see that the checkbox to protect the password of the link share by Talk is not shown
  398. */
  399. public function iSeeThatTheCheckboxToProtectThePasswordOfTheLinkShareByTalkIsNotShown() {
  400. $this->showShareLinkMenuIfNeeded();
  401. try {
  402. PHPUnit_Framework_Assert::assertFalse(
  403. $this->actor->find(self::passwordProtectByTalkCheckbox())->isVisible());
  404. } catch (NoSuchElementException $exception) {
  405. }
  406. }
  407. /**
  408. * @Given I share the link for :fileName protected by the password :password
  409. */
  410. public function iShareTheLinkForProtectedByThePassword($fileName, $password) {
  411. $this->iShareTheLinkFor($fileName);
  412. $this->iProtectTheSharedLinkWithThePassword($password);
  413. $this->iSeeThatTheWorkingIconForPasswordProtectIsShown();
  414. $this->iSeeThatTheWorkingIconForPasswordProtectIsEventuallyNotShown();
  415. }
  416. private function showShareLinkMenuIfNeeded() {
  417. // In some cases the share menu is hidden after clicking on an action of
  418. // the menu. Therefore, if the menu is visible, wait a little just in
  419. // case it is in the process of being hidden due to a previous action,
  420. // in which case it is shown again.
  421. if (WaitFor::elementToBeEventuallyNotShown(
  422. $this->actor,
  423. self::shareLinkMenu(),
  424. $timeout = 2 * $this->actor->getFindTimeoutMultiplier())) {
  425. $this->actor->find(self::shareLinkMenuButton(), 10)->click();
  426. }
  427. }
  428. private function showShareWithMenuIfNeeded($shareWithName) {
  429. // In some cases the share menu is hidden after clicking on an action of
  430. // the menu. Therefore, if the menu is visible, wait a little just in
  431. // case it is in the process of being hidden due to a previous action,
  432. // in which case it is shown again.
  433. if (WaitFor::elementToBeEventuallyNotShown(
  434. $this->actor,
  435. self::shareWithMenu($shareWithName),
  436. $timeout = 2 * $this->actor->getFindTimeoutMultiplier())) {
  437. $this->actor->find(self::shareWithMenuButton($shareWithName), 10)->click();
  438. }
  439. }
  440. }