diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index 3afcd69a2..48ee9a60c 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -32,6 +32,8 @@ import { DomSanitizer } from '@singletons'; }) export class CoreIframeComponent implements OnChanges { + static loadingTimeout = 15000; + @ViewChild('iframe') iframe?: ElementRef; @Input() src?: string; @Input() iframeWidth?: string; @@ -43,7 +45,6 @@ export class CoreIframeComponent implements OnChanges { safeUrl?: SafeResourceUrl; displayHelp = false; - protected readonly IFRAME_TIMEOUT = 15000; protected logger: CoreLogger; protected initialized = false; @@ -89,7 +90,7 @@ export class CoreIframeComponent implements OnChanges { if (this.loading) { setTimeout(() => { this.loading = false; - }, this.IFRAME_TIMEOUT); + }, CoreIframeComponent.loadingTimeout); } } diff --git a/src/core/components/tests/iframe.test.ts b/src/core/components/tests/iframe.test.ts index 2b80c90c3..75a0c754d 100644 --- a/src/core/components/tests/iframe.test.ts +++ b/src/core/components/tests/iframe.test.ts @@ -12,8 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreIframeComponent } from '@components/iframe/iframe'; + +import { renderTemplate } from '@/testing/utils'; + describe('CoreIframeComponent', () => { - it.todo('should render'); + it('should render', async () => { + // Arrange. + CoreIframeComponent.loadingTimeout = 0; + + // Act. + const { nativeElement } = await renderTemplate( + CoreIframeComponent, + '', + ); + + // Assert. + expect(nativeElement.innerHTML.trim()).not.toHaveLength(0); + + const iframe = nativeElement.querySelector('iframe'); + expect(iframe).not.toBeNull(); + expect(iframe.src).toEqual('https://moodle.org/'); + }); }); diff --git a/src/core/components/tests/user-avatar.test.ts b/src/core/components/tests/user-avatar.test.ts index a8d933837..7b4c5f12a 100644 --- a/src/core/components/tests/user-avatar.test.ts +++ b/src/core/components/tests/user-avatar.test.ts @@ -12,8 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreUserAvatarComponent } from '@components/user-avatar/user-avatar'; + +import { renderComponent } from '@/testing/utils'; + describe('CoreUserAvatarComponent', () => { - it.todo('should render'); + it('should render', async () => { + // Act. + const { nativeElement } = await renderComponent(CoreUserAvatarComponent); + + // Assert. + expect(nativeElement.innerHTML.trim()).not.toHaveLength(0); + + const image = nativeElement.querySelector('img'); + expect(image).not.toBeNull(); + expect(image.src).toEqual(document.location.href + 'assets/img/user-avatar.png'); + }); }); diff --git a/src/core/directives/tests/format-text.test.ts b/src/core/directives/tests/format-text.test.ts index 3bfc5afa8..aea7a5ded 100644 --- a/src/core/directives/tests/format-text.test.ts +++ b/src/core/directives/tests/format-text.test.ts @@ -13,34 +13,29 @@ // limitations under the License. import { IonContent } from '@ionic/angular'; -import { NgZone } from '@angular/core'; import Faker from 'faker'; import { CoreConfig } from '@services/config'; -import { CoreDomUtils, CoreDomUtilsProvider } from '@services/utils/dom'; +import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreFilepool } from '@services/filepool'; +import { CoreFilter } from '@features/filter/services/filter'; +import { CoreFilterHelper } from '@features/filter/services/filter-helper'; import { CoreFormatTextDirective } from '@directives/format-text'; import { CoreSite } from '@classes/site'; import { CoreSites } from '@services/sites'; -import { CoreUrlUtils, CoreUrlUtilsProvider } from '@services/utils/url'; -import { CoreUtils, CoreUtilsProvider } from '@services/utils/utils'; -import { Platform } from '@singletons'; +import { CoreUtils } from '@services/utils/utils'; -import { mock, mockSingleton, RenderConfig, renderWrapperComponent } from '@/testing/utils'; -import { CoreFilter } from '@features/filter/services/filter'; -import { CoreApp } from '@services/app'; +import { mock, mockSingleton, RenderConfig, renderTemplate, renderWrapperComponent } from '@/testing/utils'; describe('CoreFormatTextDirective', () => { let config: Partial; beforeEach(() => { - mockSingleton(Platform, { ready: () => Promise.resolve() }); + mockSingleton(CoreSites, { getSite: () => Promise.reject() }); mockSingleton(CoreConfig, { get: (_, defaultValue) => defaultValue }); - - CoreDomUtils.setInstance(new CoreDomUtilsProvider()); - CoreUrlUtils.setInstance(new CoreUrlUtilsProvider()); - CoreUtils.setInstance(new CoreUtilsProvider(mock())); + mockSingleton(CoreFilter, { formatText: text => Promise.resolve(text) }); + mockSingleton(CoreFilterHelper, { getFiltersAndFormatText: text => Promise.resolve({ text, filters: [] }) }); config = { providers: [ @@ -53,9 +48,6 @@ describe('CoreFormatTextDirective', () => { // Arrange const sentence = Faker.lorem.sentence(); - mockSingleton(CoreSites, { getSite: () => Promise.reject() }); - mockSingleton(CoreFilter, { formatText: (text) => Promise.resolve(text) }); - // Act const fixture = await renderWrapperComponent( CoreFormatTextDirective, @@ -70,6 +62,63 @@ describe('CoreFormatTextDirective', () => { expect(text.innerHTML).toEqual(sentence); }); + it('should format text', async () => { + // Arrange + mockSingleton(CoreFilter, { formatText: () => 'Formatted text' }); + + // Act + const { nativeElement } = await renderTemplate( + CoreFormatTextDirective, + '', + ); + + // Assert + const text = nativeElement.querySelector('core-format-text'); + expect(text).not.toBeNull(); + expect(text.textContent).toEqual('Formatted text'); + + expect(CoreFilter.formatText).toHaveBeenCalledTimes(1); + expect(CoreFilter.formatText).toHaveBeenCalledWith( + 'Lorem ipsum dolor', + expect.anything(), + expect.anything(), + undefined, + ); + }); + + it('should get filters from server and format text', async () => { + // Arrange + mockSingleton(CoreFilterHelper, { + getFiltersAndFormatText: () => Promise.resolve({ + text: 'Formatted text', + filters: [], + }), + }); + + // Act + const { nativeElement } = await renderTemplate(CoreFormatTextDirective, ` + + `); + + // Assert + const text = nativeElement.querySelector('core-format-text'); + expect(text).not.toBeNull(); + expect(text.textContent).toEqual('Formatted text'); + + expect(CoreFilterHelper.getFiltersAndFormatText).toHaveBeenCalledTimes(1); + expect(CoreFilterHelper.getFiltersAndFormatText).toHaveBeenCalledWith( + 'Lorem ipsum dolor', + 'course', + 42, + expect.anything(), + undefined, + ); + }); + it('should use external-content directive on images', async () => { // Arrange const site = mock({ @@ -86,11 +135,9 @@ describe('CoreFormatTextDirective', () => { getSite: () => Promise.resolve(site), getCurrentSite: () => Promise.resolve(site), }); - mockSingleton(CoreFilter, { formatText: (text) => Promise.resolve(text) }); - mockSingleton(CoreApp, { isMobile: () => false }); // Act - const fixture = await renderWrapperComponent( + const { nativeElement } = await renderWrapperComponent( CoreFormatTextDirective, 'core-format-text', { text: '', siteId: site.getId() }, @@ -98,7 +145,7 @@ describe('CoreFormatTextDirective', () => { ); // Assert - const image = fixture.nativeElement.querySelector('img'); + const image = nativeElement.querySelector('img'); expect(image).not.toBeNull(); expect(image.src).toEqual('file://local-path/'); @@ -106,10 +153,28 @@ describe('CoreFormatTextDirective', () => { expect(CoreFilepool.getSrcByUrl).toHaveBeenCalledTimes(1); }); - it.todo('should format text'); + it('should use link directive on anchors', async () => { + // Arrange + mockSingleton(CoreContentLinksHelper, { handleLink: () => Promise.resolve(true) }); - it.todo('should get filters from server and format text'); + // Act + const { nativeElement } = await renderWrapperComponent( + CoreFormatTextDirective, + 'core-format-text', + { text: 'Link' }, + ); + const anchor = nativeElement.querySelector('a'); - it.todo('should use link directive on anchors'); + anchor.click(); + + // Assert + expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledTimes(1); + expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledWith( + 'https://anchor-url/', + undefined, + expect.anything(), + expect.anything(), + ); + }); }); diff --git a/src/core/directives/tests/link.test.ts b/src/core/directives/tests/link.test.ts index 00bdbab0f..891df6111 100644 --- a/src/core/directives/tests/link.test.ts +++ b/src/core/directives/tests/link.test.ts @@ -13,8 +13,9 @@ // limitations under the License. import { CoreLinkDirective } from '@directives/link'; +import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; -import { renderTemplate } from '@/testing/utils'; +import { mockSingleton, renderTemplate } from '@/testing/utils'; describe('CoreLinkDirective', () => { @@ -33,6 +34,28 @@ describe('CoreLinkDirective', () => { expect(anchor.href).toEqual('https://moodle.org/'); }); - it.todo('should capture clicks'); + it('should capture clicks', async () => { + // Arrange + mockSingleton(CoreContentLinksHelper, { handleLink: () => Promise.resolve(true) }); + + // Act + const { nativeElement } = await renderTemplate( + CoreLinkDirective, + 'Link', + ); + + const anchor = nativeElement.querySelector('a'); + + anchor.click(); + + // Assert + expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledTimes(1); + expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledWith( + 'https://moodle.org/', + undefined, + expect.anything(), + expect.anything(), + ); + }); });