commit
c535b723ca
|
@ -681,7 +681,7 @@ class behat_app extends behat_app_helper {
|
|||
if (!is_null($urlpattern)) {
|
||||
$this->getSession()->switchToWindow($windowNames[1]);
|
||||
$windowurl = $this->getSession()->getCurrentUrl();
|
||||
$windowhaspattern = preg_match("/$urlpattern/", $windowurl);
|
||||
$windowhaspattern = !!preg_match("/$urlpattern/", $windowurl);
|
||||
$this->getSession()->switchToWindow($windowNames[0]);
|
||||
|
||||
if ($not === $windowhaspattern) {
|
||||
|
|
|
@ -48,7 +48,6 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
|||
import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu';
|
||||
import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
|
||||
import { CoreScreen } from '@services/screen';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { AddonModForumPrefetchHandler } from '../../services/handlers/prefetch';
|
||||
import { AddonModForumModuleHandlerService } from '../../services/handlers/module';
|
||||
import { CoreRatingProvider } from '@features/rating/services/rating';
|
||||
|
@ -561,7 +560,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
}
|
||||
|
||||
if (this.discussions?.getSource().isOnlineDiscussion(disc)) {
|
||||
return CoreArray.contains(newDiscussionData.discussionIds ?? [], disc.discussion);
|
||||
return (newDiscussionData.discussionIds ?? []).includes(disc.discussion);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -24,7 +24,6 @@ import { CoreSites } from '@services/sites';
|
|||
import { CoreSync } from '@services/sync';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import {
|
||||
AddonModForum,
|
||||
|
@ -90,7 +89,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide
|
|||
// Do not sync same forum twice.
|
||||
const syncedForumIds: number[] = [];
|
||||
const promises = discussions.map(async discussion => {
|
||||
if (CoreArray.contains(syncedForumIds, discussion.forumid)) {
|
||||
if (syncedForumIds.includes(discussion.forumid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -123,7 +122,7 @@ export class AddonModForumSyncProvider extends CoreCourseActivitySyncBaseProvide
|
|||
// Do not sync same discussion twice.
|
||||
const syncedDiscussionIds: number[] = [];
|
||||
const promises = replies.map(async reply => {
|
||||
if (CoreArray.contains(syncedDiscussionIds, reply.discussionid)) {
|
||||
if (syncedDiscussionIds.includes(reply.discussionid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -191,7 +191,6 @@ Feature: Test basic usage of forum activity in app
|
|||
And I press "Save changes" in the app
|
||||
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
|
||||
|
||||
# TODO Fix this test in all Moodle versions
|
||||
Scenario: Delete a forum post (only online)
|
||||
Given I entered the forum activity "Test forum name" on course "Course 1" as "student1" in the app
|
||||
When I press "Add discussion topic" in the app
|
||||
|
@ -215,9 +214,13 @@ Feature: Test basic usage of forum activity in app
|
|||
And I press "Cancel" in the app
|
||||
And I switch offline mode to "true"
|
||||
And I press "Display options" near "Reply" in the app
|
||||
Then I should not find "Delete" in the app
|
||||
Then I should find "Delete" in the app
|
||||
|
||||
When I close the popup in the app
|
||||
When I press "Delete" in the app
|
||||
Then I should find "There was a problem connecting to the site. Please check your connection and try again." in the app
|
||||
|
||||
When I press "OK" in the app
|
||||
And I close the popup in the app
|
||||
And I switch offline mode to "false"
|
||||
And I press "Display options" near "Reply" in the app
|
||||
And I press "Delete" in the app
|
||||
|
@ -247,7 +250,7 @@ Feature: Test basic usage of forum activity in app
|
|||
And I switch offline mode to "true"
|
||||
And I press "None" near "test2" in the app
|
||||
And I press "0" near "Cancel" in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." inside the toast in the app
|
||||
Then I should find "Data stored in the device because it couldn't be sent. It will be sent automatically later." in the app
|
||||
And I should find "Average of ratings: -" in the app
|
||||
And I should find "Average of ratings: 1" in the app
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
|
|||
try {
|
||||
await Promise.all(deletedCourseIds.map((courseId) => CoreCourseHelper.deleteCourseFiles(courseId)));
|
||||
|
||||
this.setDownloadedCourses(this.downloadedCourses.filter((course) => !CoreArray.contains(deletedCourseIds, course.id)));
|
||||
this.setDownloadedCourses(this.downloadedCourses.filter((course) => !deletedCourseIds.includes(course.id)));
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile'));
|
||||
} finally {
|
||||
|
|
|
@ -31,7 +31,6 @@ import { CoreConstants } from '@/core/constants';
|
|||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreInterceptor } from '@classes/interceptor';
|
||||
import { makeSingleton, Translate, FileTransfer, Http, NativeHttp } from '@singletons';
|
||||
import { CoreArray } from '@singletons/array';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreWSError } from '@classes/errors/wserror';
|
||||
import { CoreAjaxError } from '@classes/errors/ajaxerror';
|
||||
|
@ -282,7 +281,7 @@ export class CoreWSProvider {
|
|||
extension = CoreMimetypeUtils.getFileExtension(path) || '';
|
||||
|
||||
// Google Drive extensions will be considered invalid since Moodle usually converts them.
|
||||
if (!extension || CoreArray.contains(['gdoc', 'gsheet', 'gslides', 'gdraw', 'php'], extension)) {
|
||||
if (!extension || ['gdoc', 'gsheet', 'gslides', 'gdraw', 'php'].includes(extension)) {
|
||||
// Not valid, get the file's mimetype.
|
||||
const mimetype = await this.getRemoteFileMimeType(url);
|
||||
|
||||
|
|
|
@ -23,8 +23,12 @@ export class CoreArray {
|
|||
* @param arr Array.
|
||||
* @param item Item.
|
||||
* @return Whether item is within the array.
|
||||
* @deprecated since 4.1. Use arr.includes() instead.
|
||||
*/
|
||||
static contains<T>(arr: T[], item: T): boolean {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('CoreArray.contains is deprecated and will be removed soon. Please use array \'includes\' instead.');
|
||||
|
||||
return arr.indexOf(item) !== -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ export class CoreColors {
|
|||
* @return Color in hex format.
|
||||
*/
|
||||
static getColorHex(color: string): string {
|
||||
const rgba = CoreColors.getColorRGBA(color, true);
|
||||
const rgba = CoreColors.getColorRGBA(color);
|
||||
if (rgba.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
@ -109,21 +109,20 @@ export class CoreColors {
|
|||
* Returns RGBA color from any color format.
|
||||
*
|
||||
* @param color Color in any format.
|
||||
* @param createElement Wether create a new element is needed to calculate value.
|
||||
* @return Red, green, blue and alpha.
|
||||
*/
|
||||
static getColorRGBA(color: string, createElement = false): number[] {
|
||||
if (createElement) {
|
||||
static getColorRGBA(color: string): number[] {
|
||||
if (!color.match(/rgba?\(.*\)/)) {
|
||||
// Convert the color to RGB format.
|
||||
const d = document.createElement('span');
|
||||
d.style.color = color;
|
||||
document.body.appendChild(d);
|
||||
|
||||
// Color in RGB.
|
||||
color = getComputedStyle(d).color;
|
||||
document.body.removeChild(d);
|
||||
}
|
||||
|
||||
const matches = color.match(/\d+/g) || [];
|
||||
const matches = color.match(/\d+[^.]|\d*\.\d*/g) || [];
|
||||
|
||||
return matches.map((a, index) => index < 3 ? parseInt(a, 10) : parseFloat(a));
|
||||
}
|
||||
|
|
|
@ -192,8 +192,7 @@ export class CoreEvents {
|
|||
siteId?: string,
|
||||
): CoreEventObserver {
|
||||
const listener = CoreEvents.on<Fallback, Event>(eventName, (value) => {
|
||||
setTimeout(() => listener.off(), 0);
|
||||
|
||||
listener.off();
|
||||
callBack(value);
|
||||
}, siteId);
|
||||
|
||||
|
@ -241,7 +240,7 @@ export class CoreEvents {
|
|||
if (siteId) {
|
||||
Object.assign(data || {}, { siteId });
|
||||
}
|
||||
this.observables[eventName].next(data);
|
||||
this.observables[eventName].next(data || {});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ export class CoreForms {
|
|||
|
||||
CoreEvents.trigger(CoreEvents.FORM_ACTION, {
|
||||
action: CoreEventFormAction.CANCEL,
|
||||
form: formRef.nativeElement,
|
||||
form: formRef.nativeElement || formRef,
|
||||
}, siteId);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreArray } from '@singletons/array';
|
||||
|
||||
describe('CoreArray singleton', () => {
|
||||
|
||||
it('flattens arrays', () => {
|
||||
expect(CoreArray.flatten([])).toEqual([]);
|
||||
expect(CoreArray.flatten<number>([[1, 2], [3, 4], [5, 6]])).toEqual([1, 2, 3, 4, 5, 6]);
|
||||
});
|
||||
|
||||
it('gets array without an item', () => {
|
||||
const originalArray = ['foo', 'bar', 'baz'];
|
||||
|
||||
expect(CoreArray.withoutItem(originalArray, 'bar')).toEqual(['foo', 'baz']);
|
||||
expect(CoreArray.withoutItem(originalArray, 'not found')).toEqual(['foo', 'bar', 'baz']);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreBrowser } from '@singletons/browser';
|
||||
|
||||
describe('CoreBrowser singleton', () => {
|
||||
|
||||
it('detects if cookie exists', () => {
|
||||
document.cookie = 'first-cookie=foo';
|
||||
document.cookie = 'second-cookie=bar';
|
||||
|
||||
expect(CoreBrowser.hasCookie('first-cookie')).toBe(true);
|
||||
expect(CoreBrowser.hasCookie('second-cookie')).toBe(true);
|
||||
expect(CoreBrowser.hasCookie('third-cookie')).toBe(false);
|
||||
});
|
||||
|
||||
it('gets a cookie', () => {
|
||||
document.cookie = 'first-cookie=foo';
|
||||
document.cookie = 'second-cookie=bar';
|
||||
|
||||
expect(CoreBrowser.getCookie('first-cookie')).toEqual('foo');
|
||||
expect(CoreBrowser.getCookie('second-cookie')).toEqual('bar');
|
||||
expect(CoreBrowser.getCookie('third-cookie')).toEqual(null);
|
||||
});
|
||||
|
||||
it('detects if a localStorage entry exists', () => {
|
||||
localStorage.setItem('first', 'foo');
|
||||
localStorage.setItem('second', 'bar');
|
||||
|
||||
expect(CoreBrowser.hasLocalStorage('first')).toBe(true);
|
||||
expect(CoreBrowser.hasLocalStorage('second')).toBe(true);
|
||||
expect(CoreBrowser.hasLocalStorage('third')).toBe(false);
|
||||
});
|
||||
|
||||
it('gets a localStorage entry', () => {
|
||||
localStorage.setItem('first', 'foo');
|
||||
localStorage.setItem('second', 'bar');
|
||||
|
||||
expect(CoreBrowser.getLocalStorage('first')).toEqual('foo');
|
||||
expect(CoreBrowser.getLocalStorage('second')).toEqual('bar');
|
||||
expect(CoreBrowser.getLocalStorage('third')).toEqual(null);
|
||||
});
|
||||
|
||||
it('sets, gets and removes development settings', () => {
|
||||
CoreBrowser.setDevelopmentSetting('first', 'foo');
|
||||
CoreBrowser.setDevelopmentSetting('second', 'bar');
|
||||
|
||||
expect(CoreBrowser.getDevelopmentSetting('first')).toEqual('foo');
|
||||
expect(CoreBrowser.getDevelopmentSetting('second')).toEqual('bar');
|
||||
expect(CoreBrowser.getDevelopmentSetting('third')).toEqual(null);
|
||||
|
||||
CoreBrowser.clearDevelopmentSetting('second');
|
||||
|
||||
expect(CoreBrowser.getDevelopmentSetting('second')).toEqual(null);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,94 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreColors } from '@singletons/colors';
|
||||
|
||||
describe('CoreColors singleton', () => {
|
||||
|
||||
it('determines if white contrast is better', () => {
|
||||
expect(CoreColors.isWhiteContrastingBetter('#000000')).toBe(true);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#999999')).toBe(true);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#aaaaaa')).toBe(false);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#ffffff')).toBe(false);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#ff0000')).toBe(true);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#00ff00')).toBe(false);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#0000ff')).toBe(true);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#ff00ff')).toBe(true);
|
||||
expect(CoreColors.isWhiteContrastingBetter('#ffff00')).toBe(false);
|
||||
});
|
||||
|
||||
it('makes color darker', () => {
|
||||
expect(CoreColors.darker('#ffffff', 50)).toEqual('#7f7f7f');
|
||||
expect(CoreColors.darker('#ffffff', 20)).toEqual('#cccccc');
|
||||
expect(CoreColors.darker('#ffffff', 80)).toEqual('#323232');
|
||||
expect(CoreColors.darker('#aabbcc', 40)).toEqual('#66707a');
|
||||
});
|
||||
|
||||
it('makes color lighter', () => {
|
||||
expect(CoreColors.lighter('#000000', 50)).toEqual('#7f7f7f');
|
||||
expect(CoreColors.lighter('#000000', 20)).toEqual('#333333');
|
||||
expect(CoreColors.lighter('#000000', 80)).toEqual('#cccccc');
|
||||
expect(CoreColors.lighter('#223344', 40)).toEqual('#7a848e');
|
||||
});
|
||||
|
||||
it('gets color hex value', () => {
|
||||
expect(CoreColors.getColorHex('#123456')).toEqual('#123456');
|
||||
expect(CoreColors.getColorHex('rgb(255, 100, 70)')).toEqual('#ff6446');
|
||||
expect(CoreColors.getColorHex('rgba(255, 100, 70, 0.5)')).toEqual('#ff6446');
|
||||
|
||||
// @todo: There are problems when testing color names (e.g. violet) or hsf colors.
|
||||
// They work fine in real browsers but not in unit tests.
|
||||
});
|
||||
|
||||
it('gets color RGBA value', () => {
|
||||
expect(CoreColors.getColorRGBA('#123456')).toEqual([18, 52, 86]);
|
||||
expect(CoreColors.getColorRGBA('rgb(255, 100, 70)')).toEqual([255, 100, 70]);
|
||||
expect(CoreColors.getColorRGBA('rgba(255, 100, 70, 0.5)')).toEqual([255, 100, 70, 0.5]);
|
||||
|
||||
// @todo: There are problems when testing color names (e.g. violet) or hsf colors.
|
||||
// They work fine in real browsers but not in unit tests.
|
||||
});
|
||||
|
||||
it('converts hex to rgb', () => {
|
||||
expect(CoreColors.hexToRGB('#000000')).toEqual({
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
});
|
||||
expect(CoreColors.hexToRGB('#ffffff')).toEqual({
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255,
|
||||
});
|
||||
expect(CoreColors.hexToRGB('#aabbcc')).toEqual({
|
||||
red: 170,
|
||||
green: 187,
|
||||
blue: 204,
|
||||
});
|
||||
});
|
||||
|
||||
it('gets toolbar background color', () => {
|
||||
document.body.style.setProperty('--core-header-toolbar-background', '#aabbcc');
|
||||
expect(CoreColors.getToolbarBackgroundColor()).toEqual('#aabbcc');
|
||||
|
||||
const header = document.createElement('ion-header');
|
||||
const toolbar = document.createElement('ion-toolbar');
|
||||
toolbar.style.setProperty('--background', '#123456');
|
||||
header.appendChild(toolbar);
|
||||
document.body.appendChild(header);
|
||||
|
||||
expect(CoreColors.getToolbarBackgroundColor()).toEqual('#123456');
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { wait } from '@/testing/utils';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
|
||||
const cssClassName = 'core-components-registry-test';
|
||||
const createAndRegisterInstance = () => {
|
||||
const element = document.createElement('div');
|
||||
element.classList.add(cssClassName);
|
||||
const instance = new ComponentsRegistryTestClass();
|
||||
|
||||
CoreComponentsRegistry.register(element, instance);
|
||||
|
||||
return { element, instance };
|
||||
};
|
||||
|
||||
describe('CoreComponentsRegistry singleton', () => {
|
||||
|
||||
let element: HTMLElement;
|
||||
let testClassInstance: ComponentsRegistryTestClass;
|
||||
|
||||
beforeEach(() => {
|
||||
const result = createAndRegisterInstance();
|
||||
element = result.element;
|
||||
testClassInstance = result.instance;
|
||||
});
|
||||
|
||||
it('resolves stored instances', () => {
|
||||
expect(CoreComponentsRegistry.resolve(element)).toEqual(testClassInstance);
|
||||
expect(CoreComponentsRegistry.resolve(element, ComponentsRegistryTestClass)).toEqual(testClassInstance);
|
||||
expect(CoreComponentsRegistry.resolve(element, CoreComponentsRegistry)).toEqual(null);
|
||||
expect(CoreComponentsRegistry.resolve(document.createElement('div'))).toEqual(null);
|
||||
});
|
||||
|
||||
it('requires stored instances', () => {
|
||||
expect(CoreComponentsRegistry.require(element)).toEqual(testClassInstance);
|
||||
expect(CoreComponentsRegistry.require(element, ComponentsRegistryTestClass)).toEqual(testClassInstance);
|
||||
expect(() => CoreComponentsRegistry.require(element, CoreComponentsRegistry)).toThrow();
|
||||
expect(() => CoreComponentsRegistry.require(document.createElement('div'))).toThrow();
|
||||
});
|
||||
|
||||
it('waits for component ready', async () => {
|
||||
expect(testClassInstance.isReady).toBe(false);
|
||||
|
||||
await CoreComponentsRegistry.waitComponentReady(element);
|
||||
|
||||
expect(testClassInstance.isReady).toBe(true);
|
||||
});
|
||||
|
||||
it('waits for components ready: just one', async () => {
|
||||
expect(testClassInstance.isReady).toBe(false);
|
||||
|
||||
await CoreComponentsRegistry.waitComponentsReady(element, `.${cssClassName}`);
|
||||
|
||||
expect(testClassInstance.isReady).toBe(true);
|
||||
});
|
||||
|
||||
it('waits for components ready: multiple', async () => {
|
||||
const secondResult = createAndRegisterInstance();
|
||||
const thirdResult = createAndRegisterInstance();
|
||||
thirdResult.element.classList.remove(cssClassName); // Remove the class so the element and instance aren't treated.
|
||||
|
||||
const parent = document.createElement('div');
|
||||
parent.appendChild(element);
|
||||
parent.appendChild(secondResult.element);
|
||||
parent.appendChild(thirdResult.element);
|
||||
|
||||
expect(testClassInstance.isReady).toBe(false);
|
||||
expect(secondResult.instance.isReady).toBe(false);
|
||||
expect(thirdResult.instance.isReady).toBe(false);
|
||||
|
||||
await CoreComponentsRegistry.waitComponentsReady(parent, `.${cssClassName}`);
|
||||
|
||||
expect(testClassInstance.isReady).toBe(true);
|
||||
expect(secondResult.instance.isReady).toBe(true);
|
||||
expect(thirdResult.instance.isReady).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
class ComponentsRegistryTestClass {
|
||||
|
||||
randomId = Math.random();
|
||||
isReady = false;
|
||||
|
||||
async ready(): Promise<void> {
|
||||
await wait(50);
|
||||
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
|
||||
const eventName = 'my-event';
|
||||
|
||||
describe('CoreEvents singleton', () => {
|
||||
|
||||
it('can be used to trigger and receive events', () => {
|
||||
const callback = jest.fn();
|
||||
const secondCallback = jest.fn();
|
||||
const data = { foo: 'bar' };
|
||||
|
||||
const listener = CoreEvents.on(eventName, callback);
|
||||
CoreEvents.on('another-event', secondCallback);
|
||||
|
||||
CoreEvents.trigger(eventName, data);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(data);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(secondCallback).not.toHaveBeenCalled();
|
||||
|
||||
listener.off();
|
||||
CoreEvents.trigger(eventName, data);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('only calls the right listeners based on site ID', () => {
|
||||
const callback = jest.fn();
|
||||
const secondCallback = jest.fn();
|
||||
const thirdCallback = jest.fn();
|
||||
const siteId = 'site-id';
|
||||
const data = { foo: 'bar' };
|
||||
const dataWithSiteId = {
|
||||
...data,
|
||||
siteId,
|
||||
};
|
||||
|
||||
CoreEvents.on(eventName, callback);
|
||||
CoreEvents.on(eventName, secondCallback, siteId);
|
||||
CoreEvents.on(eventName, thirdCallback, 'another-site-id');
|
||||
|
||||
CoreEvents.trigger(eventName, data, siteId);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(dataWithSiteId);
|
||||
expect(secondCallback).toHaveBeenCalledWith(dataWithSiteId);
|
||||
expect(thirdCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can call a listener only once', async () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
CoreEvents.once(eventName, callback);
|
||||
CoreEvents.trigger(eventName);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
|
||||
CoreEvents.trigger(eventName);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('can trigger a unique event', async () => {
|
||||
const callback = jest.fn();
|
||||
const secondCallback = jest.fn();
|
||||
|
||||
CoreEvents.on(eventName, callback);
|
||||
|
||||
CoreEvents.triggerUnique(eventName, {});
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
|
||||
CoreEvents.on(eventName, secondCallback);
|
||||
expect(secondCallback).toHaveBeenCalledTimes(1);
|
||||
|
||||
CoreEvents.triggerUnique(eventName, {});
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(secondCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('allows listening to multiple events with a single call', async () => {
|
||||
const callback = jest.fn();
|
||||
const secondEventName = 'second-event';
|
||||
|
||||
const listener = CoreEvents.onMultiple([eventName, secondEventName], callback);
|
||||
|
||||
CoreEvents.trigger(eventName);
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
|
||||
CoreEvents.trigger(secondEventName);
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
|
||||
listener.off();
|
||||
|
||||
CoreEvents.trigger(eventName);
|
||||
CoreEvents.trigger(secondEventName);
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,134 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { CoreEventFormAction, CoreEvents } from '@singletons/events';
|
||||
import { CoreForms } from '@singletons/form';
|
||||
|
||||
const createInputElement = (type: string, name: string, value = ''): HTMLInputElement => {
|
||||
const input = document.createElement('input');
|
||||
input.type = type;
|
||||
input.name = name;
|
||||
input.value = value;
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
describe('CoreForms singleton', () => {
|
||||
|
||||
it('gets data from form', () => {
|
||||
// Create several types of inputs.
|
||||
const textInput = createInputElement('text', 'mytext');
|
||||
const firstRadio = createInputElement('radio', 'myradio', 'firstradio');
|
||||
const secondRadio = createInputElement('radio', 'myradio', 'secondradio');
|
||||
const checkbox = createInputElement('checkbox', 'mycheckbox');
|
||||
const hiddenInput = createInputElement('hidden', 'myhidden', 'hiddenvalue');
|
||||
const submitInput = createInputElement('submit', 'submit');
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.name = 'mytextarea';
|
||||
|
||||
const select = document.createElement('select');
|
||||
select.name = 'myselect';
|
||||
const firstOption = document.createElement('option');
|
||||
firstOption.value = 'firstoption';
|
||||
const secondOption = document.createElement('option');
|
||||
secondOption.value = 'secondoption';
|
||||
select.appendChild(firstOption);
|
||||
select.appendChild(secondOption);
|
||||
|
||||
// Create a form with the inputs.
|
||||
const form = document.createElement('form');
|
||||
form.appendChild(textInput);
|
||||
form.appendChild(firstRadio);
|
||||
form.appendChild(secondRadio);
|
||||
form.appendChild(checkbox);
|
||||
form.appendChild(hiddenInput);
|
||||
form.appendChild(submitInput);
|
||||
form.appendChild(textarea);
|
||||
form.appendChild(select);
|
||||
|
||||
// Test data is retrieved.
|
||||
const values: Record<string, string | boolean> = {
|
||||
mytext: '',
|
||||
mycheckbox: false,
|
||||
myhidden: 'hiddenvalue',
|
||||
mytextarea: '',
|
||||
myselect: 'firstoption',
|
||||
};
|
||||
|
||||
expect(CoreForms.getDataFromForm(form)).toEqual(values);
|
||||
|
||||
// Change some values and test again.
|
||||
textInput.value = values.mytext = 'a value';
|
||||
select.value = values.myselect = 'secondoption';
|
||||
firstRadio.checked = true;
|
||||
values.myradio = 'firstradio';
|
||||
checkbox.checked = values.mycheckbox = true;
|
||||
textarea.value = values.mytextarea = 'textarea value';
|
||||
|
||||
expect(CoreForms.getDataFromForm(form)).toEqual(values);
|
||||
});
|
||||
|
||||
it('triggers form action events', () => {
|
||||
const form = document.createElement('form');
|
||||
const formElRef = new ElementRef(form);
|
||||
const siteId = 'site-id';
|
||||
const callback = jest.fn();
|
||||
const secondCallback = jest.fn();
|
||||
|
||||
CoreEvents.on(CoreEvents.FORM_ACTION, callback, siteId);
|
||||
CoreEvents.on(CoreEvents.FORM_ACTION, secondCallback, 'another-site');
|
||||
|
||||
CoreForms.triggerFormCancelledEvent(form, siteId);
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
action: CoreEventFormAction.CANCEL,
|
||||
form,
|
||||
siteId,
|
||||
});
|
||||
|
||||
CoreForms.triggerFormCancelledEvent(formElRef, siteId);
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
action: CoreEventFormAction.CANCEL,
|
||||
form,
|
||||
siteId,
|
||||
});
|
||||
|
||||
CoreForms.triggerFormSubmittedEvent(form, true, siteId);
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
action: CoreEventFormAction.SUBMIT,
|
||||
form,
|
||||
online: true,
|
||||
siteId,
|
||||
});
|
||||
|
||||
CoreForms.triggerFormSubmittedEvent(form, false, siteId);
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
action: CoreEventFormAction.SUBMIT,
|
||||
form,
|
||||
online: false,
|
||||
siteId,
|
||||
});
|
||||
|
||||
CoreForms.triggerFormSubmittedEvent(formElRef, true, siteId);
|
||||
expect(callback).toHaveBeenCalledWith({
|
||||
action: CoreEventFormAction.SUBMIT,
|
||||
form,
|
||||
online: true,
|
||||
siteId,
|
||||
});
|
||||
expect(secondCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Locutus } from '@singletons/locutus';
|
||||
|
||||
describe('Locutus singleton', () => {
|
||||
|
||||
it('unserializes PHP strings', () => {
|
||||
expect(Locutus.unserialize('a:3:{s:1:"a";i:1;s:1:"b";i:2;s:3:"foo";s:3:"bar";}')).toEqual({
|
||||
a: 1,
|
||||
b: 2,
|
||||
foo: 'bar',
|
||||
});
|
||||
|
||||
expect(Locutus.unserialize(
|
||||
'O:8:"stdClass":3:{s:3:"foo";s:3:"bar";s:5:"lorem";s:5:"ipsum";s:8:"subclass";O:8:"stdClass":1:{s:2:"ok";b:1;}}',
|
||||
)).toEqual({
|
||||
foo: 'bar',
|
||||
lorem: 'ipsum',
|
||||
subclass: {
|
||||
ok: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces text within a portion of a string', () => {
|
||||
const originalText = 'A sample text.';
|
||||
const newText = 'foo';
|
||||
|
||||
expect(Locutus.substrReplace(originalText, newText, 0)).toEqual(newText);
|
||||
expect(Locutus.substrReplace(originalText, newText, 0, originalText.length)).toEqual(newText);
|
||||
expect(Locutus.substrReplace(originalText, newText, 0, 0)).toEqual(newText + originalText);
|
||||
expect(Locutus.substrReplace(originalText, newText, 9, -1)).toEqual('A sample foo.');
|
||||
expect(Locutus.substrReplace(originalText, newText, -5, -1)).toEqual('A sample foo.');
|
||||
expect(Locutus.substrReplace(originalText, newText, 2, 6)).toEqual('A foo text.');
|
||||
});
|
||||
|
||||
});
|
|
@ -17,8 +17,11 @@ import { CoreUrl } from '@singletons/url';
|
|||
describe('CoreUrl singleton', () => {
|
||||
|
||||
it('parses standard urls', () => {
|
||||
expect(CoreUrl.parse('https://my.subdomain.com/path/?query=search#hash')).toEqual({
|
||||
expect(CoreUrl.parse('https://u1:pw1@my.subdomain.com/path/?query=search#hash')).toEqual({
|
||||
protocol: 'https',
|
||||
credentials: 'u1:pw1',
|
||||
username: 'u1',
|
||||
password: 'pw1',
|
||||
domain: 'my.subdomain.com',
|
||||
path: '/path/',
|
||||
query: 'query=search',
|
||||
|
@ -43,6 +46,46 @@ describe('CoreUrl singleton', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('assembles standard urls', () => {
|
||||
expect(CoreUrl.assemble({
|
||||
protocol: 'https',
|
||||
credentials: 'u1:pw1',
|
||||
domain: 'my.subdomain.com',
|
||||
path: '/path/',
|
||||
query: 'query=search',
|
||||
fragment: 'hash',
|
||||
})).toEqual('https://u1:pw1@my.subdomain.com/path/?query=search#hash');
|
||||
});
|
||||
|
||||
it('guesses the Mooddle domain', () => {
|
||||
// Check known endpoints first.
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/my')).toEqual('school.edu/moodle');
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/?redirect=0')).toEqual('school.edu/moodle');
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/index.php')).toEqual('school.edu/moodle');
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/course/view.php')).toEqual('school.edu/moodle');
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/login/index.php')).toEqual('school.edu/moodle');
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/mod/page/view.php')).toEqual('school.edu/moodle');
|
||||
|
||||
// Check an unknown endpoint.
|
||||
expect(CoreUrl.guessMoodleDomain('https://school.edu/moodle/unknown/endpoint.php')).toEqual('school.edu');
|
||||
});
|
||||
|
||||
it('detects valid Moodle urls', () => {
|
||||
expect(CoreUrl.isValidMoodleUrl('https://school.edu')).toBe(true);
|
||||
expect(CoreUrl.isValidMoodleUrl('https://school.edu/path/?query=search#hash')).toBe(true);
|
||||
expect(CoreUrl.isValidMoodleUrl('//school.edu')).toBe(true);
|
||||
expect(CoreUrl.isValidMoodleUrl('school.edu')).toBe(true);
|
||||
expect(CoreUrl.isValidMoodleUrl('localhost')).toBe(true);
|
||||
|
||||
expect(CoreUrl.isValidMoodleUrl('some text')).toBe(false);
|
||||
});
|
||||
|
||||
it('removes protocol', () => {
|
||||
expect(CoreUrl.removeProtocol('https://school.edu')).toEqual('school.edu');
|
||||
expect(CoreUrl.removeProtocol('ftp://school.edu')).toEqual('school.edu');
|
||||
expect(CoreUrl.removeProtocol('school.edu')).toEqual('school.edu');
|
||||
});
|
||||
|
||||
it('compares domains and paths', () => {
|
||||
expect(CoreUrl.sameDomainAndPath('https://school.edu', 'https://school.edu')).toBe(true);
|
||||
expect(CoreUrl.sameDomainAndPath('https://school.edu', 'HTTPS://SCHOOL.EDU')).toBe(true);
|
||||
|
@ -56,6 +99,18 @@ describe('CoreUrl singleton', () => {
|
|||
expect(CoreUrl.sameDomainAndPath('https://school.edu/moodle', 'https://school.edu/moodle/about')).toBe(false);
|
||||
});
|
||||
|
||||
it('gets the anchor of a URL', () => {
|
||||
expect(CoreUrl.getUrlAnchor('https://school.edu#foo')).toEqual('#foo');
|
||||
expect(CoreUrl.getUrlAnchor('https://school.edu#foo#bar')).toEqual('#foo#bar');
|
||||
expect(CoreUrl.getUrlAnchor('https://school.edu')).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('removes the anchor of a URL', () => {
|
||||
expect(CoreUrl.removeUrlAnchor('https://school.edu#foo')).toEqual('https://school.edu');
|
||||
expect(CoreUrl.removeUrlAnchor('https://school.edu#foo#bar')).toEqual('https://school.edu');
|
||||
expect(CoreUrl.removeUrlAnchor('https://school.edu')).toEqual('https://school.edu');
|
||||
});
|
||||
|
||||
it('converts to absolute URLs', () => {
|
||||
expect(CoreUrl.toAbsoluteURL('https://school.edu/foo/bar', 'https://mysite.edu')).toBe('https://mysite.edu');
|
||||
expect(CoreUrl.toAbsoluteURL('https://school.edu/foo/bar', '//mysite.edu')).toBe('https://mysite.edu');
|
||||
|
|
|
@ -17,6 +17,9 @@ import { NgZone } from '@singletons';
|
|||
import { TestsBehatBlocking } from './behat-blocking';
|
||||
import { TestBehatElementLocator } from './behat-runtime';
|
||||
|
||||
// Containers that block containers behind them.
|
||||
const blockingContainers = ['ION-ALERT', 'ION-POPOVER', 'ION-ACTION-SHEET', 'CORE-USER-TOURS-USER-TOUR', 'ION-PAGE'];
|
||||
|
||||
/**
|
||||
* Behat Dom Utils helper functions.
|
||||
*/
|
||||
|
@ -251,71 +254,68 @@ export class TestsBehatDomUtils {
|
|||
};
|
||||
|
||||
/**
|
||||
* Function to find top container element.
|
||||
* Function to find top container elements.
|
||||
*
|
||||
* @param containerName Whether to search inside the a container name.
|
||||
* @return Found top container element.
|
||||
* @return Found top container elements.
|
||||
*/
|
||||
protected static getCurrentTopContainerElement(containerName: string): HTMLElement | null {
|
||||
let topContainer: HTMLElement | null = null;
|
||||
let containers: HTMLElement[] = [];
|
||||
const nonImplementedSelectors =
|
||||
'ion-alert, ion-popover, ion-action-sheet, ion-modal, core-user-tours-user-tour.is-active, page-core-mainmenu, ion-app';
|
||||
protected static getCurrentTopContainerElements(containerName: string): HTMLElement[] {
|
||||
const topContainers: HTMLElement[] = [];
|
||||
let containers = Array.from(document.querySelectorAll<HTMLElement>([
|
||||
'ion-alert.hydrated',
|
||||
'ion-popover.hydrated',
|
||||
'ion-action-sheet.hydrated',
|
||||
'ion-modal.hydrated',
|
||||
'core-user-tours-user-tour.is-active',
|
||||
'ion-toast.hydrated',
|
||||
'page-core-mainmenu',
|
||||
'ion-app',
|
||||
].join(', ')));
|
||||
|
||||
switch (containerName) {
|
||||
case 'html':
|
||||
containers = Array.from(document.querySelectorAll<HTMLElement>('html'));
|
||||
break;
|
||||
case 'toast':
|
||||
containers = Array.from(document.querySelectorAll('ion-app ion-toast.hydrated'));
|
||||
containers = containers.map(container => container?.shadowRoot?.querySelector('.toast-container') || container);
|
||||
break;
|
||||
case 'alert':
|
||||
containers = Array.from(document.querySelectorAll('ion-app ion-alert.hydrated'));
|
||||
break;
|
||||
case 'action-sheet':
|
||||
containers = Array.from(document.querySelectorAll('ion-app ion-action-sheet.hydrated'));
|
||||
break;
|
||||
case 'modal':
|
||||
containers = Array.from(document.querySelectorAll('ion-app ion-modal.hydrated'));
|
||||
break;
|
||||
case 'popover':
|
||||
containers = Array.from(document.querySelectorAll('ion-app ion-popover.hydrated'));
|
||||
break;
|
||||
case 'user-tour':
|
||||
containers = Array.from(document.querySelectorAll('core-user-tours-user-tour.is-active'));
|
||||
break;
|
||||
default:
|
||||
// Other containerName or not implemented.
|
||||
containers = Array.from(document.querySelectorAll<HTMLElement>(nonImplementedSelectors));
|
||||
containers = containers
|
||||
.filter(container => {
|
||||
if (container.tagName === 'ION-ALERT') {
|
||||
// For some reason, in Behat sometimes alerts aren't removed from DOM, the close animation doesn't finish.
|
||||
// Filter alerts with pointer-events none since that style is set before the close animation starts.
|
||||
return container.style.pointerEvents !== 'none';
|
||||
}
|
||||
|
||||
// Ignore pages that are inside other visible pages.
|
||||
return container.tagName !== 'ION-PAGE' || !container.closest('.ion-page.ion-page-hidden');
|
||||
})
|
||||
// Sort them by z-index.
|
||||
.sort((a, b) => Number(getComputedStyle(b).zIndex) - Number(getComputedStyle(a).zIndex));
|
||||
|
||||
if (containerName === 'split-view content') {
|
||||
// Find non hidden pages inside the containers.
|
||||
containers.some(container => {
|
||||
if (!container.classList.contains('ion-page')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pageContainers = Array.from(container.querySelectorAll<HTMLElement>('.ion-page:not(.ion-page-hidden)'));
|
||||
let topContainer = pageContainers.find((page) => !page.closest('.ion-page.ion-page-hidden')) ?? null;
|
||||
|
||||
topContainer = (topContainer || container).querySelector<HTMLElement>('core-split-view ion-router-outlet');
|
||||
topContainer && topContainers.push(topContainer);
|
||||
|
||||
return !!topContainer;
|
||||
});
|
||||
|
||||
return topContainers;
|
||||
}
|
||||
|
||||
if (containers.length > 0) {
|
||||
// Get the one with more zIndex.
|
||||
topContainer =
|
||||
containers.reduce((a, b) => getComputedStyle(a).zIndex > getComputedStyle(b).zIndex ? a : b, containers[0]);
|
||||
}
|
||||
|
||||
if (!topContainer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (containerName == 'page' || containerName == 'split-view content') {
|
||||
// Find non hidden pages inside the container.
|
||||
let pageContainers = Array.from(topContainer.querySelectorAll<HTMLElement>('.ion-page:not(.ion-page-hidden)'));
|
||||
pageContainers = pageContainers.filter((page) => !page.closest('.ion-page.ion-page-hidden'));
|
||||
|
||||
if (pageContainers.length > 0) {
|
||||
// Get the more general one to avoid failing.
|
||||
topContainer = pageContainers[0];
|
||||
// Get containers until one blocks other views.
|
||||
containers.find(container => {
|
||||
if (container.tagName === 'ION-TOAST') {
|
||||
container = container.shadowRoot?.querySelector('.toast-container') || container;
|
||||
}
|
||||
topContainers.push(container);
|
||||
|
||||
if (containerName == 'split-view content') {
|
||||
topContainer = topContainer.querySelector<HTMLElement>('core-split-view ion-router-outlet');
|
||||
}
|
||||
}
|
||||
return blockingContainers.includes(container.tagName);
|
||||
});
|
||||
|
||||
return topContainer;
|
||||
return topContainers;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -337,9 +337,24 @@ export class TestsBehatDomUtils {
|
|||
* @return Found elements
|
||||
*/
|
||||
protected static findElementsBasedOnText(locator: TestBehatElementLocator, containerName = ''): HTMLElement[] {
|
||||
let topContainer = this.getCurrentTopContainerElement(containerName);
|
||||
const topContainers = this.getCurrentTopContainerElements(containerName);
|
||||
|
||||
let container = topContainer;
|
||||
return topContainers.reduce((elements, container) =>
|
||||
elements.concat(this.findElementsBasedOnTextInContainer(locator, container)), <HTMLElement[]> []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to find elements based on their text or Aria label.
|
||||
*
|
||||
* @param locator Element locator.
|
||||
* @param container Container to search in.
|
||||
* @return Found elements
|
||||
*/
|
||||
protected static findElementsBasedOnTextInContainer(
|
||||
locator: TestBehatElementLocator,
|
||||
topContainer: HTMLElement,
|
||||
): HTMLElement[] {
|
||||
let container: HTMLElement | null = topContainer;
|
||||
|
||||
if (locator.within) {
|
||||
const withinElements = this.findElementsBasedOnText(locator.within);
|
||||
|
|
|
@ -256,3 +256,16 @@ export async function renderWrapperComponent<T>(
|
|||
export function agnosticPath(unixPath: string): string {
|
||||
return unixPath.replace(/\//g, sep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits a certain time.
|
||||
*
|
||||
* @param time Number of milliseconds.
|
||||
*/
|
||||
export function wait(time: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, time);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue