MOBILE-3784 calendar: Add behat for create event

main
Dani Palou 2022-11-16 12:20:48 +01:00
parent e72cf7258e
commit 82e9331357
13 changed files with 154 additions and 9 deletions

View File

@ -1012,4 +1012,22 @@ class behat_app extends behat_app_helper {
return true;
}
/**
* View a specific month in the calendar in the app.
*
* @When /^I open the calendar for "(?P<month>\d+)" "(?P<year>\d+)" in the app$/
* @param int $month the month selected as a number
* @param int $year the four digit year
*/
public function i_open_the_calendar_for($month, $year) {
$options = json_encode([
'params' => [
'month' => $month,
'year' => $year,
],
]);
$this->zone_js("navigator.navigateToSitePath('/calendar/index', $options)");
}
}

View File

@ -31,7 +31,7 @@
<p class="item-heading" [core-mark-required]="true">{{ 'core.date' | translate }}</p>
</ion-label>
<ion-datetime formControlName="timestart" [placeholder]="'core.date' | translate" [displayFormat]="dateFormat"
[max]="maxDate" [min]="minDate">
[max]="maxDate" [min]="minDate" [displayTimezone]="displayTimezone">
</ion-datetime>
<core-input-errors [control]="form.controls.timestart" [errorMessages]="errors"></core-input-errors>
</ion-item>
@ -156,7 +156,8 @@
<ion-item *ngIf="form.controls.duration.value === 1">
<ion-label position="stacked"></ion-label>
<ion-datetime formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
[placeholder]="'addon.calendar.durationuntil' | translate" [displayFormat]="dateFormat">
[placeholder]="'addon.calendar.durationuntil' | translate" [displayFormat]="dateFormat"
[displayTimezone]="displayTimezone">
</ion-datetime>
</ion-item>
<ion-item>

View File

@ -46,6 +46,7 @@ import { CoreForms } from '@singletons/form';
import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@features/reminders/services/reminders';
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
import moment from 'moment-timezone';
import { CoreAppProvider } from '@services/app';
/**
* Page that displays a form to create/edit an event.
@ -78,6 +79,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
eventId?: number;
maxDate: string;
minDate: string;
displayTimezone?: string;
// Form variables.
form: FormGroup;
@ -109,6 +111,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
// Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them.
this.dateFormat = CoreTimeUtils.convertPHPToMoment(Translate.instant('core.strftimedatetimeshort'))
.replace(/[[\]]/g, '');
this.displayTimezone = CoreAppProvider.getForcedTimezone();
this.form = new FormGroup({});
@ -489,6 +492,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
description: {
text: formData.description || '',
format: 1,
itemid: 0, // Files not supported yet.
},
location: formData.location,
duration: formData.duration,

View File

@ -253,6 +253,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider<AddonCalenda
description: {
text: event.description || '',
format: 1,
itemid: 0, // Files not supported yet.
},
},
); // Clone the object because it will be modified in the submit function.

View File

@ -0,0 +1,64 @@
@core @core_calendar @app @javascript
Feature: Test creation of calendar events in app
In order to take advantage of all the calendar features while using the mobile app
As a student
I need basic to be able to create and edit calendar events in the app
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | teacher | teacher1@example.com |
| student1 | Student1 | student1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: Create user event as student from monthly view
Given I entered the app as "student1"
When I press "More" in the app
And I press "Calendar" in the app
And I press "New event" in the app
Then the field "Date" matches value "## now ##%d/%m/%y, %H:%M##" in the app
And I should not be able to press "Save" in the app
# Check that student can only create User events.
When I press "Type of event" in the app
Then I should not find "Cancel" in the app
And I should find "User" within "Type of event" "ion-item" in the app
# Create the event.
When I set the field "Event title" to "User Event 01" in the app
And I set the field "Date" to "2025-04-11T09:00+08:00" in the app
And I press "Without duration" in the app
And I set the field "Description" to "This is User Event 01 description." in the app
And I set the field "Location" to "Barcelona" in the app
And I press "Save" in the app
Then I should find "Calendar events" in the app
# Verify that event was created right.
When I open the calendar for "4" "2025" in the app
And I press "Friday, 11 April 2025" in the app
Then I should find "User Event 01" in the app
When I press "User Event 01" in the app
Then I should find "Friday, 11 April" in the app
And I should find "Starting time: 9:00 AM" in the app
And I should find "User event" within "Event type" "ion-item" in the app
And I should find "This is User Event 01 description." in the app
And I should find "Barcelona" in the app
But I should not find "Ending time" in the app
When I press "Display options" in the app
Then I should find "Edit" in the app
And I should find "Delete" in the app
When I close the popup in the app
And I press "Barcelona" in the app
And I press "OK" in the app
Then the app should have opened a browser tab with url "google.com"
# @todo: Add more Scenarios to test teacher, different values, and creating events from other views (e.g. day view).

View File

@ -1,7 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-datetime [formControlName]="'f_'+field.id" [placeholder]="'core.date' | translate" [max]="maxDate" [min]="minDate"
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format">
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format" [displayTimezone]="displayTimezone">
</ion-datetime>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>

View File

@ -13,6 +13,7 @@
// limitations under the License.
import { Component } from '@angular/core';
import { CoreAppProvider } from '@services/app';
import { CoreTimeUtils } from '@services/utils/time';
import { Translate } from '@singletons';
import moment, { Moment } from 'moment-timezone';
@ -31,6 +32,7 @@ export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginBaseC
displayDate?: number;
maxDate?: string;
minDate?: string;
displayTimezone?: string;
/**
* @inheritdoc
@ -52,6 +54,7 @@ export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginBaseC
));
this.maxDate = CoreTimeUtils.getDatetimeDefaultMax();
this.minDate = CoreTimeUtils.getDatetimeDefaultMin();
this.displayTimezone = CoreAppProvider.getForcedTimezone();
if (this.searchMode) {
this.addControl('f_' + this.field.id + '_z');

View File

@ -12,7 +12,7 @@
<span [core-mark-required]="required">{{ field.name }}</span>
</ion-label>
<ion-datetime [formControlName]="modelName" [placeholder]="'core.choosedots' | translate" [displayFormat]="format" [max]="max"
[min]="min" [monthNames]="monthNames">
[min]="min" [monthNames]="monthNames" [displayTimezone]="displayTimezone">
</ion-datetime>
<core-input-errors [control]="form.controls[modelName]"></core-input-errors>
</ion-item>

View File

@ -22,6 +22,7 @@ import { CoreUserProfileField } from '@features/user/services/user';
import { Translate } from '@singletons';
import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component';
import { CoreLang } from '@services/lang';
import { CoreAppProvider } from '@services/app';
/**
* Directive to render a datetime user profile field.
@ -37,6 +38,7 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField
max?: string;
valueNumber = 0;
monthNames?: string[];
displayTimezone?: string;
/**
* Init the data when the field is meant to be displayed without editing.
@ -56,6 +58,7 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField
super.initForEdit(field);
this.monthNames = CoreLang.getMonthNames();
this.displayTimezone = CoreAppProvider.getForcedTimezone();
// Check if it's only date or it has time too.
const hasTime = CoreUtils.isTrueOrOne(field.param3);

View File

@ -70,6 +70,18 @@ export class CoreAppProvider {
return !!navigator.webdriver;
}
/**
* Returns the forced timezone to use. Timezone is forced for automated tests.
*
* @return Timezone. Undefined to use the user's timezone.
*/
static getForcedTimezone(): string | undefined {
if (CoreAppProvider.isAutomated()) {
// Use the same timezone forced for LMS in tests.
return 'Australia/Perth';
}
}
/**
* Initialize database.
*/

