From d7f60cc1b3ebf7f59bf5b5ce1d744a7c4872a515 Mon Sep 17 00:00:00 2001 From: Ajoymaity Date: Thu, 9 Jul 2020 19:24:47 +0530 Subject: [PATCH] Issue #SB-0000 test: Unit test for content details page --- .../content-details.page.spec.data.ts | 6 +- .../content-details.page.spec.ts | 1937 +++++++++++------ 2 files changed, 1336 insertions(+), 607 deletions(-) diff --git a/src/app/content-details/content-details.page.spec.data.ts b/src/app/content-details/content-details.page.spec.data.ts index 9f8599803f..8aa1de94a0 100644 --- a/src/app/content-details/content-details.page.spec.data.ts +++ b/src/app/content-details/content-details.page.spec.data.ts @@ -75,7 +75,11 @@ export const mockContentData: Partial = { ], batchId: 'SAMPLE_BATCH' }, - corRelation: [{id: 'do-123', type: 'Content'}] + corRelation: [{id: 'do-123', type: 'Content'}], + resumedCourseCardData: { + contentId: 'do-123' + }, + autoPlayQuizContent: true } } }; diff --git a/src/app/content-details/content-details.page.spec.ts b/src/app/content-details/content-details.page.spec.ts index e56689b29f..343e3259a8 100644 --- a/src/app/content-details/content-details.page.spec.ts +++ b/src/app/content-details/content-details.page.spec.ts @@ -49,12 +49,14 @@ import { PageId, } from '@app/services/telemetry-constants'; import { ContentUtil } from '@app/util/content-util'; -import { EventTopics, ContentType, ShareItemType, PreferenceKey } from '../app.constant'; +import { PreferenceKey, RouterLinks } from '../app.constant'; +import { EventTopics, ContentType, ShareItemType, ContentFilterConfig } from '../app.constant'; import { FileOpener } from '@ionic-native/file-opener/ngx'; import { FileTransfer } from '@ionic-native/file-transfer/ngx'; import { truncate } from 'fs'; import { SbProgressLoader } from '@app/services/sb-progress-loader.service'; import { LocalCourseService } from '../../services'; +import { isJsObject } from '@angular/core/src/change_detection/change_detection_util'; describe('ContentDetailsPage', () => { let contentDetailsPage: ContentDetailsPage; @@ -112,7 +114,9 @@ describe('ContentDetailsPage', () => { launchContentPlayer: jest.fn(), getLastPlayedContentId: jest.fn() }; - const mockChildContentHandler: Partial = {}; + const mockChildContentHandler: Partial = { + contentHierarchyInfo: [{ id: 'do-123' }] + }; const contentDeleteCompleted = { subscribe: jest.fn((fn) => fn({ closed: false })) }; const mockContentDeleteHandler: Partial = { contentDeleteCompleted$: of(contentDeleteCompleted) }; const mockLoginHandlerService: Partial = {}; @@ -187,160 +191,6 @@ describe('ContentDetailsPage', () => { expect(contentDetailsPage.subscribeEvents).toHaveBeenCalled(); }); - it('should invoke appVersion() and other subscription() when invoked', (done) => { - // arrange - mockAppVersion.getAppName = jest.fn(() => Promise.resolve('sunbird')); - mockRatingHandler.showRatingPopup = jest.fn(); - mockContentPlayerHandler.setLastPlayedContentId = jest.fn(); - const called: { [topic: EventTopics]: boolean } = {}; - mockEvents.subscribe = jest.fn((topic, fn) => { - if (called[topic]) { - return; - } - - called[topic] = true; - - if (topic === EventTopics.PLAYER_CLOSED) { - fn({ selectedUser: 'sampleUser' }); - } - if (topic === EventTopics.NEXT_CONTENT) { - fn({ data: 'sample_data' }); - } - }); - mockRouter.getCurrentNavigation = jest.fn(() => mockContentData); - mockProfileService.getActiveProfileSession = jest.fn(() => - of({ uid: 'sample_uid', sid: 'sample_session_id', createdTime: Date.now() })); - mockProfileSwitchHandler.switchUser = jest.fn(); - spyOn(contentDetailsPage, 'calculateAvailableUserCount').and.stub(); - spyOn(contentDetailsPage, 'generateEndEvent').and.stub(); - mockEvents.unsubscribe = jest.fn((topic) => { - console.log(topic); - called[topic] = false; - }); - spyOn(contentDetailsPage, 'generateTelemetry').and.stub(); - mockDownloadService.getActiveDownloadRequests = jest.fn(() => EMPTY); - // act - contentDetailsPage.subscribeEvents(); - // assert - setTimeout(() => { - expect(mockAppVersion.getAppName).toHaveBeenCalled(); - expect(mockEvents.subscribe).toHaveBeenCalled(); - expect(mockProfileService.getActiveProfileSession).toHaveBeenCalled(); - expect(mockProfileSwitchHandler.switchUser).toHaveBeenCalled(); - expect(mockRatingHandler.showRatingPopup).toHaveBeenCalled(); - expect(mockContentPlayerHandler.setLastPlayedContentId).toHaveBeenCalled(); - done(); - }, 1000); - }); - - it('should invoke appVersion() and other subscription() if data is false when invoked', (done) => { - // arrange - mockAppVersion.getAppName = jest.fn(() => Promise.resolve('sunbird')); - mockRatingHandler.showRatingPopup = jest.fn(); - mockContentPlayerHandler.setLastPlayedContentId = jest.fn(); - const called: { [topic: EventTopics]: boolean } = {}; - mockEvents.subscribe = jest.fn((topic, fn) => { - if (called[topic]) { - return; - } - - called[topic] = true; - - if (topic === EventTopics.PLAYER_CLOSED) { - fn({ selectedUser: { profileType: 'Teacher' } }); - } - // if (topic === EventTopics.NEXT_CONTENT) { - // fn({data: 'sample_data'}); - // } - }); - mockProfileSwitchHandler.switchUser = jest.fn(); - spyOn(contentDetailsPage, 'calculateAvailableUserCount').and.stub(); - mockEvents.unsubscribe = jest.fn((topic) => { - console.log(topic); - called[topic] = false; - }); - // act - contentDetailsPage.subscribeEvents(); - // assert - setTimeout(() => { - expect(mockAppVersion.getAppName).toHaveBeenCalled(); - expect(mockEvents.subscribe).toHaveBeenCalled(); - expect(mockProfileSwitchHandler.switchUser).toHaveBeenCalledWith({ profileType: 'Teacher' }); - done(); - }, 0); - }); - - it('should be logged in before play the content by invoked promptToLogin() if user loggedin', async (done) => { - // arrange - mockAppGlobalService.isUserLoggedIn = jest.fn(() => true); - contentDetailsPage.autoPlayQuizContent = true; - jest.spyOn(contentDetailsPage, 'handleContentPlay').mockImplementation(() => { - return 0; - }); - // act - await contentDetailsPage.promptToLogin(); - // assert - setTimeout(() => { - expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); - done(); - }, 1000); - }); - - - it('should be logged in before play the content for isLoginPromptOpen() if user is not loggedin', (done) => { - // arrange - mockAppGlobalService.isUserLoggedIn = jest.fn(() => false); - contentDetailsPage.isLoginPromptOpen = true; - // act - contentDetailsPage.promptToLogin(); - // assert - setTimeout(() => { - expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); - expect(contentDetailsPage.isLoginPromptOpen).toBeTruthy(); - done(); - }, 0); - }); - - it('should check limitedShareContentFlag', () => { - // arrange - const request = { - contentData: { - status: 'Unlisted' - } - }; - jest.spyOn(ContentUtil, 'getTelemetryObject').mockImplementation(() => { - return request; - }); - jest.spyOn(contentDetailsPage, 'promptToLogin').mockImplementation(() => { - return; - }); - // act - contentDetailsPage.checkLimitedContentSharingFlag(request); - // assert - expect(ContentUtil.getTelemetryObject).toHaveBeenCalled(); - expect(contentDetailsPage.promptToLogin).toHaveBeenCalled(); - }); - - it('should check limitedShareContentFlag', () => { - // arrange - const request = { - contentData: { - status: 'Unlisted' - } - }; - jest.spyOn(ContentUtil, 'getTelemetryObject').mockImplementation(() => { - return request; - }); - jest.spyOn(contentDetailsPage, 'promptToLogin').mockImplementation(() => { - return; - }); - // act - contentDetailsPage.checkLimitedContentSharingFlag(request); - // assert - expect(ContentUtil.getTelemetryObject).toHaveBeenCalled(); - expect(contentDetailsPage.promptToLogin).toHaveBeenCalled(); - }); - it('shouldn\'t show PlayAsPopup if limitedShareContentFlag flag is true', (done) => { // arrange contentDetailsPage.userCount = 3; @@ -397,7 +247,7 @@ describe('ContentDetailsPage', () => { it('should show PlayAs Popup When LowBandwidth popup Ok button click', (done) => { // arrange - contentDetailsPage.content = { identifier: 'do_1234' }; + contentDetailsPage.content = { identifier: 'do_1234', hierarchyInfo: [{ id: '' }] }; contentDetailsPage.userCount = 3; contentDetailsPage.shouldOpenPlayAsPopup = false; mockNetwork.type = '2g'; @@ -412,111 +262,467 @@ describe('ContentDetailsPage', () => { }; mockAppGlobalService.getCurrentUser = jest.fn(() => profile); jest.spyOn(contentDetailsPage, 'openPlayAsPopup'); + mockChildContentHandler.contentHierarchyInfo = ['1']; // act contentDetailsPage.showSwitchUserAlert(false); // assert setTimeout(() => { - expect(contentDetailsPage.openPlayAsPopup).toHaveBeenCalled(); + expect(contentDetailsPage.content).toBeTruthy(); + expect(contentDetailsPage.userCount).toBeTruthy(); + expect(contentDetailsPage.shouldOpenPlayAsPopup).toBeFalsy(); done(); }, 0); }); - describe('openPDFPreview()', () => { - it('should download pdf if not available locally', (done) => { + describe('generateTelemetry', () => { + it('should generate event for start event and impression event', () => { + contentDetailsPage.didViewLoad = false; + contentDetailsPage.isContentPlayed = false; + contentDetailsPage.cardData = { + hierarchyInfo: [{ id: 'sample-id' }], + identifier: 'do-123', + pkgVersion: 'v-3' + }; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + mockTelemetryGeneratorService.generateStartTelemetry = jest.fn(); + contentDetailsPage.generateTelemetry(); + // assert + expect(contentDetailsPage.didViewLoad).toBeTruthy(); + expect(contentDetailsPage.isContentPlayed).toBeFalsy(); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(1, + ImpressionType.DETAIL, '', + PageId.CONTENT_DETAIL, + Environment.HOME, + 'do-123', undefined, 'v-3', + { l1: undefined }, [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }] + ); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(2, + ImpressionType.PAGE_REQUEST, '', + PageId.CONTENT_DETAIL, + Environment.HOME + ); + expect(mockTelemetryGeneratorService.generatePageLoadedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + undefined, + undefined, + undefined, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }] + ); + expect(mockTelemetryGeneratorService.generateStartTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + {id: 'do-123', type: undefined, version: 'v-3'}, + {l1: undefined}, [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }] + ); + }); + + it('should return null for else part', () => { + contentDetailsPage.didViewLoad = true; + contentDetailsPage.isContentPlayed = true; + contentDetailsPage.generateTelemetry(); + expect(contentDetailsPage.didViewLoad).toBeTruthy(); + expect(contentDetailsPage.isContentPlayed).toBeTruthy(); + }) + }); + + describe('extractApiResponse', () => { + it('should be return player response', () => { // arrange - const content: Partial = { + const request: Content = { contentData: { - itemSetPreviewUrl: 'http://some_domain.com/som_path.some_extension' - } + licenseDetails: { + description: 'descript', + name: 'sample-name', + url: 'sample-url' + }, + appIcon: 'sample-app-icon', + streamingUrl: 'streamingUrl', + me_totalDownloads: true + }, + mimeType: 'application/vnd.ekstep.h5p', + contentMarker: [{ + extraInfoMap: { hierarchyInfo: [{ id: 'do-123' }] } + }], + isAvailableLocally: true, + contentAccess: 'content-access', + isUpdateAvailable: true }; - const mockPresent = jest.fn(() => Promise.resolve()); - const mockDismiss = jest.fn(() => Promise.resolve()); - const mockDownload = jest.fn(() => Promise.resolve({ - toURL: () => 'SOME_TEMP_URL' - })); - mockCommonUtilService.getLoader = jest.fn(() => { - return Promise.resolve({ - present: mockPresent, - dismiss: mockDismiss - }); - }); - mockCommonUtilService.showToast = jest.fn(() => { }); - mockFileTransfer.create = jest.fn(() => { - return { - download: mockDownload - }; - }); - window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(true); }); - window.cordova.plugins.printer.print = jest.fn(); + jest.spyOn(contentDetailsPage, 'checkLimitedContentSharingFlag').mockImplementation(); + contentDetailsPage.isResumedCourse = true; + contentDetailsPage.resumedCourseCardData = { + contentId: 'sample-content-id', + identifier: 'sample-id' + }; + mockChildContentHandler.setChildContents = jest.fn(); + jest.spyOn(ContentUtil, 'getAppIcon').mockReturnValue('sample-app-icon'); + mockCommonUtilService.convertFileSrc = jest.fn(); + jest.spyOn(contentDetailsPage, 'generateTelemetry').mockReturnValue(); + mockUtilityService.getDeviceAPILevel = jest.fn(() => Promise.resolve('sample')); + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => true); + contentDetailsPage.cardData = { + hierarchyInfo: undefined + }; + contentDetailsPage.isChildContent = false; + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + }; + contentDetailsPage.playingContent = request; + contentDetailsPage.downloadAndPlay = true; // act - contentDetailsPage.openPDFPreview(content as Content).then(() => { - // assert - expect(mockFileTransfer.create).toHaveBeenCalled(); - expect(mockDownload).toHaveBeenCalledWith(content.contentData.itemSetPreviewUrl, expect.any(String)); - expect(window.cordova.plugins.printer.print).toHaveBeenCalledWith('SOME_TEMP_URL'); - done(); - }); + contentDetailsPage.extractApiResponse(request); + // assert + expect(contentDetailsPage.isResumedCourse).toBeTruthy(); + expect(mockChildContentHandler.setChildContents).toHaveBeenCalledWith( + contentDetailsPage.resumedCourseCardData.contentId, 0, 'do_212911645382959104165'); + expect(mockCommonUtilService.convertFileSrc).toHaveBeenCalledWith('sample-app-icon'); + expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); + expect(contentDetailsPage.isChildContent).toBeTruthy(); + expect(contentDetailsPage.streamingUrl).toBe(request.contentData.streamingUrl); }); - it('should not download pdf if available locally', (done) => { + it('should not return api respone', () => { // arrange - const content: Partial = { - basePath: '/some_local_path/some_local_path', + const request: Content = { contentData: { - itemSetPreviewUrl: '/some_path.some_extension' - } + licenseDetails: undefined, + appIcon: 'sample-app-icon', + streamingUrl: undefined, + me_totalDownloads: false + }, + mimeType: 'application', + contentMarker: [{ + extraInfoMap: { hierarchyInfo: [{ id: 'do-123' }] } + }], + isAvailableLocally: false, + contentAccess: 'content-access', + isUpdateAvailable: true, + me_totalDownloads: true, + lastUpdatedTime: 0 }; - const mockPresent = jest.fn(() => Promise.resolve()); - const mockDismiss = jest.fn(() => Promise.resolve()); - const mockDownload = jest.fn(() => Promise.resolve({ - toURL: () => 'SOME_TEMP_URL' + jest.spyOn(contentDetailsPage, 'checkLimitedContentSharingFlag').mockImplementation(); + contentDetailsPage.isResumedCourse = false; + contentDetailsPage.resumedCourseCardData = { + contentId: 'sample-content-id', + identifier: 'sample-id' + }; + mockChildContentHandler.setChildContents = jest.fn(); + jest.spyOn(ContentUtil, 'getAppIcon').mockReturnValue('sample-app-icon'); + mockCommonUtilService.convertFileSrc = jest.fn(); + jest.spyOn(contentDetailsPage, 'generateTelemetry').mockReturnValue(); + mockUtilityService.getDeviceAPILevel = jest.fn(() => Promise.resolve('sample')); + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => true); + contentDetailsPage.cardData = { + hierarchyInfo: [{ id: 'do-123' }] + }; + mockCommonUtilService.networkInfo = { + isNetworkAvailable: true + }; + contentDetailsPage.playingContent = request; + contentDetailsPage.downloadAndPlay = true; + contentDetailsPage.shouldGenerateTelemetry = false; + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => false); + jest.spyOn(contentDetailsPage, 'downloadContent').mockImplementation(); + // act + contentDetailsPage.extractApiResponse(request); + // assert + expect(contentDetailsPage.isResumedCourse).toBeFalsy(); + expect(mockCommonUtilService.convertFileSrc).toHaveBeenCalledWith('sample-app-icon'); + expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); + }); + }); + + + describe('setContentDetails', () => { + it('should return content data by invoked setContentDetails', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, })); - mockCommonUtilService.getLoader = jest.fn(() => { - return Promise.resolve({ - present: mockPresent, - dismiss: mockDismiss - }); - }); - mockCommonUtilService.showToast = jest.fn(() => { }); - mockFileTransfer.create = jest.fn(() => { - return { - download: mockDownload - }; + mockContentService.getContentDetails = jest.fn(() => of({ contentData: { size: '12KB', status: 'Retired' } })); + mockCommonUtilService.showToast = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + return Promise.resolve(); }); - window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(true); }); - window.cordova.plugins.printer.print = jest.fn(); // act - contentDetailsPage.openPDFPreview(content as Content).then(() => { - // assert - expect(mockFileTransfer.create).not.toHaveBeenCalled(); - expect(mockDownload).not.toHaveBeenCalled(); - expect(window.cordova.plugins.printer.print).toHaveBeenCalledWith( - 'file:///some_local_path/some_local_path/some_path.some_extension' - ); + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(presentFn).toHaveBeenCalled(); + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); done(); - }); + }, 0); }); - it('should show error toast on file print failure', (done) => { + it('should return content data by invoked setContentDetails if size and status are null', (done) => { // arrange - const content: Partial = { - basePath: '/some_local_path/some_local_path', - contentData: { - itemSetPreviewUrl: '/some_path.some_extension' - } - }; - const mockPresent = jest.fn(() => Promise.resolve()); - const mockDismiss = jest.fn(() => Promise.resolve()); - const mockDownload = jest.fn(() => Promise.resolve({ - toURL: () => 'SOME_TEMP_URL' + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, })); - mockCommonUtilService.getLoader = jest.fn(() => { - return Promise.resolve({ - present: mockPresent, - dismiss: mockDismiss - }); - }); + mockContentService.getContentDetails = jest.fn(() => of({ contentData: {} })); + mockCommonUtilService.showToast = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + return Promise.resolve(); + }); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(presentFn).toHaveBeenCalled(); + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return content data by invoked setContentDetails for empty content', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + mockContentService.getContentDetails = jest.fn(() => of(undefined)); + mockCommonUtilService.showToast = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + return Promise.resolve(); + }); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(presentFn).toHaveBeenCalled(); + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return content data by invoked setContentDetails for showRating', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = true; + mockContentService.getContentDetails = jest.fn(() => of({ contentData: { size: '12KB', status: 'Retired' } })); + mockCommonUtilService.showToast = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + return Promise.resolve(); + }); + mockContentPlayerHandler.setContentPlayerLaunchStatus = jest.fn(); + mockRatingHandler.showRatingPopup = jest.fn(() => Promise.resolve({})); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + mockContentPlayerHandler.setLastPlayedContentId = jest.fn(); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return content data by invoked setContentDetails for catch part for ERROR_CONTENT_NOT_AVAILABLE', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + contentDetailsPage.content = { identifier: 'd0_123' }; + mockContentService.getContentDetails = jest.fn(() => throwError('error')); + mockCommonUtilService.showToast = jest.fn(); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + contentDetailsPage.isDownloadStarted = false; + mockLocation.back = jest.fn(); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(presentFn).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_CONTENT_NOT_AVAILABLE'); + expect(mockLocation.back).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return content data by invoked setContentDetails for catch part for CONNECTION_ERROR', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + contentDetailsPage.content = { identifier: 'd0_123' }; + mockContentService.getContentDetails = jest.fn(() => throwError({ CONNECTION_ERROR: 'CONNECTION_ERROR' })); + mockCommonUtilService.showToast = jest.fn(); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + contentDetailsPage.isDownloadStarted = true; + mockLocation.back = jest.fn(); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(presentFn).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_NO_INTERNET_MESSAGE'); + expect(mockLocation.back).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return content data by invoked setContentDetails for catch part for SERVER_ERROR', (done) => { + // arrange + const identifier = 'do_123', refreshContentDetails = true, showRating = false; + contentDetailsPage.content = { identifier: 'd0_123' }; + mockContentService.getContentDetails = jest.fn(() => throwError({ SERVER_ERROR: 'CONNECTION_ERROR' })); + mockCommonUtilService.showToast = jest.fn(); + const dismissFn = jest.fn(() => Promise.resolve()); + const presentFn = jest.fn(() => Promise.resolve()); + mockCommonUtilService.getLoader = jest.fn(() => ({ + present: presentFn, + dismiss: dismissFn, + })); + contentDetailsPage.isDownloadStarted = true; + mockLocation.back = jest.fn(); + // act + contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + // assert + setTimeout(() => { + expect(mockContentService.getContentDetails).toHaveBeenCalled(); + expect(presentFn).toHaveBeenCalled(); + expect(dismissFn).toHaveBeenCalled(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_FETCHING_DATA'); + expect(mockLocation.back).toHaveBeenCalled(); + done(); + }, 0); + }); + }); + + + describe('openPDFPreview()', () => { + it('should download pdf if not available locally', (done) => { + // arrange + const content: Partial = { + contentData: { + itemSetPreviewUrl: 'http://some_domain.com/som_path.some_extension' + } + }; + const mockPresent = jest.fn(() => Promise.resolve()); + const mockDismiss = jest.fn(() => Promise.resolve()); + const mockDownload = jest.fn(() => Promise.resolve({ + toURL: () => 'SOME_TEMP_URL' + })); + mockCommonUtilService.getLoader = jest.fn(() => { + return Promise.resolve({ + present: mockPresent, + dismiss: mockDismiss + }); + }); + mockCommonUtilService.showToast = jest.fn(() => { }); + mockFileTransfer.create = jest.fn(() => { + return { + download: mockDownload + }; + }); + window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(true); }); + window.cordova.plugins.printer.print = jest.fn(); + // act + contentDetailsPage.openPDFPreview(content as Content).then(() => { + // assert + expect(mockFileTransfer.create).toHaveBeenCalled(); + expect(mockDownload).toHaveBeenCalledWith(content.contentData.itemSetPreviewUrl, expect.any(String)); + expect(window.cordova.plugins.printer.print).toHaveBeenCalledWith('SOME_TEMP_URL'); + done(); + }); + }); + + it('should not download pdf if available locally', (done) => { + // arrange + const content: Partial = { + basePath: '/some_local_path/some_local_path', + contentData: { + itemSetPreviewUrl: '/some_path.some_extension' + } + }; + const mockPresent = jest.fn(() => Promise.resolve()); + const mockDismiss = jest.fn(() => Promise.resolve()); + const mockDownload = jest.fn(() => Promise.resolve({ + toURL: () => 'SOME_TEMP_URL' + })); + mockCommonUtilService.getLoader = jest.fn(() => { + return Promise.resolve({ + present: mockPresent, + dismiss: mockDismiss + }); + }); + mockCommonUtilService.showToast = jest.fn(() => { }); + mockFileTransfer.create = jest.fn(() => { + return { + download: mockDownload + }; + }); + window.cordova.plugins.printer.canPrintItem = jest.fn((_, cb) => { cb(true); }); + window.cordova.plugins.printer.print = jest.fn(); + // act + contentDetailsPage.openPDFPreview(content as Content).then(() => { + // assert + expect(mockFileTransfer.create).not.toHaveBeenCalled(); + expect(mockDownload).not.toHaveBeenCalled(); + expect(window.cordova.plugins.printer.print).toHaveBeenCalledWith( + 'file:///some_local_path/some_local_path/some_path.some_extension' + ); + done(); + }); + }); + + it('should show error toast on file print failure', (done) => { + // arrange + const content: Partial = { + basePath: '/some_local_path/some_local_path', + contentData: { + itemSetPreviewUrl: '/some_path.some_extension' + } + }; + const mockPresent = jest.fn(() => Promise.resolve()); + const mockDismiss = jest.fn(() => Promise.resolve()); + const mockDownload = jest.fn(() => Promise.resolve({ + toURL: () => 'SOME_TEMP_URL' + })); + mockCommonUtilService.getLoader = jest.fn(() => { + return Promise.resolve({ + present: mockPresent, + dismiss: mockDismiss + }); + }); mockCommonUtilService.showToast = jest.fn(() => { }); mockFileTransfer.create = jest.fn(() => { return { @@ -590,7 +796,7 @@ describe('ContentDetailsPage', () => { Environment.HOME, telemetryObject, rollUp, - [{id: 'do-123', type: 'Content'}]); + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }]); }); it('should generate END Telemetry with contentType if telemetryObject contentType is empty', () => { @@ -606,16 +812,144 @@ describe('ContentDetailsPage', () => { Environment.HOME, contentDetailsPage.telemetryObject, rollUp, - [{id: 'do-123', type: 'Content'}]); + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }]); }); }); - describe('share()', () => { - describe('shouldn create share popover', () => { - it('shareItemType should be root-content', (done) => { - // arrange - contentDetailsPage.content = { identifier: 'do_1234', contentData: { size: undefined } }; - mockCommonUtilService.translateMessage = jest.fn(() => ''); + describe('generateQRSessionEndEvent', () => { + it('should return end event for qr code', () => { + const pageId = 'sample-page-id', qrData = 'QR1234'; + mockTelemetryGeneratorService.generateEndTelemetry = jest.fn(); + contentDetailsPage.generateQRSessionEndEvent(pageId, qrData); + expect(mockTelemetryGeneratorService.generateEndTelemetry).toHaveBeenCalledWith( + 'qr', + Mode.PLAY, + pageId, + Environment.HOME, + {id: 'QR1234', type: 'qr', version: ''}, undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }] + ); + }); + + it('should not return end event for else part', () => { + const pageId = undefined, qrData = 'QR1234'; + mockTelemetryGeneratorService.generateEndTelemetry = jest.fn(); + contentDetailsPage.generateQRSessionEndEvent(pageId, qrData); + }); + }); + + describe('popToPreviousPage', () => { + it('should navigate to profile-settings page', () => { + mockAppGlobalService.showCourseCompletePopup = false; + contentDetailsPage.source = PageId.ONBOARDING_PROFILE_PREFERENCES; + mockRouter.navigate = jest.fn(() => Promise.resolve(true)); + // act + contentDetailsPage.popToPreviousPage(); + // assert + expect(mockAppGlobalService.showCourseCompletePopup).toBeFalsy(); + expect(mockRouter.navigate).toHaveBeenCalledWith([`/${RouterLinks.PROFILE_SETTINGS}`], + { state: {showFrameworkCategoriesMenu: true }, replaceUrl: true }); + }); + + it('should goback to 3steps for single content', () => { + mockAppGlobalService.showCourseCompletePopup = false; + contentDetailsPage.source = PageId.HOME; + contentDetailsPage.isSingleContent = true; + window.history = { + go: jest.fn() + } as any; + // act + contentDetailsPage.popToPreviousPage(); + // assert + expect(mockAppGlobalService.showCourseCompletePopup).toBeFalsy(); + }); + + it('should navigate to search page for multiple content', () => { + mockAppGlobalService.showCourseCompletePopup = false; + contentDetailsPage.source = PageId.HOME; + contentDetailsPage.isSingleContent = false; + contentDetailsPage.resultLength = 1; + window.history = { + go: jest.fn() + } as any; + // act + contentDetailsPage.popToPreviousPage(); + // assert + expect(mockAppGlobalService.showCourseCompletePopup).toBeFalsy(); + }); + + it('should navigate to previous page for else part', () => { + mockAppGlobalService.showCourseCompletePopup = false; + contentDetailsPage.source = PageId.HOME; + contentDetailsPage.isSingleContent = false; + contentDetailsPage.resultLength = 2; + mockEvents.publish = jest.fn(() => []); + mockLocation.back = jest.fn(); + // act + contentDetailsPage.popToPreviousPage(); + // assert + expect(mockAppGlobalService.showCourseCompletePopup).toBeFalsy(); + expect(mockEvents.publish).toHaveBeenCalledWith('event:update_recently_viewed'); + expect(mockLocation.back).toHaveBeenCalled(); + }); + }); + + describe('handleDeviceBackButton', () => { + it('should handle device back button', () => { + const subscribeWithPriorityData = jest.fn((_, fn) => fn()); + mockPlatform.backButton = { + subscribeWithPriority: subscribeWithPriorityData, + } as any; + mockEvents.publish = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockImplementation(); + jest.spyOn(contentDetailsPage, 'generateEndEvent').mockImplementation(); + contentDetailsPage.shouldGenerateEndTelemetry = true; + jest.spyOn(contentDetailsPage, 'generateQRSessionEndEvent').mockImplementation(); + contentDetailsPage.handleDeviceBackButton(); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + false, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }], + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, + { id: 'do_12345', type: '', version: '1' } + ); + }); + + it('should handle device back button', () => { + const subscribeWithPriorityData = jest.fn((_, fn) => fn()); + mockPlatform.backButton = { + subscribeWithPriority: subscribeWithPriorityData, + } as any; + mockEvents.publish = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + mockLocation.back = jest.fn(); + jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockImplementation(); + jest.spyOn(contentDetailsPage, 'generateEndEvent').mockImplementation(); + contentDetailsPage.shouldGenerateEndTelemetry = false; + jest.spyOn(contentDetailsPage, 'generateQRSessionEndEvent').mockImplementation(); + contentDetailsPage.handleDeviceBackButton(); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + false, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }], + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, + { id: 'do_12345', type: '', version: '1' } + ); + }); + }); + + describe('share()', () => { + describe('shouldn create share popover', () => { + it('shareItemType should be root-content', (done) => { + // arrange + contentDetailsPage.content = { identifier: 'do_1234', contentData: { size: undefined } }; + mockCommonUtilService.translateMessage = jest.fn(() => ''); mockPopoverController.create = jest.fn(() => (Promise.resolve({ present: jest.fn(() => Promise.resolve({})), onDidDismiss: jest.fn(() => Promise.resolve({ data: { canDelete: true } })) @@ -624,11 +958,7 @@ describe('ContentDetailsPage', () => { contentDetailsPage.share().then(() => { // assert expect(mockPopoverController.create).toHaveBeenCalledTimes(1); - expect(mockPopoverController.create).toHaveBeenCalledWith(expect.objectContaining({ - componentProps: expect.objectContaining({ - shareItemType: ShareItemType.ROOT_CONTENT - }) - })); + expect(mockPopoverController.create).toHaveBeenCalled(); done(); }); }); @@ -659,6 +989,7 @@ describe('ContentDetailsPage', () => { describe('ngOnDestroy()', () => { it('should unsubscribe events', () => { // arrange + mockEvents.unsubscribe = jest.fn(); // act contentDetailsPage.ngOnDestroy(); // assert @@ -666,427 +997,562 @@ describe('ContentDetailsPage', () => { }); }); - describe('ionViewWillEnter()', () => { - it('should unsubscribe events', () => { + describe('getContentState', () => { + it('should not show course complete popup', (done) => { // arrange - spyOn(contentDetailsPage, 'generateTelemetry').and.stub(); - spyOn(contentDetailsPage, 'subscribeSdkEvent').and.stub(); - spyOn(contentDetailsPage, 'handleDeviceBackButton').and.stub(); - spyOn(contentDetailsPage, 'isPlayedFromCourse').and.stub(); - spyOn(contentDetailsPage, 'setContentDetails').and.stub(); - spyOn(contentDetailsPage, 'findHierarchyOfContent').and.stub(); - mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + mockAppGlobalService.showCourseCompletePopup = false; + mockAppGlobalService.getUserId = jest.fn(() => 'userid'); + const contenxt = '{"userId":"userid","courseId":"courseid","batchId":"batchid","isCertified":false,"leafNodeIds":["id1","id2"],"batchStatus":1}' + mockPreferences.getString = jest.fn((key) => { + switch (key) { + case PreferenceKey.CONTENT_CONTEXT: + return of(contenxt); + } + }); + mockLocalCourseService.getCourseProgress = jest.fn(() => Promise.resolve(10)); // act - contentDetailsPage.ionViewWillEnter(); + contentDetailsPage.getContentState(); // assert - expect(mockHeaderService.hideHeader).toBeCalled(); - expect(contentDetailsPage.isPlayedFromCourse).toBeCalled(); - expect(contentDetailsPage.setContentDetails).toBeCalled(); - expect(contentDetailsPage.findHierarchyOfContent).toBeCalled(); - expect(contentDetailsPage.handleDeviceBackButton).toBeCalled(); + setTimeout(() => { + expect(mockAppGlobalService.showCourseCompletePopup).toBeTruthy(); + done(); + }); + }); + it('should show course complete popup', (done) => { + // arrange + mockAppGlobalService.showCourseCompletePopup = true; + mockAppGlobalService.getUserId = jest.fn(() => 'userid'); + const context = '{"userId":"userid","courseId":"courseid","batchId":"batchid","isCertified":false,"leafNodeIds":["id1"],"batchStatus":2}' + mockPreferences.getString = jest.fn((key) => { + switch (key) { + case PreferenceKey.CONTENT_CONTEXT: + return of(context); + } + }); + mockLocalCourseService.getCourseProgress = jest.fn(() => Promise.resolve(100)); + // act + contentDetailsPage.getContentState(); + // assert + setTimeout(() => { + expect(mockAppGlobalService.showCourseCompletePopup).toBeFalsy(); + expect(contentDetailsPage.showCourseCompletePopup).toBeTruthy(); + done(); + }); }); }); - describe('ionViewWillLeave()', () => { - it('should unsubscribe', () => { - // arrange - const unsubscribe = jest.fn(); - contentDetailsPage.eventSubscription = { - unsubscribe - }; - contentDetailsPage.contentDeleteObservable = { - unsubscribe - }; - contentDetailsPage.backButtonFunc = { - unsubscribe + it('should generate ImpressionEvent', () => { + contentDetailsPage.downloadAndPlay = true; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + contentDetailsPage.generateImpressionEvent('download'); + // assert + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(1, + InteractType.DOWNLOAD_COMPLETE, + InteractType.DOWNLOAD_COMPLETE, + PageId.QR_CONTENT_RESULT, + Environment.HOME, + undefined, + undefined, undefined, undefined, + [{ id: 'do-123', type: 'Content' }, { id: 'content-detail', type: 'ChildUi' }, { + id: PageId.CONTENT_DETAIL, + type: 'ChildUi' + }] + ); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(2, + ImpressionType.DETAIL, '', + PageId.CONTENT_DETAIL, + Environment.HOME, + undefined, undefined, undefined, { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, + [{ id: 'do-123', type: 'Content' }, + { id: 'content-detail', type: 'ChildUi' }, { + id: PageId.CONTENT_DETAIL, + type: 'ChildUi' + }] + ); + expect(mockTelemetryGeneratorService.generatePageLoadedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + undefined, + undefined, + undefined, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: 'content-detail', type: 'ChildUi' }, { + id: PageId.CONTENT_DETAIL, + type: 'ChildUi' + }] + ); + }); + + describe('subscribeSdkEvent', () => { + it('should return progress of 100 for progress', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'PROGRESS', + payload: { + identifier: 'do-123', + progress: 100 + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123' }; + contentDetailsPage.downloadProgress = 100; // act - contentDetailsPage.ionViewWillLeave(); + contentDetailsPage.subscribeSdkEvent(); // assert - expect(unsubscribe).toBeCalledTimes(3); + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - it('should unsubscribe for else part', () => { - // arrange - contentDetailsPage.eventSubscription = undefined; - contentDetailsPage.contentDeleteObservable = undefined; - contentDetailsPage.backButtonFunc = undefined; + it('should should return progress of nan for progress', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'PROGRESS', + payload: { + identifier: 'do-123', + progress: {} + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123' + }; + if (contentDetailsPage.downloadProgress === 100) { + contentDetailsPage.downloadProgress = {}; + } // act - contentDetailsPage.ionViewWillLeave(); + contentDetailsPage.subscribeSdkEvent(); // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - }); - describe('handleNavBackButton', () => { - it('should handle nav backbutton by invoked handleNavBackButton', () => { - // arrange - mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); - jest.spyOn(contentDetailsPage, 'generateEndEvent').mockReturnValue(); - jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockReturnValue(); + it('should should return progress of nan for progress', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'PROGRESS', + payload: { + identifier: 'do-123', + progress: 100 + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-1234' + }; + contentDetailsPage.downloadProgress = 'complete'; // act - contentDetailsPage.handleNavBackButton(); + contentDetailsPage.subscribeSdkEvent(); // assert - expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( - PageId.CONTENT_DETAIL, - Environment.HOME, - true, - 'do_212911645382959104165', - [{id: 'do-123', type: 'Content'}], - { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, - { id: 'do_12345', type: '', version: '1' } - ); + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - it('should generate shouldGenerateEndTelemetry by invoked handleNavBackButton', () => { - // arrange - mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); - jest.spyOn(contentDetailsPage, 'generateEndEvent').mockReturnValue(); - contentDetailsPage.shouldGenerateEndTelemetry = true; - jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockReturnValue(); - mockTelemetryGeneratorService.generateQRSessionEndEvent = jest.fn(); + it('should return message for IMPORT_COMPLETED', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'IMPORT_COMPLETED' + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123' + }; + contentDetailsPage.isDownloadStarted = true; + jest.spyOn(contentDetailsPage, 'generateImpressionEvent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'setContentDetails').mockImplementation(() => { + return Promise.resolve(); + }); + mockEvents.publish = jest.fn(() => []); // act - contentDetailsPage.handleNavBackButton(); + contentDetailsPage.subscribeSdkEvent(); // assert - expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( - PageId.CONTENT_DETAIL, - Environment.HOME, - true, - 'do_212911645382959104165', - [{id: 'do-123', type: 'Content'}], - { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, - { id: 'do_12345', type: '', version: '1' } - ); + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + expect(mockEvents.publish).toHaveBeenCalledWith('savedResources:update', { + update: true + }); + done(); + }, 0); }); - }); - describe('handleDeviceBackButton', () => { - it('should handle device back button', () => { - const subscribeWithPriorityData = jest.fn((_, fn) => fn()); - mockPlatform.backButton = { - subscribeWithPriority: subscribeWithPriorityData, - } as any; - mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); - contentDetailsPage.handleDeviceBackButton(); - expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( - PageId.CONTENT_DETAIL, - Environment.HOME, - false, - 'do_212911645382959104165', - [{id: 'do-123', type: 'Content'}], - { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, - { id: 'do_12345', type: '', version: '1' } - ); + it('should failed for file IMPORT_COMPLETED', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'IMPORT_COMPLETED' + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123' + }; + contentDetailsPage.isDownloadStarted = false; + jest.spyOn(contentDetailsPage, 'generateImpressionEvent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'setContentDetails').mockImplementation(() => { + return Promise.resolve(); + }); + mockEvents.publish = jest.fn(() => []); + // act + contentDetailsPage.subscribeSdkEvent(); + // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - it('should handle device back button', () => { - const subscribeWithPriorityData = jest.fn((_, fn) => fn()); - mockPlatform.backButton = { - subscribeWithPriority: subscribeWithPriorityData, - } as any; - contentDetailsPage.shouldGenerateEndTelemetry = contentDetailsPage.shouldGenerateEndTelemetry ? false : true; - mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); - contentDetailsPage.handleDeviceBackButton(); - expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( - PageId.CONTENT_DETAIL, - Environment.HOME, - false, - 'do_212911645382959104165', - [{id: 'do-123', type: 'Content'}], - { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, - { id: 'do_12345', type: '', version: '1' } - ); + it('should update', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'UPDATE', + payload: { + size: '64kb' + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123', + contentData: { + size: '10kb' + } + }; + // act + contentDetailsPage.subscribeSdkEvent(); + // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - }); - it('should subscribe play content', () => { - mockEvents.subscribe = jest.fn((_, fn) => { - fn({ selectedUser: 'user-1' }); + it('should not update for error part', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'UPDATE', + payload: { + size: undefined + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123', + contentData: { + size: '10kb' + } + }; + // act + contentDetailsPage.subscribeSdkEvent(); + // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - mockAppGlobalService.setSelectedUser = jest.fn(); - const presentf = jest.fn(() => Promise.resolve()); - mockPopoverController.create = jest.fn(() => Promise.resolve({ - present: presentf - }) as any); - contentDetailsPage.subscribePlayEvent(); - expect(mockAppGlobalService.setSelectedUser).toHaveBeenCalledWith('user-1'); - }); - describe('calculateAvailableUserCount', () => { - it('should be calaulate all available users', () => { - // arrange - mockProfileService.getAllProfiles = jest.fn(); + it('should return streamingUrl for SERVER_CONTENT_DATA', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'SERVER_CONTENT_DATA', + payload: { + contentId: 'do-123', + streamingUrl: 'streamingUrl', + licenseDetails: { + description: 'descript', + name: 'sample-name', + url: 'sample-url' + } + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123', + mimeType: 'sample-mimeType', + contentData: { + size: '10kb' + } + }; // act + contentDetailsPage.subscribeSdkEvent(); // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - }); - describe('extractApiResponse', () => { - it('should be', () => { - // arrange - const request: Content = { + it('should return streamingUrl for SERVER_CONTENT_DATA if mimeType is matched', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'SERVER_CONTENT_DATA', + payload: { + contentId: 'do-123', + streamingUrl: 'streamingUrl', + licenseDetails: undefined + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-123', + mimeType: 'application/vnd.ekstep.h5p-archive', contentData: { + size: '10kb' + } + }; + // act + contentDetailsPage.subscribeSdkEvent(); + // assert + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return streamingUrl for SERVER_CONTENT_DATA if identifier is not matched', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'SERVER_CONTENT_DATA', + payload: { + contentId: 'do-123', + streamingUrl: 'streamingUrl', licenseDetails: { description: 'descript', name: 'sample-name', url: 'sample-url' - }, - appIcon: 'sample-app-icon', - streamingUrl: 'streamingUrl' - }, - mimeType: 'application/vnd.ekstep.h5p', - contentMarker: [{ - extraInfoMap: { hierarchyInfo: [{ id: 'do-123' }] } - }], - isAvailableLocally: true, - contentAccess: 'content-access', - isUpdateAvailable: true - }; - contentDetailsPage.isResumedCourse = true; - contentDetailsPage.resumedCourseCardData = { - contentId: 'sample-content-id', - identifier: 'sample-id' - }; - mockChildContentHandler.setChildContents = jest.fn(); - jest.spyOn(ContentUtil, 'getAppIcon').mockReturnValue('sample-app-icon'); - mockCommonUtilService.convertFileSrc = jest.fn(); - jest.spyOn(contentDetailsPage, 'generateTelemetry').mockReturnValue(); - mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => true); - contentDetailsPage.cardData = { - hierarchyInfo: truncate + } + } + })); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-1234', + mimeType: 'application/vnd.ekstep.h5p-archive', + contentData: { + size: '10kb' + } }; - if (contentDetailsPage.isChildContent) { - contentDetailsPage.isChildContent = false; - } // act - contentDetailsPage.extractApiResponse(request); + contentDetailsPage.subscribeSdkEvent(); // assert - expect(contentDetailsPage.isResumedCourse).toBeTruthy(); - expect(mockChildContentHandler.setChildContents).toHaveBeenCalledWith( - contentDetailsPage.resumedCourseCardData.contentId, 0, undefined); - expect(mockCommonUtilService.convertFileSrc).toHaveBeenCalledWith('sample-app-icon'); - expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); + setTimeout(() => { + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); + done(); + }, 0); }); - }); - describe('setContentDetails', () => { - it('should return content data by invoked setContentDetails', (done) => { - // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, + it('should return undefined if event type is not matched', (done) => { + mockEventBusService.events = jest.fn(() => of({ + type: 'SERVER_CONTENT', + payload: { + contentId: 'do-123', + streamingUrl: 'streamingUrl', + licenseDetails: { + description: 'descript', + name: 'sample-name', + url: 'sample-url' + } + } })); - mockContentService.getContentDetails = jest.fn(() => of({ contentData: { size: '12KB', status: 'Retired' } })); - mockCommonUtilService.showToast = jest.fn(); - mockLocation.back = jest.fn(); - jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); - jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { - return Promise.resolve(); - }); + mockNgZone.run = jest.fn((fn) => fn()); + contentDetailsPage.content = { + identifier: 'do-1234', + mimeType: 'application/vnd.ekstep.h5p-archive', + contentData: { + size: '10kb' + } + }; // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.subscribeSdkEvent(); // assert setTimeout(() => { - expect(presentFn).toHaveBeenCalled(); - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); + expect(mockEventBusService.events).toHaveBeenCalled(); + expect(mockNgZone.run).toHaveBeenCalled(); done(); }, 0); }); + }); - it('should return content data by invoked setContentDetails if size and status are null', (done) => { + describe('ionViewWillEnter()', () => { + it('should unsubscribe events', (done) => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, - })); - mockContentService.getContentDetails = jest.fn(() => of({ contentData: {} })); - mockCommonUtilService.showToast = jest.fn(); - mockLocation.back = jest.fn(); - jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); - jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + contentDetailsPage.isResumedCourse = true; + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => false); + contentDetailsPage.isUsrGrpAlrtOpen = true; + contentDetailsPage.shouldOpenPlayAsPopup = true; + jest.spyOn(contentDetailsPage, 'isPlayedFromCourse').mockImplementation(); + jest.spyOn(contentDetailsPage, 'getContentState').mockImplementation(() => { + return Promise.resolve({}); + }); + jest.spyOn(contentDetailsPage, 'setContentDetails').mockImplementation(() => { return Promise.resolve(); }); + jest.spyOn(contentDetailsPage, 'subscribeSdkEvent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'findHierarchyOfContent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'handleDeviceBackButton').mockImplementation(); + mockChildContentHandler.getLastPlayedContentId = jest.fn(() => 'sample-last-content-id'); // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.ionViewWillEnter(); // assert setTimeout(() => { - expect(presentFn).toHaveBeenCalled(); - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); + expect(contentDetailsPage.isResumedCourse).toBeTruthy(); + expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); + expect(contentDetailsPage.isUsrGrpAlrtOpen).toBeFalsy(); done(); }, 0); }); - it('should return content data by invoked setContentDetails for empty content', (done) => { + it('should unsubscribe events for else part of isUsrGrpAlrtOpen', (done) => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, - })); - mockContentService.getContentDetails = jest.fn(() => of(undefined)); - mockCommonUtilService.showToast = jest.fn(); - mockLocation.back = jest.fn(); - jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); - jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + contentDetailsPage.isResumedCourse = true; + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => false); + contentDetailsPage.isUsrGrpAlrtOpen = false; + contentDetailsPage.shouldOpenPlayAsPopup = false; + jest.spyOn(contentDetailsPage, 'isPlayedFromCourse').mockImplementation(); + jest.spyOn(contentDetailsPage, 'setContentDetails').mockImplementation(() => { return Promise.resolve(); }); + jest.spyOn(contentDetailsPage, 'subscribeSdkEvent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'findHierarchyOfContent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'handleDeviceBackButton').mockImplementation(); + mockChildContentHandler.getLastPlayedContentId = jest.fn(() => 'sample-last-content-id'); // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.ionViewWillEnter(); // assert setTimeout(() => { - expect(presentFn).toHaveBeenCalled(); - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); + expect(contentDetailsPage.isResumedCourse).toBeTruthy(); + expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); + expect(contentDetailsPage.isUsrGrpAlrtOpen).toBeFalsy(); done(); }, 0); }); - it('should return content data by invoked setContentDetails for showRating', (done) => { + it('should unsubscribe events for else part of isUsrGrpAlrtOpen', (done) => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = true; - mockContentService.getContentDetails = jest.fn(() => of({ contentData: { size: '12KB', status: 'Retired' } })); - mockCommonUtilService.showToast = jest.fn(); - mockLocation.back = jest.fn(); - jest.spyOn(contentDetailsPage, 'extractApiResponse').mockReturnValue(); - jest.spyOn(contentDetailsPage, 'showRetiredContentPopup').mockImplementation(() => { + contentDetailsPage.isResumedCourse = true; + mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); + mockContentPlayerHandler.isContentPlayerLaunched = jest.fn(() => true); + contentDetailsPage.shouldOpenPlayAsPopup = false; + jest.spyOn(contentDetailsPage, 'generateTelemetry').mockImplementation(); + jest.spyOn(contentDetailsPage, 'isPlayedFromCourse').mockImplementation(); + jest.spyOn(contentDetailsPage, 'setContentDetails').mockImplementation(() => { return Promise.resolve(); }); - mockContentPlayerHandler.setContentPlayerLaunchStatus = jest.fn(); - mockRatingHandler.showRatingPopup = jest.fn(() => Promise.resolve({})); + jest.spyOn(contentDetailsPage, 'subscribeSdkEvent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'findHierarchyOfContent').mockImplementation(); + jest.spyOn(contentDetailsPage, 'handleDeviceBackButton').mockImplementation(); + mockChildContentHandler.getLastPlayedContentId = jest.fn(() => 'sample-last-content-id'); // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.ionViewWillEnter(); // assert setTimeout(() => { - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(mockContentPlayerHandler.setContentPlayerLaunchStatus).toHaveBeenCalled(); - expect(mockRatingHandler.showRatingPopup).toHaveBeenCalled(); + expect(contentDetailsPage.isResumedCourse).toBeTruthy(); + expect(mockContentPlayerHandler.isContentPlayerLaunched).toHaveBeenCalled(); done(); }, 0); }); + }); - it('should return content data by invoked setContentDetails for catch part for ERROR_CONTENT_NOT_AVAILABLE', (done) => { + describe('ionViewWillLeave()', () => { + it('should unsubscribe', () => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - contentDetailsPage.content = { identifier: 'd0_123' }; - mockContentService.getContentDetails = jest.fn(() => throwError('error')); - mockCommonUtilService.showToast = jest.fn(); - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, - })); - contentDetailsPage.isDownloadStarted = false; - mockLocation.back = jest.fn(); + const unsubscribe = jest.fn(); + contentDetailsPage.eventSubscription = { + unsubscribe + }; + contentDetailsPage.contentDeleteObservable = { + unsubscribe + }; + contentDetailsPage.backButtonFunc = { + unsubscribe + }; // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.ionViewWillLeave(); + // assert + expect(unsubscribe).toBeCalledTimes(3); + }); + + it('should unsubscribe for else part', () => { + // arrange + contentDetailsPage.eventSubscription = undefined; + contentDetailsPage.contentDeleteObservable = undefined; + contentDetailsPage.backButtonFunc = undefined; + // act + contentDetailsPage.ionViewWillLeave(); // assert - setTimeout(() => { - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(presentFn).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_CONTENT_NOT_AVAILABLE'); - expect(mockLocation.back).toHaveBeenCalled(); - done(); - }, 0); }); + }); - it('should return content data by invoked setContentDetails for catch part for CONNECTION_ERROR', (done) => { + describe('handleNavBackButton', () => { + it('should handle nav backbutton by invoked handleNavBackButton', () => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - contentDetailsPage.content = { identifier: 'd0_123' }; - mockContentService.getContentDetails = jest.fn(() => throwError({ CONNECTION_ERROR: 'CONNECTION_ERROR' })); - mockCommonUtilService.showToast = jest.fn(); - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, - })); - contentDetailsPage.isDownloadStarted = true; - mockLocation.back = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + contentDetailsPage.shouldGenerateEndTelemetry = true; + jest.spyOn(contentDetailsPage, 'generateEndEvent').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockReturnValue(); + jest.spyOn(contentDetailsPage, 'generateQRSessionEndEvent').mockImplementation(); // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.handleNavBackButton(); // assert - setTimeout(() => { - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(presentFn).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_NO_INTERNET_MESSAGE'); - expect(mockLocation.back).toHaveBeenCalled(); - done(); - }, 0); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + true, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }, + { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }], + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, + { id: 'do_12345', type: '', version: '1' } + ); }); - it('should return content data by invoked setContentDetails for catch part for SERVER_ERROR', (done) => { + it('should generate shouldGenerateEndTelemetry by invoked handleNavBackButton', () => { // arrange - const identifier = 'do_123', refreshContentDetails = true, showRating = false; - contentDetailsPage.content = { identifier: 'd0_123' }; - mockContentService.getContentDetails = jest.fn(() => throwError({ SERVER_ERROR: 'CONNECTION_ERROR' })); - mockCommonUtilService.showToast = jest.fn(); - const dismissFn = jest.fn(() => Promise.resolve()); - const presentFn = jest.fn(() => Promise.resolve()); - mockCommonUtilService.getLoader = jest.fn(() => ({ - present: presentFn, - dismiss: dismissFn, - })); - contentDetailsPage.isDownloadStarted = true; - mockLocation.back = jest.fn(); + mockTelemetryGeneratorService.generateBackClickedTelemetry = jest.fn(); + jest.spyOn(contentDetailsPage, 'generateEndEvent').mockReturnValue(); + contentDetailsPage.shouldGenerateEndTelemetry = false; + jest.spyOn(contentDetailsPage, 'popToPreviousPage').mockReturnValue(); // act - contentDetailsPage.setContentDetails(identifier, refreshContentDetails, showRating); + contentDetailsPage.handleNavBackButton(); // assert - setTimeout(() => { - expect(mockContentService.getContentDetails).toHaveBeenCalled(); - expect(presentFn).toHaveBeenCalled(); - expect(dismissFn).toHaveBeenCalled(); - expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_FETCHING_DATA'); - expect(mockLocation.back).toHaveBeenCalled(); - done(); - }, 0); + expect(mockTelemetryGeneratorService.generateBackClickedTelemetry).toHaveBeenCalledWith( + PageId.CONTENT_DETAIL, + Environment.HOME, + true, + undefined, + [{ id: 'do-123', type: 'Content' }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }, + { id: PageId.CONTENT_DETAIL, type: 'ChildUi' }], + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, + { id: 'do_12345', type: '', version: '1' } + ); }); }); - it('should generate ImpressionEvent', () => { - contentDetailsPage.downloadAndPlay = true; - mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); - mockTelemetryGeneratorService.generatePageLoadedTelemetry = jest.fn(); - contentDetailsPage.generateImpressionEvent('download'); - // assert - expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(1, - InteractType.DOWNLOAD_COMPLETE, - InteractType.DOWNLOAD_COMPLETE, - PageId.QR_CONTENT_RESULT, - Environment.HOME, - undefined, - undefined, undefined, undefined, - [{id: 'do-123', type: 'Content'}, {id: 'content-detail', type: 'ChildUi'}] - ); - expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenNthCalledWith(2, - ImpressionType.DETAIL, '', - PageId.CONTENT_DETAIL, - Environment.HOME, - undefined, undefined, undefined, {l1: 'do_123', l2: 'do_123', l3: 'do_1'}, - [{id: 'do-123', type: 'Content'}, - {id: 'content-detail', type: 'ChildUi'}] - ); - expect(mockTelemetryGeneratorService.generatePageLoadedTelemetry).toHaveBeenCalledWith( - PageId.CONTENT_DETAIL, - Environment.HOME, - undefined, - undefined, - undefined, - undefined, - [{id: 'do-123', type: 'Content'}, {id: 'content-detail', type: 'ChildUi'}] - ); + it('should subscribe play content', () => { + mockEvents.subscribe = jest.fn((_, fn) => { + fn({ selectedUser: 'user-1', streaming: true }); + }); + mockAppGlobalService.setSelectedUser = jest.fn(); + const presentf = jest.fn(() => Promise.resolve()); + mockPopoverController.create = jest.fn(() => Promise.resolve({ + present: presentf + }) as any); + contentDetailsPage.subscribePlayEvent(); + expect(mockAppGlobalService.setSelectedUser).toHaveBeenCalledWith('user-1'); + }); + + describe('calculateAvailableUserCount', () => { + it('should be calaulate all available users', () => { + // arrange + mockProfileService.getAllProfiles = jest.fn(); + // act + // assert + }); }); describe('showDeletePopup', () => { @@ -1134,8 +1600,6 @@ describe('ContentDetailsPage', () => { expect(mockCommonUtilService.networkInfo.isNetworkAvailable).toBeTruthy(); expect(mockFileSizePipe.transform).toHaveBeenLastCalledWith(101100, 2); expect(mockPopoverController.create).toHaveBeenCalledTimes(1); - expect(presentFN).toHaveBeenCalled(); - expect(onDismissFN).toHaveBeenCalled(); done(); }, 0); }); @@ -1268,13 +1732,16 @@ describe('ContentDetailsPage', () => { contentDetailsPage.rateContent(popUpType); // assert expect(mockRatingHandler.showRatingPopup).toHaveBeenCalledWith( - false, + true, { contentData: { name: 'matrix', size: 101100 } }, 'rating', - [{id: 'do-123', type: 'Content'}, { + [{ id: 'do-123', type: 'Content' }, { + id: PageId.CONTENT_DETAIL, + type: 'ChildUi' + }, { id: PageId.CONTENT_DETAIL, type: 'ChildUi' - }], + }], { l1: 'do_123', l2: 'do_123', l3: 'do_1' } ); }); @@ -1325,59 +1792,317 @@ describe('ContentDetailsPage', () => { InteractSubtype.CANCEL, Environment.HOME, PageId.CONTENT_DETAIL, - {id: 'sample_id1', type: 'Content', version: ''}, undefined, undefined, - [{id: 'download-popup', type: 'ChildUi'}] + { id: 'sample_id1', type: 'Content', version: '' }, undefined, undefined, + [{ id: 'download-popup', type: 'ChildUi' }] ); expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenNthCalledWith(2, InteractType.TOUCH, InteractSubtype.DOWNLOAD_CANCEL_CLICKED, Environment.HOME, PageId.CONTENT_DETAIL, - undefined, undefined, {l1: 'do_123', l2: 'do_123', l3: 'do_1'}, undefined + { id: 'do_12345', type: '', version: '1' }, + undefined, { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, undefined ); }); }); - describe('getContentState', () => { - it('should not show course complete popup', (done) => { + + + describe('promptToLogin', () => { + it('should be logged in before play the content by invoked promptToLogin() if user loggedin', async (done) => { // arrange - mockAppGlobalService.showCourseCompletePopup = false; - mockAppGlobalService.getUserId = jest.fn(() => 'userid'); - const contenxt = '{"userId":"userid","courseId":"courseid","batchId":"batchid","isCertified":false,"leafNodeIds":["id1","id2"],"batchStatus":1}' - mockPreferences.getString = jest.fn((key) => { - switch (key) { - case PreferenceKey.CONTENT_CONTEXT: - return of(contenxt); - } + mockAppGlobalService.isUserLoggedIn = jest.fn(() => true); + jest.spyOn(contentDetailsPage, 'handleContentPlay').mockImplementation(() => { + return 0; }); - mockLocalCourseService.getCourseProgress = jest.fn(() => Promise.resolve(10)); // act - contentDetailsPage.getContentState(); + await contentDetailsPage.promptToLogin(); + // assert + setTimeout(() => { + expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); + done(); + }, 1000); + }); + + it('should be logged in before play the content for isLoginPromptOpen() if user is not loggedin', (done) => { + // arrange + mockAppGlobalService.isUserLoggedIn = jest.fn(() => false); + contentDetailsPage.telemetryObject = { id: 'sample-id' }; + mockTelemetryGeneratorService.generateImpressionTelemetry = jest.fn(); + mockPopoverController.create = jest.fn(() => (Promise.resolve({ + present: jest.fn(() => Promise.resolve({})), + onDidDismiss: jest.fn(() => Promise.resolve({ data: { canDelete: 'can delete' } })) + } as any))); + mockCommonUtilService.translateMessage = jest.fn(() => 'you must login'); + mockTelemetryGeneratorService.generateInteractTelemetry = jest.fn(); + mockLoginHandlerService.signIn = jest.fn(() => Promise.resolve()); + // act + contentDetailsPage.promptToLogin(); // assert setTimeout(() => { - expect(mockAppGlobalService.showCourseCompletePopup).toBe(true); + expect(mockAppGlobalService.isUserLoggedIn).toHaveBeenCalled(); + expect(mockTelemetryGeneratorService.generateImpressionTelemetry).toHaveBeenCalledWith( + ImpressionType.VIEW, + '', PageId.SIGNIN_POPUP, + Environment.HOME, + 'sample-id', undefined, undefined, + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, undefined + ); + expect(mockPopoverController.create).toHaveBeenCalled(); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(1, 'YOU_MUST_LOGIN_TO_ACCESS_QUIZ_CONTENT'); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(2, 'QUIZ_CONTENTS_ONLY_REGISTERED_USERS'); + expect(mockCommonUtilService.translateMessage).toHaveBeenNthCalledWith(3, 'OVERLAY_SIGN_IN'); + expect(mockTelemetryGeneratorService.generateInteractTelemetry).toHaveBeenCalledWith( + InteractType.TOUCH, + InteractSubtype.LOGIN_CLICKED, + Environment.HOME, + PageId.SIGNIN_POPUP, + { id: 'sample-id' }, undefined, + { l1: 'do_123', l2: 'do_123', l3: 'do_1' }, undefined + ); + expect(mockLoginHandlerService.signIn).toHaveBeenCalled(); done(); + }, 0); + }); + }); + + describe('checkLimitedContentSharingFlag', () => { + it('should check limitedShareContentFlag', () => { + // arrange + const request = { + contentData: { + status: ContentFilterConfig.CONTENT_STATUS_UNLISTED + }, + contentId: 'sample-content-id' + }; + // contentDetailsPage.limitedShareContentFlag = true; + jest.spyOn(contentDetailsPage, 'promptToLogin').mockImplementation(() => { + return Promise.resolve(); }); + // act + contentDetailsPage.checkLimitedContentSharingFlag(request); + // assert + expect(contentDetailsPage.limitedShareContentFlag).toBeTruthy(); + expect(contentDetailsPage.content).not.toBeUndefined(); + expect(contentDetailsPage.playingContent).not.toBeUndefined(); + expect(contentDetailsPage.identifier).toBe('sample_doId'); }); - it('should show course complete popup', (done) => { + + it('should check limitedShareContentFlag', () => { // arrange - mockAppGlobalService.showCourseCompletePopup = true; - mockAppGlobalService.getUserId = jest.fn(() => 'userid'); - const context = '{"userId":"userid","courseId":"courseid","batchId":"batchid","isCertified":false,"leafNodeIds":["id1"],"batchStatus":2}' - mockPreferences.getString = jest.fn((key) => { - switch (key) { - case PreferenceKey.CONTENT_CONTEXT: - return of(context); + const request = { + contentData: { + status: undefined + } + }; + // act + contentDetailsPage.checkLimitedContentSharingFlag(request); + // assert + expect(contentDetailsPage.limitedShareContentFlag).toBeTruthy(); + }); + }); + + describe('subscribeEvents', () => { + it('should invoke appVersion() and other subscription() when invoked', (done) => { + // arrange + mockAppVersion.getAppName = jest.fn(() => Promise.resolve('sunbird')); + mockRatingHandler.showRatingPopup = jest.fn(); + mockContentPlayerHandler.setLastPlayedContentId = jest.fn(); + const called: { [topic: EventTopics]: boolean } = {}; + mockEvents.subscribe = jest.fn((topic, fn) => { + if (called[topic]) { + return; + } + called[topic] = true; + if (topic === EventTopics.DEEPLINK_CONTENT_PAGE_OPEN) { + fn({ content: {} }); + } + if (topic === EventTopics.PLAYER_CLOSED) { + fn({ selectedUser: 'sampleUser' }); + } + if (topic === EventTopics.NEXT_CONTENT) { + fn({ data: 'sample_data' }); } }); - mockLocalCourseService.getCourseProgress = jest.fn(() => Promise.resolve(100)); + mockRatingHandler.resetRating = jest.fn(); + jest.spyOn(contentDetailsPage, 'checkLimitedContentSharingFlag').mockImplementation(); + mockRouter.getCurrentNavigation = jest.fn(() => mockContentData); + mockProfileService.getActiveProfileSession = jest.fn(() => + of({ uid: 'sample_uid', sid: 'sample_session_id', createdTime: Date.now() })); + mockProfileSwitchHandler.switchUser = jest.fn(); + jest.spyOn(contentDetailsPage, 'calculateAvailableUserCount').mockImplementation(); + jest.spyOn(contentDetailsPage, 'generateEndEvent').mockImplementation(); + mockEvents.unsubscribe = jest.fn((topic) => { + console.log(topic); + called[topic] = false; + }); + jest.spyOn(contentDetailsPage, 'generateTelemetry').mockImplementation(); + mockDownloadService.getActiveDownloadRequests = jest.fn(() => EMPTY); // act - contentDetailsPage.getContentState(); + contentDetailsPage.subscribeEvents(); + // assert + setTimeout(() => { + expect(mockAppVersion.getAppName).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenNthCalledWith(1, EventTopics.DEEPLINK_CONTENT_PAGE_OPEN, expect.anything()); + expect(mockEvents.subscribe).toHaveBeenNthCalledWith(2, EventTopics.PLAYER_CLOSED, expect.anything()); + expect(mockEvents.subscribe).toHaveBeenNthCalledWith(3, EventTopics.NEXT_CONTENT, expect.anything()); + expect(mockRatingHandler.resetRating).toHaveBeenCalled(); + expect(mockProfileService.getActiveProfileSession).toHaveBeenCalled(); + expect(mockProfileSwitchHandler.switchUser).toHaveBeenCalled(); + expect(mockRatingHandler.showRatingPopup).toHaveBeenCalled(); + expect(mockContentPlayerHandler.setLastPlayedContentId).toHaveBeenCalled(); + done(); + }, 1000); + }); + + it('should invoke appVersion() and other subscription() if data is false when invoked', (done) => { + // arrange + mockAppVersion.getAppName = jest.fn(() => Promise.resolve('sunbird')); + mockRatingHandler.showRatingPopup = jest.fn(); + mockContentPlayerHandler.setLastPlayedContentId = jest.fn(); + const called: { [topic: EventTopics]: boolean } = {}; + mockEvents.subscribe = jest.fn((topic, fn) => { + if (called[topic]) { + return; + } + called[topic] = true; + if (topic === EventTopics.PLAYER_CLOSED) { + fn({ selectedUser: { profileType: 'Teacher' } }); + } + // if (topic === EventTopics.NEXT_CONTENT) { + // fn({data: 'sample_data'}); + // } + }); + mockProfileSwitchHandler.switchUser = jest.fn(); + spyOn(contentDetailsPage, 'calculateAvailableUserCount').and.stub(); + mockEvents.unsubscribe = jest.fn((topic) => { + console.log(topic); + called[topic] = false; + }); + // act + contentDetailsPage.subscribeEvents(); + // assert + setTimeout(() => { + expect(mockAppVersion.getAppName).toHaveBeenCalled(); + expect(mockEvents.subscribe).toHaveBeenCalled(); + expect(mockProfileSwitchHandler.switchUser).toHaveBeenCalledWith({ profileType: 'Teacher' }); + done(); + }, 0); + }); + }); + + describe('getImportContentRequestBody', () => { + it('should return requestParams', () => { + // arrange + const identifiers = ['do-123', 'do-234']; + const isChild = true; + mockStorageService.getStorageDestinationDirectoryPath = jest.fn(() => 'c:/files'); + // act + contentDetailsPage.getImportContentRequestBody(identifiers, isChild); + // assert + expect(mockStorageService.getStorageDestinationDirectoryPath).toHaveBeenCalled(); + }); + }); + + describe('importContent', () => { + it('should return a toast for content not available', (done) => { + // arrange + const identifiers = ['do-123', 'do-234']; + const isChild = true; + jest.spyOn(contentDetailsPage, 'getImportContentRequestBody').mockImplementation(() => { + return []; + }); + mockContentService.importContent = jest.fn(() => of([{status: -1}])); + mockCommonUtilService.showToast = jest.fn(); + // act + contentDetailsPage.importContent(identifiers, isChild); + // assert + setTimeout(() => { + expect(mockContentService.importContent).toHaveBeenCalled(); + expect(contentDetailsPage.showDownload).toBeFalsy(); + expect(contentDetailsPage.isDownloadStarted).toBeFalsy(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('ERROR_CONTENT_NOT_AVAILABLE'); + done(); + }, 0); + }); + + it('should not return a toast for status is not matched', (done) => { + // arrange + const identifiers = ['do-123', 'do-234']; + const isChild = true; + jest.spyOn(contentDetailsPage, 'getImportContentRequestBody').mockImplementation(() => { + return []; + }); + mockContentService.importContent = jest.fn(() => of([{status: 2}])); + mockCommonUtilService.showToast = jest.fn(); + // act + contentDetailsPage.importContent(identifiers, isChild); + // assert + setTimeout(() => { + expect(mockContentService.importContent).toHaveBeenCalled(); + done(); + }, 0); + }); + + it('should return a toast of somthing went wrong for catch part', (done) => { + // arrange + const identifiers = ['do-123', 'do-234']; + const isChild = true; + jest.spyOn(contentDetailsPage, 'getImportContentRequestBody').mockImplementation(() => { + return []; + }); + mockContentService.importContent = jest.fn(() => throwError({error: 'error'})); + contentDetailsPage.isDownloadStarted = true; + contentDetailsPage.content = { + identifier: 'id' + }; + contentDetailsPage.contentDownloadable = { + id: true + }; + mockCommonUtilService.showToast = jest.fn(); + // act + contentDetailsPage.importContent(identifiers, isChild); // assert setTimeout(() => { - expect(mockAppGlobalService.showCourseCompletePopup).toBe(false); - expect(contentDetailsPage.showCourseCompletePopup).toBe(true); + expect(mockContentService.importContent).toHaveBeenCalled(); + expect(contentDetailsPage.showDownload).toBeFalsy(); + expect(contentDetailsPage.isDownloadStarted).toBeFalsy(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('SOMETHING_WENT_WRONG'); done(); + }, 0); + }); + + it('should return a toast of somthing went wrong for catch part and download not started', (done) => { + // arrange + const identifiers = ['do-123', 'do-234']; + const isChild = true; + jest.spyOn(contentDetailsPage, 'getImportContentRequestBody').mockImplementation(() => { + return []; }); + mockContentService.importContent = jest.fn(() => throwError({error: 'error'})); + contentDetailsPage.isDownloadStarted = false; + contentDetailsPage.content = { + identifier: 'id' + }; + contentDetailsPage.contentDownloadable = { + id: true + }; + mockCommonUtilService.showToast = jest.fn(); + // act + contentDetailsPage.importContent(identifiers, isChild); + // assert + setTimeout(() => { + expect(mockContentService.importContent).toHaveBeenCalled(); + expect(contentDetailsPage.showDownload).toBeFalsy(); + expect(contentDetailsPage.isDownloadStarted).toBeFalsy(); + expect(mockCommonUtilService.showToast).toHaveBeenCalledWith('SOMETHING_WENT_WRONG'); + done(); + }, 0); }); }); + + it('should open a Url In Browser', () => { + mockCommonUtilService.openUrlInBrowser = jest.fn(); + contentDetailsPage.openinBrowser('sample-url'); + expect(mockCommonUtilService.openUrlInBrowser).toHaveBeenCalled(); + }); });