From f11819f698e269daf4b79f5caca112d7d2a99ec8 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 6 Feb 2023 11:26:51 +0100 Subject: [PATCH 1/4] MOBILE-4069 behat: Fix timeline test --- .../block/timeline/tests/behat/basic_usage.feature | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/addons/block/timeline/tests/behat/basic_usage.feature b/src/addons/block/timeline/tests/behat/basic_usage.feature index f69fc3f3a..9042ad307 100644 --- a/src/addons/block/timeline/tests/behat/basic_usage.feature +++ b/src/addons/block/timeline/tests/behat/basic_usage.feature @@ -25,11 +25,11 @@ Feature: Timeline block. | assign | C1 | assign03 | Assignment 03 | ##tomorrow## | | assign | C2 | assign04 | Assignment 04 | ##+2 days## | | assign | C1 | assign05 | Assignment 05 | ##+5 days## | - | assign | C2 | assign06 | Assignment 06 | ##+1 month## | - | assign | C2 | assign07 | Assignment 07 | ##+1 month## | - | assign | C3 | assign08 | Assignment 08 | ##+1 month## | - | assign | C2 | assign09 | Assignment 09 | ##+1 month## | - | assign | C1 | assign10 | Assignment 10 | ##+1 month## | + | assign | C2 | assign06 | Assignment 06 | ##+31 days## | + | assign | C2 | assign07 | Assignment 07 | ##+31 days## | + | assign | C3 | assign08 | Assignment 08 | ##+31 days## | + | assign | C2 | assign09 | Assignment 09 | ##+31 days## | + | assign | C1 | assign10 | Assignment 10 | ##+31 days## | | assign | C1 | assign11 | Assignment 11 | ##+6 months## | | assign | C1 | assign12 | Assignment 12 | ##+6 months## | | assign | C1 | assign13 | Assignment 13 | ##+6 months## | From ebb6e393cf58cc8fe0b1046fbcfe6c5280abe1d9 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 7 Feb 2023 12:07:10 +0100 Subject: [PATCH 2/4] MOBILE-4069 book: Fix PTR in book index page --- src/addons/mod/book/components/index/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/addons/mod/book/components/index/index.ts b/src/addons/mod/book/components/index/index.ts index 838f154e8..191a698fa 100644 --- a/src/addons/mod/book/components/index/index.ts +++ b/src/addons/mod/book/components/index/index.ts @@ -60,6 +60,13 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp ]); } + /** + * @inheritdoc + */ + protected async invalidateContent(): Promise { + await AddonModBook.invalidateContent(this.module.id, this.courseId); + } + /** * Load book data. * From 8f826185b6d1e34210b84f5dc09e85d20ccabfda Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 7 Feb 2023 13:04:33 +0100 Subject: [PATCH 3/4] MOBILE-4069 behat: Allow searching text split in different elements --- src/testing/services/behat-dom.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/testing/services/behat-dom.ts b/src/testing/services/behat-dom.ts index 28fd807f5..270ba3fc8 100644 --- a/src/testing/services/behat-dom.ts +++ b/src/testing/services/behat-dom.ts @@ -24,6 +24,8 @@ import { TestingBehatElementLocator, TestingBehatFindOptions } from './behat-run @Injectable({ providedIn: 'root' }) export class TestingBehatDomUtilsService { + protected static readonly MULTI_ELEM_ALLOWED = ['P', 'SPAN', 'ION-LABEL']; + /** * Check if an element is clickable. * @@ -154,6 +156,7 @@ export class TestingBehatDomUtilsService { }, ); + let fallbackCandidates: ElementsWithExact[] = []; let currentNode: Node | null = null; // eslint-disable-next-line no-cond-assign while (currentNode = treeWalker.nextNode()) { @@ -202,9 +205,24 @@ export class TestingBehatDomUtilsService { elements.push(...this.findElementsBasedOnTextWithinWithExact(childNode, text, options)); } } + + // Allow searching text split into different elements in some cases. + if ( + elements.length === 0 && + currentNode instanceof HTMLElement && + TestingBehatDomUtilsService.MULTI_ELEM_ALLOWED.includes(currentNode.tagName) && + currentNode.innerText.includes(text) + ) { + // Only keep the child elements in the candidates list. + fallbackCandidates = fallbackCandidates.filter(entry => !entry.element.contains(currentNode)); + fallbackCandidates.push({ + element: currentNode, + exact: currentNode.innerText.trim() == text, + }); + } } - return elements; + return elements.length > 0 ? elements : fallbackCandidates; } /** From 0732722882b0e9197c70cb5c9e1abaa3074a67ee Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 7 Feb 2023 16:20:48 +0100 Subject: [PATCH 4/4] MOBILE-4069 behat: Test book swipe and numbering --- .../tests/behat/behat_app.php | 16 +- .../index/addon-mod-book-index.html | 4 +- src/addons/mod/book/components/toc/toc.html | 4 +- src/addons/mod/book/services/book.ts | 4 +- .../mod/book/tests/behat/basic_usage.feature | 139 ++++++++++++++++-- src/testing/services/behat-runtime.ts | 67 ++++++++- 6 files changed, 211 insertions(+), 23 deletions(-) diff --git a/local_moodleappbehat/tests/behat/behat_app.php b/local_moodleappbehat/tests/behat/behat_app.php index 7a815ec17..04395fbaf 100644 --- a/local_moodleappbehat/tests/behat/behat_app.php +++ b/local_moodleappbehat/tests/behat/behat_app.php @@ -237,13 +237,21 @@ class behat_app extends behat_app_helper { /** * Trigger swipe gesture. * - * @When /^I swipe to the (left|right) in the app$/ + * @When /^I swipe to the (left|right) (in (".+") )?in the app$/ * @param string $direction Swipe direction + * @param bool $hasLocator Whether a reference locator is used. + * @param string $locator Reference locator. */ - public function i_swipe_in_the_app(string $direction) { - $method = 'swipe' . ucwords($direction); + public function i_swipe_in_the_app(string $direction, bool $hasLocator = false, string $locator = '') { + if ($hasLocator) { + $locator = $this->parse_element_locator($locator); + } - $this->zone_js("getAngularInstance('ion-content', 'CoreSwipeNavigationDirective').$method()"); + $result = $this->zone_js("swipe('$direction'" . ($hasLocator ? ", $locator" : '') . ')'); + + if ($result !== 'OK') { + throw new DriverException('Error when swiping - ' . $result); + } $this->wait_for_pending_js(); diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index 380582d58..b186dfb3c 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -24,8 +24,8 @@ (click)="openBook(chapter.id)">

- {{chapter.indexNumber}}  - •  + {{chapter.indexNumber}} +

diff --git a/src/addons/mod/book/components/toc/toc.html b/src/addons/mod/book/components/toc/toc.html index 42165ea28..663370ab8 100644 --- a/src/addons/mod/book/components/toc/toc.html +++ b/src/addons/mod/book/components/toc/toc.html @@ -17,8 +17,8 @@ [attr.aria-current]="selected == chapter.id ? 'page' : 'false'" button [class.item-dimmed]="chapter.hidden" detail="false">

- {{chapter.indexNumber}}  - •  + {{chapter.indexNumber}} +

diff --git a/src/addons/mod/book/services/book.ts b/src/addons/mod/book/services/book.ts index 8681ef369..2365c5291 100644 --- a/src/addons/mod/book/services/book.ts +++ b/src/addons/mod/book/services/book.ts @@ -291,7 +291,9 @@ export class AddonModBookProvider { }); } - chapterNumber++; + if (!parseInt(chapter.hidden, 10)) { + chapterNumber++; + } }); return chapters; diff --git a/src/addons/mod/book/tests/behat/basic_usage.feature b/src/addons/mod/book/tests/behat/basic_usage.feature index 6e24b7b2a..65a09b54e 100755 --- a/src/addons/mod/book/tests/behat/basic_usage.feature +++ b/src/addons/mod/book/tests/behat/basic_usage.feature @@ -27,6 +27,7 @@ Feature: Test basic usage of book activity in app | Basic book | Hidden chapter | This is a hidden chapter | 0 | 1 | 4 | | Basic book | Hidden subchapter | This is a hidden subchapter | 1 | 1 | 5 | | Basic book | Chapt 3 | This is the third chapter | 0 | 0 | 6 | + | Basic book | Last hidden | Another hidden subchapter | 1 | 1 | 7 | Scenario: View book table of contents (student) Given I entered the course "Course 1" as "student1" in the app @@ -39,6 +40,7 @@ Feature: Test basic usage of book activity in app And I should find "Start" in the app But I should not find "Hidden chapter" in the app And I should not find "Hidden subchapter" in the app + And I should not find "Last hidden" in the app And I should not find "This is the first chapter" in the app When I press "Start" in the app @@ -49,6 +51,7 @@ Feature: Test basic usage of book activity in app And I should find "Chapt 3" in the app But I should not find "Hidden chapter" in the app And I should not find "Hidden subchapter" in the app + And I should not find "Last hidden" in the app Scenario: View book table of contents (teacher) Given I entered the course "Course 1" as "teacher1" in the app @@ -60,6 +63,7 @@ Feature: Test basic usage of book activity in app And I should find "Hidden chapter" in the app And I should find "Hidden subchapter" in the app And I should find "Chapt 3" in the app + And I should find "Last hidden" in the app And I should find "Start" in the app And I should not find "This is the first chapter" in the app @@ -71,6 +75,7 @@ Feature: Test basic usage of book activity in app And I should find "Hidden chapter" in the app And I should find "Hidden subchapter" in the app And I should find "Chapt 3" in the app + And I should find "Last hidden" in the app Scenario: Open chapters from table of contents Given I entered the course "Course 1" as "student1" in the app @@ -127,7 +132,26 @@ Feature: Test basic usage of book activity in app And I should find "4 / 4" in the app But I should not find "This is the first chapter" in the app - # TODO: Test navigate using swipe. + # Navigate using swipe. + When I swipe to the left in "Chapt 3" "ion-slides" in the app + Then I should find "Chapt 3" in the app + And I should find "This is the third chapter" in the app + And I should find "4 / 4" in the app + + When I swipe to the right in "Chapt 3" "ion-slides" in the app + Then I should find "Chapt 2" in the app + And I should find "This is the second chapter" in the app + And I should find "3 / 4" in the app + + When I swipe to the right in "Chapt 2" "ion-slides" in the app + Then I should find "Chapt 1.1" in the app + And I should find "This is a subchapter" in the app + And I should find "2 / 4" in the app + + When I swipe to the left in "Chapt 1.1" "ion-slides" in the app + Then I should find "Chapt 2" in the app + And I should find "This is the second chapter" in the app + And I should find "3 / 4" in the app Scenario: View and navigate book contents (teacher) Given I entered the course "Course 1" as "teacher1" in the app @@ -135,36 +159,36 @@ Scenario: View and navigate book contents (teacher) And I press "Start" in the app Then I should find "Chapt 1" in the app And I should find "This is the first chapter" in the app - And I should find "1 / 6" in the app + And I should find "1 / 7" in the app When I press "Next" in the app Then I should find "Chapt 1.1" in the app And I should find "This is a subchapter" in the app - And I should find "2 / 6" in the app + And I should find "2 / 7" in the app But I should not find "This is the first chapter" in the app When I press "Next" in the app Then I should find "Chapt 2" in the app And I should find "This is the second chapter" in the app - And I should find "3 / 6" in the app + And I should find "3 / 7" in the app But I should not find "This is a subchapter" in the app When I press "Next" in the app Then I should find "Hidden chapter" in the app And I should find "This is a hidden chapter" in the app - And I should find "4 / 6" in the app + And I should find "4 / 7" in the app But I should not find "This is the second chapter" in the app When I press "Next" in the app Then I should find "Hidden subchapter" in the app And I should find "This is a hidden subchapter" in the app - And I should find "5 / 6" in the app + And I should find "5 / 7" in the app But I should not find "This is a hidden chapter" in the app When I press "Previous" in the app Then I should find "Hidden chapter" in the app And I should find "This is a hidden chapter" in the app - And I should find "4 / 6" in the app + And I should find "4 / 7" in the app But I should not find "This is a hidden subchapter" in the app # Navigate using TOC. @@ -172,20 +196,113 @@ Scenario: View and navigate book contents (teacher) And I press "Chapt 1" in the app Then I should find "Chapt 1" in the app And I should find "This is the first chapter" in the app - And I should find "1 / 6" in the app + And I should find "1 / 7" in the app But I should not find "This is a hidden chapter" in the app When I press "Table of contents" in the app And I press "Hidden subchapter" in the app Then I should find "Hidden subchapter" in the app And I should find "This is a hidden subchapter" in the app - And I should find "5 / 6" in the app + And I should find "5 / 7" in the app But I should not find "This is the first chapter" in the app - # TODO: Test navigate using swipe. + # Navigate using swipe. + When I swipe to the left in "Hidden subchapter" "ion-slides" in the app + Then I should find "Chapt 3" in the app + And I should find "This is the third chapter" in the app + And I should find "6 / 7" in the app + + When I swipe to the left in "Chapt 3" "ion-slides" in the app + Then I should find "Last hidden" in the app + And I should find "Another hidden subchapter" in the app + And I should find "7 / 7" in the app + + When I swipe to the left in "Last hidden" "ion-slides" in the app + Then I should find "Last hidden" in the app + And I should find "Another hidden subchapter" in the app + And I should find "7 / 7" in the app + + When I swipe to the right in "Last hidden" "ion-slides" in the app + Then I should find "Chapt 3" in the app + And I should find "This is the third chapter" in the app + And I should find "6 / 7" in the app Scenario: Link to book opens chapter content Given I entered the book activity "Basic book" on course "Course 1" as "student1" in the app Then I should find "This is the first chapter" in the app - # TODO: Scenario to test book numbering (numbers, bullets, etc.). + Scenario: Test numbering (student) + Given the following "activities" exist: + | activity | name | intro | course | idnumber | numbering | + | book | Bull book | Test book description | C1 | book2 | 2 | + | book | Ind book | Test book description | C1 | book2 | 3 | + | book | None book | Test book description | C1 | book2 | 0 | + And the following "mod_book > chapter" exist: + | book | title | content | subchapter | hidden | pagenum | + | Bull book | Chapt 1 | This is the first chapter | 0 | 0 | 1 | + | Ind book | Chapt 1 | This is the first chapter | 0 | 0 | 1 | + | None book | Chapt 1 | This is the first chapter | 0 | 0 | 1 | + And I entered the course "Course 1" as "student1" in the app + And I press "Basic book" in the app + Then I should find "1. Chapt 1" in the app + And I should find "1.1. Chapt 1.1" in the app + And I should find "2. Chapt 2" in the app + And I should find "3. Chapt 3" in the app + + When I press "Start" in the app + And I press "Table of contents" in the app + Then I should find "1. Chapt 1" in the app + And I should find "1.1. Chapt 1.1" in the app + And I should find "2. Chapt 2" in the app + And I should find "3. Chapt 3" in the app + + When I press "Close" in the app + And I press the back button in the app + And I press the back button in the app + And I press "Bull book" in the app + Then I should find "• Chapt 1" in the app + But I should not find "1. Chapt 1" in the app + + When I press "Start" in the app + And I press "Table of contents" in the app + Then I should find "• Chapt 1" in the app + But I should not find "1. Chapt 1" in the app + + When I press "Close" in the app + And I press the back button in the app + And I press the back button in the app + And I press "Ind book" in the app + Then I should find "Chapt 1" in the app + But I should not find "• Chapt 1" in the app + And I should not find "1. Chapt 1" in the app + + When I press "Start" in the app + And I press "Table of contents" in the app + Then I should find "Chapt 1" in the app + But I should not find "• Chapt 1" in the app + And I should not find "1. Chapt 1" in the app + + When I press "Close" in the app + And I press the back button in the app + And I press the back button in the app + And I press "None book" in the app + Then I should find "Chapt 1" in the app + But I should not find "• Chapt 1" in the app + And I should not find "1. Chapt 1" in the app + + When I press "Start" in the app + And I press "Table of contents" in the app + Then I should find "Chapt 1" in the app + But I should not find "• Chapt 1" in the app + And I should not find "1. Chapt 1" in the app + + Scenario: Test numbering (teacher) + Given I entered the course "Course 1" as "teacher1" in the app + And I press "Basic book" in the app + Then I should find "1. Chapt 1" in the app + And I should find "1.1. Chapt 1.1" in the app + And I should find "2. Chapt 2" in the app + And I should find "x. Hidden chapter" in the app + And I should find "x.x. Hidden subchapter" in the app + And I should find "3. Chapt 3" in the app + And I should find "3.x. Last hidden" in the app diff --git a/src/testing/services/behat-runtime.ts b/src/testing/services/behat-runtime.ts index 892667cca..0f7eb5052 100644 --- a/src/testing/services/behat-runtime.ts +++ b/src/testing/services/behat-runtime.ts @@ -28,6 +28,8 @@ import { CoreDom } from '@singletons/dom'; import { Injectable } from '@angular/core'; import { CoreSites, CoreSitesProvider } from '@services/sites'; import { CoreNavigator, CoreNavigatorService } from '@services/navigator'; +import { CoreSwipeNavigationDirective } from '@directives/swipe-navigation'; +import { IonSlides } from '@ionic/angular'; /** * Behat runtime servive with public API. @@ -493,13 +495,36 @@ export class TestingBehatRuntimeService { * * @param selector Element selector * @param className Constructor class name + * @param referenceLocator The locator to the reference element to start looking for. If not specified, document body. * @returns Component instance */ - getAngularInstance(selector: string, className: string): T | null { - this.log('Action - Get Angular instance ' + selector + ', ' + className); + getAngularInstance( + selector: string, + className: string, + referenceLocator?: TestingBehatElementLocator, + ): T | null { + this.log('Action - Get Angular instance ' + selector + ', ' + className, referenceLocator); + + let startingElement: HTMLElement | undefined = document.body; + let queryPrefix = ''; + + if (referenceLocator) { + startingElement = TestingBehatDomUtils.findElementBasedOnText(referenceLocator, { + onlyClickable: false, + containerName: '', + }); + + if (!startingElement) { + return null; + } + } else { + // Searching the whole DOM, search only in visible pages. + queryPrefix = '.ion-page:not(.ion-page-hidden) '; + } // eslint-disable-next-line @typescript-eslint/no-explicit-any - const activeElement = Array.from(document.querySelectorAll(`.ion-page:not(.ion-page-hidden) ${selector}`)).pop(); + const activeElement = Array.from(startingElement.querySelectorAll(`${queryPrefix}${selector}`)).pop() ?? + startingElement.closest(selector); if (!activeElement || !activeElement.__ngContext__) { return null; @@ -565,6 +590,42 @@ export class TestingBehatRuntimeService { return 'OK'; } + /** + * Swipe in the app. + * + * @param direction Left or right. + * @param locator Element locator to swipe. If not specified, swipe in the first ion-content found. + * @returns OK if successful, or ERROR: followed by message + */ + swipe(direction: string, locator?: TestingBehatElementLocator): string { + this.log('Action - Swipe', { direction, locator }); + + if (locator) { + // Locator specified, try to find ion-slides first. + const instance = this.getAngularInstance('ion-slides', 'IonSlides', locator); + if (instance) { + direction === 'left' ? instance.slideNext() : instance.slidePrev(); + + return 'OK'; + } + } + + // No locator specified or ion-slides not found, search swipe navigation now. + const instance = this.getAngularInstance( + 'ion-content', + 'CoreSwipeNavigationDirective', + locator, + ); + + if (!instance) { + return 'ERROR: Element to swipe not found.'; + } + + direction === 'left' ? instance.swipeLeft() : instance.swipeRight(); + + return 'OK'; + } + } export const TestingBehatRuntime = makeSingleton(TestingBehatRuntimeService);