Merge pull request #3543 from dpalou/MOBILE-4069

Mobile 4069
main
Noel De Martin 2023-02-08 10:43:09 +01:00 committed by GitHub
commit 1b56572860
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 242 additions and 29 deletions

View File

@ -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();

View File

@ -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## |

View File

@ -24,8 +24,8 @@
(click)="openBook(chapter.id)">
<ion-label>
<p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null">
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}}&nbsp;</span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull;&nbsp;</span>
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull; </span>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</p>

View File

@ -60,6 +60,13 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
]);
}
/**
* @inheritdoc
*/
protected async invalidateContent(): Promise<void> {
await AddonModBook.invalidateContent(this.module.id, this.courseId);
}
/**
* Load book data.
*

View File

@ -17,8 +17,8 @@
[attr.aria-current]="selected == chapter.id ? 'page' : 'false'" button [class.item-dimmed]="chapter.hidden" detail="false">
<ion-label>
<p [class.ion-padding-start]="addPadding && chapter.level == 1 ? true : null" class="item-heading">
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}}&nbsp;</span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull;&nbsp;</span>
<span *ngIf="showNumbers" class="addon-mod-book-number">{{chapter.indexNumber}} </span>
<span *ngIf="showBullets" class="addon-mod-book-bullet">&bull; </span>
<core-format-text [text]="chapter.title" contextLevel="module" [contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
</p>

View File

@ -291,7 +291,9 @@ export class AddonModBookProvider {
});
}
chapterNumber++;
if (!parseInt(chapter.hidden, 10)) {
chapterNumber++;
}
});
return chapters;

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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<T = unknown>(selector: string, className: string): T | null {
this.log('Action - Get Angular instance ' + selector + ', ' + className);
getAngularInstance<T = unknown>(
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<any>(`.ion-page:not(.ion-page-hidden) ${selector}`)).pop();
const activeElement = Array.from(startingElement.querySelectorAll<any>(`${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<IonSlides>('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<CoreSwipeNavigationDirective>(
'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);