commit
d1ec772ed3
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { EventEmitter } from '@angular/core';
|
import { EventEmitter } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribable object.
|
* Subscribable object.
|
||||||
|
@ -33,20 +33,29 @@ export class CoreSubscriptions {
|
||||||
* @param onError Callback to run when the an error happens.
|
* @param onError Callback to run when the an error happens.
|
||||||
*/
|
*/
|
||||||
static once<T>(subscribable: Subscribable<T>, onSuccess: (value: T) => unknown, onError?: (error: unknown) => unknown): void {
|
static once<T>(subscribable: Subscribable<T>, onSuccess: (value: T) => unknown, onError?: (error: unknown) => unknown): void {
|
||||||
const subscription = subscribable.subscribe(
|
let unsubscribe = false;
|
||||||
|
let subscription: Subscription | null = null;
|
||||||
|
|
||||||
|
subscription = subscribable.subscribe(
|
||||||
value => {
|
value => {
|
||||||
// Unsubscribe using a timeout because we can receive a value immediately.
|
// Subscription variable might not be set because we can receive a value immediately.
|
||||||
setTimeout(() => subscription.unsubscribe(), 0);
|
unsubscribe = true;
|
||||||
|
subscription?.unsubscribe();
|
||||||
|
|
||||||
onSuccess(value);
|
onSuccess(value);
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
// Unsubscribe using a timeout because we can receive a value immediately.
|
// Subscription variable might not be set because we can receive a value immediately.
|
||||||
setTimeout(() => subscription.unsubscribe(), 0);
|
unsubscribe = true;
|
||||||
|
subscription?.unsubscribe();
|
||||||
|
|
||||||
onError && onError(error);
|
onError && onError(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (unsubscribe) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
// (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.
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
import { CoreConstants } from '@/core/constants';
|
||||||
|
import { CoreBrowser } from '@singletons/browser';
|
||||||
|
import { CoreLogger } from '@singletons/logger';
|
||||||
|
|
||||||
|
describe('CoreLogger singleton', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
console.log = jest.fn();
|
||||||
|
console.info = jest.fn();
|
||||||
|
console.warn = jest.fn();
|
||||||
|
console.debug = jest.fn();
|
||||||
|
console.error = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds logs to the console in dev environment', () => {
|
||||||
|
// Simulate dev environment.
|
||||||
|
const isTesting = CoreConstants.BUILD.isTesting;
|
||||||
|
const isProduction = CoreConstants.BUILD.isProduction;
|
||||||
|
CoreConstants.BUILD.isTesting = false;
|
||||||
|
CoreConstants.BUILD.isProduction = false;
|
||||||
|
|
||||||
|
const logger = CoreLogger.getInstance('TestName');
|
||||||
|
|
||||||
|
logger.log('Log message');
|
||||||
|
expect((<jest.Mock> console.log).mock.calls[0][0]).toContain('TestName: Log message');
|
||||||
|
|
||||||
|
logger.info('Info message');
|
||||||
|
expect((<jest.Mock> console.info).mock.calls[0][0]).toContain('TestName: Info message');
|
||||||
|
|
||||||
|
logger.warn('Warn message');
|
||||||
|
expect((<jest.Mock> console.warn).mock.calls[0][0]).toContain('TestName: Warn message');
|
||||||
|
|
||||||
|
logger.debug('Debug message');
|
||||||
|
expect((<jest.Mock> console.debug).mock.calls[0][0]).toContain('TestName: Debug message');
|
||||||
|
|
||||||
|
logger.error('Error message');
|
||||||
|
expect((<jest.Mock> console.error).mock.calls[0][0]).toContain('TestName: Error message');
|
||||||
|
|
||||||
|
CoreConstants.BUILD.isTesting = isTesting;
|
||||||
|
CoreConstants.BUILD.isProduction = isProduction;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds logs to the console if enabled via dev setting', () => {
|
||||||
|
// Enable logging.
|
||||||
|
CoreBrowser.setDevelopmentSetting('LoggingEnabled', '1');
|
||||||
|
|
||||||
|
const logger = CoreLogger.getInstance('TestName');
|
||||||
|
|
||||||
|
logger.log('Log message');
|
||||||
|
expect((<jest.Mock> console.log).mock.calls[0][0]).toContain('TestName: Log message');
|
||||||
|
|
||||||
|
logger.info('Info message');
|
||||||
|
expect((<jest.Mock> console.info).mock.calls[0][0]).toContain('TestName: Info message');
|
||||||
|
|
||||||
|
logger.warn('Warn message');
|
||||||
|
expect((<jest.Mock> console.warn).mock.calls[0][0]).toContain('TestName: Warn message');
|
||||||
|
|
||||||
|
logger.debug('Debug message');
|
||||||
|
expect((<jest.Mock> console.debug).mock.calls[0][0]).toContain('TestName: Debug message');
|
||||||
|
|
||||||
|
logger.error('Error message');
|
||||||
|
expect((<jest.Mock> console.error).mock.calls[0][0]).toContain('TestName: Error message');
|
||||||
|
|
||||||
|
CoreBrowser.clearDevelopmentSetting('LoggingEnabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t log to the console in testing environment', () => {
|
||||||
|
// Disable production.
|
||||||
|
const isProduction = CoreConstants.BUILD.isProduction;
|
||||||
|
CoreConstants.BUILD.isProduction = false;
|
||||||
|
|
||||||
|
const logger = CoreLogger.getInstance('TestName');
|
||||||
|
|
||||||
|
logger.log('Log message');
|
||||||
|
expect(console.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
logger.info('Info message');
|
||||||
|
expect(console.info).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
logger.warn('Warn message');
|
||||||
|
expect(console.warn).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
logger.debug('Debug message');
|
||||||
|
expect(console.debug).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
logger.error('Error message');
|
||||||
|
expect(console.error).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
CoreConstants.BUILD.isProduction = isProduction;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays a warning in production environment', () => {
|
||||||
|
// Enable production.
|
||||||
|
const isProduction = CoreConstants.BUILD.isProduction;
|
||||||
|
CoreConstants.BUILD.isProduction = true;
|
||||||
|
|
||||||
|
CoreLogger.getInstance('TestName');
|
||||||
|
|
||||||
|
expect(console.warn).toHaveBeenCalledWith('Log is disabled in production app');
|
||||||
|
|
||||||
|
CoreConstants.BUILD.isProduction = isProduction;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
// (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 { CoreMath } from '@singletons/math';
|
||||||
|
|
||||||
|
describe('CoreMath singleton', () => {
|
||||||
|
|
||||||
|
it('clamps values', () => {
|
||||||
|
expect(CoreMath.clamp(150, 100, 200)).toEqual(150);
|
||||||
|
expect(CoreMath.clamp(25, 100, 200)).toEqual(100);
|
||||||
|
expect(CoreMath.clamp(-100, 100, 200)).toEqual(100);
|
||||||
|
expect(CoreMath.clamp(500, 100, 200)).toEqual(200);
|
||||||
|
expect(CoreMath.clamp(50.55, 100.11, 200.22)).toEqual(100.11);
|
||||||
|
expect(CoreMath.clamp(100, -200.22, -100.11)).toEqual(-100.11);
|
||||||
|
expect(CoreMath.clamp(-500, -200.22, -100.11)).toEqual(-200.22);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,184 @@
|
||||||
|
// (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 { CoreObject } from '@singletons/object';
|
||||||
|
|
||||||
|
describe('CoreObject singleton', () => {
|
||||||
|
|
||||||
|
it('compares two values, checking all subproperties if needed', () => {
|
||||||
|
expect(CoreObject.deepEquals(1, 1)).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals(1, 2)).toBe(false);
|
||||||
|
expect(CoreObject.deepEquals(NaN, NaN)).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals(NaN, 0)).toBe(false);
|
||||||
|
|
||||||
|
expect(CoreObject.deepEquals('foo', 'foo')).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals('foo', 'bar')).toBe(false);
|
||||||
|
|
||||||
|
expect(CoreObject.deepEquals(true, true)).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals(true, false)).toBe(false);
|
||||||
|
|
||||||
|
expect(CoreObject.deepEquals(null, null)).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals(undefined, undefined)).toBe(true);
|
||||||
|
expect(CoreObject.deepEquals(null, undefined)).toBe(false);
|
||||||
|
|
||||||
|
const firstObject = {
|
||||||
|
foo: 'bar',
|
||||||
|
subobject: {
|
||||||
|
foo: 'bar',
|
||||||
|
subobject: {
|
||||||
|
foo: 'bar',
|
||||||
|
items: [1, 2, 3],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const secondObject = {
|
||||||
|
foo: 'bar',
|
||||||
|
subobject: {
|
||||||
|
foo: 'bar',
|
||||||
|
subobject: {
|
||||||
|
foo: 'bar',
|
||||||
|
items: [1, 2, 3],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(true);
|
||||||
|
|
||||||
|
secondObject.foo = 'baz';
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(false);
|
||||||
|
|
||||||
|
secondObject.foo = 'bar';
|
||||||
|
secondObject.subobject.foo = 'baz';
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(false);
|
||||||
|
|
||||||
|
secondObject.subobject.foo = 'bar';
|
||||||
|
secondObject.subobject.subobject.foo = 'baz';
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(false);
|
||||||
|
|
||||||
|
secondObject.subobject.subobject.foo = 'bar';
|
||||||
|
secondObject.subobject.subobject.items[0] = 0;
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(false);
|
||||||
|
|
||||||
|
secondObject.subobject.subobject.items[0] = 1;
|
||||||
|
expect(CoreObject.deepEquals(firstObject, secondObject)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets all property names', () => {
|
||||||
|
expect(CoreObject.getAllPropertyNames(null)).toEqual(new Set([]));
|
||||||
|
expect(CoreObject.getAllPropertyNames(undefined)).toEqual(new Set([]));
|
||||||
|
expect(CoreObject.getAllPropertyNames(1)).toEqual(new Set([]));
|
||||||
|
expect(CoreObject.getAllPropertyNames('foo')).toEqual(new Set([]));
|
||||||
|
|
||||||
|
expect(CoreObject.getAllPropertyNames({
|
||||||
|
foo: 1,
|
||||||
|
bar: 2,
|
||||||
|
doSomething: () => {
|
||||||
|
// Nothing to do.
|
||||||
|
},
|
||||||
|
})).toEqual(new Set(['foo', 'bar', 'doSomething']));
|
||||||
|
|
||||||
|
expect(CoreObject.getAllPropertyNames(new TestParentClass()))
|
||||||
|
.toEqual(new Set(['constructor', 'foo', 'bar', 'baz', 'doSomething']));
|
||||||
|
expect(CoreObject.getAllPropertyNames(new TestSubClass()))
|
||||||
|
.toEqual(new Set(['constructor', 'foo', 'bar', 'baz', 'doSomething', 'sub', 'doSomethingElse']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if an object is empty', () => {
|
||||||
|
expect(CoreObject.isEmpty({})).toEqual(true);
|
||||||
|
expect(CoreObject.isEmpty({ foo: 1 })).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a copy of an object without certain properties', () => {
|
||||||
|
const originalObject = {
|
||||||
|
foo: 1,
|
||||||
|
bar: 2,
|
||||||
|
baz: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(CoreObject.without(originalObject, [])).toEqual(originalObject);
|
||||||
|
expect(CoreObject.without(originalObject, ['foo'])).toEqual({
|
||||||
|
bar: 2,
|
||||||
|
baz: 3,
|
||||||
|
});
|
||||||
|
expect(CoreObject.without(originalObject, ['foo', 'baz'])).toEqual({
|
||||||
|
bar: 2,
|
||||||
|
});
|
||||||
|
expect(originalObject).toEqual({
|
||||||
|
foo: 1,
|
||||||
|
bar: 2,
|
||||||
|
baz: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a copy of an object without null/undefined properties', () => {
|
||||||
|
const objectWithoutEmpty = {
|
||||||
|
bool: false,
|
||||||
|
num: 0,
|
||||||
|
nan: NaN,
|
||||||
|
str: '',
|
||||||
|
obj: {},
|
||||||
|
arr: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(CoreObject.withoutEmpty({
|
||||||
|
...objectWithoutEmpty,
|
||||||
|
foo: null,
|
||||||
|
bar: undefined,
|
||||||
|
baz: null,
|
||||||
|
})).toEqual(objectWithoutEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a copy of an object without undefined properties', () => {
|
||||||
|
const objectWithoutUndefined = {
|
||||||
|
bool: false,
|
||||||
|
num: 0,
|
||||||
|
nan: NaN,
|
||||||
|
str: '',
|
||||||
|
obj: {},
|
||||||
|
arr: [],
|
||||||
|
foo: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(CoreObject.withoutUndefined({
|
||||||
|
...objectWithoutUndefined,
|
||||||
|
bar: undefined,
|
||||||
|
baz: undefined,
|
||||||
|
})).toEqual(objectWithoutUndefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
class TestParentClass {
|
||||||
|
|
||||||
|
foo = 1;
|
||||||
|
protected bar = 2;
|
||||||
|
private baz = 3;
|
||||||
|
|
||||||
|
protected doSomething(): void {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestSubClass extends TestParentClass {
|
||||||
|
|
||||||
|
foo = 10;
|
||||||
|
protected bar = 20;
|
||||||
|
private sub = 30;
|
||||||
|
|
||||||
|
protected doSomethingElse(): void {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// (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 { CoreSubscriptions } from '@singletons/subscriptions';
|
||||||
|
import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
|
|
||||||
|
describe('CoreSubscriptions singleton', () => {
|
||||||
|
|
||||||
|
it('calls callbacks only once', async () => {
|
||||||
|
// Test call success function.
|
||||||
|
let subject = new Subject();
|
||||||
|
let success = jest.fn();
|
||||||
|
let error = jest.fn();
|
||||||
|
CoreSubscriptions.once(subject, success, error);
|
||||||
|
|
||||||
|
subject.next('foo');
|
||||||
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
|
expect(success).toHaveBeenCalledWith('foo');
|
||||||
|
|
||||||
|
subject.next('bar');
|
||||||
|
subject.error('foo');
|
||||||
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
|
expect(error).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Test call error function.
|
||||||
|
subject = new Subject(); // Create a new Subject because the previous one already has an error.
|
||||||
|
success = jest.fn();
|
||||||
|
CoreSubscriptions.once(subject, success, error);
|
||||||
|
|
||||||
|
subject.error('foo');
|
||||||
|
expect(error).toHaveBeenCalledWith('foo');
|
||||||
|
|
||||||
|
subject.next('foo');
|
||||||
|
subject.error('bar');
|
||||||
|
expect(error).toHaveBeenCalledTimes(1);
|
||||||
|
expect(success).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Test with behaviour subject (success callback called immediately).
|
||||||
|
const beaviourSubject = new BehaviorSubject('foo');
|
||||||
|
error = jest.fn();
|
||||||
|
CoreSubscriptions.once(beaviourSubject, success, error);
|
||||||
|
|
||||||
|
expect(success).toHaveBeenCalledWith('foo');
|
||||||
|
|
||||||
|
beaviourSubject.next('bar');
|
||||||
|
beaviourSubject.error('foo');
|
||||||
|
expect(success).toHaveBeenCalledTimes(1);
|
||||||
|
expect(error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
// (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 { CoreText } from '@singletons/text';
|
||||||
|
|
||||||
|
describe('CoreText singleton', () => {
|
||||||
|
|
||||||
|
it('adds a starting slash if needed', () => {
|
||||||
|
expect(CoreText.addStartingSlash('')).toEqual('/');
|
||||||
|
expect(CoreText.addStartingSlash('foo')).toEqual('/foo');
|
||||||
|
expect(CoreText.addStartingSlash('/foo')).toEqual('/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('remove ending slash if needed', () => {
|
||||||
|
expect(CoreText.removeEndingSlash('/')).toEqual('');
|
||||||
|
expect(CoreText.removeEndingSlash('foo')).toEqual('foo');
|
||||||
|
expect(CoreText.removeEndingSlash('foo/')).toEqual('foo');
|
||||||
|
expect(CoreText.removeEndingSlash('foo//')).toEqual('foo/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('concatenates paths', () => {
|
||||||
|
expect(CoreText.concatenatePaths('', 'foo/bar')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo/bar', '')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo', 'bar')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo/', 'bar')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo', '/bar')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo/', '/bar')).toEqual('foo/bar');
|
||||||
|
expect(CoreText.concatenatePaths('foo/bar', 'baz')).toEqual('foo/bar/baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,69 @@
|
||||||
|
// (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 { mockTranslate } from '@/testing/utils';
|
||||||
|
import { CoreTime } from '@singletons/time';
|
||||||
|
|
||||||
|
describe('CoreTime singleton', () => {
|
||||||
|
|
||||||
|
it('formats time in a human readable format', () => {
|
||||||
|
mockTranslate({
|
||||||
|
'core.days': 'days',
|
||||||
|
'core.day': 'day',
|
||||||
|
'core.hours': 'hours',
|
||||||
|
'core.hour': 'hour',
|
||||||
|
'core.mins': 'mins',
|
||||||
|
'core.min': 'min',
|
||||||
|
'core.now': 'now',
|
||||||
|
'core.secs': 'secs',
|
||||||
|
'core.sec': 'sec',
|
||||||
|
'core.years': 'years',
|
||||||
|
'core.year': 'year',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(CoreTime.formatTime(0)).toEqual('now');
|
||||||
|
expect(CoreTime.formatTime(-5)).toEqual('5 secs');
|
||||||
|
expect(CoreTime.formatTime(61)).toEqual('1 min 1 sec');
|
||||||
|
expect(CoreTime.formatTime(7321)).toEqual('2 hours 2 mins');
|
||||||
|
expect(CoreTime.formatTime(352861)).toEqual('4 days 2 hours');
|
||||||
|
expect(CoreTime.formatTime(31888861)).toEqual('1 year 4 days');
|
||||||
|
expect(CoreTime.formatTime(-31888861)).toEqual('1 year 4 days');
|
||||||
|
|
||||||
|
// Test different precisions.
|
||||||
|
expect(CoreTime.formatTime(31888861, 1)).toEqual('1 year');
|
||||||
|
expect(CoreTime.formatTime(31888861, 3)).toEqual('1 year 4 days 2 hours');
|
||||||
|
expect(CoreTime.formatTime(31888861, 4)).toEqual('1 year 4 days 2 hours 1 min');
|
||||||
|
expect(CoreTime.formatTime(31888861, 5)).toEqual('1 year 4 days 2 hours 1 min 1 sec');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats time in a "short" human readable format', () => {
|
||||||
|
expect(CoreTime.formatTimeShort(0)).toEqual('0\'\'');
|
||||||
|
expect(CoreTime.formatTimeShort(61)).toEqual('1\' 1\'\'');
|
||||||
|
expect(CoreTime.formatTimeShort(7321)).toEqual('122\' 1\'\'');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls a function only once', () => {
|
||||||
|
const testFunction = jest.fn();
|
||||||
|
const onceFunction = CoreTime.once(testFunction);
|
||||||
|
|
||||||
|
expect(testFunction).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
onceFunction('foo', 'bar');
|
||||||
|
expect(testFunction).toHaveBeenCalledWith('foo', 'bar');
|
||||||
|
|
||||||
|
onceFunction('baz');
|
||||||
|
expect(testFunction).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -39,7 +39,7 @@ export class CoreTime {
|
||||||
let remainder = totalSecs - (years * CoreConstants.SECONDS_YEAR);
|
let remainder = totalSecs - (years * CoreConstants.SECONDS_YEAR);
|
||||||
const days = Math.floor(remainder / CoreConstants.SECONDS_DAY);
|
const days = Math.floor(remainder / CoreConstants.SECONDS_DAY);
|
||||||
|
|
||||||
remainder = totalSecs - (days * CoreConstants.SECONDS_DAY);
|
remainder = remainder - (days * CoreConstants.SECONDS_DAY);
|
||||||
|
|
||||||
const hours = Math.floor(remainder / CoreConstants.SECONDS_HOUR);
|
const hours = Math.floor(remainder / CoreConstants.SECONDS_HOUR);
|
||||||
remainder = remainder - (hours * CoreConstants.SECONDS_HOUR);
|
remainder = remainder - (hours * CoreConstants.SECONDS_HOUR);
|
||||||
|
|
|
@ -326,7 +326,7 @@ export class TestsBehatDomUtils {
|
||||||
* @return First found element.
|
* @return First found element.
|
||||||
*/
|
*/
|
||||||
static findElementBasedOnText(locator: TestBehatElementLocator, containerName = ''): HTMLElement {
|
static findElementBasedOnText(locator: TestBehatElementLocator, containerName = ''): HTMLElement {
|
||||||
return this.findElementsBasedOnText(locator, containerName)[0];
|
return this.findElementsBasedOnText(locator, containerName, true)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -334,13 +334,25 @@ export class TestsBehatDomUtils {
|
||||||
*
|
*
|
||||||
* @param locator Element locator.
|
* @param locator Element locator.
|
||||||
* @param containerName Whether to search only inside a specific container.
|
* @param containerName Whether to search only inside a specific container.
|
||||||
|
* @param stopWhenFound Stop looking in containers once an element is found.
|
||||||
* @return Found elements
|
* @return Found elements
|
||||||
*/
|
*/
|
||||||
protected static findElementsBasedOnText(locator: TestBehatElementLocator, containerName = ''): HTMLElement[] {
|
protected static findElementsBasedOnText(
|
||||||
|
locator: TestBehatElementLocator,
|
||||||
|
containerName = '',
|
||||||
|
stopWhenFound = false,
|
||||||
|
): HTMLElement[] {
|
||||||
const topContainers = this.getCurrentTopContainerElements(containerName);
|
const topContainers = this.getCurrentTopContainerElements(containerName);
|
||||||
|
let elements: HTMLElement[] = [];
|
||||||
|
|
||||||
return topContainers.reduce((elements, container) =>
|
for (let i = 0; i < topContainers.length; i++) {
|
||||||
elements.concat(this.findElementsBasedOnTextInContainer(locator, container)), <HTMLElement[]> []);
|
elements = elements.concat(this.findElementsBasedOnTextInContainer(locator, topContainers[i]));
|
||||||
|
if (stopWhenFound && elements.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -357,7 +369,7 @@ export class TestsBehatDomUtils {
|
||||||
let container: HTMLElement | null = topContainer;
|
let container: HTMLElement | null = topContainer;
|
||||||
|
|
||||||
if (locator.within) {
|
if (locator.within) {
|
||||||
const withinElements = this.findElementsBasedOnText(locator.within);
|
const withinElements = this.findElementsBasedOnTextInContainer(locator.within, topContainer);
|
||||||
|
|
||||||
if (withinElements.length === 0) {
|
if (withinElements.length === 0) {
|
||||||
throw new Error('There was no match for within text');
|
throw new Error('There was no match for within text');
|
||||||
|
@ -375,7 +387,7 @@ export class TestsBehatDomUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topContainer && locator.near) {
|
if (topContainer && locator.near) {
|
||||||
const nearElements = this.findElementsBasedOnText(locator.near);
|
const nearElements = this.findElementsBasedOnTextInContainer(locator.near, topContainer);
|
||||||
|
|
||||||
if (nearElements.length === 0) {
|
if (nearElements.length === 0) {
|
||||||
throw new Error('There was no match for near text');
|
throw new Error('There was no match for near text');
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { Observable, Subject } from 'rxjs';
|
||||||
import { sep } from 'path';
|
import { sep } from 'path';
|
||||||
|
|
||||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||||
import { CoreSingletonProxy, Network, Platform } from '@singletons';
|
import { CoreSingletonProxy, Network, Platform, Translate } from '@singletons';
|
||||||
import { CoreTextUtilsProvider } from '@services/utils/text';
|
import { CoreTextUtilsProvider } from '@services/utils/text';
|
||||||
|
|
||||||
import { TranslatePipeStub } from './stubs/pipes/translate';
|
import { TranslatePipeStub } from './stubs/pipes/translate';
|
||||||
|
@ -269,3 +269,14 @@ export function wait(time: number): Promise<void> {
|
||||||
}, time);
|
}, time);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks translate service with certain translations.
|
||||||
|
*
|
||||||
|
* @param translations List of translations.
|
||||||
|
*/
|
||||||
|
export function mockTranslate(translations: Record<string, string>): void {
|
||||||
|
mockSingleton(Translate, {
|
||||||
|
instant: (key) => translations[key] ?? key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue