// (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 { IonContent } from '@ionic/angular'; import Faker from 'faker'; import { CoreConfig } from '@services/config'; 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/sites/site'; import { CoreSites } from '@services/sites'; import { CorePromiseUtils } from '@singletons/promise-utils'; import { mock, mockSingleton, RenderConfig, renderTemplate, renderWrapperComponent } from '@/testing/utils'; import { ContextLevel } from '@/core/constants'; describe('CoreFormatTextDirective', () => { let config: Partial; beforeEach(() => { mockSingleton(CoreSites, { getSite: () => Promise.reject() }); mockSingleton(CoreConfig, { get(name, defaultValue) { if (defaultValue === undefined) { throw Error(`Default value not provided for '${name}'`); } return Promise.resolve(defaultValue); }, }); mockSingleton(CoreFilter, { formatText: text => Promise.resolve(text) }); mockSingleton(CoreFilterHelper, { getFiltersAndFormatText: text => Promise.resolve({ text, filters: [] }) }); config = { providers: [ { provide: IonContent, useValue: null }, ], }; }); it('should render', async () => { // Arrange const sentence = Faker.lorem.sentence(); // Act const fixture = await renderWrapperComponent( CoreFormatTextDirective, 'core-format-text', { text: sentence }, config, ); // Assert const text = fixture.nativeElement.querySelector('core-format-text'); expect(text).not.toBeNull(); expect(text?.innerHTML).toEqual(sentence); }); it('should format text', async () => { // Arrange mockSingleton(CoreFilter, { formatText: () => Promise.resolve('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 const site = mock(new CoreSite('25', 'https://mysite.com', 'token'), { getId: () => site.id, }); mockSingleton(CoreSites, { getSite: () => Promise.resolve(site), getCurrentSite: () => site, getCurrentSiteId: () => site.id, }); // 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', ContextLevel.COURSE, 42, expect.anything(), '25', ); }); it('should use external-content directive on images', async () => { // Arrange const site = mock(new CoreSite('42', 'https://mysite.com', 'token'), { canDownloadFiles: () => true, }); // @todo this is done because we cannot mock image being loaded, we should find an alternative... CorePromiseUtils.timeoutPromise = () => Promise.resolve(null as unknown as T); mockSingleton(CoreFilepool, { getSrcByUrl: () => Promise.resolve('file://local-path') }); mockSingleton(CoreSites, { getSite: () => Promise.resolve(site), getCurrentSite: () => site, }); // Act const { nativeElement } = await renderWrapperComponent( CoreFormatTextDirective, 'core-format-text', { text: '', siteId: site.getId() }, config, ); // Assert const image = nativeElement.querySelector('img'); expect(image).not.toBeNull(); expect(image?.src).toEqual('file://local-path/'); expect(CoreSites.getSite).toHaveBeenCalledWith(site.getId()); expect(CoreFilepool.getSrcByUrl).toHaveBeenCalledTimes(1); }); it('should use link directive on anchors', async () => { // Arrange mockSingleton(CoreContentLinksHelper, { handleLink: () => Promise.resolve(true) }); // Act const { nativeElement } = await renderWrapperComponent( CoreFormatTextDirective, 'core-format-text', { text: 'Link' }, ); const anchor = nativeElement.querySelector('a'); anchor?.click(); // Assert expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledTimes(1); expect(CoreContentLinksHelper.handleLink).toHaveBeenCalledWith( 'https://anchor-url/', undefined, expect.anything(), expect.anything(), ); }); });