View File

@ -192,9 +192,25 @@ export class TestingBehatBlockingService {
*/
protected async checkUIBlocked(): Promise<void> {
await CoreUtils.nextTick();
const blocked = document.querySelector<HTMLElement>('div.core-loading-container, ion-loading, .click-block-active');
if (blocked?.offsetParent) {
const blockingElements = Array.from(
document.querySelectorAll<HTMLElement>('div.core-loading-container, ion-loading, .click-block-active'),
);
const isBlocked = blockingElements.some(element => {
if (!element.offsetParent) {
return false;
}
const slide = element.closest('ion-slide');
if (slide && !slide.classList.contains('swiper-slide-active')) {
return false;
}
return true;
});
if (isBlocked) {
if (!this.waitingBlocked) {
this.block('blocked');
this.waitingBlocked = true;

View File

@ -27,6 +27,7 @@ import { CoreComponentsRegistry } from '@singletons/components-registry';
import { CoreDom } from '@singletons/dom';
import { Injectable } from '@angular/core';
import { CoreSites, CoreSitesProvider } from '@services/sites';
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
/**
* Behat runtime servive with public API.
@ -56,6 +57,10 @@ export class TestingBehatRuntimeService {
return CoreSites.instance;
}
get navigator(): CoreNavigatorService {
return CoreNavigator.instance;
}
/**
* Init behat functions and set options like skipping onboarding.
*
@ -436,7 +441,7 @@ export class TestingBehatRuntimeService {
return 'ERROR: No element matches field to set.';
}
const foundValue = 'value' in found ? found.value : found.innerText;
const foundValue = this.getFieldValue(found);
if (value !== foundValue) {
return `ERROR: Expecting value "${value}", found "${foundValue}" instead.`;
}
@ -457,6 +462,24 @@ export class TestingBehatRuntimeService {
);
}
/**
* Get the value of a certain field.
*
* @param element Field to get the value.
* @return Value.
*/
protected getFieldValue(element: HTMLElement | HTMLInputElement): string {
if (element.tagName === 'ION-DATETIME') {
// ion-datetime's value is a timestamp in ISO format. Use the text displayed to the user instead.
const dateTimeTextElement = element.shadowRoot?.querySelector<HTMLElement>('.datetime-text');
if (dateTimeTextElement) {
return dateTimeTextElement.innerText;
}
}
return 'value' in element ? element.value : element.innerText;
}
/**
* Get an Angular component instance.
*

View File

@ -28,8 +28,8 @@ function initializeAutomatedTests(window: AutomatedTestsWindow) {
window.behat = TestingBehatRuntime.instance;
// Force timezone for automated tests. Use the same timezone forced for LMS in tests.
moment.tz.setDefault('Australia/Perth');
// Force timezone for automated tests.
moment.tz.setDefault(CoreAppProvider.getForcedTimezone());
}
@NgModule({