MOBILE-3947 reminders: Fix tests

main
Noel De Martin 2024-01-11 15:25:49 +01:00
parent 21d0dc6821
commit 44606242fd
5 changed files with 139 additions and 74 deletions

View File

@ -781,13 +781,10 @@ class behat_app extends behat_app_helper {
/**
* Sets a field to the given text value in the app.
*
* Currently this only works for input fields which must be identified using a partial or
* exact match on the placeholder text.
*
* @Given /^I set the field "((?:[^"]|\\")+)" to "((?:[^"]|\\")*)" in the app$/
* @param string $field Text identifying field
* @param string $value Value for field
* @throws DriverException If the field set doesn't work
* @param string $field Text identifying the field.
* @param string $value Value to set. In select fields, this can be either the value or text included in the select option.
* @throws DriverException If the field set doesn't work.
*/
public function i_set_the_field_in_the_app(string $field, string $value) {
$field = addslashes_js($field);

View File

@ -16,52 +16,48 @@ Feature: Set a new reminder on activity
| assign | C1 | assign01 | Assignment 01 | ## yesterday ## | ## now +70 minutes ## |
| assign | C1 | assign02 | Assignment 02 | ## yesterday ## | ## 1 January 2050 ## |
@ionic7_failure
Scenario: Add, delete and update reminder on activity
Given I entered the assign activity "Assignment 01" on course "Course 1" as "student1" in the app
Then I should not find "Set a reminder for \"Assignment 01\" (Opened)" in the app
And I should find "Set a reminder for \"Assignment 01\" (Due)" in the app
And "Set a reminder for \"Assignment 01\" (Due)" should not be selected in the app
And I should not find "Reminder set for" in the app
But I should find "Set a reminder for \"Assignment 01\" (Due)" in the app
# Default set
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Assignment 01\" (Due)" should be selected in the app
Then I should find "Reminder set for" in the app
# Set from list
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then I should find "Set a reminder" in the app
And "At the time of the event" should be selected in the app
And "1 hour before" should not be selected in the app
But "1 hour before" should not be selected in the app
When I press "1 hour before" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Assignment 01\" (Due)" should be selected in the app
Then I should find "Reminder set for" in the app
# Custom set
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then I should find "Set a reminder" in the app
And "At the time of the event" should not be selected in the app
And "1 hour before" should be selected in the app
But "At the time of the event" should not be selected in the app
When I press "Custom..." in the app
Then I should find "Custom reminder" in the app
When I set the following fields to these values in the app:
| Value | 4 |
| Units | minutes |
And I press "Set reminder" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Assignment 01\" (Due)" should be selected in the app
Then I should find "Reminder set for" in the app
# Remove
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then "4 minutes before" should be selected in the app
When I press "Delete reminder" in the app
Then I should find "Reminder deleted" in the app
And "Set a reminder for \"Assignment 01\" (Due)" should not be selected in the app
But I should not find "Reminder set for" in the app
# Set and check reminder
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then I should find "Reminder set for " in the app
Then I should find "Reminder set for" in the app
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
And I press "Custom..." in the app
Then I should find "Custom reminder" in the app
@ -69,7 +65,7 @@ Feature: Set a new reminder on activity
| Value | 69 |
| Units | minutes |
And I press "Set reminder" in the app
Then I should find "Reminder set for " in the app
Then I should find "Reminder set for" in the app
When I wait "50" seconds
Then a notification with title "Due: Assignment 01" is present in the app
And I close a notification with title "Due: Assignment 01" in the app
@ -82,9 +78,9 @@ Feature: Set a new reminder on activity
| Value | 68 |
| Units | minutes |
And I press "Set reminder" in the app
Then I should find "Reminder set for " in the app
Then I should find "Reminder set for" in the app
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
Then I should find "Reminder set for " in the app
Then I should find "Reminder set for" in the app
When I press "Delete reminder" in the app
Then I should find "Reminder deleted" in the app
When I wait "50" seconds

View File

@ -12,34 +12,33 @@ Feature: Set a new reminder on course
| user | course | role |
| student1 | C1 | student |
@ionic7_failure
Scenario: Add, delete and update reminder on course
Given I entered the course "Course 1" as "student1" in the app
And I press "Course summary" in the app
Then I should not find "Set a reminder for \"Course 1\" (Course start date)" in the app
And I should find "Set a reminder for \"Course 1\" (Course end date)" in the app
And "Set a reminder for \"Course 1\" (Course end date)" should not be selected in the app
And I should not find "Reminder set for" in the app
But I should find "Set a reminder for \"Course 1\" (Course end date)" in the app
# Default set
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Course 1\" (Course end date)" should be selected in the app
And I should find "Reminder set for" in the app
# Set from list
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
Then I should find "Set a reminder" in the app
And "At the time of the event" should be selected in the app
And "12 hours before" should not be selected in the app
But "12 hours before" should not be selected in the app
When I press "12 hours before" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Course 1\" (Course end date)" should be selected in the app
And I should find "Reminder set for" in the app
# Custom set
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
Then I should find "Set a reminder" in the app
And "At the time of the event" should not be selected in the app
And "12 hours before" should be selected in the app
But "12 hours before" should be selected in the app
When I press "Custom..." in the app
Then I should find "Custom reminder" in the app
When I set the following fields to these values in the app:
@ -47,11 +46,11 @@ Feature: Set a new reminder on course
| Units | hours |
And I press "Set reminder" in the app
Then I should find "Reminder set for " in the app
And "Set a reminder for \"Course 1\" (Course end date)" should be selected in the app
And I should find "Reminder set for" in the app
# Remove
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
Then "2 hours before" should be selected in the app
When I press "Delete reminder" in the app
Then I should find "Reminder deleted" in the app
And "Set a reminder for \"Course 1\" (Course end date)" should not be selected in the app
But I should not find "Reminder set for" in the app

View File

@ -276,7 +276,7 @@ export class TestingBehatDomUtilsService {
/**
* Given a list of elements, get the top ancestors among all of them.
*
* This will remote duplicates and drop any elements nested within each other.
* This will remove duplicates and drop any elements nested within each other.
*
* @param elements Elements list.
* @returns Top ancestors.
@ -480,6 +480,34 @@ export class TestingBehatDomUtilsService {
return this.findElementsBasedOnText(locator, options)[0];
}
/**
* Wait until an element with the given selector is found.
*
* @param selector Element selector.
* @param timeout Timeout after which an error is thrown.
* @param retryFrequency Frequency for retries when the element is not found.
* @returns Element.
*/
async waitForElement<T extends HTMLElement = HTMLElement>(
selector: string,
timeout: number = 2000,
retryFrequency: number = 100,
): Promise<T> {
const element = document.querySelector<T>(selector);
if (!element) {
if (timeout < retryFrequency) {
throw new Error(`Element with '${selector}' selector not found`);
}
await new Promise(resolve => setTimeout(resolve, retryFrequency));
return this.waitForElement<T>(selector, timeout - retryFrequency, retryFrequency);
}
return element;
}
/**
* Function to find elements based on their text or Aria label.
*
@ -515,7 +543,7 @@ export class TestingBehatDomUtilsService {
protected findElementsBasedOnTextInContainer(
locator: TestingBehatElementLocator,
topContainer: HTMLElement,
options: TestingBehatFindOptions,
options: TestingBehatFindOptions = {},
): HTMLElement[] {
let container: HTMLElement | null = topContainer;
@ -667,37 +695,26 @@ export class TestingBehatDomUtilsService {
}
/**
* Set an element value.
* Set an input element value.
*
* @param element HTML to set.
* @param value Value to be set.
* @param element Input element.
* @param value Value.
*/
async setElementValue(element: HTMLInputElement | HTMLElement, value: string): Promise<void> {
async setInputValue(element: HTMLInputElement | HTMLElement, value: string): Promise<void> {
await NgZone.run(async () => {
const promise = new CorePromisedValue<void>();
// Functions to get/set value depending on field type.
const setValue = (text: string) => {
if (! ('value' in element)) {
element.innerHTML = text;
return;
}
const setValue = async (text: string) => {
if (element.tagName === 'ION-SELECT') {
value = value.trim();
const optionValue = Array.from(element.querySelectorAll('ion-select-option'))
.find((option) => option.innerHTML.trim() === value);
if (optionValue) {
element.value = optionValue.value;
}
} else {
this.setIonSelectInputValue(element, value);
} else if ('value' in element) {
element.value = text;
} else {
element.innerHTML = text;
}
element.dispatchEvent(new Event('ionChange'));
};
const getValue = () => {
if ('value' in element) {
return element.value;
@ -707,38 +724,79 @@ export class TestingBehatDomUtilsService {
};
// Pretend we have cut and pasted the new text.
let event: InputEvent;
if (getValue() !== '') {
event = new InputEvent('input', {
if (element.tagName !== 'ION-SELECT' && getValue() !== '') {
await CoreUtils.nextTick();
await setValue('');
element.dispatchEvent(new InputEvent('input', {
bubbles: true,
view: window,
cancelable: true,
inputType: 'deleteByCut',
});
await CoreUtils.nextTick();
setValue('');
element.dispatchEvent(event);
}));
}
if (value !== '') {
event = new InputEvent('input', {
await CoreUtils.nextTick();
await setValue(value);
element.dispatchEvent(new InputEvent('input', {
bubbles: true,
view: window,
cancelable: true,
inputType: 'insertFromPaste',
data: value,
}));
}
});
await CoreUtils.nextTick();
setValue(value);
element.dispatchEvent(event);
}
promise.resolve();
/**
* Select an option in an ion-select element.
*
* @param element IonSelect element.
* @param value Value.
*/
protected async setIonSelectInputValue(element: HTMLElement, value: string): Promise<void> {
// Press select.
await TestingBehatDomUtils.pressElement(element);
return promise;
});
// Press option.
type IonSelectInterface = 'alert' | 'action-sheet' | 'popover';
const selectInterface = element.getAttribute('interface') as IonSelectInterface ?? 'alert';
const containerSelector = ({
'alert': 'ion-alert.select-alert',
'action-sheet': 'ion-action-sheet.select-action-sheet',
'popover': 'ion-popover.select-popover',
})[selectInterface];
const optionSelector = ({
'alert': 'button',
'action-sheet': 'button',
'popover': 'ion-radio',
})[selectInterface] ?? '';
const optionsContainer = await TestingBehatDomUtils.waitForElement(containerSelector);
const options = this.findElementsBasedOnTextInContainer(
{ text: value, selector: optionSelector },
optionsContainer,
{},
);
if (options.length === 0) {
throw new Error('Couldn\'t find ion-select option.');
}
await TestingBehatDomUtils.pressElement(options[0]);
// Press options submit.
if (selectInterface === 'alert') {
const submitButton = optionsContainer.querySelector<HTMLElement>('.alert-button-group button:last-child');
if (!submitButton) {
throw new Error('Couldn\'t find ion-select submit button.');
}
await TestingBehatDomUtils.pressElement(submitButton);
}
}
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { TestingBehatDomUtils } from './behat-dom';
import { TestingBehatDomUtils, TestingBehatDomUtilsService } from './behat-dom';
import { TestingBehatBlocking } from './behat-blocking';
import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes';
import { ONBOARDING_DONE } from '@features/login/constants';
@ -63,6 +63,10 @@ export class TestingBehatRuntimeService {
return CoreNavigator.instance;
}
get domUtils(): TestingBehatDomUtilsService {
return TestingBehatDomUtils.instance;
}
/**
* Init behat functions and set options like skipping onboarding.
*
@ -468,11 +472,22 @@ export class TestingBehatRuntimeService {
?? options.find(option => option.text === value)?.value
?? options.find(option => option.text.includes(value))?.value
?? value;
} else if (input.tagName === 'ION-SELECT') {
const options = Array.from(input.querySelectorAll('ion-select-option'));
value = options.find(option => option.value?.toString() === value)?.textContent?.trim()
?? options.find(option => option.textContent?.trim() === value)?.textContent?.trim()
?? options.find(option => option.textContent?.includes(value))?.textContent?.trim()
?? value;
}
await TestingBehatDomUtils.setElementValue(input, value);
try {
await TestingBehatDomUtils.setInputValue(input, value);
return 'OK';
} catch (error) {
return `ERROR: ${error.message ?? 'Unknown error'}`;
}
}
/**